feat(review): 医生核对包入口;CEUS 运行目录的读取收进 runs

- 新模块 review(组织层,只依赖 library、answers、runs):把一次构建和它装入的
  CEUS 运行整理成医生可读的文件夹(CSV 带 BOM、说明与报告转 PDF、图片视频逐字节拷贝),
  入口 scripts/review_pack.py + config/review_pack.yaml。任务书 03。
- 读 CEUS 运行目录(failed.csv、结果目录、关键帧文件)从 build 挪进 runs,build 与
  review 都经它读;failed.csv 读取在边界上收严(步号须为整数、视频号不重复、列数固定)。
- library、answers 补只读函数;import-linter 增 review 契约。
- 文档:guides/医生核对包.md(过一轮硕士生阅读),结构.md 第三至八部分同步。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-17 06:04:19 -04:00
parent 14d6f8e153
commit c7373efde2
23 changed files with 1718 additions and 83 deletions
+37
View File
@@ -0,0 +1,37 @@
"""出医生核对包:把一次构建(资料库 + 答案库)和它装入的 CEUS 运行整理成医生能直接看的文件夹。
用法:uv run python scripts/review_pack.py --config config/review_pack.yaml
产物写进 `<output_root>/<运行号>/`:`医生核对包_<构建号>/` 是要交给医生的,`snapshot/` 不给。
终端只打印计数,不打印病例号。
"""
import argparse
import time
from datetime import datetime
from pathlib import Path
from cadx.review import run_review_pack
REPO_ROOT = Path(__file__).resolve().parents[1]
def main() -> None:
parser = argparse.ArgumentParser(description="出医生核对包")
parser.add_argument("--config", type=Path, required=True, help="review_pack.yaml 的路径")
config: Path = parser.parse_args().config
clock = time.monotonic()
summary = run_review_pack(config, REPO_ROOT, datetime.now().astimezone())
print(f"运行目录:{summary.run_dir}")
print(f"核对包:{summary.pack_dir}")
print(f"病例数:{summary.cases}(进库 {summary.passed},跳过 {summary.skipped})")
by_stage = ",".join(f"{stage} {n}" for stage, n in summary.problems_by_stage.items())
print(f"问题清单行数:{sum(summary.problems_by_stage.values())}({by_stage or '无'})")
print(f"PDF 份数:{summary.pdfs}")
print(f"拷贝的视频总大小:{summary.video_bytes / 2**20:.1f} MiB")
print(f"耗时:{time.monotonic() - clock:.1f} 秒")
if __name__ == "__main__":
main()