feat(review): 跳过病例表只进快照;规模表 review 行注明已接受
负责人 2026-10-17:跳过病例.csv 不放进医生目录,只留在 snapshot/,病例总表和问题清单已用大白话说了同样的内容;review 行数超预期一倍,原因对照表本来就该这么长,接受。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -206,7 +206,7 @@ library/<构建号>/
|
|||||||
|
|
||||||
- 病理诊断:三张表的诊断列各存一份,用哪一份算分由评价决定;
|
- 病理诊断:三张表的诊断列各存一份,用哪一份算分由评价决定;
|
||||||
- 化名对照:化名、真实病例号、CEUS 视频号。会诊产物只用化名,评价靠这张表接回真实病例;
|
- 化名对照:化名、真实病例号、CEUS 视频号。会诊产物只用化名,评价靠这张表接回真实病例;
|
||||||
- 跳过清单:没进资料库的病例、没过的那一项检查、涉及的文件和列,以及对不上的各表原值,供医生核对。40 例里已知有 6 例临床表与超声表对不上(`../待定问题/04-交付数据的跨表不一致.md`),会出现在这份清单里;
|
- 跳过清单:没进资料库的病例、没过的那一项检查、涉及的文件和列,以及对不上的各表原值,供医生核对(核对包里由病例总表和问题清单写成医生能懂的话,原表只进核对包的快照)。40 例里已知有 6 例临床表与超声表对不上(`../待定问题/04-交付数据的跨表不一致.md`),会出现在这份清单里;
|
||||||
- 将来加医生标注工作簿。
|
- 将来加医生标注工作簿。
|
||||||
|
|
||||||
### 医生核对包(`runs/review_pack/<运行号>/`)
|
### 医生核对包(`runs/review_pack/<运行号>/`)
|
||||||
@@ -215,10 +215,10 @@ library/<构建号>/
|
|||||||
|
|
||||||
```
|
```
|
||||||
runs/review_pack/<运行号>/
|
runs/review_pack/<运行号>/
|
||||||
snapshot/ 快照,含程序原文的 问题清单_原文.csv(不给医生)
|
snapshot/ 快照,含程序原文的 问题清单_原文.csv 和答案库跳过清单的拷贝 跳过病例.csv(不给医生)
|
||||||
医生核对包_<构建号>/
|
医生核对包_<构建号>/
|
||||||
说明.pdf 问题清单.csv 病例总表.csv
|
说明.pdf 问题清单.csv 病例总表.csv
|
||||||
标准答案/ 答案库 4 张表原样拷贝
|
标准答案/ 答案库的化名对照、诊断、报告复核句 3 张表原样拷贝
|
||||||
病例/<病例号>/ 临床信息.csv 超声报告.pdf MR报告.pdf
|
病例/<病例号>/ 临床信息.csv 超声报告.pdf MR报告.pdf
|
||||||
关键帧/医生截图.png、<槽位>_第<秒>秒.png
|
关键帧/医生截图.png、<槽位>_第<秒>秒.png
|
||||||
TIC曲线.png TIC参数.csv 回放视频.mp4
|
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) | — |
|
| runs | 100–250 | 201(快照 `_snapshot.py` 98、读 CEUS 运行的 `_ceus_run.py` 82、`__init__.py` 21) | — |
|
||||||
| consult | 500–1000(会诊口径定后重估) | — | — |
|
| consult | 500–1000(会诊口径定后重估) | — | — |
|
||||||
| evaluate | 300–600(评价口径定后重估) | — | 已删的 `core/eval` 622 |
|
| 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 |
|
| 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 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ runs/review_pack/<出包号>/ (output_root 下)
|
|||||||
- `review_pack.yaml`:配置原文副本;
|
- `review_pack.yaml`:配置原文副本;
|
||||||
- `review.json`:构建号、CEUS 运行号、pandoc 与 soffice 的版本行;
|
- `review.json`:构建号、CEUS 运行号、pandoc 与 soffice 的版本行;
|
||||||
- `问题清单_原文.csv`:问题清单的程序原文(检查编号、原因码、程序记的原因),列同包里的问题清单,给我们自己追溯;
|
- `问题清单_原文.csv`:问题清单的程序原文(检查编号、原因码、程序记的原因),列同包里的问题清单,给我们自己追溯;
|
||||||
|
- `跳过病例.csv`:答案库跳过清单 `skipped.csv` 的原样拷贝。它是程序原文,病例总表和问题清单已用大白话写了同样的内容,所以不进医生的目录(负责人 2026-10-17);
|
||||||
- `finished.json`:结束时间、病例数、问题清单行数。
|
- `finished.json`:结束时间、病例数、问题清单行数。
|
||||||
- `医生核对包_<构建号>/`:
|
- `医生核对包_<构建号>/`:
|
||||||
|
|
||||||
@@ -98,7 +99,7 @@ runs/review_pack/<出包号>/ (output_root 下)
|
|||||||
说明.pdf 给医生的说明,源文件是 src/cadx/review/readme_template.md
|
说明.pdf 给医生的说明,源文件是 src/cadx/review/readme_template.md
|
||||||
问题清单.csv 病例号、视频号、环节、问题(一句话)
|
问题清单.csv 病例号、视频号、环节、问题(一句话)
|
||||||
病例总表.csv 全部病例一例一行
|
病例总表.csv 全部病例一例一行
|
||||||
标准答案/ 化名对照.csv、诊断.csv、跳过病例.csv、报告复核句.csv(答案库原样拷贝)
|
标准答案/ 化名对照.csv、诊断.csv、报告复核句.csv(答案库原样拷贝)
|
||||||
病例/<病例号>/ 只有进了资料库的病例才有
|
病例/<病例号>/ 只有进了资料库的病例才有
|
||||||
临床信息.csv 临床一路的字段
|
临床信息.csv 临床一路的字段
|
||||||
超声报告.pdf、MR报告.pdf 两路报告的描述段原文
|
超声报告.pdf、MR报告.pdf 两路报告的描述段原文
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
```
|
```
|
||||||
<output_root>/<运行号>/
|
<output_root>/<运行号>/
|
||||||
snapshot/ 快照(不给医生),含程序原文的 问题清单_原文.csv
|
snapshot/ 快照(不给医生),含程序原文的 问题清单_原文.csv、跳过病例.csv
|
||||||
医生核对包_<构建号>/
|
医生核对包_<构建号>/
|
||||||
说明.pdf 问题清单.csv 病例总表.csv
|
说明.pdf 问题清单.csv 病例总表.csv
|
||||||
标准答案/化名对照.csv 诊断.csv 跳过病例.csv 报告复核句.csv
|
标准答案/化名对照.csv 诊断.csv 报告复核句.csv
|
||||||
病例/<病例号>/临床信息.csv 超声报告.pdf MR报告.pdf
|
病例/<病例号>/临床信息.csv 超声报告.pdf MR报告.pdf
|
||||||
关键帧/医生截图.png <槽位>_第<秒>秒.png
|
关键帧/医生截图.png <槽位>_第<秒>秒.png
|
||||||
TIC曲线.png TIC参数.csv 回放视频.mp4
|
TIC曲线.png TIC参数.csv 回放视频.mp4
|
||||||
@@ -66,9 +66,11 @@ RAW_PROBLEMS = "问题清单_原文.csv"
|
|||||||
ANSWER_TABLES = {
|
ANSWER_TABLES = {
|
||||||
"aliases.csv": "化名对照.csv",
|
"aliases.csv": "化名对照.csv",
|
||||||
"diagnoses.csv": "诊断.csv",
|
"diagnoses.csv": "诊断.csv",
|
||||||
"skipped.csv": "跳过病例.csv",
|
|
||||||
"review.csv": "报告复核句.csv",
|
"review.csv": "报告复核句.csv",
|
||||||
}
|
}
|
||||||
|
# 跳过清单是程序原文(检查编号、交付路径),病例总表和问题清单已用大白话写了同样的内容,
|
||||||
|
# 所以只原样拷进快照,不进医生目录(负责人 2026-10-17)。
|
||||||
|
SKIPPED_TABLE = ("skipped.csv", "跳过病例.csv")
|
||||||
# TIC参数.csv 的两组(tic.json 里 perfusion_metrics 的键,按列的顺序)和 11 行:
|
# TIC参数.csv 的两组(tic.json 里 perfusion_metrics 的键,按列的顺序)和 11 行:
|
||||||
# tic.json 里的键、中文名加缩写、单位。时间类(单位秒)保留 1 位小数,其余 2 位。
|
# tic.json 里的键、中文名加缩写、单位。时间类(单位秒)保留 1 位小数,其余 2 位。
|
||||||
TIC_GROUPS = ("roi_pan", "roi_perf")
|
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)
|
(pack / "标准答案").mkdir(parents=True)
|
||||||
for name, target in ANSWER_TABLES.items():
|
for name, target in ANSWER_TABLES.items():
|
||||||
_copy(verified_file(answers, name), pack / "标准答案" / target)
|
_copy(verified_file(answers, name), pack / "标准答案" / target)
|
||||||
|
_copy(verified_file(answers, SKIPPED_TABLE[0]), snapshot / SKIPPED_TABLE[1])
|
||||||
|
|
||||||
# failed.csv:病例号(三位数字)→(视频号,第几步,原因原文)。
|
# failed.csv:病例号(三位数字)→(视频号,第几步,原因原文)。
|
||||||
failures: dict[str, tuple[str, int, str]] = {}
|
failures: dict[str, tuple[str, int, str]] = {}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ $ceus_note
|
|||||||
|
|
||||||
## 一、包里有什么
|
## 一、包里有什么
|
||||||
|
|
||||||
- **说明.pdf**(这份说明)、**问题清单.csv**、**病例总表.csv**、**标准答案/**(4 张表):几张表见第六节。
|
- **说明.pdf**(这份说明)、**问题清单.csv**、**病例总表.csv**、**标准答案/**(3 张表):几张表见第六节。
|
||||||
- **病例/**:每个进了资料库的病例一个文件夹,名字是病例号(如 P001)。打开能看到:
|
- **病例/**:每个进了资料库的病例一个文件夹,名字是病例号(如 P001)。打开能看到:
|
||||||
- **临床信息.csv**:临床那位 AI 专家看到的临床信息,列名沿用你们临床工作簿的原列名。$codes_note
|
- **临床信息.csv**:临床那位 AI 专家看到的临床信息,列名沿用你们临床工作簿的原列名。$codes_note
|
||||||
- **超声报告.pdf**、**MR报告.pdf**:报告里的描述部分,一字未改。结论(印象)部分写着诊断倾向,没有给 AI 专家,这里也就没有。
|
- **超声报告.pdf**、**MR报告.pdf**:报告里的描述部分,一字未改。结论(印象)部分写着诊断倾向,没有给 AI 专家,这里也就没有。
|
||||||
@@ -87,5 +87,4 @@ TIC参数.csv 每行一个参数,两组数并排:**胰腺实质(紫圈)*
|
|||||||
- **病例总表.csv**:全部病例一例一行:进没进资料库、没进的原因、造影视频处理成功没有(没成功时写停在第几步)、7 个关键帧各在视频第几秒。
|
- **病例总表.csv**:全部病例一例一行:进没进资料库、没进的原因、造影视频处理成功没有(没成功时写停在第几步)、7 个关键帧各在视频第几秒。
|
||||||
- **标准答案/化名对照.csv**:资料库里代替病例号的化名与病例号的对照。
|
- **标准答案/化名对照.csv**:资料库里代替病例号的化名与病例号的对照。
|
||||||
- **标准答案/诊断.csv**:三张表的病理诊断编码(1 IPMN,2 SPN,3 MCN,4 SCN)。
|
- **标准答案/诊断.csv**:三张表的病理诊断编码(1 IPMN,2 SPN,3 MCN,4 SCN)。
|
||||||
- **标准答案/跳过病例.csv**:内容和问题清单里没进资料库的那几条一样,是程序原文版本,一般不用看。
|
|
||||||
- **标准答案/报告复核句.csv**:报告描述里含「考虑」「可能」「倾向」「提示」的句子。这些句子目前仍留在给 AI 专家的报告里,想请你们看看它们算不算带出了诊断倾向、该不该删掉。
|
- **标准答案/报告复核句.csv**:报告描述里含「考虑」「可能」「倾向」「提示」的句子。这些句子目前仍留在给 AI 专家的报告里,想请你们看看它们算不算带出了诊断倾向、该不该删掉。
|
||||||
|
|||||||
@@ -89,7 +89,6 @@ BLANK = [""] * 11
|
|||||||
ANSWER_HEADERS = {
|
ANSWER_HEADERS = {
|
||||||
"化名对照.csv": ["化名", "病例号", "视频号"],
|
"化名对照.csv": ["化名", "病例号", "视频号"],
|
||||||
"诊断.csv": ["化名", "病例号", "临床表诊断编码", "超声表诊断编码", "MR表诊断编码", "诊断名"],
|
"诊断.csv": ["化名", "病例号", "临床表诊断编码", "超声表诊断编码", "MR表诊断编码", "诊断名"],
|
||||||
"跳过病例.csv": ["病例", "检查编号", "检查说明", "文件", "列", "涉及值"],
|
|
||||||
"报告复核句.csv": ["化名", "路", "命中的词", "所在句子"],
|
"报告复核句.csv": ["化名", "路", "命中的词", "所在句子"],
|
||||||
}
|
}
|
||||||
CASE_FILES = {"临床信息.csv", "超声报告.pdf", "MR报告.pdf", "关键帧"}
|
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:
|
def test_every_csv_has_bom_and_no_placeholder_for_missing(packed: Packed) -> None:
|
||||||
csvs = sorted(packed.pack.rglob("*.csv"))
|
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 path in csvs:
|
||||||
for row in _csv(path):
|
for row in _csv(path):
|
||||||
assert not {"null", "None", "NaN", "nan"} & set(row), 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 的写法留在快照里,不进医生的目录。"""
|
"""程序原文照任务书 03 的写法留在快照里,不进医生的目录。"""
|
||||||
assert not (packed.pack / "问题清单_原文.csv").exists()
|
assert not (packed.pack / "问题清单_原文.csv").exists()
|
||||||
rows = _csv(packed.result.run_dir / "snapshot" / "问题清单_原文.csv")
|
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]
|
fit_failed = [f"{slot} 未取到:{FIT_FAILED}" for slot in DYNAMIC]
|
||||||
p008 = {
|
p008 = {
|
||||||
"B0": "视频开头没有观测到稳定的增强前基线(基线是估计的),B0 不定义",
|
"B0": "视频开头没有观测到稳定的增强前基线(基线是估计的),B0 不定义",
|
||||||
@@ -551,12 +550,19 @@ def test_copies_are_byte_identical(packed: Packed) -> None:
|
|||||||
for source, name in (
|
for source, name in (
|
||||||
("aliases.csv", "化名对照.csv"),
|
("aliases.csv", "化名对照.csv"),
|
||||||
("diagnoses.csv", "诊断.csv"),
|
("diagnoses.csv", "诊断.csv"),
|
||||||
("skipped.csv", "跳过病例.csv"),
|
|
||||||
("review.csv", "报告复核句.csv"),
|
("review.csv", "报告复核句.csv"),
|
||||||
):
|
):
|
||||||
assert (packed.pack / "标准答案" / name).read_bytes() == (answers / source).read_bytes()
|
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:
|
def _tool_output(tool: str, *args: str) -> str:
|
||||||
path = shutil.which(tool)
|
path = shutil.which(tool)
|
||||||
assert path is not None, f"找不到 {tool}"
|
assert path is not None, f"找不到 {tool}"
|
||||||
|
|||||||
Reference in New Issue
Block a user