# JV データ取得 — 完全手順（Windows + 新本番 Linux）

**これ1本で足りるように書いた。** バッチの順番・成功の見え方・よく出るエラーを全部入れる。

- 新本番: **163.44.117.9** / `/var/www/html/tools.kachiumaai.com`
- 成功パターン（ログ根拠）: [ops_jv_ACTUAL_success_2026-05.md](./ops_jv_ACTUAL_success_2026-05.md)
- **Windows コマンドの貼り方（必読）:** [ops_windows_cmd_guidelines.md](./ops_windows_cmd_guidelines.md) — cmd に1ブロックで貼る・`python -c` で SQL `||` 禁止

---

## 0. 全体像（何がどこで動くか）

```
[あなたの Windows PC]
  ┌─ JV-Link 公式インストール（JRA-VAN）
  │    └─ Windows サービス「JVLinkAgent」… COM ワーカー（ここが死ぬと全部失敗）
  ├─ JVLinkServer.exe … TCP 8765（pyjvlink 同梱）
  └─ ssh -N -R 18765:127.0.0.1:8765 root@163.44.117.9
        （この cmd 窓は閉じない）

[新本番 Linux]
  fetch_jv_raw.py --linux-tunnel-tcp --host 127.0.0.1 --port 18765
    → MySQL keiba_ai.jv_raw_records に INSERT
```

| 部品 | 役割 | 動いてる確認 |
|------|------|----------------|
| **JV-Link 本体** | JRA-VAN 契約・利用キー | `C:\Program Files (x86)\JRA-VAN` がある |
| **JVLinkAgent** | COM ワーカー | `services.msc` で **Running** |
| **JVLinkServer.exe** | Python から JV に触る HTTP/TCP サーバー | `netstat` で **8765 LISTENING** |
| **SSH 逆トンネル** | Linux の 18765 → PC の 8765 | Linux で `jv_link_tcp_healthcheck.py` が **OK** |
| **fetch（Linux）** | 生データを DB に保存 | `jv_raw_records` の件数が増える |

**重要:** データは **Linux 側の DB** に入る。Windows の `1-JV取得-2019から一括.bat` だけだと **PC の MySQL** に入り、新本番とズレることがある。

---

## ★ 作業の前提（必読・2026-06 追記）

**「この手順が正しい」という前提で進めない。**

