# アーキテクチャ設計 (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/<sha256>.<拡張子>      │
│        ▼                       │  tiasdata     │        ▲                       │
│ 目録 catalog.sqlite            │  deliver      │        │ tiasdata.load()      │
│ ストア store/<sha256>          │ ────────────▶ │ 使う側（TIASJudgeModel 等）    │
│ バンドル bundles/<名前>/v<版>/ │  （直送）     └──────────────────────────────┘
└──────────────────────────────┘
        ▲ ssh labpc "tiasdata ..."（手元 PC から操作．ENV_02）
```

| 要素 | 役割 |
| --- | --- |
| 目録（`catalog.sqlite`） | 症例・撮影・ファイル・ラベル・由来・データセットの版・受け渡しの記録（[SPEC_02](SPEC_02_データモデル.md)） |
| ストア（`store/`） | 登録したファイルの実体．中身の SHA-256 をファイル名にし，変更しない |
| 対応表（`identity.sqlite`） | 実際の患者 ID ↔ 研究用 ID．目録とは別のファイル・別のフォルダに置く．現在の舌色データは対応表の xlsx から作る |
| バンドル | データセットの 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
E:\clinical-data\
├── TIASJudgeModel\            # 取り込み元（原本．基盤は読むだけで変更しない）
├── TongueSegmentation\        # 取り込み元（原本）
├── DeepTIAS_TIASJudgeModel\   # 取り込み元（舌色データセットの DeepTIAS 出力．番号ごと）
├── DeepTIAS後_ID_番号.xlsx    # 画像の番号 → 患者 ID・何枚目の対応表（取り込みで読む）
├── tiasdata\                  # 基盤のデータ（catalog.root）
│   ├── catalog.sqlite         # 目録
│   ├── store\                 # ストア（store\<先頭 2 文字>\<sha256>.<拡張子>）
│   ├── bundles\               # 書き出したバンドル（受け渡し後は削除してよい．再生成できる）
│   └── backups\               # 目録のバックアップ
└── tiasdata-identity\         # 対応表（identity.sqlite）．目録とは別のフォルダ
```

- 研究室 PC の内蔵 SSD の E ドライブ（約 465GB．OneDrive の同期対象外）に置く．2026-10-06 に `C:\Users\Papin\clinical-data\` から移した（`docs/PROGRESS.md`）
- E ドライブは BitLocker で暗号化していない（C ドライブも同じ）．倫理審査の保管ルールで暗号化が必要なら，管理者権限で有効にする（人間の作業）
- `.claude/repo-access.json` の `deny_read` に `E:/clinical-data` が入っているため，上記はすべて Claude の読み取り禁止の対象になる

## 設定ファイル (Configuration)

研究室 PC のユーザー領域に置き（リポジトリには入れない），pydantic で検証する（未知のキーは拒否する）．

- 場所: 環境変数 `TIASDATA_CONFIG` があればそのパス，無ければ `%USERPROFILE%\.config\tiasdata\config.toml`（Linux では `~/.config/tiasdata/config.toml`）
- 相対パスは設定ファイルのあるフォルダを基準にする．`~` を展開する

```toml
[catalog]
root = "E:/clinical-data/tiasdata"                     # catalog.sqlite・store/・bundles/・backups/
identity = "E:/clinical-data/tiasdata-identity/identity.sqlite"

[sources.tiasjudgemodel]                                            # 取り込み元（名前で指定する）
path = "E:/clinical-data/TIASJudgeModel"
importer = "tongue_color_folders"
patient_map = "E:/clinical-data/DeepTIAS後_ID_番号.xlsx"  # 画像の番号 → 患者 ID・何枚目（患者 ID は identity.sqlite にだけ入れる）

[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")` は許さない（暗黙に最新を使うと再現性が崩れるため）
