# データセットと受け渡し (Datasets & Delivery)

データセットの定義・版の作成・バンドル（manifest ＋ファイル）の書き出し・計算サーバへの受け渡し・CLI・出力の規則を定める．表の定義は [SPEC_02](SPEC_02_データモデル.md)．

## データセットの定義 (Dataset Definition)

`datasets/<名前>.toml` に置く（リポジトリで管理し，PR で変更する）．

```toml
name = "tongue_color"
description = "舌色 5 分類＋程度（TIASJudgeModel の学習データ）"

[select]
asset_kind = "tongue_crop_masked"      # 集めるファイルの種類
require = ["tongue_color"]             # この所見を持つものだけ（撮影に付いた所見を含む）

[columns]                              # manifest に出す所見の列
tongue_color = ["label", "severity"]
```

- 条件は「ファイルの種類」と「必須の所見」から始め，必要になったら所見の値による絞り込み等を足す
- 1 つのファイル・撮影に同じ種類の有効な所見（訂正されていないもの）が 2 つ以上ある場合は，版の作成を止める（どちらを使うかを黙って決めない）

## 版の作成 (Creating a Version)

`tiasdata dataset freeze <名前>` で，その時点の目録に定義を当てはめて新しい版を作る．

1. 定義を検証し，条件に合うファイルと採用する所見を集める
2. 直前の版と構成（ファイルと所見）が同じなら，版を作らずにその旨を表示する
3. 構成・定義の内容・本リポジトリのコミット・`manifest.csv` の SHA-256 を記録する（以後変更しない）

- 本リポジトリに未コミットの変更がある状態では版を作らない（定義とコードの版を記録できなくなるため）
- 行の順序は `subject_id`・`capture_id`・`sha256` の順に並べる（同じ構成なら同じ `manifest.csv` になるようにする）

## バンドル (Bundle)

データセットの 1 つの版を，計算サーバ・Python 以外のツールに渡す形にまとめたもの．

```text
<名前>/v<版>/
├── dataset.json          # 名前・版・作成日時・hub_commit・件数・manifest.csv の SHA-256
├── manifest.csv          # 1 行 1 ファイル
├── SHA256SUMS            # files/ の照合用（sha256sum -c で検証できる形式）
└── files/<sha256>.<ext>  # ストアからのコピー
```

### manifest.csv の列

| 列 | 内容 |
| --- | --- |
| `path` | バンドルのルートからの相対パス（`files/<sha256>.<ext>`．区切りは `/`） |
| `sha256` | ファイルの SHA-256 |
| `kind` | ファイルの種類 |
| `subject_id` | 研究用の症例 ID（分割の単位にする） |
| `capture_id` | 撮影 ID |
| `<所見>.<項目>` | 定義の `[columns]` で指定した所見の値（例: `tongue_color.label`・`tongue_color.severity`） |

- 文字コードは UTF-8（BOM なし），改行は LF
- 実際の患者 ID・元のファイル名は出さない

### TIASJudgeModel との互換 (Compatibility)

TIASJudgeModel の manifest（`image_path`・`patient_id`・`label`・`severity`・`note`）は，次の対応で同じ情報を持つ．

| TIASJudgeModel | 本基盤 |
| --- | --- |
| `image_path` | `path` |
| `patient_id` | `subject_id` |
| `label` | `tongue_color.label` |
| `severity` | `tongue_color.severity` |

- TIASJudgeModel 側は `tiasdata.load()` でバンドルを読み，自分の manifest の形に変換して使う（変換は TIASJudgeModel 側の実装）．基盤側は使う側ごとの形式を持たない
- `subject_id` は患者単位である．TIASJudgeModel の現行の `patient_id` は画像 ID（「1 患者 1 枚」という誤った前提）のため，**切り替えると分割が患者単位に変わり，交差検証の成績が変わりうる**（同じ患者の画像が学習と評価に分かれなくなる）

## 計算サーバへの受け渡し (Delivery)

`tiasdata deliver <名前> --version <版> --to <サーバ名>` を研究室 PC で実行する（手元 PC からは `ssh labpc` 経由．データは手元 PC を通らない）．

1. バンドルを書き出す（`bundles/<名前>/v<版>/`．既にあれば中身を照合して使う）
2. tar にまとめ，設定ファイルの鍵で `scp` する
3. サーバで `<root>/<名前>/v<版>/` に展開し，`chmod -R u+rwX,go-rwx` で自分だけに絞る（Windows で作った tar は `tar --delay-directory-restore -xf` で展開する．TIASJudgeModel の ENV_03 の実測）
4. サーバで `sha256sum -c SHA256SUMS` を実行して照合する（サーバに `tiasdata` が無くても検証できる）
5. tar ファイルを両側で削除し，`deliveries` に記録する

- 受け渡し先に同じ版が既にあれば，照合だけ行って記録する（上書きしない）
- 版は凍結されているため，一度渡した版の中身が変わることはない．新しいデータは新しい版として渡す

## ライブラリの API (Library API)

```python
import tiasdata

bundle = tiasdata.load("tongue_color", version=1)   # TIASDATA_BUNDLES/<名前>/v<版>/ を開く
bundle.verify()                                     # ハッシュを照合する（任意．時間がかかる）
for row in bundle.rows:                             # manifest の行（pydantic の型）
    row.path, row.subject_id, row.annotations["tongue_color"]["label"]
bundle.fingerprint                                  # {"name", "version", "manifest_sha256"}．実験記録に残す
```

- `load()` は版の指定を必須にする
- `tiasdata.open_bundle(path)` でパスを直接指定して開くこともできる（テスト・Python 以外との突き合わせ用）
- 読み込み時に `manifest.csv` の SHA-256 が `dataset.json` と一致しなければ停止する

## CLI (Commands)

研究室 PC で使う．パスは引数に取らず，名前で指定する（[SPEC_01「設定ファイル」](SPEC_01_アーキテクチャ設計.md)）．

| コマンド | 役割 |
| --- | --- |
| `tiasdata init` | 設定ファイルに従って目録・ストアを作り，マイグレーションを適用する |
| `tiasdata import <取り込み元>` | 取り込み元を読み，ファイルをストアにコピーして症例・撮影・ファイル・所見・由来を登録する（取り込み元の形ごとに importer を用意する．最初は舌色の 9 フォルダ `tongue_color_folders`） |
| `tiasdata dataset list` / `show <名前>` | データセットと版の一覧・件数 |
| `tiasdata dataset freeze <名前>` | 新しい版を作る |
| `tiasdata export <名前> --version <版>` | バンドルを `bundles/` に書き出す |
| `tiasdata deliver <名前> --version <版> --to <サーバ名>` | 計算サーバへ渡す |
| `tiasdata check` | 点検: ストアのファイルのハッシュの照合，目録にあってストアに無いファイル・その逆，所見の参照切れ，凍結した版の構成の欠け．件数だけを表示する |
| `tiasdata stats` | 種類・所見の値ごとの件数 |
| `tiasdata backup` | 目録を `backups/catalog-<日時>.sqlite` に複製する（SQLite のバックアップ API を使う） |

- 取り込みは何度実行しても同じ結果になるようにする（既に登録済みのファイルは飛ばす）
- 登録・版の作成は 1 つのトランザクションで行い，途中で失敗したら何も残さない
- 問題を見つけたら黙って飛ばさず，件数と理由を表示して停止する（`check`・`stats` は止めずに件数を報告する）

## 出力の規則 (Output Rules)

CLI の出力・ログ・エラーメッセージに出してよいもの・出さないもの．Claude が SSH 経由で CLI を実行し，その出力を読むことを前提にする．

| 出してよい | 出さない |
| --- | --- |
| 件数・割合・サイズの合計 | 実際の患者 ID・対応表の中身 |
| 研究用 ID（`S…`・`C…`）・SHA-256 | 画像の画素値・画像そのもの |
| 種類名・所見の値（舌色の slug 等）・版 | 取り込み元のファイルの中身 |
| 設定ファイルで付けた名前（取り込み元・サーバ） | — |

- 元のファイル名（`source_name`）は個人をたどれる情報を含まないが，既定では表示しない（エラーの特定に必要な場合だけ表示する）
