From 9d82210f3185a244088c9514a0ef1a16d8e1f264 Mon Sep 17 00:00:00 2001 From: iomgaa Date: Sat, 17 Oct 2026 10:33:10 -0400 Subject: [PATCH] =?UTF-8?q?feat(review):=20=E8=B7=B3=E8=BF=87=E7=97=85?= =?UTF-8?q?=E4=BE=8B=E8=A1=A8=E5=8F=AA=E8=BF=9B=E5=BF=AB=E7=85=A7=EF=BC=9B?= =?UTF-8?q?=E8=A7=84=E6=A8=A1=E8=A1=A8=20review=20=E8=A1=8C=E6=B3=A8?= =?UTF-8?q?=E6=98=8E=E5=B7=B2=E6=8E=A5=E5=8F=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 负责人 2026-10-17:跳过病例.csv 不放进医生目录,只留在 snapshot/,病例总表和问题清单已用大白话说了同样的内容;review 行数超预期一倍,原因对照表本来就该这么长,接受。 Co-Authored-By: Claude Opus 5.5 --- research-wiki/explanation/结构.md | 8 ++++---- research-wiki/guides/医生核对包.md | 3 ++- src/cadx/review/_pack.py | 9 ++++++--- src/cadx/review/readme_template.md | 3 +-- tests/test_review_pack.py | 14 ++++++++++---- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/research-wiki/explanation/结构.md b/research-wiki/explanation/结构.md index 53efc8b..6477ffa 100644 --- a/research-wiki/explanation/结构.md +++ b/research-wiki/explanation/结构.md @@ -206,7 +206,7 @@ library/<构建号>/ - 病理诊断:三张表的诊断列各存一份,用哪一份算分由评价决定; - 化名对照:化名、真实病例号、CEUS 视频号。会诊产物只用化名,评价靠这张表接回真实病例; -- 跳过清单:没进资料库的病例、没过的那一项检查、涉及的文件和列,以及对不上的各表原值,供医生核对。40 例里已知有 6 例临床表与超声表对不上(`../待定问题/04-交付数据的跨表不一致.md`),会出现在这份清单里; +- 跳过清单:没进资料库的病例、没过的那一项检查、涉及的文件和列,以及对不上的各表原值,供医生核对(核对包里由病例总表和问题清单写成医生能懂的话,原表只进核对包的快照)。40 例里已知有 6 例临床表与超声表对不上(`../待定问题/04-交付数据的跨表不一致.md`),会出现在这份清单里; - 将来加医生标注工作簿。 ### 医生核对包(`runs/review_pack/<运行号>/`) @@ -215,10 +215,10 @@ library/<构建号>/ ``` runs/review_pack/<运行号>/ - snapshot/ 快照,含程序原文的 问题清单_原文.csv(不给医生) + snapshot/ 快照,含程序原文的 问题清单_原文.csv 和答案库跳过清单的拷贝 跳过病例.csv(不给医生) 医生核对包_<构建号>/ 说明.pdf 问题清单.csv 病例总表.csv - 标准答案/ 答案库 4 张表原样拷贝 + 标准答案/ 答案库的化名对照、诊断、报告复核句 3 张表原样拷贝 病例/<病例号>/ 临床信息.csv 超声报告.pdf MR报告.pdf 关键帧/医生截图.png、<槽位>_第<秒>秒.png TIC曲线.png TIC参数.csv 回放视频.mp4 @@ -350,7 +350,7 @@ Python 环境用 uv;安装与检查命令见 `../guides/开发环境.md`。 | runs | 100–250 | 201(快照 `_snapshot.py` 98、读 CEUS 运行的 `_ceus_run.py` 82、`__init__.py` 21) | — | | consult | 500–1000(会诊口径定后重估) | — | — | | evaluate | 300–600(评价口径定后重估) | — | 已删的 `core/eval` 622 | -| review | 300–500 | 1152(`_pack.py` 447、`_problems.py` 502、`_pdf.py` 129、`_config.py` 64、`__init__.py` 10;不含说明模板 `readme_template.md` 91 行)。超出一倍:任务书 04 要求的原因对照表(`_problems.py`)大半是逐条的原因与医生写法,待负责人确认是否接受 | — | +| review | 300–500 | 1152(`_pack.py` 447、`_problems.py` 502、`_pdf.py` 129、`_config.py` 64、`__init__.py` 10;不含说明模板 `readme_template.md` 91 行)。超出一倍,负责人 2026-10-17 已接受:原因对照表(`_problems.py`)要逐条覆盖全部失败原因,本来就该这么长 | — | | scripts/ 每个 | ≤ 300 | `build.py` 37、`scan_library.py` 88、`ceus_quant.py` 403(超出,含目视核对表的读取与核对、开跑即写快照)、`ceus_align_review.py` 243、`review_pack.py` 37 | `t1_local_debug.py` 762、`tic_lognormal_replay.py` 627 | --- diff --git a/research-wiki/guides/医生核对包.md b/research-wiki/guides/医生核对包.md index 5bec77d..1e63816 100644 --- a/research-wiki/guides/医生核对包.md +++ b/research-wiki/guides/医生核对包.md @@ -91,6 +91,7 @@ runs/review_pack/<出包号>/ (output_root 下) - `review_pack.yaml`:配置原文副本; - `review.json`:构建号、CEUS 运行号、pandoc 与 soffice 的版本行; - `问题清单_原文.csv`:问题清单的程序原文(检查编号、原因码、程序记的原因),列同包里的问题清单,给我们自己追溯; + - `跳过病例.csv`:答案库跳过清单 `skipped.csv` 的原样拷贝。它是程序原文,病例总表和问题清单已用大白话写了同样的内容,所以不进医生的目录(负责人 2026-10-17); - `finished.json`:结束时间、病例数、问题清单行数。 - `医生核对包_<构建号>/`: @@ -98,7 +99,7 @@ runs/review_pack/<出包号>/ (output_root 下) 说明.pdf 给医生的说明,源文件是 src/cadx/review/readme_template.md 问题清单.csv 病例号、视频号、环节、问题(一句话) 病例总表.csv 全部病例一例一行 - 标准答案/ 化名对照.csv、诊断.csv、跳过病例.csv、报告复核句.csv(答案库原样拷贝) + 标准答案/ 化名对照.csv、诊断.csv、报告复核句.csv(答案库原样拷贝) 病例/<病例号>/ 只有进了资料库的病例才有 临床信息.csv 临床一路的字段 超声报告.pdf、MR报告.pdf 两路报告的描述段原文 diff --git a/src/cadx/review/_pack.py b/src/cadx/review/_pack.py index c7ef077..4acefca 100644 --- a/src/cadx/review/_pack.py +++ b/src/cadx/review/_pack.py @@ -2,10 +2,10 @@ ``` /<运行号>/ - snapshot/ 快照(不给医生),含程序原文的 问题清单_原文.csv + snapshot/ 快照(不给医生),含程序原文的 问题清单_原文.csv、跳过病例.csv 医生核对包_<构建号>/ 说明.pdf 问题清单.csv 病例总表.csv - 标准答案/化名对照.csv 诊断.csv 跳过病例.csv 报告复核句.csv + 标准答案/化名对照.csv 诊断.csv 报告复核句.csv 病例/<病例号>/临床信息.csv 超声报告.pdf MR报告.pdf 关键帧/医生截图.png <槽位>_第<秒>秒.png TIC曲线.png TIC参数.csv 回放视频.mp4 @@ -66,9 +66,11 @@ RAW_PROBLEMS = "问题清单_原文.csv" ANSWER_TABLES = { "aliases.csv": "化名对照.csv", "diagnoses.csv": "诊断.csv", - "skipped.csv": "跳过病例.csv", "review.csv": "报告复核句.csv", } +# 跳过清单是程序原文(检查编号、交付路径),病例总表和问题清单已用大白话写了同样的内容, +# 所以只原样拷进快照,不进医生目录(负责人 2026-10-17)。 +SKIPPED_TABLE = ("skipped.csv", "跳过病例.csv") # TIC参数.csv 的两组(tic.json 里 perfusion_metrics 的键,按列的顺序)和 11 行: # tic.json 里的键、中文名加缩写、单位。时间类(单位秒)保留 1 位小数,其余 2 位。 TIC_GROUPS = ("roi_pan", "roi_perf") @@ -348,6 +350,7 @@ def run_review_pack(config_path: Path, repo_root: Path, now: datetime) -> PackSu (pack / "标准答案").mkdir(parents=True) for name, target in ANSWER_TABLES.items(): _copy(verified_file(answers, name), pack / "标准答案" / target) + _copy(verified_file(answers, SKIPPED_TABLE[0]), snapshot / SKIPPED_TABLE[1]) # failed.csv:病例号(三位数字)→(视频号,第几步,原因原文)。 failures: dict[str, tuple[str, int, str]] = {} diff --git a/src/cadx/review/readme_template.md b/src/cadx/review/readme_template.md index 5474cd0..7e1e9ae 100644 --- a/src/cadx/review/readme_template.md +++ b/src/cadx/review/readme_template.md @@ -10,7 +10,7 @@ $ceus_note ## 一、包里有什么 -- **说明.pdf**(这份说明)、**问题清单.csv**、**病例总表.csv**、**标准答案/**(4 张表):几张表见第六节。 +- **说明.pdf**(这份说明)、**问题清单.csv**、**病例总表.csv**、**标准答案/**(3 张表):几张表见第六节。 - **病例/**:每个进了资料库的病例一个文件夹,名字是病例号(如 P001)。打开能看到: - **临床信息.csv**:临床那位 AI 专家看到的临床信息,列名沿用你们临床工作簿的原列名。$codes_note - **超声报告.pdf**、**MR报告.pdf**:报告里的描述部分,一字未改。结论(印象)部分写着诊断倾向,没有给 AI 专家,这里也就没有。 @@ -87,5 +87,4 @@ TIC参数.csv 每行一个参数,两组数并排:**胰腺实质(紫圈)* - **病例总表.csv**:全部病例一例一行:进没进资料库、没进的原因、造影视频处理成功没有(没成功时写停在第几步)、7 个关键帧各在视频第几秒。 - **标准答案/化名对照.csv**:资料库里代替病例号的化名与病例号的对照。 - **标准答案/诊断.csv**:三张表的病理诊断编码(1 IPMN,2 SPN,3 MCN,4 SCN)。 -- **标准答案/跳过病例.csv**:内容和问题清单里没进资料库的那几条一样,是程序原文版本,一般不用看。 - **标准答案/报告复核句.csv**:报告描述里含「考虑」「可能」「倾向」「提示」的句子。这些句子目前仍留在给 AI 专家的报告里,想请你们看看它们算不算带出了诊断倾向、该不该删掉。 diff --git a/tests/test_review_pack.py b/tests/test_review_pack.py index f41a3ab..b8a091e 100644 --- a/tests/test_review_pack.py +++ b/tests/test_review_pack.py @@ -89,7 +89,6 @@ BLANK = [""] * 11 ANSWER_HEADERS = { "化名对照.csv": ["化名", "病例号", "视频号"], "诊断.csv": ["化名", "病例号", "临床表诊断编码", "超声表诊断编码", "MR表诊断编码", "诊断名"], - "跳过病例.csv": ["病例", "检查编号", "检查说明", "文件", "列", "涉及值"], "报告复核句.csv": ["化名", "路", "命中的词", "所在句子"], } CASE_FILES = {"临床信息.csv", "超声报告.pdf", "MR报告.pdf", "关键帧"} @@ -281,7 +280,7 @@ def test_directory_layout(packed: Packed) -> None: def test_every_csv_has_bom_and_no_placeholder_for_missing(packed: Packed) -> None: csvs = sorted(packed.pack.rglob("*.csv")) - assert len(csvs) == 2 + 4 + 4 * 2 + assert len(csvs) == 2 + 3 + 4 * 2 for path in csvs: for row in _csv(path): assert not {"null", "None", "NaN", "nan"} & set(row), path @@ -435,7 +434,7 @@ def test_raw_problem_list_in_snapshot(packed: Packed) -> None: """程序原文照任务书 03 的写法留在快照里,不进医生的目录。""" assert not (packed.pack / "问题清单_原文.csv").exists() rows = _csv(packed.result.run_dir / "snapshot" / "问题清单_原文.csv") - skipped = {row[0]: row for row in _csv(packed.pack / "标准答案" / "跳过病例.csv")[1:]} + skipped = {row[0]: row for row in _csv(packed.result.run_dir / "snapshot" / "跳过病例.csv")[1:]} fit_failed = [f"{slot} 未取到:{FIT_FAILED}" for slot in DYNAMIC] p008 = { "B0": "视频开头没有观测到稳定的增强前基线(基线是估计的),B0 不定义", @@ -551,12 +550,19 @@ def test_copies_are_byte_identical(packed: Packed) -> None: for source, name in ( ("aliases.csv", "化名对照.csv"), ("diagnoses.csv", "诊断.csv"), - ("skipped.csv", "跳过病例.csv"), ("review.csv", "报告复核句.csv"), ): assert (packed.pack / "标准答案" / name).read_bytes() == (answers / source).read_bytes() +def test_skipped_table_only_in_snapshot(packed: Packed) -> None: + """跳过清单是程序原文:不进医生目录,原样拷进快照(负责人 2026-10-17)。""" + assert not [p for p in packed.pack.rglob("*") if p.name == "跳过病例.csv"] + copied = packed.result.run_dir / "snapshot" / "跳过病例.csv" + assert copied.read_bytes() == (packed.build.answers_dir / "skipped.csv").read_bytes() + assert _csv(copied)[0] == ["病例", "检查编号", "检查说明", "文件", "列", "涉及值"] + + def _tool_output(tool: str, *args: str) -> str: path = shutil.which(tool) assert path is not None, f"找不到 {tool}"