新六章有 12 节读错公开包字段,整节只剩未判定,而且六章绕过了口径替换。按资料包改读路径,先拼六章再统一替换措辞,并把中文附录实际丢掉的组合句数出来。

Co-Authored-By: Grok (agent loop)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
jesse-ux
2026-10-22 18:51:54 +08:00
co-authored by Grok (agent loop) Cursor
parent 365b1e647e
commit 92b4e220f2
8 changed files with 647 additions and 135 deletions
+4
View File
@@ -278,6 +278,9 @@ def render_english(index: int) -> dict:
edition = _english_edition(packet, True)
chinese = render_full_data_markdown(packet)
gates = chinese_prose_gates(chinese)
from scripts.report_render.appendix_language import snapshot_appendix_drop_log
drops = snapshot_appendix_drop_log()
markdown = edition.get("markdown_en") or ""
flags = _flags_from_packet(packet)
applicability = edition.get("reader_dasha_applicability_en") or ""
@@ -298,6 +301,7 @@ def render_english(index: int) -> dict:
"missing": rendered.count("[translation missing:"),
"zh_english_signs": gates["english_signs"],
"zh_han_function_glue": gates["han_function_glue"],
"appendix_drops": drops,
**_guard_snapshot(),
**flags,
}
+103
View File
@@ -6,6 +6,7 @@ are separate headings. Fictional charts only.
from __future__ import annotations
import copy
import re
import pytest
@@ -142,6 +143,108 @@ def test_three_fictional_charts_render_the_same_chapters(fictional_reports: dict
assert "blocked" not in zh.lower()
_UNDETERMINED = {"zh": "未判定", "en": "Not determined"}
_GAP_LINE = {
"zh": "公开报告资料包未带此模块",
"en": "This module is not in the public report packet",
}
# These four modules are absent from the public worksheet. The reason is why
# the section may stay undetermined. Anything else must show a real cell.
_PACKET_GAPS = {
"Navamsha AD Duration": "navamsha_dasha is computed on the reading but is not copied into the public worksheet",
"Pancha Pakshi": "pancha_pakshi is not copied into the public worksheet",
"Rashi Tulya Navamsa": "rashi_tulya_navamsa is not copied into the public worksheet",
"Current Dasha Convergence": "dasa_convergence per-domain rows are not copied into the public worksheet",
}
def _table_blocks(body: str) -> list[list[list[str]]]:
blocks = []
current: list[str] = []
for line in body.splitlines():
if line.startswith("|"):
current.append(line)
continue
if current:
blocks.append(current)
current = []
if current:
blocks.append(current)
parsed = []
for block in blocks:
rows = []
for index, line in enumerate(block):
cells = [cell.strip() for cell in line.strip().strip("|").split("|")]
if cells and all(set(cell) <= set("-: ") and cell for cell in cells):
continue
if index == 0:
continue
rows.append(cells)
parsed.append(rows)
return parsed
def _section_has_real_cell(body: str, undetermined: str) -> bool:
blocks = _table_blocks(body)
if blocks:
return any(
cell and cell != undetermined
for rows in blocks
for row in rows
for cell in row
)
prose = [line.strip() for line in body.splitlines() if line.strip()]
return any(line and line != undetermined for line in prose)
def test_every_section_has_a_real_cell_except_the_four_public_gaps(fictional_reports: dict[int, tuple[str, str]]) -> None:
titles = dict(SECTIONS)
for reason in _PACKET_GAPS.values():
assert reason
for index, (chinese, english) in fictional_reports.items():
for language, markdown in (("zh", chinese), ("en", english)):
chapter = _chapter_text(markdown, language)
for title_en, title_zh in SECTIONS:
heading = title_zh if language == "zh" else title_en
body = _section_body(chapter, heading)
if title_en in _PACKET_GAPS:
first = next(line.strip() for line in body.splitlines() if line.strip())
assert first == _GAP_LINE[language], f"{index} {heading}"
continue
assert _section_has_real_cell(body, _UNDETERMINED[language]), f"{index} {heading}"
def test_parity_chapters_go_through_local_conventions() -> None:
from scripts.pl9_full_data_export import _apply_local_report_conventions
packet = {
"report_language": "zh",
"worksheets": {
"strengths_and_scores": {
"vimsopaka": {
"Sun": {"planet": "Sun", "total_score": 12.5, "category": "必然"},
}
}
},
}
chinese = render_full_data_markdown(packet)
zh_body = _section_body(chinese, "Vimsopaka 分盘贡献")
zh_replaced = _apply_local_report_conventions("必然", language="zh")
assert zh_replaced == "可能"
assert zh_replaced in zh_body
assert "必然" not in zh_body
english_packet = copy.deepcopy(packet)
english_packet["report_language"] = "en"
english_packet["worksheets"]["strengths_and_scores"]["vimsopaka"]["Sun"]["category"] = "divorce"
english = render_full_data_markdown(english_packet)
en_body = _section_body(english, "Vimsopaka Contribution")
en_replaced = _apply_local_report_conventions("divorce", language="en")
assert en_replaced == "relationship strain"
assert en_replaced in en_body
assert "divorce" not in en_body.lower()
def test_chart_nine_partial_kaal_and_friendship(fictional_reports: dict[int, tuple[str, str]]) -> None:
chinese, english = fictional_reports[9]
assert _KAAL_ZH.search(chinese)
+86 -1
View File
@@ -3,24 +3,109 @@
This file is not on the quick gate. Windows timing is recorded in
docs/tasks/PROGRESS-report-english-han-leak-20261021.md. Set
REPORT_ENGLISH_SWEEP_DUMP to a directory to write the English markdown for
the acceptance pass. Workers stay at 4.
the acceptance pass. Workers stay at 4. Set J4_DROP_STATS to a json path to
write the Chinese-appendix drop counts from the same run.
"""
from __future__ import annotations
import json
import os
from concurrent.futures import ProcessPoolExecutor, as_completed
from pathlib import Path
from tests.report_english_fictional import CHART_COUNT, init_english_worker, render_english, row_problem
def _j4_summary(rows: list[dict]) -> dict:
annual_names: dict[str, int] = {}
annual_charts = []
annual_tables = 0
effect_entries = 0
effect_calls = 0
effect_charts = []
effect_keys: dict[str, int] = {}
combination_entries = 0
combination_calls = 0
combination_charts = []
combination_keys: dict[str, int] = {}
replaced_entries = 0
replaced_charts = []
replaced_keys: dict[str, int] = {}
for row in rows:
drops = row.get("appendix_drops") if isinstance(row.get("appendix_drops"), dict) else {}
annual = drops.get("annual_columns") if isinstance(drops.get("annual_columns"), dict) else {}
tables = drops.get("annual_tables") if isinstance(drops.get("annual_tables"), dict) else {}
effects = drops.get("effects") if isinstance(drops.get("effects"), dict) else {}
combinations = drops.get("combinations_undetermined") if isinstance(drops.get("combinations_undetermined"), dict) else {}
replaced = drops.get("combinations_replaced") if isinstance(drops.get("combinations_replaced"), dict) else {}
if annual:
annual_charts.append(row.get("i"))
for name, count in annual.items():
annual_names[name] = annual_names.get(name, 0) + int(count)
annual_tables += int(tables.get("seen") or 0)
if effects:
effect_charts.append(row.get("i"))
effect_entries += len(effects)
effect_calls += sum(int(count) for count in effects.values())
for key, count in effects.items():
effect_keys[key] = effect_keys.get(key, 0) + int(count)
if combinations:
combination_charts.append(row.get("i"))
combination_entries += len(combinations)
combination_calls += sum(int(count) for count in combinations.values())
for key, count in combinations.items():
combination_keys[key] = combination_keys.get(key, 0) + int(count)
if replaced:
replaced_charts.append(row.get("i"))
replaced_entries += len(replaced)
for key, count in replaced.items():
replaced_keys[key] = replaced_keys.get(key, 0) + int(count)
return {
"charts": len(rows),
"annual_columns": annual_names,
"annual_chart_count": len(annual_charts),
"annual_tables_seen": annual_tables,
"effect_entries": effect_entries,
"effect_calls": effect_calls,
"effect_chart_count": len(effect_charts),
"effect_keys": effect_keys,
"combination_undetermined_entries": combination_entries,
"combination_undetermined_calls": combination_calls,
"combination_undetermined_chart_count": len(combination_charts),
"combination_undetermined_keys": combination_keys,
"combination_replaced_entries": replaced_entries,
"combination_replaced_chart_count": len(replaced_charts),
"combination_replaced_keys": replaced_keys,
}
def test_two_hundred_fictional_charts_have_clean_english() -> None:
workers = min(4, os.cpu_count() or 1, CHART_COUNT)
failures = []
rows = []
with ProcessPoolExecutor(max_workers=workers, initializer=init_english_worker) as pool:
futures = [pool.submit(render_english, index) for index in range(CHART_COUNT)]
for future in as_completed(futures):
row = future.result()
rows.append(row)
problem = row_problem(row)
if problem:
failures.append(problem)
summary = _j4_summary(rows)
print("J4_APPENDIX_DROPS " + json.dumps({
"annual_columns": summary["annual_columns"],
"annual_chart_count": summary["annual_chart_count"],
"annual_tables_seen": summary["annual_tables_seen"],
"effect_entries": summary["effect_entries"],
"effect_calls": summary["effect_calls"],
"effect_chart_count": summary["effect_chart_count"],
"combination_undetermined_entries": summary["combination_undetermined_entries"],
"combination_undetermined_calls": summary["combination_undetermined_calls"],
"combination_undetermined_chart_count": summary["combination_undetermined_chart_count"],
"combination_replaced_entries": summary["combination_replaced_entries"],
"combination_replaced_chart_count": summary["combination_replaced_chart_count"],
}, ensure_ascii=False))
stats_path = os.environ.get("J4_DROP_STATS")
if stats_path:
Path(stats_path).write_text(json.dumps(summary, ensure_ascii=False, indent=2), encoding="utf-8")
assert failures == []