|
| 1 | +# 設計ドキュメント: テーブルUX改善 |
| 2 | + |
| 3 | +## 概要 |
| 4 | + |
| 5 | +result_serverのサマリーテーブル(`_results_table.html`)に対するUX改善を実装する。 |
| 6 | + |
| 7 | +1. **Compareカラム位置変更**: 左端からFOMカラムの右隣に移動し、ツールチップの見切れ問題を解消 |
| 8 | +2. **Expフィルタのカスケード連動**: Codeフィルタ選択時にExpの選択肢を連動絞り込み |
| 9 | +3. **Proc/nodeカラム追加**: ノード当たりプロセス数をNodesカラムの隣に表示 |
| 10 | +4. **Thread/procカラム追加**: プロセス当たりスレッド数をProc/nodeカラムの隣に表示。result.shでFOM行から`nthreads:`をパースしResult_JSONに記録 |
| 11 | +5. **ハードスペック系カラム削除**: CPU Name, GPU Name, CPU/node, GPU/node, CPU Core Countをテーブルから削除(SYSTEMツールチップで既に表示済み)。`_build_row`からも対応フィールド抽出を削除 |
| 12 | + |
| 13 | +すべての変更はサーバーサイド処理(Python/Jinja2テンプレート)とCIスクリプト(shell)で完結し、クライアントサイドJavaScriptの変更は最小限に留める。既存のテストが引き続きパスすることを保証する。 |
| 14 | + |
| 15 | +変更後のテーブルカラム順序: |
| 16 | +`Timestamp | CODE | Exp | FOM | Compare | FOM version | SYSTEM | Nodes | Proc/node | Thread/proc | JSON | PA Data | Mode | Trigger | Pipeline | Detail` |
| 17 | + |
| 18 | +## アーキテクチャ |
| 19 | + |
| 20 | +### 変更対象ファイルと責務 |
| 21 | + |
| 22 | +```mermaid |
| 23 | +graph TD |
| 24 | + A[results.py<br/>ルート] -->|filter_code渡し| B[results_loader.py<br/>データ読み込み] |
| 25 | + B -->|rows, columns, filter_options| C[_results_table.html<br/>テーブル描画] |
| 26 | + C -->|include| D[_filter_dropdowns.html<br/>フィルタUI] |
| 27 | + E[test_results_loader.py<br/>テスト] -->|検証| B |
| 28 | + F[result.sh<br/>JSON生成] -->|nthreadsパース追加| B |
| 29 | +``` |
| 30 | + |
| 31 | +### 変更の流れ |
| 32 | + |
| 33 | +1. `results_loader.py`: カラム順序変更(ハードスペック系削除、Proc/node・Thread/proc追加)、`_build_row`からハードスペック系フィールド抽出削除・`nthreads`/`numproc_node`フィールド追加、`get_filter_options`にカスケードフィルタ対応 |
| 34 | +2. `results.py`: `get_filter_options`呼び出し時に`filter_code`パラメータを渡す |
| 35 | +3. `_results_table.html`: Compareカラムの位置変更、Proc/node・Thread/procカラムの描画追加、ハードスペック系カラムの描画削除 |
| 36 | +4. `_filter_dropdowns.html`: 変更不要(サーバーサイドで絞り込み済みの選択肢が渡される) |
| 37 | +5. `result.sh`: FOM行から`nthreads:`をパースする処理を追加(`numproc_node`と同じパターン) |
| 38 | + |
| 39 | +## コンポーネントとインターフェース |
| 40 | + |
| 41 | +### 1. `results_loader.py` の変更 |
| 42 | + |
| 43 | +#### カラムリスト変更 |
| 44 | + |
| 45 | +現在の`columns`リスト(`load_results_table`内)を以下の順序に変更する。ハードスペック系カラム(CPU Name, GPU Name, CPU/node, GPU/node, CPU Core Count)を削除し、Proc/nodeとThread/procを追加: |
| 46 | + |
| 47 | +```python |
| 48 | +columns = [ |
| 49 | + ("Timestamp", "timestamp"), |
| 50 | + ("CODE", "code"), |
| 51 | + ("Exp", "exp"), |
| 52 | + ("FOM", "fom"), |
| 53 | + ("FOM version", "fom_version"), |
| 54 | + ("SYSTEM", "system"), |
| 55 | + ("Nodes", "nodes"), |
| 56 | + ("Proc/node", "numproc_node"), # 新規追加 |
| 57 | + ("Thread/proc", "nthreads"), # 新規追加 |
| 58 | + ("JSON", "json_link"), |
| 59 | + ("PA Data", "data_link"), |
| 60 | + ("Mode", "execution_mode"), |
| 61 | + ("Trigger", "ci_trigger"), |
| 62 | + ("Pipeline", "pipeline_id"), |
| 63 | +] |
| 64 | +``` |
| 65 | + |
| 66 | +注: Compareカラムはテンプレート側で挿入されるため、columnsリストには含まれない。テンプレート側でFOMの後にCompareを挿入することで、最終的なカラム順序は: |
| 67 | +`Timestamp | CODE | Exp | FOM | Compare | FOM version | SYSTEM | Nodes | Proc/node | Thread/proc | JSON | PA Data | Mode | Trigger | Pipeline | Detail` |
| 68 | + |
| 69 | +#### `_build_row` の変更 |
| 70 | + |
| 71 | +ハードスペック系フィールドの抽出コードを削除し、`numproc_node`および`nthreads`フィールドを追加する。 |
| 72 | + |
| 73 | +削除するフィールド抽出: |
| 74 | +```python |
| 75 | +# 以下を削除 |
| 76 | +cpu = data.get("cpu_name", "N/A") |
| 77 | +gpu = data.get("gpu_name", "N/A") |
| 78 | +cpus = data.get("cpus_per_node", "N/A") |
| 79 | +gpus = data.get("gpus_per_node", "N/A") |
| 80 | +cpu_cores = data.get("cpu_cores", "N/A") |
| 81 | +``` |
| 82 | + |
| 83 | +追加するフィールド抽出: |
| 84 | +```python |
| 85 | +numproc_node = data.get("numproc_node", "N/A") |
| 86 | +if numproc_node is None or numproc_node == "": |
| 87 | + numproc_node = "N/A" |
| 88 | + |
| 89 | +nthreads = data.get("nthreads", "N/A") |
| 90 | +if nthreads is None or nthreads == "": |
| 91 | + nthreads = "N/A" |
| 92 | +``` |
| 93 | + |
| 94 | +rowから削除するキー: `"cpu"`, `"gpu"`, `"cpus"`, `"gpus"`, `"cpu_cores"` |
| 95 | + |
| 96 | +rowに追加するキー: |
| 97 | +```python |
| 98 | +row = { |
| 99 | + # ... 既存フィールド |
| 100 | + "numproc_node": numproc_node, |
| 101 | + "nthreads": nthreads, |
| 102 | + # ... 既存フィールド |
| 103 | +} |
| 104 | +``` |
| 105 | + |
| 106 | +#### `get_filter_options` のシグネチャ変更 |
| 107 | + |
| 108 | +```python |
| 109 | +def get_filter_options(directory, public_only=True, authenticated=False, |
| 110 | + affiliations=None, field_map=None, filter_code=None): |
| 111 | +``` |
| 112 | + |
| 113 | +`filter_code`が指定された場合、Exp抽出時にそのCodeを持つJSONからのみExpを収集する。`filter_code=None`の場合は従来通り全Expを返す。 |
| 114 | + |
| 115 | +設計判断: `filter_code`パラメータをキーワード引数として末尾に追加することで、既存の呼び出し元(estimated_results等)に影響を与えない。 |
| 116 | + |
| 117 | +### 2. `results.py` の変更 |
| 118 | + |
| 119 | +`_render_results_list`内の`get_filter_options`呼び出しに`filter_code`を渡す: |
| 120 | + |
| 121 | +```python |
| 122 | +filter_options = get_filter_options(received_dir, filter_code=filter_code, **filter_kwargs) |
| 123 | +``` |
| 124 | + |
| 125 | +### 3. `_results_table.html` の変更 |
| 126 | + |
| 127 | +#### Compareカラムの位置変更 |
| 128 | + |
| 129 | +- ヘッダー: `<thead>`内でCompareカラムをFOMの後に配置(`{% if col_name == "FOM" %}`の後にCompare thを挿入) |
| 130 | +- ボディ: `<tbody>`内で各行のチェックボックスセルをFOMセルの後に配置 |
| 131 | +- ツールチップ: `tooltip-left`クラスを削除し、デフォルト方向(中央上)を使用 |
| 132 | + |
| 133 | +#### Proc/nodeおよびThread/procカラムの追加 |
| 134 | + |
| 135 | +テンプレートの`{% if col_name in [...] %}`リストに`"Proc/node"`, `"Thread/proc"`を追加し、ツールチップ付きヘッダーを描画する。ボディ側の`{% elif key in [...] %}`リストに`"numproc_node"`, `"nthreads"`を追加する。 |
| 136 | + |
| 137 | +ツールチップテキスト: |
| 138 | +- Proc/node: "Number of processes per node" |
| 139 | +- Thread/proc: "Number of threads per process" |
| 140 | + |
| 141 | +#### ハードスペック系カラムの削除 |
| 142 | + |
| 143 | +テンプレートの`{% if col_name in [...] %}`リストから`"CPU Name"`, `"GPU Name"`, `"CPU/node"`, `"GPU/node"`, `"CPU Core Count"`を削除する。ボディ側の`{% elif key in [...] %}`リストからも対応するキー(`"cpu"`, `"gpu"`, `"cpus"`, `"gpus"`, `"cpu_cores"`)を削除する。 |
| 144 | + |
| 145 | +SYSTEMカラムのツールチップは変更しない(CPU Name, CPU/node, CPU Cores, GPU Name, GPU/node, Memoryの情報を`system_info.py`から取得して引き続き表示)。 |
| 146 | + |
| 147 | +### 4. `_filter_dropdowns.html` |
| 148 | + |
| 149 | +変更不要。サーバーサイドで絞り込まれた`filter_options.exps`がそのまま描画される。フィルタ変更時の`applyServerFilter()`は既存のページリロード方式を維持する。 |
| 150 | + |
| 151 | +### 5. `result.sh` の変更 |
| 152 | + |
| 153 | +#### nthreadsのFOM行パース追加 |
| 154 | + |
| 155 | +`numproc_node`と同じパターンで、FOM行から`nthreads:`をパースする処理を追加する。 |
| 156 | + |
| 157 | +既存の`numproc_node`パース処理(参考): |
| 158 | +```bash |
| 159 | +numproc_node_line=$(echo $line | grep -Eo 'numproc_node:[ ]*[0-9]*' | head -n1 | awk -F':' '{print $2}' | sed 's/^ *//') |
| 160 | +if [ -n "$numproc_node_line" ]; then |
| 161 | + numproc_node=${numproc_node_line} |
| 162 | +else |
| 163 | + numproc_node="" |
| 164 | +fi |
| 165 | +``` |
| 166 | + |
| 167 | +追加する`nthreads`パース処理: |
| 168 | +```bash |
| 169 | +nthreads_line=$(echo $line | grep -Eo 'nthreads:[ ]*[0-9]*' | head -n1 | awk -F':' '{print $2}' | sed 's/^ *//') |
| 170 | +if [ -n "$nthreads_line" ]; then |
| 171 | + nthreads=${nthreads_line} |
| 172 | +else |
| 173 | + nthreads="" |
| 174 | +fi |
| 175 | +``` |
| 176 | + |
| 177 | +#### nthreadsの初期値 |
| 178 | + |
| 179 | +スクリプト冒頭で`numproc_node`と同様に初期値を設定: |
| 180 | +```bash |
| 181 | +nthreads="" |
| 182 | +``` |
| 183 | + |
| 184 | +#### Result_JSONへのnthreadsフィールド追加 |
| 185 | + |
| 186 | +`write_result_json`関数内のJSON出力テンプレートに`nthreads`フィールドを追加: |
| 187 | + |
| 188 | +```bash |
| 189 | +cat <<EOF > results/result${idx}.json |
| 190 | +{ |
| 191 | + "code": "$code", |
| 192 | + "system": "$system", |
| 193 | + "FOM": "$fom", |
| 194 | + "FOM_version": "$fom_version", |
| 195 | + "Exp": "$exp", |
| 196 | + "node_count": "$node_count", |
| 197 | + "numproc_node": "$numproc_node", |
| 198 | + "nthreads": "$nthreads", |
| 199 | + ... |
| 200 | +} |
| 201 | +EOF |
| 202 | +``` |
| 203 | + |
| 204 | +## データモデル |
| 205 | + |
| 206 | +### Result_JSON構造(関連フィールド) |
| 207 | + |
| 208 | +```json |
| 209 | +{ |
| 210 | + "code": "qws", |
| 211 | + "system": "Fugaku", |
| 212 | + "Exp": "CASE0", |
| 213 | + "FOM": 1.234, |
| 214 | + "node_count": 4, |
| 215 | + "numproc_node": "4", |
| 216 | + "nthreads": "12" |
| 217 | +} |
| 218 | +``` |
| 219 | + |
| 220 | +`numproc_node`は既存フィールド(FOM行からパース)。`nthreads`は新規フィールド(FOM行からパース、`numproc_node`と同じ方式)。古いJSONファイルには`nthreads`が存在しない場合があり、その場合は`"N/A"`を表示する。 |
| 221 | + |
| 222 | +`cpu_name`, `gpu_name`, `cpus_per_node`, `gpus_per_node`, `cpu_cores`はResult_JSONに引き続き記録される(result.shは変更しない)が、テーブルカラムとしては表示しない。`_build_row`からもこれらのフィールド抽出を削除する。 |
| 223 | + |
| 224 | +### テーブル行データ(row dict) |
| 225 | + |
| 226 | +`_build_row`が返すdictの変更: |
| 227 | + |
| 228 | +削除するキー: `"cpu"`, `"gpu"`, `"cpus"`, `"gpus"`, `"cpu_cores"` |
| 229 | + |
| 230 | +追加するキー: |
| 231 | +```python |
| 232 | +row = { |
| 233 | + "timestamp": "2025-01-01 12:00:00", |
| 234 | + "code": "qws", |
| 235 | + "exp": "CASE0", |
| 236 | + "fom": 1.234, |
| 237 | + "fom_version": "v1", |
| 238 | + "system": "Fugaku", |
| 239 | + "nodes": 4, |
| 240 | + "numproc_node": "4", # 新規追加(値またはN/A) |
| 241 | + "nthreads": "12", # 新規追加(値またはN/A) |
| 242 | + "json_link": "/results/...", |
| 243 | + "data_link": "/results/...", |
| 244 | + "has_vector": False, |
| 245 | + "detail_link": None, |
| 246 | + "filename": "result_...", |
| 247 | + "build_time": "-", |
| 248 | + "queue_time": "-", |
| 249 | + "run_time": "-", |
| 250 | + "execution_mode": "-", |
| 251 | + "ci_trigger": "-", |
| 252 | + "build_job": "-", |
| 253 | + "run_job": "-", |
| 254 | + "pipeline_id": "-", |
| 255 | +} |
| 256 | +``` |
| 257 | + |
| 258 | +### フィルタオプション(filter_options dict) |
| 259 | + |
| 260 | +```python |
| 261 | +{ |
| 262 | + "systems": ["Fugaku", "RC_GH200"], |
| 263 | + "codes": ["qws", "genesis"], |
| 264 | + "exps": ["CASE0", "CASE1"] # filter_code指定時は絞り込み済み |
| 265 | +} |
| 266 | +``` |
| 267 | + |
| 268 | +### FOM行フォーマット(result.sh入力) |
| 269 | + |
| 270 | +``` |
| 271 | +FOM:1.234 node_count:4 numproc_node:4 nthreads:12 Exp:CASE0 FOM_version:v1 |
| 272 | +``` |
| 273 | + |
| 274 | +`nthreads:N` は `numproc_node:N` と同じパターンでFOM行に記述される。 |
| 275 | + |
| 276 | + |
| 277 | +## 正確性プロパティ (Correctness Properties) |
| 278 | + |
| 279 | +*プロパティとは、システムの全ての有効な実行において成り立つべき特性や振る舞いのことである。人間が読める仕様と機械的に検証可能な正確性保証の橋渡しとなる。* |
| 280 | + |
| 281 | +### Property 1: カスケードフィルタの正確性 |
| 282 | + |
| 283 | +*任意の* Result_JSONファイル群と *任意の* `filter_code`値(Noneを含む)に対して、`get_filter_options`が返す`exps`は、`filter_code`が指定されている場合はそのCodeを持つJSONに含まれるExp値の集合と一致し、`filter_code`がNoneの場合は全JSONに含まれるExp値の集合と一致する。 |
| 284 | + |
| 285 | +**Validates: Requirements 2.1, 2.2** |
| 286 | + |
| 287 | +### Property 2: numproc_nodeフィールド抽出の正確性 |
| 288 | + |
| 289 | +*任意の* Result_JSONデータに対して、`_build_row`が返すrowの`numproc_node`値は、JSONに`numproc_node`フィールドが存在し空でない場合はその値と一致し、存在しないかNullか空文字列の場合は`"N/A"`となる。 |
| 290 | + |
| 291 | +**Validates: Requirements 3.2, 3.5** |
| 292 | + |
| 293 | +### Property 3: nthreadsフィールド抽出の正確性 |
| 294 | + |
| 295 | +*任意の* Result_JSONデータに対して、`_build_row`が返すrowの`nthreads`値は、JSONに`nthreads`フィールドが存在し空でない場合はその値と一致し、存在しないかNullか空文字列の場合は`"N/A"`となる。 |
| 296 | + |
| 297 | +**Validates: Requirements 4.2, 4.5** |
| 298 | + |
| 299 | +### Property 4: ハードスペック系カラムの非表示 |
| 300 | + |
| 301 | +*任意の* `load_results_table`呼び出しに対して、返されるカラムリストに`("CPU Name", "cpu")`, `("GPU Name", "gpu")`, `("CPU/node", "cpus")`, `("GPU/node", "gpus")`, `("CPU Core Count", "cpu_cores")`が含まれない。 |
| 302 | + |
| 303 | +**Validates: Requirements 5.1** |
| 304 | + |
| 305 | +## エラーハンドリング |
| 306 | + |
| 307 | +### `numproc_node`および`nthreads`フィールドの欠損 |
| 308 | + |
| 309 | +古いResult_JSONファイルには`nthreads`フィールドが存在しない場合がある。`_build_row`は`data.get("nthreads", "N/A")`でフォールバックし、テーブルに`"N/A"`を表示する。`None`値や空文字列の場合も`"N/A"`に変換する。`numproc_node`も同様の処理を行う。 |
| 310 | + |
| 311 | +### `get_filter_options`の`filter_code`パラメータ |
| 312 | + |
| 313 | +- `filter_code=None`: 従来通り全Expを返す(後方互換性維持) |
| 314 | +- `filter_code`に存在しないCode値が指定された場合: Expsは空リストとなる(正常動作) |
| 315 | +- `filter_code=""`(空文字列): Noneと同様に扱い、全Expを返す |
| 316 | + |
| 317 | +### result.shのnthreadsパース |
| 318 | + |
| 319 | +- FOM行に`nthreads:`が含まれない場合: `nthreads`フィールドは空文字列として記録 |
| 320 | +- FOM行に`nthreads:12`のように含まれる場合: `nthreads`フィールドに`"12"`を記録 |
| 321 | + |
| 322 | +### 既存テストへの影響 |
| 323 | + |
| 324 | +カラムリストの変更により、`test_existing_columns_unchanged`テストが失敗する。このテストの期待値を新しいカラムリスト(ハードスペック系削除、Proc/node・Thread/proc追加済み)に更新する必要がある。 |
| 325 | + |
| 326 | +## テスト戦略 |
| 327 | + |
| 328 | +### テストフレームワーク |
| 329 | + |
| 330 | +- **ユニットテスト**: pytest(既存のテストスイートに追加) |
| 331 | +- **プロパティベーステスト**: hypothesis(既存プロジェクトで使用済み) |
| 332 | +- 各プロパティテストは最低100イテレーション実行 |
| 333 | + |
| 334 | +### ユニットテスト |
| 335 | + |
| 336 | +既存の`test_results_loader.py`に以下のテストを追加: |
| 337 | + |
| 338 | +1. **カラムリスト検証**: `Proc/node`カラムが`Nodes`の直後、`Thread/proc`が`Proc/node`の直後に存在し、ハードスペック系カラムが含まれないことを確認(Requirements 3.1, 4.1, 5.1) |
| 339 | +2. **既存カラムテスト更新**: `test_existing_columns_unchanged`の期待値を新カラムリストに更新 |
| 340 | +3. **カスケードフィルタの統合テスト**: `get_filter_options`に`filter_code`を渡した場合のExpフィルタリング動作確認(Requirements 2.3) |
| 341 | +4. **numproc_nodeフォールバック**: `numproc_node`なしのJSONで`"N/A"`が返ることを確認(Requirements 3.5のエッジケース) |
| 342 | +5. **nthreadsフォールバック**: `nthreads`なしのJSONで`"N/A"`が返ることを確認(Requirements 4.5のエッジケース) |
| 343 | + |
| 344 | +### プロパティベーステスト |
| 345 | + |
| 346 | +各テストはhypothesisの`@given`デコレータを使用し、`@settings(max_examples=100)`で最低100イテレーション実行する。 |
| 347 | + |
| 348 | +1. **Feature: table-ux-improvements, Property 1: カスケードフィルタの正確性** |
| 349 | + - ランダムなResult_JSONデータセット(複数のcode/exp組み合わせ)を生成 |
| 350 | + - ランダムなfilter_code値(Noneまたはデータセット内のcode値)を選択 |
| 351 | + - `get_filter_options`の返すexpsが期待される集合と一致することを検証 |
| 352 | + |
| 353 | +2. **Feature: table-ux-improvements, Property 2: numproc_nodeフィールド抽出の正確性** |
| 354 | + - ランダムなResult_JSONデータ(numproc_nodeあり/なし/None/空文字列)を生成 |
| 355 | + - `_build_row`の返すrowのnumproc_node値が期待値と一致することを検証 |
| 356 | + |
| 357 | +3. **Feature: table-ux-improvements, Property 3: nthreadsフィールド抽出の正確性** |
| 358 | + - ランダムなResult_JSONデータ(nthreadsあり/なし/None/空文字列)を生成 |
| 359 | + - `_build_row`の返すrowのnthreads値が期待値と一致することを検証 |
| 360 | + |
| 361 | +4. **Feature: table-ux-improvements, Property 4: ハードスペック系カラムの非表示** |
| 362 | + - ランダムなResult_JSONデータを生成 |
| 363 | + - `load_results_table`の返すカラムリストにハードスペック系カラムが含まれないことを検証 |
0 commit comments