| やること | 理由 |
|----------|------|
| **目的は `races`（JRA）が年約3,400件前後**（[JRA 企業情報の開催レース数](https://www.jra.go.jp/)と照合） | raw 百万件でも races が 27 件なら **失敗** |
| **パラメータは試して結果を残す** | option / from / to の正解は環境・契約で変わる。決めつけ禁止 |
| **1年ずつ・小さく・確認してから次** | 全年一括は時間の無駄になりやすい |
| **うまくいった手順だけ「正規」に昇格** | 試行ログ: `docs/ops_jv_fetch_param_results.md` |

### 推奨スクリプト（この順）

```bash
cd /var/www/html/tools.kachiumaai.com

# 1) トンネルだけ（数十件）
./scripts/jv_smoke_test.sh 20240101 50

# 2) パラメータ試行（各 max 30 件・結果を Markdown に追記）
./scripts/jv_fetch_param_probe.sh 2020 30

# 3) 2020年だけ本番取得（setup4→3→1 を順に試し、増分正規化・件数判定）
./scripts/jv_fetch_one_year.sh 2020

# 4) 2020 の races が ~3400 前後になったら 2021 へ（同じ 3) を 2021 で）
```

**完了の判定（これ以外で「終わった」と言わない）:**

```sql
SELECT YEAR(race_date) y, COUNT(*) c
FROM races WHERE circuit='JRA' AND y BETWEEN 2020 AND 2026
GROUP BY y ORDER BY y;
-- 各年がおおよそ 3,000〜3,500 前後（JRA 中央競馬の年間レース数）
```

---

### PowerShell でバッチを叩くとき（毎回出るエラー）

**PowerShell** では `jvlink_start.bat` だけだと **CommandNotFoundException** になる（現在フォルダは自動で探さない）。

```powershell
cd C:\work\ai-horse-race.maspis.com.gitclone\scripts\windows
.\jvlink_start.bat
.\fix_sid_and_start_jvlink.bat
.\step01_start_tunnel.bat
```

エクスプローラで **ダブルクリック** なら `.\` は不要。`cmd.exe` でも `jvlink_start.bat` だけで動く。

`scripts\windows\` に `_load-env.bat` や `jvlink_start.bat` が無いと、別の「見つかりません」になる。`git pull` で最新を取る。

---

## 1. 初回だけ（PC）

### 1-1. JV-Link インストール

1. ブラウザで開く: https://dl.cdn.jra-van.ne.jp/datalab/JV-Link/web/JV-Link.exe  
2. インストール完了まで進める  
3. スタートメニュー **「JV-Link」設定** を開き、**利用キー** を入力して保存  
   - **利用キー ≠ ソフトウェアID（SID）**（別物）

### 1-2. Python + pyjvlink

```bat
py -3.11 -m pip install pyjvlink
```

`JVLinkServer.exe` の既定パス:

```text
%USERPROFILE%\AppData\Local\Python\pythoncore-3.11-64\Lib\site-packages\pyjvlink\lib\JVLinkServer.exe
```

### 1-3. リポジトリ + 設定ファイル

PC に clone（例）:

```text
C:\work\ai-horse-race.maspis.com
```

**ダブルクリック（1回）:**

```text
scripts\windows\step00_first_setup.bat
```

→ `jv-windows.local.env` ができる。中身の例:

```ini
REPO_ROOT=C:\work\ai-horse-race.maspis.com
SERVER_SSH=root@163.44.117.9
SERVER_PATH=/var/www/html/tools.kachiumaai.com
JVLINK_SID=UNKNOWN
JVLINK_SERVER_PORT=8765
JV_BACKFILL_FROM_YEAR=2020
JV_BACKFILL_TO_YEAR=2026
```

- **`JVLINK_SID=UNKNOWN`** … 2026-04-30 に成功していたパターン。`7UJC-...` で worker unreachable になる PC がある  
- 本番の SID が分かっていて動くならその値でもよい

### 1-4. SSH 鍵（任意・パスワード入力を減らす）

```text
scripts\windows\setup_ssh_key.bat
```

---

## 2. 毎回の手順（取得するとき）

### チェックリスト（印刷用）

| # | やること | 成功の見え方 |
|---|----------|----------------|
| A | JVLinkAgent が動いている | `services.msc` → **Running** |
| B | JVLinkServer 8765 | `netstat -an \| findstr 8765` → **LISTENING** |
| C | SSH トンネル | 窓が開いたまま・エラー連打なし |
| D | Linux healthcheck | `OK jv_tcp ... 18765` |
| E | raw 件数 | `SELECT COUNT(*) FROM jv_raw_records` が増える |

---

### ステップ A — JV-Link を動かす（Windows）

**おすすめ（管理者で）:**

1. `scripts\windows\fix_sid_and_start_jvlink.bat` を **右クリック → 管理者として実行**  
2. 最後に **`[OK] port 8765 LISTENING`** が出る

**または:**

- `scripts\windows\jvlink_start.bat` を **管理者として実行**  
- 窓に **`supervisor started on port 8765`** または **port 8765 is listening**  
- **この窓は閉じない**

#### スクショの赤エラーについて（よくある）

```
Restart-Service : Cannot open JVLinkAgent service on computer '.'
```

| 意味 | 対処 |
|------|------|
| **管理者権限がない** PowerShell からサービスを再起動できない | バッチを **管理者として実行**。または下の手動操作 |
| **そのあと `[OK] port 8765 LISTENING` が出ている** | **Agent 再起動は失敗していても Server は動いている** → **step01（トンネル）に進んでよい** |
| 8765 が LISTENING にならない | 下記「worker unreachable」 |

**手動で Agent を直す:**

1. `Win + R` → `services.msc`  
2. **JVLinkAgent** を探す → **再起動**（なければ JV-Link の再インストール）  
3. もう一度 `jvlink_start.bat`（管理者）

#### worker unreachable

- 意味: **JVLinkServer は起動したが、JV-Link COM が応答しない**  
- 詳細: `scripts\windows\FIX_worker_unreachable.txt`  
- 診断: `diagnose_jvlink.bat`  
- ログ: `%USERPROFILE%\...\pyjvlink\lib\logs\JVLinkServer_*.log`

---

### ステップ B — トンネル（Windows・2窓目）

**ダブルクリック:**

```text
scripts\windows\step01_start_tunnel.bat
```

開く窓:

1. **JVLinkServer-8765**（A で既に開いていれば重複 OK）  
2. **SSH-reverse-18765** … **何も表示されないまま待機 = 正常。閉じない**

中身は実質:

```bat
ssh -N -R 18765:127.0.0.1:8765 root@163.44.117.9
```

---

### ステップ C — 新本番で接続確認（Windows）

```text
scripts\windows\step02_check_new_server.bat
```

**成功:**

```text
OK jv_tcp host=127.0.0.1 port=18765 connect_ms=...
```

**失敗 `Connection refused`:**

- step01 の **2窓** を確認  
- JVLinkServer が落ちていないか `netstat` で 8765 確認

**失敗 `python3.11: command not found`:**

- トンネル自体の失敗ではない。**step02 が古い**（サーバーは `python3` のみのことが多い）  
- `git pull` 後に step02 を再実行。手動確認:  
  `ssh root@163.44.117.9 "cd /var/www/html/tools.kachiumaai.com && python3 scripts/jv_link_tcp_healthcheck.py"`

---

### ステップ D — 生データ取得（Linux で実行）

#### 正規手順（2020年〜当年・これだけ）

**スクリプト:** `scripts/jv_fetch_2020_to_now.sh`（待機版: `jv_wait_tunnel_and_fetch_raw.sh`）

JV の `from_datetime` は **更新日以降** の差分。2020〜を取るには **2フェーズが必須**:

| フェーズ | 内容 |
|----------|------|
| 1 | `--all-stored --from-datetime 20200101`（SLOP/DIFN/…＋RACE 差分） |
| 2 | **2020〜当年** 各年: `RACE option=4`（セットアップ）`from YYYY0101 to YYYY1231` ＋ 当年のみ `weekly-odds` |

```bash
cd /var/www/html/tools.kachiumaai.com
rm -f config/JV_FETCH_PAUSED
nohup ./scripts/jv_wait_tunnel_and_fetch_raw.sh >> logs/jv_wait_fetch.nohup.log 2>&1 &
```

ログ:

```bash
tail -f /var/www/html/tools.kachiumaai.com/logs/jv_fetch_2020_to_now.*.log
```

**禁止:** 同じ `--all-stored` だけを何度も再実行。取得中の `jv_delete_*`。**エージェントが TRUNCATE しない**（データ消去は人間が明示したときだけ）。

**途中で止まったとき（2020 だけ終わって 2021 以降が進まない等）:** phase1 を飛ばし、未完了の年から phase2 だけ再開する。

```bash
rm -f logs/.jv_fetch_2020_to_now.lock
nohup env JV_SKIP_ALL_STORED=1 JV_BACKFILL_FROM_YEAR=2021 JV_BACKFILL_TO_YEAR=2026 \
  ./scripts/jv_wait_tunnel_and_fetch_raw.sh >> logs/jv_wait_fetch.nohup.log 2>&1 &
```

（`JV_BACKFILL_FROM_YEAR` を止まった年に合わせる。checkpoint の RA 全件集計は重いためログでは RACE_total のみ。）

**数時間〜1日以上。** トンネル + JVLinkServer を **最後まで閉じない**。

#### Windows から

```text
scripts\windows\step01_start_tunnel.bat   … トンネル＋Server
scripts\windows\step03_jv_backfill_2019_on_linux.bat  … SSH で上記と同等（jv_backfill_years.sh）
```

**進捗確認（phpMyAdmin でも可）:**

```sql
SELECT COUNT(*) FROM jv_raw_records;
SELECT JSON_UNQUOTE(JSON_EXTRACT(payload_json,'$.meet_year')) y, COUNT(*) c
FROM jv_raw_records GROUP BY y ORDER BY y;
```

---

### ステップ E — 正規化（生データが溜まってから）

```text
scripts\windows\step04_server_normalize.bat
```

または新本番:

```bash
cd /var/www/html/tools.kachiumaai.com
python3 collectors/normalize_jv_raw.py --from-id 0 --batch-size 2000
python3 collectors/materialize_jv_app_records.py
python3 scripts/audit_data_coverage.py --from-year 2020 --to-year 2026
```

### ステップ F — 予想（任意）

```text
scripts\windows\step05_server_repredict_p6.bat
```

---

## 3. バッチ早見表（`scripts\windows\`）

| ファイル | いつ使う |
|----------|----------|
| **step00_first_setup.bat** | 初回設定 |
| **fix_sid_and_start_jvlink.bat** | JV が不安定・worker unreachable・8765 が空 |
| **jvlink_start.bat** | JVLinkServer だけ起動（管理者推奨） |
| **step01_start_tunnel.bat** | トンネル + Server 起動 |
| **step02_check_new_server.bat** | 18765 確認 |
| **step03_jv_backfill_2019_on_linux.bat** | Linux で年単位取得+正規化（backfill） |
| **step04_server_normalize.bat** | 正規化のみ |
| **check-jv-link.bat** | 8765 だけ確認 |
| **diagnose_jvlink.bat** | 障害時ログ収集 |
| **preflight_jvlink.bat** | 取得前の一括確認 |
| ~~1-JV取得-2019から一括.bat~~ | **非推奨**（PC 直 DB） |

---

## 4. 新本番 Linux の設定（既に入っている想定）

`config/jv_local.env`:

```ini
JVLINK_SERVER_HOST=127.0.0.1
JVLINK_SERVER_PORT=18765
JV_FETCH_EXTRA="--linux-tunnel-tcp --host 127.0.0.1 --port 18765"
```

**取得を止める:**

```bash
./scripts/jv_stop_fetch_cron.sh
# → config/JV_FETCH_PAUSED ができる
```

**DB を空にしてから取り直す:**

```bash
mysql keiba_ai < db/jv_reset_for_full_refetch.sql
```

---

## 5. エラー別クイック表

| 表示 | 原因 | やること |
|------|------|----------|
| `Cannot open JVLinkAgent service` | 管理者権限なし | **管理者で実行** or `services.msc` で手動再起動。**8765 OK なら次へ** |
| `worker unreachable` | JV-Link / 利用キー / Agent | `FIX_worker_unreachable.txt` の 1〜5 |
| `Connection refused` 18765 | トンネル切れ | `step01` の SSH 窓を開き直す |
| `JVLinkServer is not running` | 8765 死んでる | `jvlink_start.bat` |
| `JV fetch PAUSED` | 停止フラグ | `rm config/JV_FETCH_PAUSED`（意図的停止中は触らない） |
| Linux fetch 0件のまま | 上記すべて | A〜D を順に確認 |

---

## 6. 削除で全消ししないために

**2025/2026 だけ消すスクリプト**は `payload_json` の `meet_year` で判別する。  
**削除前に必ず:**

```sql
SELECT JSON_UNQUOTE(JSON_EXTRACT(payload_json,'$.meet_year')) y, COUNT(*) c
FROM jv_raw_records GROUP BY y ORDER BY y;
```

2019〜2023（または取りたい年）に件数があることを確認してから削除系を実行する。

---

## 7. 参照 URL・ファイル

| 種類 | 場所 |
|------|------|
| phpMyAdmin | https://tools.kachiumaai.com/phpmyadmin/ （Web 門なし） |
| Windows 索引 | `scripts/windows/0-最初に読む-手順.txt` |
| worker unreachable | `scripts/windows/FIX_worker_unreachable.txt` |
| 成功ログ | `docs/ops_jv_ACTUAL_success_2026-05.md` |

---

## 8. いまの運用メモ（2026-06-04）

- DB は **`jv_reset_for_full_refetch.sql` 済み**（空から開始）  
- **2020〜当年**は **`jv_fetch_2020_to_now.sh`**（all-stored + 年別 RACE/odds）  
- トンネル待機: `jv_wait_tunnel_and_fetch_raw.sh`（ログ `jv_fetch_2020_to_now.*.log`）
