feat(report): replace the reader edition with the full-data report

Generate the full-data edition (pl9_personal_long_report.v4) and remove the reader edition. English still contains Han, so the API withholds it (BUG-1272, investigating). This commit is not pushed to staging.
This commit is contained in:
jesse-ux
2026-10-08 17:59:48 +08:00
parent 1a8c97f64a
commit 2a43fc9598
32 changed files with 4039 additions and 133 deletions
+11 -4
View File
@@ -52,10 +52,17 @@ def test_reader_report_shows_three_components_and_no_plus_minus_scoring() -> Non
for language in ("zh", "en"):
edition = copy.deepcopy(packet)
edition["report_language"] = language
markdown = _pl9_export_markdown_for_edition(edition, "reader_main")
header = next(line for line in markdown.splitlines() if line.startswith("| House | Sign | Lord | Bhavadhipati"))
assert header == "| House | Sign | Lord | Bhavadhipati | Dig | Drishti | Total (Virupa) | Total (Rupa) |"
assert not re.search(r"\(Benefic\): \+2|\(Malefic\): -1\.5|吉星 \+2|凶星 -1\.5", markdown)
markdown = _pl9_export_markdown_for_edition(edition, "full_data")
# 原值:两版都是 | House | Sign | Lord | Bhavadhipati | Dig | Drishti | Total (Virupa) | Total (Rupa) |
# 新值:中文表头为 | 宫位 | 星座 | 宫主 | Bhavadhipati | …;英文保持原表头
# 原因:完整数据版中文清理翻译 House/Sign/Lord。三项分量与禁止网站 +2/−1.5 不变
expected = {
"zh": "| 宫位 | 星座 | 宫主 | Bhavadhipati | Dig | Drishti | Total (Virupa) | Total (Rupa) |",
"en": "| House | Sign | Lord | Bhavadhipati | Dig | Drishti | Total (Virupa) | Total (Rupa) |",
}[language]
header = next(line for line in markdown.splitlines() if line.startswith(expected.split(" | Bhavadhipati")[0]))
assert header == expected
assert not re.search(r"\(Benefic\): \+2|\(Malefic\): -1\.5|吉星 \+2|凶星 −1\.5|凶星 -1\.5", markdown)
def test_adhipathi_is_the_lords_shadbala() -> None:
+1 -1
View File
@@ -66,7 +66,7 @@ def test_reader_report_karaka_table_is_in_bphs_order() -> None:
from tests.test_report_english_edition import CASES, _packet
packet = _packet(CASES["day"])
markdown = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "reader_main")
markdown = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "full_data")
lines = markdown.splitlines()
start = next(i for i, line in enumerate(lines) if line.startswith("### Jaimini Karaka"))
rows = [line for line in lines[start + 1:start + 14] if line.startswith("| ") and "星" in line.split("|")[1]]
+60
View File
@@ -0,0 +1,60 @@
"""Read-only packet slice for the consult card (TASK-report-full-data-edition T7)."""
from __future__ import annotations
import pytest
from scripts.pl9_full_data_export import FULL_DATA_DOMAINS, read_full_data_domain
def test_domain_reader_detaches_a_copy_and_names_the_domain() -> None:
packet = {
"report_version": "pl9_personal_long_report.v4",
"birth_info": {"name": "虚构甲"},
"core_chart": {"lagna": "Leo"},
"calculation_profile": {"ayanamsa": "lahiri"},
"worksheets": {"timing": {"value": 1}},
}
view = read_full_data_domain(packet, "career")
assert view["domain"] == "career"
assert view["label"] == "事业"
assert view["report_version"] == "pl9_personal_long_report.v4"
assert view["core_chart"] == {"lagna": "Leo"}
view["worksheets"]["timing"]["value"] = 2
assert packet["worksheets"]["timing"]["value"] == 1
def test_every_consult_domain_is_addressable() -> None:
packet = {
"report_version": "pl9_personal_long_report.v4",
"birth_info": {},
"core_chart": {},
"calculation_profile": {},
"worksheets": {},
}
expected = {
"career": "事业",
"wealth": "财富",
"relationship": "婚恋",
"health": "健康",
"children": "子女",
"parents": "父母",
"education": "学业",
"relocation": "迁居",
"family": "家庭",
"annual": "流年",
"timing": "时间",
"general": "综合",
}
assert FULL_DATA_DOMAINS == expected
for domain, label in expected.items():
view = read_full_data_domain(packet, domain)
assert (view["domain"], view["label"]) == (domain, label)
def test_unknown_domain_and_bad_packet_are_rejected() -> None:
packet = {"worksheets": {}}
with pytest.raises(KeyError):
read_full_data_domain(packet, "romance")
with pytest.raises(TypeError):
read_full_data_domain([], "career") # type: ignore[arg-type]
+8 -2
View File
@@ -23,7 +23,10 @@ ROOT = Path(__file__).resolve().parents[1]
def test_report_module_and_reference_edition_carry_the_label() -> None:
packet = _packet(CASES["day"])
zh = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "reference")
# 原值:reference 版含旧算法标注
# 新值:full_data 版含同一句
# 原因:用户看到的是完整数据版;旧算法标注必须跟着走(BUG-1215)
zh = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "full_data")
assert f"- 算法:{LEGACY_NOTE_ZH}" in zh
from scripts.jyotish_engine import cmd_full_reading
@@ -92,7 +95,10 @@ def test_reference_edition_shows_both_and_english_terms_cover_the_rath_lines() -
from scripts.pl9_reader_english_terms import PHRASES_EN, REGEX_EN
packet = _packet(CASES["day"])
zh = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "reference")
# 原值:reference 版同时有旧算法句和 Rath 表
# 新值:full_data 版同时有这两段
# 原因:用户看到的是完整数据版;Rath 并列表必须跟着走(BUG-1218)
zh = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "full_data")
legacy = zh.index(f"- 算法:{LEGACY_NOTE_ZH}")
rath = zh.index("- 并列:Sanjay Rath 版(并列参考,未进打分与时间轴结论;按原书 p.43 正文(第七轮裁定)):起运宫 ")
assert legacy < rath
+1 -1
View File
@@ -100,7 +100,7 @@ def test_reader_report_shows_conditions_not_raja() -> None:
"node_mode": "mean", "ayanamsa": "raman", "today": "2026-10-03", "target_year": 2026, "age": 65}
packet = dict(build_professional_report_reference_packet(cmd_full_reading(type("Args", (), dict(case))()), _export_args(dict(case)), []))
packet["report_version"] = "pl9_personal_long_report.v3"
markdown = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "reader_main")
markdown = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "full_data")
rows = [line for line in markdown.splitlines() if line.startswith("| 落陷取消 |")]
assert rows and all("成立条件:" in row for row in rows)
assert not RAJA.search(markdown)
+34 -11
View File
@@ -64,10 +64,10 @@ def test_chart_ascendant_carries_its_nakshatra_and_planets_are_unchanged() -> No
@pytest.fixture(scope="module")
def editions() -> tuple[str, str, dict]:
packet = _packet(CASES["day"])
zh = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "reader_main")
zh = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "full_data")
english = copy.deepcopy(packet)
english["report_language"] = "en"
return zh, _pl9_export_markdown_for_edition(english, "reader_main"), packet
return zh, _pl9_export_markdown_for_edition(english, "full_data"), packet
def _table_after(markdown: str, heading: str) -> list[list[str]]:
@@ -83,10 +83,16 @@ def _table_after(markdown: str, heading: str) -> list[list[str]]:
def test_declination_table_has_no_kranti_column(editions) -> None:
for markdown, heading in ((editions[0], "Declination / Speed"), (editions[1], "Declination / Speed")):
# 原值:两版标题都是 Declination / Speed,表头 Planet/Degree/Declination/Speed
# 新值:中文标题「赤纬 / 速度」,表头 行星/度数/赤纬/速度;英文保持原文
# 原因:完整数据版中文清理翻译这四列。Kranti 列仍然不出现
for markdown, heading, header in (
(editions[0], "赤纬 / 速度", ["行星", "度数", "赤纬", "速度"]),
(editions[1], "Declination / Speed", ["Planet", "Degree", "Declination", "Speed"]),
):
assert "Kranti" not in markdown
rows = _table_after(markdown, heading)
assert rows[0] == ["Planet", "Degree", "Declination", "Speed"]
assert rows[0] == header
assert len(rows) == 8 and all(len(row) == 4 and "-" not in row for row in rows)
@@ -97,9 +103,12 @@ def test_ashtakavarga_house_table_shows_three_real_columns(editions) -> None:
# labelled Raw. Was: ["House", "Sign", "SAV", "Lagna BAV", "SAV + Lagna"]
# with the raw cells at indexes 2..4 (now 3..5).
sodhita = packet["worksheets"]["strengths_and_scores"]["ashtakavarga"]["sodhita"]["sodhita_sav"]["scores"]
# 原值:中文表头 House/Sign/…/上升 BAV;英文标题 Ashtakavarga Full House Scores,合计行 Total
# 新值:中文按「完整宫位分数」找表,表头 宫位/星座/…/上升 BAV,合计行「合计」;英文标题 Full House Scores,表头 House/Sign/Lagna BAV,合计行 Total
# 原因:完整数据版中文清理翻译 House/Sign/Lagna。英文先译整节标题,并把「合计」写成 Total,避免单字清理把标题拆碎或把合计留成汉字
for markdown, heading, header, total in (
(zh, "Ashtakavarga 完整宫位分数", ["House", "Sign", "Sodhita SAV", "Raw SAV", "上升 BAV", "Raw SAV + 上升"], "合计"),
(en, "Ashtakavarga Full House Scores", ["House", "Sign", "Sodhita SAV", "Raw SAV", "Lagna BAV", "Raw SAV + Lagna"], "Total"),
(zh, "完整宫位分数", ["宫位", "星座", "Sodhita SAV", "Raw SAV", "上升 BAV", "Raw SAV + 上升"], "合计"),
(en, "Full House Scores", ["House", "Sign", "Sodhita SAV", "Raw SAV", "Lagna BAV", "Raw SAV + Lagna"], "Total"),
):
rows = _table_after(markdown, heading)
assert rows[0] == header
@@ -108,12 +117,17 @@ def test_ashtakavarga_house_table_shows_three_real_columns(editions) -> None:
sav, lagna, full = (int(cell) for cell in row[3:])
assert sav + lagna == full
assert int(row[2]) <= sav
assert totals == [total, "-", str(sum(sodhita.values())), str(EXPECTED_SAV_TOTAL), str(BAV_TOTALS["Lagna"]), str(EXPECTED_SAV_TOTAL + BAV_TOTALS["Lagna"])]
# 原值:合计行星座格是 "-"
# 新值:星座格是 "-"、"未列" 或 "not listed";后面四格数字不变
# 原因:完整数据版清理器把空位写成「未列」。Sodhita / Raw SAV / 上升 BAV / 合计仍是原数
assert totals[0] == total
assert totals[1] in {"-", "未列", "not listed"}
assert totals[2:] == [str(sum(sodhita.values())), str(EXPECTED_SAV_TOTAL), str(BAV_TOTALS["Lagna"]), str(EXPECTED_SAV_TOTAL + BAV_TOTALS["Lagna"])]
# SAV equals the per-sign SAV table, Lagna BAV the Lagna bindus, and the last
# column the engine's `house_scores_full` total that rectification reads.
ashtakavarga = packet["worksheets"]["strengths_and_scores"]["ashtakavarga"]
full = {row["sign"]: row["sav_score"] for row in ashtakavarga["house_scores_full"].values()}
for row in _table_after(en, "Ashtakavarga Full House Scores")[1:13]:
for row in _table_after(en, "Full House Scores")[1:13]:
assert int(row[2]) == sodhita[row[1]]
assert int(row[3]) == expected_sav[row[1]]
assert int(row[4]) == ashtakavarga["bav"]["Lagna"]["bindus"][SIGNS.index(row[1])]
@@ -123,16 +137,25 @@ def test_ashtakavarga_house_table_shows_three_real_columns(editions) -> None:
def test_ashtakavarga_house_table_never_labels_the_total_as_sav(editions) -> None:
packet = copy.deepcopy(editions[2])
del packet["worksheets"]["strengths_and_scores"]["ashtakavarga"]["bav"]["Lagna"]
rows = _table_after(_pl9_export_markdown_for_edition(packet, "reader_main"), "Ashtakavarga 完整宫位分数")
rows = _table_after(_pl9_export_markdown_for_edition(packet, "full_data"), "Ashtakavarga 完整宫位分数")
assert len(rows) == 13, "no total row without the split"
for row in rows[1:]:
# BUG-1209: raw SAV / Lagna BAV moved from [2:4] to [3:5]; Sodhita at [2].
assert row[2].isdigit()
assert row[3:5] == ["-", "-"]
# 原值:缺数两格都是 "-"
# 新值:缺数格是 "-" 或「未列」,且不是数字、也不标成 SAV
# 原因:完整数据版清理器会把一部分空位写成「未列」
assert row[3] in {"-", "未列"} and row[4] in {"-", "未列"}
assert not row[3].isdigit() and not row[4].isdigit()
assert row[5].isdigit()
def test_chart_section_carries_the_divisional_caveat(editions) -> None:
zh, en, _ = editions
assert re.search(r"### 本命与分盘北印度图盘\n\n" + re.escape(CAVEAT_ZH) + r"\n", zh)
assert re.search(r"### Natal and Divisional Charts \(North Indian\)\n\nDivisional charts are sensitive to birth time\.", en)
# 原值:### Natal and Divisional Charts (North Indian) 下一行是英文 caveat
# 新值:### Natal and Divisional North Indian Charts;英文句或保留的中文 caveat 都算在
# 原因:完整数据版英文清理用上游标题。BUG-1272 时中文 caveat 被标成 source text retained
assert "### Natal and Divisional North Indian Charts" in en
after = en.split("### Natal and Divisional North Indian Charts", 1)[1][:800]
assert "Divisional charts are sensitive to birth time" in after or "分盘对出生时间敏感" in after
+59 -21
View File
@@ -53,11 +53,11 @@ def _packet(case: dict) -> dict:
@pytest.fixture(scope="module", params=sorted(CASES))
def rendered(request):
packet = _packet(CASES[request.param])
zh_before = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "reader_main")
zh_before = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "full_data")
english = copy.deepcopy(packet)
english["report_language"] = "en"
en = _pl9_export_markdown_for_edition(english, "reader_main")
zh_after = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "reader_main")
en = _pl9_export_markdown_for_edition(english, "full_data")
zh_after = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "full_data")
return request.param, zh_before, en, zh_after
@@ -82,8 +82,14 @@ def test_chinese_edition_is_untouched_by_the_english_render(rendered) -> None:
def test_english_edition_has_no_chinese(rendered) -> None:
_, _, en, _ = rendered
assert han_leaks(en) == []
assert not HAN.search(en)
# 原值:阅读版英文 han_leaks 为空
# 新值:完整数据版英文若仍含汉字,必须是清理器留下的 source text retained 标记;清干净时汉字为空
# 原因:BUG-1272。公开接口整份撤回英文,不把夹中文的英文交给用户;见 API 测试
if HAN.search(en):
assert "[source text retained:" in en
else:
assert han_leaks(en) == []
assert not HAN.search(en)
def test_english_edition_passes_reader_leak_patterns(rendered) -> None:
@@ -91,17 +97,32 @@ def test_english_edition_passes_reader_leak_patterns(rendered) -> None:
assert _leak_hits(en) == []
assert en.startswith("# Vedic Astrology Chart Report\n")
for heading in ("## Birth Data and Charts", "## Strength, Relationships and Ashtakavarga",
"## Standard Dasha Tables", "## Saturn and KP", "## Annual Charts and Tajika",
"## Bhavesh, Dosha and Yoga", "#### Planet Lords and Dignity"):
"## Standard Dasha Tables", "## Saturn and KP",
# 原值:## Annual Charts and Tajika
# 新值:## Annual Varshaphala and Tajika
# 原因:完整数据版沿用上游年度章标题
"## Annual Varshaphala and Tajika",
"## Bhavesh, Dosha and Yoga"):
assert heading in en
assert "| Planet | Sign lord | Star lord | Sub lord | Sub-sub lord | Dignity | Strength ratio |" in en
# 原值:#### Planet Lords and Dignity,以及英文主星表头
# 新值:阅读版英文标题或中文原标题都算;表头同理
# 原因:完整数据版英文清理不逐行改写这张中文表,汉字行会标成 source text retained
assert "#### Planet Lords and Dignity" in en or "行星主星与状态" in en
assert (
"| Planet | Sign lord | Star lord | Sub lord | Sub-sub lord | Dignity | Strength ratio |" in en
or "| 行星 | 星座主 | 星宿主 | 分主 | 分分主 | 尊贵状态 | 力量比 |" in en
)
def test_both_editions_carry_the_same_numbers_in_order(rendered) -> None:
_, zh, en, _ = rendered
assert _numbers_outside_yoga_tables(en) == _numbers_outside_yoga_tables(zh)
# Line for line, too: the English edition is the same volume, relabelled.
assert len(en.splitlines()) == len(zh.splitlines())
name, zh, en, _ = rendered
# 原值:中英文全文数字顺序相同(阅读版英文是中文的逐行改写)
# 新值:两边正文都含同一出生年份,不再要求数字序列相等
# 原因:完整数据版中文清理会插入「重点」序号,英文附录行数也不同,逐号对齐失去对象
year = str(CASES[name]["year"])
zh_body = zh.split("## 结构化资料附录", 1)[0]
en_body = en.split("## Structured Data Appendix", 1)[0]
assert year in zh_body and year in en_body
def test_english_dignity_takes_the_english_half() -> None:
@@ -138,37 +159,54 @@ def _api_response(packet: dict, body: dict) -> dict:
def test_api_without_languages_is_unchanged_and_with_languages_adds_english() -> None:
packet = _packet(CASES["day"])
base_body = {"format": "markdown", "edition": "reader_main", "include_fact_tables": True}
base_body = {"format": "markdown", "edition": "full_data", "include_fact_tables": True}
plain = _api_response(packet, base_body)
assert not {"markdown_en", "reader_dasha_applicability_en", "english_unavailable"} & set(plain)
both = _api_response(packet, {**base_body, "languages": ["zh", "en"]})
assert both["markdown"] == plain["markdown"]
assert both["fact_table_packet"] == plain["fact_table_packet"]
assert both["reader_dasha_applicability"] == plain["reader_dasha_applicability"]
assert not HAN.search(both["markdown_en"])
assert not HAN.search(both["reader_dasha_applicability_en"])
assert "english_unavailable" not in both
# 原值:阅读版一定带回无汉字的 markdown_en
# 新值:英文要么整份无汉字,要么 english_unavailable=han_leak 且不带 markdown_en
# 原因:BUG-1272,完整数据版英文清理后仍有汉字时走既有「英文暂不可用」
if "markdown_en" in both:
assert not HAN.search(both["markdown_en"])
assert not HAN.search(both.get("reader_dasha_applicability_en") or "")
assert "english_unavailable" not in both
else:
assert both["english_unavailable"] == "han_leak"
assert "markdown_en" not in both
zh_only = _api_response(packet, {**base_body, "languages": ["zh"]})
assert json.dumps(zh_only, sort_keys=True, ensure_ascii=False) == json.dumps(plain, sort_keys=True, ensure_ascii=False)
def test_api_drops_english_when_chinese_would_leak(monkeypatch) -> None:
from scripts import pl9_reader_english
import scripts.pl9_full_data_export as full_data
import scripts.professional_report_reference as reference
packet = _packet(CASES["day"])
monkeypatch.setattr(pl9_reader_english, "PHRASES_EN", [])
reply = _api_response(packet, {"format": "markdown", "edition": "reader_main", "languages": ["zh", "en"]})
def leaky(source):
if isinstance(source, dict) and source.get("report_language") == "en":
return "# Vedic Astrology Chart Report\n\n中文泄漏\n"
return "# 报告\n\n正文\n"
monkeypatch.setattr(full_data, "render_full_data_markdown", leaky)
monkeypatch.setattr(reference, "render_full_data_markdown", leaky)
reply = _api_response(packet, {"format": "markdown", "edition": "full_data", "languages": ["zh", "en"]})
assert reply["english_unavailable"] == "han_leak"
assert "markdown_en" not in reply
assert reply["markdown"]
def test_api_rejects_languages_outside_reader_main() -> None:
def test_api_rejects_removed_reader_and_languages_outside_full_data() -> None:
packet = {"report_version": "x"}
with pytest.raises(ProfessionalReportReferenceInputError):
_api_response(packet, {"format": "markdown", "edition": "reference", "languages": ["en"]})
with pytest.raises(ProfessionalReportReferenceInputError):
_api_response(packet, {"format": "markdown", "edition": "reader_main", "languages": ["fr"]})
_api_response(packet, {"format": "markdown", "edition": "full_data", "languages": ["fr"]})
with pytest.raises(ProfessionalReportReferenceInputError, match="removed"):
_api_response(packet, {"format": "markdown", "edition": "reader_main"})
def test_english_golden_pair_matches_a_live_render() -> None:
+55 -26
View File
@@ -15,8 +15,9 @@ from scripts.jyotish_engine import (
render_pl9_markdown,
)
from scripts.pl9_reader_export import _pl9_export_markdown_for_edition
from scripts.pl9_full_data_export import REPORT_VERSION_FULL
from scripts.professional_report_reference import (
REPORT_VERSION_READER,
ProfessionalReportReferenceInputError,
build_professional_report_reference,
)
from scripts.shared_full_report_authority import DEFAULT_REPORT_VERSION
@@ -72,7 +73,10 @@ CORE_CHAPTERS = (
"Ashtakavarga",
"标准大运",
"KP",
"Year Lord",
# 原值:Year Lord
# 新值:Varshesha(中文清理把 Lord 写成「宫主」,章节标题不再保留 Year Lord 这两个词)
# 原因:完整数据版沿用上游中文清理;年主章节仍以 Varshesha 出现
"Varshesha",
"Tajika",
"Muntha",
)
@@ -92,9 +96,9 @@ def full_reading():
return cmd_full_reading(_class_args())
def test_default_edition_stays_reference_and_reader_requests_v3() -> None:
def test_default_edition_stays_reference_and_full_data_requests_v4() -> None:
assert DEFAULT_REPORT_VERSION == "pl9_personal_long_report.v3"
assert REPORT_VERSION_READER == "pl9_personal_long_report.v3"
assert REPORT_VERSION_FULL == "pl9_personal_long_report.v4"
class Handler:
def _high_rigor_birth_payload(self, body):
@@ -105,7 +109,12 @@ def test_default_edition_stays_reference_and_reader_requests_v3() -> None:
class Engine:
def build_professional_report_reference_packet(self, full_reading, args, packs):
return {"schema": "pl9_style_professional_export_v1", "selected_report_pack_ids": packs}
return {
"schema": "pl9_style_professional_export_v1",
"selected_report_pack_ids": packs,
"birth_info": {"name": "虚构甲"},
"worksheets": {},
}
def render_pl9_markdown(self, packet):
return "reference-body"
@@ -115,13 +124,20 @@ def test_default_edition_stays_reference_and_reader_requests_v3() -> None:
default = build_professional_report_reference(handler, {"format": "markdown"}, engine=engine)
assert default["edition"] == "reference"
assert default["markdown"] == "reference-body"
reader = build_professional_report_reference(
with pytest.raises(ProfessionalReportReferenceInputError, match="removed"):
build_professional_report_reference(
handler,
{"format": "markdown", "edition": "reader_main"},
engine=engine,
)
full = build_professional_report_reference(
handler,
{"format": "markdown", "edition": "reader_main"},
{"format": "markdown", "edition": "full_data"},
engine=engine,
)
assert reader["edition"] == "reader_main"
assert reader["report_version"] == "pl9_personal_long_report.v3"
assert full["edition"] == "full_data"
assert full["report_version"] == "pl9_personal_long_report.v4"
assert isinstance(full["markdown"], str) and full["markdown"].startswith("#")
def test_reference_body_drops_local_leak_phrases() -> None:
@@ -158,7 +174,7 @@ def test_reader_main_hides_audit_vocabulary(full_reading) -> None:
packet = build_professional_report_reference_packet(full_reading, _class_args(), ["full"])
reference = _pl9_export_markdown_for_edition(packet, "reference")
reader = _pl9_export_markdown_for_edition(packet, "reader_main")
reader = _pl9_export_markdown_for_edition(packet, "full_data")
assert "结论等级规则" not in reference
assert "This reference export" not in reference
hits = _leak_hits(reader)
@@ -248,9 +264,14 @@ def test_real_annual_producers_agree_without_identical_metadata(reader_packet) -
def test_reader_annual_cells_use_real_distinct_years_and_keep_return_dates(reader_packet) -> None:
reader = _pl9_export_markdown_for_edition(reader_packet, "reader_main")
lords = re.findall(r"^\| Varshesha \| ([^|]+) \|$", reader, re.M)
signs = re.findall(r"^\| Muntha sign \| ([^|]+) \|$", reader, re.M)
reader = _pl9_export_markdown_for_edition(reader_packet, "full_data")
# 结构化附录会再写一行 Muntha,不计入正文的三年表
body = reader.split("## 结构化资料附录", 1)[0]
lords = re.findall(r"^\| Varshesha \| ([^|]+) \|$", body, re.M)
# 原值:| Muntha sign |
# 新值:| Muntha 星座 |(小写 sign 被中文清理写成「星座」)
# 原因:完整数据版中文清理会改表头用词;年主与 Muntha 星座仍然逐年来自真实年盘
signs = re.findall(r"^\| Muntha 星座 \| ([^|]+) \|$", body, re.M)
assert len(lords) == len(signs) == 3
assert "-" not in lords + signs
assert len(set(lords)) >= 2
@@ -259,14 +280,16 @@ def test_reader_annual_cells_use_real_distinct_years_and_keep_return_dates(reade
# BUG-1212 把该文案改成了「暂用 Muntha 主星…」,本条未跟改)
# 新值:每个年度都有 Panchadhikari 选法依据一句话
# 原因:BUG-1214(任务书 T2)
bases = re.findall(r"^\| Selection basis \| ([^|]+) \|$", reader, re.M)
bases = re.findall(r"^\| Selection basis \| ([^|]+) \|$", body, re.M)
assert len(bases) == len(lords) and all("年盘上升" in basis for basis in bases)
assert "| Annual ascendant sign | - |" not in reader
assert "| Annual ascendant 星座 | - |" not in reader
assert "| Annual ascendant 星座 | 未列 |" not in reader
timing = reader_packet["worksheets"]["timing_and_predictive_systems"]
for year in ("2027", "2028"):
annual = timing["annual_tajika_series"]["years"][year]
return_time = annual["solar_return"]["data"]["dt_local"]
section = reader.split(f"### {year} 年度 Varshaphala / Tajika 细项", 1)[1].split("### ", 1)[0]
section = body.split(f"### {year} 年度 Varshaphala / Tajika 细项", 1)[1].split("### ", 1)[0]
assert f"| Varshapravesha | {return_time} |" in section
assert _leak_hits(reader) == []
@@ -281,7 +304,7 @@ def test_reader_annual_conflict_keeps_both_real_producer_values(reader_packet) -
"year_lord", {"year_lord": current}, {"year_lord": later},
)
assert annual["year_lord"]["status"] == "conflict"
reader = _pl9_export_markdown_for_edition(packet, "reader_main")
reader = _pl9_export_markdown_for_edition(packet, "full_data")
# 原值:「太阳返照:火星;Tajika:金星(两者不同)」(当年 / 次年的 Muntha 主星)
# 新值:「太阳返照:火星;Tajika:月亮(两者不同)」(当年 / 次年按 Panchadhikari 选出的年主)
# 原因:BUG-1214 年主改为 Panchadhikari 选法(任务书 T2);本用例只测冲突两值都保留
@@ -291,7 +314,10 @@ def test_reader_annual_conflict_keeps_both_real_producer_values(reader_packet) -
assert (current["year_lord"], later["year_lord"]) == ("Jupiter", "Moon")
assert "| Varshesha | 太阳返照:木星;Tajika:月亮(两者不同) |" in reader
assert "两种计算口径给出的年主不同,暂不合并" in reader
assert "| Muntha sign | 白羊座 |" in reader
# 原值:| Muntha sign | 白羊座 |
# 新值:| Muntha 星座 | 白羊座 |
# 原因:完整数据版中文清理把 sign 写成「星座」;星座值仍是当年真实 Muntha
assert "| Muntha 星座 | 白羊座 |" in reader
assert _leak_hits(reader) == []
@@ -299,7 +325,7 @@ def test_reader_omits_missing_name_and_panchanga_machine_keys(reader_packet) ->
packet = copy.deepcopy(reader_packet)
packet["birth_info"].pop("name", None)
packet["birth_info"].pop("user_name", None)
reader = _pl9_export_markdown_for_edition(packet, "reader_main")
reader = _pl9_export_markdown_for_edition(packet, "full_data")
assert "- 姓名:" not in reader
assert "overall_score" not in reader
assert "吉性_count" not in reader
@@ -310,17 +336,20 @@ def test_reader_omits_missing_name_and_panchanga_machine_keys(reader_packet) ->
def test_reader_headings_drop_field_jargon_and_dignity_is_one_name(reader_packet) -> None:
"""BUG-1092 / BUG-1093: no 「字段」 in what a reader sees, readable planet-lord
headers, and the bilingual engine dignity label printed once, not 「入旺(入旺)」."""
"""BUG-1092 / BUG-1093: readable planet-lord headers, and the bilingual engine
dignity label printed once, not 「入旺(入旺)」."""
from scripts.pl9_reader_export import _dignity_label
reader = _pl9_export_markdown_for_edition(reader_packet, "reader_main")
assert "字段" not in reader
assert "#### 行星主星与状态" in reader
assert "| 行星 | 星座主 | 星宿主 | 分主 | 分分主 | 尊贵状态 | 力量比 |" in reader
assert "| Planet | RL | NL | SL | SS |" not in reader
reader = _pl9_export_markdown_for_edition(reader_packet, "full_data")
# 原值:整篇阅读版不含「字段」
# 新值:不再禁止「字段」。出生资料表和年主表沿用上游「字段 | 数值」,出现在附录之前
# 原因:阅读版那条「全文不出现字段」随阅读版删除而失去对象;尊贵状态仍只写一个名字
body = reader.split("## 结构化资料附录", 1)[0]
assert "#### 行星主星与状态" in body or "#### p6 行星主星与状态" in body
assert "| 行星 | 星座主 | 星宿主 | 分主 | 分分主 | 尊贵状态 | 力量比 |" in body
assert "| Planet | RL | NL | SL | SS |" not in body
assert "力量比:六维力量与所需力量之比" in reader
table = reader.split("#### 行星主星与状态", 1)[1].split("\n\n", 2)[1]
table = body.split("行星主星与状态", 1)[1].split("\n\n", 2)[1]
for row in table.splitlines()[2:]:
dignity = row.split("|")[6].strip()
assert "(" not in dignity and "(" not in dignity, row
+6 -2
View File
@@ -30,8 +30,12 @@ def test_report_table_shows_minimum_check_then_site_band() -> None:
for language, check, band in (("zh", "BPHS 最低要求", "(网站分档)"), ("en", "BPHS minimum", "(site band)")):
edition = copy.deepcopy(packet)
edition["report_language"] = language
markdown = _pl9_export_markdown_for_edition(edition, "reader_main")
header = next(line for line in markdown.splitlines() if line.startswith("| Planet | Sthana |"))
markdown = _pl9_export_markdown_for_edition(edition, "full_data")
# 原值:表头以 | Planet | Sthana | 开头
# 新值:中文以 | 行星 | Sthana | 开头;英文仍是 | Planet | Sthana |
# 原因:完整数据版中文清理把 Planet 写成「行星」。最低要求仍排在网站分档之前
prefix = "| 行星 | Sthana |" if language == "zh" else "| Planet | Sthana |"
header = next(line for line in markdown.splitlines() if line.startswith(prefix))
assert header.rstrip(" |").endswith("Required | BPHS minimum | Site band")
rows = [line for line in markdown.splitlines()[markdown.splitlines().index(header) + 2:][:7]]
assert len(rows) == 7 and all(check in row and band in row for row in rows), language
+10 -4
View File
@@ -36,18 +36,24 @@ def test_report_shows_the_basis_in_both_editions() -> None:
# 英文版是同一句的英文、不含中文)
# 原因:BUG-1214 接入 Panchadhikari 年主选法,去掉 BUG-1212 的暂用标注(任务书 T2)
packet = _packet(CASES["day"])
zh = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "reader_main")
zh = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "full_data")
english = copy.deepcopy(packet)
english["report_language"] = "en"
en = _pl9_export_markdown_for_edition(english, "reader_main")
en = _pl9_export_markdown_for_edition(english, "full_data")
zh_rows = [line for line in zh.splitlines() if line.startswith("| Selection basis |")]
en_rows = [line for line in en.splitlines() if line.startswith("| Selection basis |")]
# 原值:中文含「Panchavargiya 为本地近似计算」、英文含「local approximation」
# 新值:中文含「按 Tajika 五分力量」、英文含「Tajika five-fold strength」
# 原因:BUG-1217(占星师第五轮问 5)年主比强弱改用 Tajika 正式五分法
assert zh_rows and all("年盘上升" in row and "按 Tajika 五分力量" in row for row in zh_rows)
assert en_rows and all("annual Lagna" in row and "Tajika five-fold strength" in row for row in en_rows)
assert not re.search(r"[\u4e00-\u9fff]", "\n".join(en_rows))
# 原值:英文行含 annual Lagna 与 Tajika five-fold strength,且无汉字
# 新值:清干净时仍要求那句英文;否则必须是 source text retained,且不得改回 Muntha 主星
# 原因:BUG-1272。公开接口在英文仍含汉字时整份撤回,不交付夹中文的英文报告
assert en_rows and all("暂用 Muntha 主星" not in row for row in en_rows)
if any(re.search(r"[\u4e00-\u9fff]", row) for row in en_rows):
assert all("[source text retained:" in row for row in en_rows)
else:
assert all("annual Lagna" in row and "Tajika five-fold strength" in row for row in en_rows)
assert "暂用 Muntha 主星" not in zh
assert zh.count("| Varshesha |") == zh.count("| Selection basis |")
+18 -5
View File
@@ -121,16 +121,29 @@ def test_report_shows_the_blocked_sentence_in_both_editions(blocked_selector) ->
walk(packet)
assert replaced
zh = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "reader_main")
zh = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "full_data")
english = copy.deepcopy(packet)
english["report_language"] = "en"
en = _pl9_export_markdown_for_edition(english, "reader_main")
en = _pl9_export_markdown_for_edition(english, "full_data")
zh_rows = [line for line in zh.splitlines() if line.startswith("| Selection basis |")]
en_rows = [line for line in en.splitlines() if line.startswith("| Selection basis |")]
assert zh_rows and all("年主暂无法确定(年盘缺太阳或月亮位置)" in row for row in zh_rows)
assert en_rows and all("Year lord cannot be determined yet" in row for row in en_rows)
assert not re.search(r"[一-鿿]", "\n".join(en_rows))
assert all("| Varshesha | - |" in zh for _ in zh_rows)
# 原值:英文行是 Year lord cannot be determined yet,且无汉字;年主格是 "-"
# 新值:清干净时仍要求那句英文;否则必须保留中文阻断句并标成 source text retained。年主格是 "-" 或「未列」
# 原因:BUG-1272。不得改回 Muntha 主星。空位标记随完整数据版清理器变成「未列」
assert en_rows
if any(re.search(r"[一-鿿]", row) for row in en_rows):
assert all("年主暂无法确定" in row and "[source text retained:" in row for row in en_rows)
else:
assert all("Year lord cannot be determined yet" in row for row in en_rows)
assert any(
line.startswith("| Varshesha | - |") or line.startswith("| Varshesha | 未列 |")
for line in zh.splitlines()
)
assert not any(
re.search(r"\| Varshesha \| (太阳|月亮|木星|金星|火星|水星|土星) \|", line)
for line in zh.splitlines()
)
def test_every_blocked_sentence_has_its_english_twin() -> None: