fix(report): draw charts in the full-data body and clean Chinese residue
New reports draw one north-Indian chart for each ready divisional in the body, so the trailing duplicate section drops. Chinese full-data prose no longer glues English onto Han or leaves listed, background, or native. The birth table starts with the ascendant. The D9 same-sign column says yes or no. Self-test: tsc exit 0; npm run lint exit 0 (0 errors, 125 existing warnings); three frontend test files 39 pass, 0 fail, 0 cancelled; pytest test_report_chart_blank_columns.py 16 passed; seven related report files 88 passed, exit 0; privacy markers 63 passed. Co-Authored-By: Grok (agent loop)
This commit is contained in:
co-authored by
Grok (agent loop)
parent
05f8800531
commit
a295afa40f
@@ -150,6 +150,114 @@ def test_ashtakavarga_house_table_never_labels_the_total_as_sav(editions) -> Non
|
||||
assert row[5].isdigit()
|
||||
|
||||
|
||||
_CALCULATION_CHART_IDS = {
|
||||
"D1", "D2", "D3", "D4", "D7", "D9", "D10", "D11", "D12",
|
||||
"D16", "D20", "D24", "D27", "D30", "D40", "D60",
|
||||
}
|
||||
|
||||
|
||||
def _section_until_next_h2(markdown: str, heading: str) -> str:
|
||||
start = markdown.index(heading)
|
||||
lines = [heading]
|
||||
for line in markdown[start + len(heading):].splitlines():
|
||||
if line.startswith("## "):
|
||||
break
|
||||
lines.append(line)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def test_full_data_chart_section_draws_each_ready_chart_once(editions) -> None:
|
||||
from scripts.report_chart_block import FENCE_RE
|
||||
zh, en, _packet = editions
|
||||
for markdown, heading in (
|
||||
(zh, "### 本命与分盘北印度图盘"),
|
||||
(en, "### Natal and Divisional North Indian Charts"),
|
||||
):
|
||||
section = _section_until_next_h2(markdown, heading)
|
||||
blocks = []
|
||||
for match in FENCE_RE.finditer(section):
|
||||
block = json.loads(match.group(1))
|
||||
assert block["layout"] == "north"
|
||||
blocks.append(block)
|
||||
ids = [block["id"] for block in blocks]
|
||||
assert ids
|
||||
assert len(ids) == len(set(ids))
|
||||
assert _CALCULATION_CHART_IDS <= set(ids)
|
||||
assert "MOON" in ids
|
||||
assert not re.search(r"(?m)^##### .+\n\n+##### ", section)
|
||||
assert not re.search(r"(?m)^##### .*Bhava", section)
|
||||
assert "##### Navamsha" not in section and "##### p7 · Navamsha" not in section
|
||||
|
||||
|
||||
def _heading_table(markdown: str, heading: str) -> list[list[str]]:
|
||||
"""Like _table_after, but the lord table is a #### heading, not ###."""
|
||||
lines = markdown.splitlines()
|
||||
start = next(i for i, line in enumerate(lines) if line.startswith("#") and heading in line)
|
||||
rows = []
|
||||
for line in lines[start + 1:]:
|
||||
if line.startswith("#") or (rows and not line.startswith("|")):
|
||||
break
|
||||
if line.startswith("|") and not set(line) <= set("|- "):
|
||||
rows.append([cell.strip() for cell in line.strip("|").split("|")])
|
||||
return rows
|
||||
|
||||
|
||||
def test_birth_position_table_starts_with_the_ascendant(editions) -> None:
|
||||
zh, en, packet = editions
|
||||
asc = packet["core_chart"]["ascendant"]
|
||||
longitude = asc.get("lon") if isinstance(asc.get("lon"), (int, float)) else asc.get("degree_raw")
|
||||
fields = _nakshatra_fields(float(longitude))
|
||||
lord_zh = {
|
||||
"Sun": "太阳", "Moon": "月亮", "Mars": "火星", "Mercury": "水星",
|
||||
"Jupiter": "木星", "Venus": "金星", "Saturn": "土星",
|
||||
"Rahu": "北交点", "Ketu": "南交点",
|
||||
}
|
||||
zh_rows = _heading_table(zh, "行星原始位置")
|
||||
assert zh_rows[1][0] == "上升"
|
||||
assert fields["nakshatra"] in zh_rows[1][6]
|
||||
assert zh_rows[1][7] == str(fields["nakshatra_pada"])
|
||||
assert zh_rows[1][5] == "1"
|
||||
lords = _heading_table(zh, "行星主星与状态")
|
||||
assert lords[1][0] == "上升"
|
||||
assert lords[1][5] == "不适用"
|
||||
assert lords[1][2] in {fields["nakshatra_lord"], lord_zh.get(fields["nakshatra_lord"])}
|
||||
en_rows = _heading_table(en, "Planetary Raw Positions")
|
||||
assert en_rows[1][0] in {"Ascendant", "Lagna", "上升"}
|
||||
assert fields["nakshatra"] in en_rows[1][6]
|
||||
assert en_rows[1][7] == str(fields["nakshatra_pada"])
|
||||
|
||||
|
||||
def test_zh_hygiene_rejects_glued_english(editions) -> None:
|
||||
from scripts.pl9_full_data_export import _pl9_markdown_hygiene_receipt, _zh_body_residue
|
||||
injected = _pl9_markdown_hygiene_receipt("木星Jupiterexalted在巨蟹座\n", language="zh")
|
||||
assert injected["status"] == "fail"
|
||||
assert any(str(item).startswith("glued_latin:") for item in injected["violations"])
|
||||
residue = _zh_body_residue(editions[0])
|
||||
assert residue == {"glued_latin": 0, "function_words": 0}
|
||||
|
||||
|
||||
def test_zh_hygiene_is_clean_on_night_and_a_public_chart() -> None:
|
||||
from scripts.pl9_full_data_export import _zh_body_residue
|
||||
# Public birth used by tests/smoke_test_runner.py. Not a private user.
|
||||
public = {
|
||||
**CASES["day"],
|
||||
"name": "Albert Einstein",
|
||||
"year": 1879,
|
||||
"month": 3,
|
||||
"day": 14,
|
||||
"hour": 11,
|
||||
"minute": 30,
|
||||
"lat": 48.4,
|
||||
"lon": 9.98,
|
||||
"tz": 0.89,
|
||||
"age": 76,
|
||||
}
|
||||
for case in (CASES["night"], public):
|
||||
packet = _packet(case)
|
||||
markdown = _pl9_export_markdown_for_edition(copy.deepcopy(packet), "full_data")
|
||||
assert _zh_body_residue(markdown) == {"glued_latin": 0, "function_words": 0}
|
||||
|
||||
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user