diff --git a/CLAUDE.md b/CLAUDE.md index 198149b..2d83ce6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -86,6 +86,10 @@ ### 04_SPEC(仕様・設計) +- アーキテクチャ設計: docs/04_SPEC/SPEC_01_アーキテクチャ設計.md +- データモデル: docs/04_SPEC/SPEC_02_データモデル.md +- データセットと受け渡し: docs/04_SPEC/SPEC_03_データセットと受け渡し.md + ### 05_TECH(技術設計) diff --git "a/docs/02_ENV/ENV_02_\347\222\260\345\242\203\346\247\213\347\257\211\346\211\213\351\240\206.md" "b/docs/02_ENV/ENV_02_\347\222\260\345\242\203\346\247\213\347\257\211\346\211\213\351\240\206.md" index e6fc79c..4b944f9 100644 --- "a/docs/02_ENV/ENV_02_\347\222\260\345\242\203\346\247\213\347\257\211\346\211\213\351\240\206.md" +++ "b/docs/02_ENV/ENV_02_\347\222\260\345\242\203\346\247\213\347\257\211\346\211\213\351\240\206.md" @@ -61,7 +61,7 @@ - ※ 未検証(GitBucket の `main` がまだ空のため.立ち上げの成果物を `main` に取り込み,`git push nakaguchi main` で反映した後に実行する) - 置き場所は `C:\Users\Papin\source\`(OneDrive の同期対象外).**ドキュメント・デスクトップ配下には置かない**(研究室 PC ではドキュメントフォルダが OneDrive にリダイレクトされている) -- 目録の DB ファイル・データ置き場・設定ファイルの場所は仕様設計で決め,決まり次第ここに追記する +- 目録・ストア・対応表は `C:\Users\Papin\clinical-data\tiasdata\`・`tiasdata-identity\` に置き,場所は設定ファイル `C:\Users\Papin\.config\tiasdata\config.toml` に書く(配置と設定のキーは [SPEC_01「配置」「設定ファイル」](../04_SPEC/SPEC_01_アーキテクチャ設計.md)).設定ファイルの作成と `tiasdata init` は実装後に行う ※ 未検証 ## 使う側のリポジトリへの組み込み (Using from Other Repositories) diff --git "a/docs/04_SPEC/SPEC_01_\343\202\242\343\203\274\343\202\255\343\203\206\343\202\257\343\203\201\343\203\243\350\250\255\350\250\210.md" "b/docs/04_SPEC/SPEC_01_\343\202\242\343\203\274\343\202\255\343\203\206\343\202\257\343\203\201\343\203\243\350\250\255\350\250\210.md" new file mode 100644 index 0000000..b6e62a9 --- /dev/null +++ "b/docs/04_SPEC/SPEC_01_\343\202\242\343\203\274\343\202\255\343\203\206\343\202\257\343\203\201\343\203\243\350\250\255\350\250\210.md" @@ -0,0 +1,118 @@ +# アーキテクチャ設計 (Architecture) + +## 基本方針 (Principles) + +1. **置き場所をコードに書かない**: 目録・データ置き場・取り込み元・計算サーバの場所は,研究室 PC の設定ファイルに書く.使う側はデータセット名と版で指定する +2. **登録したファイルは変えない**: 取り込むときに管理用の置き場(ストア)へコピーし,ファイル名を中身の SHA-256 にする.同じ名前のファイルは常に同じ中身であり,過去の版を確実に再現できる +3. **版は凍結する**: データセットの版は作った時点の構成(ファイルとラベル)を記録し,以後変更しない.データやラベルを足しても過去の版の中身は変わらない +4. **由来を必ず記録する**: 派生データは,元データ・プログラム名・プログラムの版とひも付けて登録する +5. **個人情報を出さない**: 実際の患者 ID は目録に入れない(研究用 ID だけを使う).CLI の出力・ログ・エラーメッセージは集計値と研究用 ID に限る([SPEC_03「出力の規則」](SPEC_03_データセットと受け渡し.md)) +6. **拡張は行の追加で行う**: 所見の種類・ファイルの種類の追加は,種類の定義(表の行と検証用の型)の追加で済ませ,表の構造を作り直さない([SPEC_02](SPEC_02_データモデル.md)) + +## 構成要素 (Components) + +```text +研究室 PC(原本・目録) 計算サーバ(Newton・Archimedes) +┌──────────────────────────────┐ ┌──────────────────────────────┐ +│ 取り込み元(原本.読むだけ) │ │ ~/data/tiasdata/<名前>/v<版>/ │ +│ clinical-data/TIASJudgeModel │ │ manifest.csv・dataset.json │ +│ │ tiasdata import │ │ files/.<拡張子> │ +│ ▼ │ tiasdata │ ▲ │ +│ 目録 catalog.sqlite │ deliver │ │ tiasdata.load() │ +│ ストア store/ │ ────────────▶ │ 使う側(TIASJudgeModel 等) │ +│ バンドル bundles/<名前>/v<版>/ │ (直送) └──────────────────────────────┘ +└──────────────────────────────┘ + ▲ ssh labpc "tiasdata ..."(手元 PC から操作.ENV_02) +``` + +| 要素 | 役割 | +| --- | --- | +| 目録(`catalog.sqlite`) | 症例・撮影・ファイル・ラベル・由来・データセットの版・受け渡しの記録([SPEC_02](SPEC_02_データモデル.md)) | +| ストア(`store/`) | 登録したファイルの実体.中身の SHA-256 をファイル名にし,変更しない | +| 対応表(`identity.sqlite`) | 実際の患者 ID ↔ 研究用 ID.目録とは別のファイル・別のフォルダに置く.現在のデータには実際の患者 ID が無いため空 | +| バンドル | データセットの 1 つの版を,manifest とファイルのコピーにまとめたもの.計算サーバ・Python 以外のツールへ渡す単位([SPEC_03](SPEC_03_データセットと受け渡し.md)) | +| ライブラリ(`tiasdata`) | 目録の操作(研究室 PC)と,バンドルの読み込み・検証(どこでも) | +| CLI(`tiasdata`) | 取り込み・データセットの版の作成・バンドルの書き出し・受け渡し・点検(研究室 PC) | +| データセットの定義(`datasets/*.toml`) | どのファイル・ラベルを集めるかの条件.個人情報を含まないので**リポジトリで管理**し,PR で変更する | + +## 配置 (Locations) + +### リポジトリ (Repository) + +```text +TIASDataHub/ +├── src/tiasdata/ +│ ├── config.py # 設定ファイルの読み込み・検証(pydantic) +│ ├── catalog/ # 目録(SQLite)の接続・マイグレーション・各表の操作 +│ ├── store.py # ストア(ハッシュの計算・コピー・検証) +│ ├── kinds/ # ファイルの種類・所見の種類の定義(値の検証用の型) +│ ├── importers/ # 取り込み元ごとの読み取り(例: 舌色の 9 フォルダ) +│ ├── datasets.py # データセットの定義の評価・版の作成 +│ ├── bundle.py # バンドルの書き出し・読み込み・検証 +│ ├── deliver.py # 計算サーバへの受け渡し +│ └── cli.py # エントリポイント +├── migrations/ # 目録のスキーマ変更(0001_*.sql …) +├── datasets/ # データセットの定義(TOML) +├── tests/ # ダミーデータのみで動くテスト +└── docs/ +``` + +依存の向きは `cli` → `datasets`・`bundle`・`deliver`・`importers` → `catalog`・`store`・`kinds`・`config` とする.**`bundle` の読み込み側は `catalog` に依存しない**(目録の無い計算サーバで使うため). + +### 研究室 PC (Lab PC) + +```text +C:\Users\Papin\clinical-data\ +├── TIASJudgeModel\ # 取り込み元(原本.基盤は読むだけで変更しない) +├── TongueSegmentation\ # 取り込み元(原本) +├── tiasdata\ # 基盤のデータ(catalog.root) +│ ├── catalog.sqlite # 目録 +│ ├── store\ # ストア(store\<先頭 2 文字>\.<拡張子>) +│ ├── bundles\ # 書き出したバンドル(受け渡し後は削除してよい.再生成できる) +│ └── backups\ # 目録のバックアップ +└── tiasdata-identity\ # 対応表(identity.sqlite).目録とは別のフォルダ +``` + +- すべて OneDrive の同期対象外(`C:\Users\Papin\` 直下の独自フォルダ)に置く +- `.claude/repo-access.json` の `deny_read` に `C:/Users/Papin/clinical-data` が入っているため,上記はすべて Claude の読み取り禁止の対象になる + +## 設定ファイル (Configuration) + +研究室 PC のユーザー領域に置き(リポジトリには入れない),pydantic で検証する(未知のキーは拒否する). + +- 場所: 環境変数 `TIASDATA_CONFIG` があればそのパス,無ければ `%USERPROFILE%\.config\tiasdata\config.toml`(Linux では `~/.config/tiasdata/config.toml`) +- 相対パスは設定ファイルのあるフォルダを基準にする.`~` を展開する + +```toml +[catalog] +root = "C:/Users/Papin/clinical-data/tiasdata" # catalog.sqlite・store/・bundles/・backups/ +identity = "C:/Users/Papin/clinical-data/tiasdata-identity/identity.sqlite" + +[sources.tiasjudgemodel] # 取り込み元(名前で指定する) +path = "C:/Users/Papin/clinical-data/TIASJudgeModel" +importer = "tongue_color_folders" + +[servers.newton] # 受け渡し先 +host = "hasegawa@192.168.23.14" +key = "C:/Users/Papin/.ssh/id_ed25519_newton" +root = "~/data/tiasdata" + +[servers.archimedes] +host = "hasegawa@192.168.23.15" +key = "C:/Users/Papin/.ssh/id_ed25519_newton" +root = "~/data/tiasdata" +``` + +- CLI はパスを引数に取らず,**取り込み元・受け渡し先を名前で指定する**(SSH 経由のコマンドにデータのパスが現れないようにするため.ENV_02「研究室 PC の操作方針」) +- テストは一時ディレクトリに設定ファイル・目録・ストアを作り,`TIASDATA_CONFIG` で指す + +## 使う側からの利用 (Consumer Usage) + +| 場所 | 使い方 | +| --- | --- | +| 計算サーバ(Python) | `tiasdata.load("tongue_color", version=1)` がバンドルを開く.バンドルの置き場所は環境変数 `TIASDATA_BUNDLES`(既定 `~/data/tiasdata`)の下の `<名前>/v<版>/` | +| Python 以外(DeepTIAS 等) | バンドルの `manifest.csv` と `files/` を読む | +| 研究室 PC(Python) | `tiasdata.Catalog.open()` で目録を直接開ける(通常は CLI を使う) | + +- 使う側の実験記録には,バンドルの**指紋**(データセット名・版・`manifest.csv` の SHA-256)を残す(`Bundle.fingerprint`).これにより「どの実験がどの時点のデータ集合を使ったか」を辿れる +- 版を省略した `load("tongue_color")` は許さない(暗黙に最新を使うと再現性が崩れるため) diff --git "a/docs/04_SPEC/SPEC_02_\343\203\207\343\203\274\343\202\277\343\203\242\343\203\207\343\203\253.md" "b/docs/04_SPEC/SPEC_02_\343\203\207\343\203\274\343\202\277\343\203\242\343\203\207\343\203\253.md" new file mode 100644 index 0000000..a96996c --- /dev/null +++ "b/docs/04_SPEC/SPEC_02_\343\203\207\343\203\274\343\202\277\343\203\242\343\203\207\343\203\253.md" @@ -0,0 +1,176 @@ +# データモデル (Data Model) + +目録(`catalog.sqlite`)の表の定義.スキーマは `migrations/` の連番 SQL で管理し,適用済みの版を `PRAGMA user_version` に記録する([ENV_01](../02_ENV/ENV_01_技術スタック.md)).外部キー制約を有効にする(`PRAGMA foreign_keys = ON`). + +## 全体像 (Overview) + +```text +subjects(症例) +└── captures(撮影) + ├── assets(ファイル.舌画像・カラーチャート画像・派生データ) + │ ▲ derivation_outputs ── derivations(由来: プログラム・版)── derivation_inputs ▶ assets + └── annotations(所見.舌色等.撮影またはファイルに付く) + +datasets ── dataset_versions ── dataset_members(ファイル)── dataset_member_annotations(所見) + └── deliveries(受け渡しの記録) + +kinds(ファイルの種類・所見の種類の定義) +``` + +## ID の書式 (Identifiers) + +| 対象 | 書式 | 例 | +| --- | --- | --- | +| 症例(研究用 ID) | `S` + 6 桁の連番 | `S000123` | +| 撮影 | `C` + 6 桁の連番 | `C000456` | +| ファイル | 中身の SHA-256(16 進 64 文字.小文字) | `3f9a…` | +| データセット | 英小文字・数字・`_`(英小文字で始まる) | `tongue_color` | +| データセットの版 | 1 から始まる整数 | `1` | + +- 研究用 ID は基盤が振る.実際の患者 ID・カルテ番号は目録に入れない(対応表 `identity.sqlite` にだけ置く) +- 現在の舌色データは実際の患者 ID が無く,1 患者 1 枚であるため,**画像 1 枚ごとに症例と撮影を 1 つずつ**作る(対応表は使わない) + +## 表の定義 (Tables) + +### kinds(種類の定義) + +ファイルの種類と所見の種類を 1 つの表で管理する.種類の追加はこの表への行の追加と,`src/tiasdata/kinds/` への検証用の型の追加で行う. + +| 列 | 型 | 内容 | +| --- | --- | --- | +| `kind` | TEXT PK | 種類名(例: `tongue_crop_masked`・`tongue_color`) | +| `category` | TEXT | `asset`(ファイル)または `annotation`(所見) | +| `description` | TEXT | 説明 | +| `value_schema_version` | INTEGER | 所見の値の型の版(`annotation` のみ.値の形を変えたら上げる) | + +最初に用意する種類: + +| `kind` | `category` | 内容 | +| --- | --- | --- | +| `tongue_raw` | `asset` | 撮影した舌画像(補正前) | +| `color_chart` | `asset` | カラーチャート画像 | +| `tongue_crop_masked` | `asset` | 色補正後の画像を舌の枠で切り出し,マスク外を黒 (0,0,0) にしたもの(DeepTIAS Image&Calib の `2-cropped` × `3-resized`.現在の舌色データ) | +| `tongue_mask` | `asset` | 舌のマスク画像 | +| `tongue_color` | `annotation` | 舌色 5 分類+程度.値は `{"label": , "severity": "strong" \| "mild" \| "normal"}`.slug は `pale`・`light_red`・`red`・`dark_red`・`purple`(TIASJudgeModel の SPEC_01「クラス定義」と同じ).`light_red` のときだけ `normal` | + +### subjects(症例) + +| 列 | 型 | 内容 | +| --- | --- | --- | +| `subject_id` | TEXT PK | 研究用 ID | +| `created_at` | TEXT | 登録日時(ISO 8601) | +| `note` | TEXT | 備考 | + +### captures(撮影) + +| 列 | 型 | 内容 | +| --- | --- | --- | +| `capture_id` | TEXT PK | 撮影 ID | +| `subject_id` | TEXT FK | 症例 | +| `captured_on` | TEXT NULL | 撮影日(日付まで.不明なら NULL.現在のデータは不明) | +| `device` | TEXT NULL | 撮影装置(例: `TIAS`) | +| `note` | TEXT | 備考 | + +### assets(ファイル) + +| 列 | 型 | 内容 | +| --- | --- | --- | +| `sha256` | TEXT PK | 中身の SHA-256 | +| `kind` | TEXT FK | ファイルの種類(`kinds.category = 'asset'`) | +| `capture_id` | TEXT FK NULL | 撮影(撮影にひも付かないファイルは NULL) | +| `ext` | TEXT | 拡張子(`png` 等.小文字) | +| `size_bytes` | INTEGER | サイズ | +| `source` | TEXT | 取り込み元の名前(設定ファイルの `sources.<名前>`) | +| `source_name` | TEXT | 取り込み元での相対パス(現在のデータのファイル名は個人をたどれる情報を含まないことを 2026-10-02 にユーザーが確認) | +| `registered_at` | TEXT | 登録日時 | + +- ストア上のパスは `store//.` で決まるため,目録には保存しない +- 同じ中身のファイルを 2 度取り込むと,同じ行を指す(重複登録しない).ただし別の撮影・別の種類として登録しようとした場合は停止する + +### derivations・derivation_inputs・derivation_outputs(由来) + +派生データを「どのファイルから,どのプログラムのどの版で」作ったかを記録する. + +| 表 | 列 | 内容 | +| --- | --- | --- | +| `derivations` | `derivation_id` INTEGER PK | — | +| | `tool` | プログラム名(例: `DeepTIAS`) | +| | `tool_version` | プログラムの版(例: `2021-12-exe`・`2.7.1`・git のコミット).不明なら `unknown` | +| | `mode` | 実行モード・設定(例: `Image&Calib`.JSON で詳細を持ってよい) | +| | `performed_at` | 実行日時(不明なら NULL) | +| | `note` | 備考 | +| `derivation_inputs` | `derivation_id`・`sha256`・`role` | 入力ファイルと役割(例: `tongue`・`chart`) | +| `derivation_outputs` | `derivation_id`・`sha256`・`role` | 出力ファイルと役割(例: `cropped`・`mask`・`analysis`) | + +- 同じ入力から版の違うプログラムで作った出力は,別の `derivations` 行になる(DeepTIAS の旧 exe と現行ソースの結果を区別できる) +- 現在の舌色データ(`tongue_crop_masked`)は元の撮影画像が基盤に無いため,入力なし・`tool = 'DeepTIAS'`・`tool_version = 'unknown'`・`mode = 'Image&Calib'` の由来を付けて登録する(TIASJudgeModel の SPEC_04 の確認結果を `note` に書く) + +### annotations(所見) + +| 列 | 型 | 内容 | +| --- | --- | --- | +| `annotation_id` | INTEGER PK | — | +| `kind` | TEXT FK | 所見の種類(`kinds.category = 'annotation'`) | +| `capture_id` | TEXT FK NULL | 撮影に付く所見の場合 | +| `sha256` | TEXT FK NULL | ファイルに付く所見の場合(`capture_id` とどちらか一方だけ) | +| `value` | TEXT | 値(JSON.`kinds/` の型で検証してから保存する) | +| `annotator` | TEXT NULL | 評価者(研究用の記号.個人名は入れない) | +| `source` | TEXT | 由来(例: `tiasjudgemodel_folder`=フォルダ名から読み取った) | +| `created_at` | TEXT | 登録日時 | +| `superseded_by` | INTEGER NULL | 訂正したときの新しい行 | + +- **所見の行は書き換えない**.訂正は新しい行を追加し,古い行の `superseded_by` を埋める(過去の版が古い行を参照し続けられるようにするため) +- 現在の舌色ラベルは撮影に付ける(医師が撮影を見て付けたラベルのため) + +### datasets・dataset_versions・dataset_members・dataset_member_annotations(データセット) + +| 表 | 列 | 内容 | +| --- | --- | --- | +| `datasets` | `name` TEXT PK・`description`・`created_at` | — | +| `dataset_versions` | `name`・`version`(PK は 2 列) | — | +| | `definition` | 版を作ったときのデータセットの定義(TOML の内容をそのまま保存) | +| | `hub_commit` | 版を作ったときの本リポジトリのコミット | +| | `manifest_sha256` | この版の `manifest.csv` の SHA-256(指紋) | +| | `created_at`・`note` | — | +| `dataset_members` | `name`・`version`・`sha256` | 版に含まれるファイル | +| `dataset_member_annotations` | `name`・`version`・`sha256`・`annotation_id` | 版を作った時点で採用した所見 | + +- **版の行と,その版の構成の行は書き換え・削除しない**(凍結) +- 版の作り方・manifest の形は [SPEC_03](SPEC_03_データセットと受け渡し.md) + +### deliveries(受け渡しの記録) + +計算サーバにコピーした作業用データと,データセットの版との対応を記録する. + +| 列 | 内容 | +| --- | --- | +| `delivery_id` INTEGER PK | — | +| `name`・`version` | 渡したデータセットの版 | +| `server` | 受け渡し先(設定ファイルの `servers.<名前>`) | +| `path` | 受け渡し先のパス(例: `~/data/tiasdata/tongue_color/v1`) | +| `delivered_at` | 日時 | +| `verified` | 受け渡し先でハッシュを照合できたか | + +## 対応表 (Identity Map) + +`identity.sqlite`(目録とは別のファイル・別のフォルダ).実際の患者 ID が分かるデータを取り込むときだけ使う. + +| 列 | 内容 | +| --- | --- | +| `patient_key` TEXT PK | 実際の患者 ID(取り込み元が持つもの) | +| `subject_id` TEXT | 研究用 ID | +| `created_at` | — | + +- 同じ患者の別の撮影を取り込むと,同じ研究用 ID にひも付く(複数枚・経時的な撮影に対応する) +- CLI は対応表の中身を表示しない.対応表を開くのは取り込みのときだけ +- 匿名化の方法・対応表の保管ルールは倫理審査の保管ルールを確認して確定する([PLAN_01「人間が確認すること」](../03_PLAN/PLAN_01_要件定義書.md)) + +## 拡張の例 (Extension Examples) + +| 追加したいもの | やること | +| --- | --- | +| 苔の色・厚さ等の所見 | `kinds` に行を追加し,`kinds/` に値の型を足す.マイグレーションは種類の行の追加だけ | +| 問診・体質 | 撮影または症例に付く所見として同様に追加する(症例に付く所見が必要になったら `annotations` に `subject_id` 列を足す) | +| 経時的な撮影 | 同じ症例に撮影を追加する(対応表で同じ研究用 ID にひも付ける) | +| 別の撮影装置 | `captures.device` に記録する.装置固有の情報が必要なら所見として持つ | +| DeepTIAS の解析結果 | 出力ファイルの種類(`tongue_mask`・`analysis_csv` 等)を足し,`derivations` で入力の撮影画像とひも付ける | diff --git "a/docs/04_SPEC/SPEC_03_\343\203\207\343\203\274\343\202\277\343\202\273\343\203\203\343\203\210\343\201\250\345\217\227\343\201\221\346\270\241\343\201\227.md" "b/docs/04_SPEC/SPEC_03_\343\203\207\343\203\274\343\202\277\343\202\273\343\203\203\343\203\210\343\201\250\345\217\227\343\201\221\346\270\241\343\201\227.md" new file mode 100644 index 0000000..dfa452c --- /dev/null +++ "b/docs/04_SPEC/SPEC_03_\343\203\207\343\203\274\343\202\277\343\202\273\343\203\203\343\203\210\343\201\250\345\217\227\343\201\221\346\270\241\343\201\227.md" @@ -0,0 +1,135 @@ +# データセットと受け渡し (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/. # ストアからのコピー +``` + +### manifest.csv の列 + +| 列 | 内容 | +| --- | --- | +| `path` | バンドルのルートからの相対パス(`files/.`.区切りは `/`) | +| `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 側の実装).基盤側は使う側ごとの形式を持たない +- 現在のデータは 1 患者 1 枚のため,`subject_id` は TIASJudgeModel の現行の `patient_id`(画像 ID)と同じ分け方になる + +## 計算サーバへの受け渡し (Delivery) + +`tiasdata deliver <名前> --version <版> --to <サーバ名>` を研究室 PC で実行する(手元 PC からは `ssh labpc` 経由.データは手元 PC を通らない). + +1. バンドルを書き出す(`bundles/<名前>/v<版>/`.既にあれば中身を照合して使う) +2. tar にまとめ,設定ファイルの鍵で `scp` する +3. サーバで `/<名前>/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`)は個人をたどれる情報を含まないが,既定では表示しない(エラーの特定に必要な場合だけ表示する)