fix(report): keep the English edition when one phrase was untranslated
The English personal report was discarded whenever a Chinese fragment survived the trailing glossary. English sections now render the English fields that were already calculated. Co-Authored-By: Grok (agent loop) Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Grok (agent loop)
Cursor
parent
9812f31737
commit
ba1ffeb35f
@@ -0,0 +1,205 @@
|
||||
"""Fixed fictional charts for the English-edition sweep (issue #89).
|
||||
|
||||
Not collected by pytest. Birth data stays inside the generator; callers report
|
||||
only the chart index and leftover fragments.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import builtins
|
||||
import importlib
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
HAN = re.compile(r"[㐀-鿿豈-]")
|
||||
GLUE = (
|
||||
"scoreandtimeline",
|
||||
"marriagequality",
|
||||
"inperiod start",
|
||||
"Dashasignpresent",
|
||||
"aturninperiod",
|
||||
"atpresent",
|
||||
"planetsinbetween",
|
||||
"whetherinunderway",
|
||||
"withSaturn",
|
||||
"careerplanetAmK",
|
||||
"careerdirection",
|
||||
"supportingstrongdegree",
|
||||
"beneficfactor count",
|
||||
"comparativelystrong",
|
||||
"occupiesplanetcount",
|
||||
"lordandstrong",
|
||||
"lordstrongand",
|
||||
"weakafflicted",
|
||||
"partialreducedweak",
|
||||
"debilitatedsign",
|
||||
"planetenterssign",
|
||||
"planetsdistributed",
|
||||
"beneficlocated",
|
||||
"house 5 level",
|
||||
"house seven-round",
|
||||
"condition planets",
|
||||
"[source text retained",
|
||||
)
|
||||
SEED = 20261021
|
||||
TODAY = "2026-10-21"
|
||||
CHART_COUNT = 200
|
||||
|
||||
|
||||
def fictional_case(index: int) -> dict:
|
||||
"""Chart ``index`` from the fixed sequence. Replaying 0..index keeps the seed stable."""
|
||||
rng = random.Random(SEED)
|
||||
chosen = None
|
||||
for i in range(index + 1):
|
||||
year = rng.randint(1940, 2015)
|
||||
month = rng.randint(1, 12)
|
||||
day = rng.randint(1, 28)
|
||||
hour = rng.randint(0, 23)
|
||||
minute = rng.randint(0, 59)
|
||||
lat = round(rng.uniform(-45, 60), 4)
|
||||
lon = round(rng.uniform(-120, 150), 4)
|
||||
tz = max(-12.0, min(14.0, round(lon / 7.5) * 0.5))
|
||||
chosen = {
|
||||
"name": f"chart-{i:03d}",
|
||||
"year": year,
|
||||
"month": month,
|
||||
"day": day,
|
||||
"hour": hour,
|
||||
"minute": minute,
|
||||
"second": 0,
|
||||
"lat": lat,
|
||||
"lon": lon,
|
||||
"tz": tz,
|
||||
"node_mode": rng.choice(("mean", "true")),
|
||||
"ayanamsa": rng.choice(("lahiri", "raman", "kp")),
|
||||
"today": TODAY,
|
||||
"transit_date": TODAY,
|
||||
"target_year": 2026,
|
||||
"age": 2026 - year,
|
||||
"birth_time_accuracy": "confirmed",
|
||||
}
|
||||
return chosen
|
||||
|
||||
|
||||
def block_jhora() -> None:
|
||||
"""Fail closed if this process imports PyJHora. Call once per process."""
|
||||
real_import = builtins.__import__
|
||||
|
||||
def guarded(name, globals=None, locals=None, fromlist=(), level=0):
|
||||
if str(name).split(".", 1)[0] == "jhora":
|
||||
raise ImportError("jhora blocked")
|
||||
return real_import(name, globals, locals, fromlist, level)
|
||||
|
||||
builtins.__import__ = guarded
|
||||
real_import_module = importlib.import_module
|
||||
|
||||
def guarded_module(name, package=None):
|
||||
if str(name).split(".", 1)[0] == "jhora":
|
||||
raise ImportError("jhora blocked")
|
||||
return real_import_module(name, package)
|
||||
|
||||
importlib.import_module = guarded_module
|
||||
for key in list(sys.modules):
|
||||
if key == "jhora" or key.startswith("jhora."):
|
||||
del sys.modules[key]
|
||||
|
||||
|
||||
def _prepare_process() -> None:
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
for entry in (str(root), str(root / "scripts")):
|
||||
if entry not in sys.path:
|
||||
sys.path.insert(0, entry)
|
||||
block_jhora()
|
||||
|
||||
|
||||
def _residue(text: str) -> dict:
|
||||
found = HAN.findall(text or "")
|
||||
glue = [word for word in GLUE if word in (text or "")]
|
||||
return {"han": len(found), "han_chars": "".join(sorted(set(found)))[:40], "glue": glue}
|
||||
|
||||
|
||||
def render_english(index: int) -> dict:
|
||||
"""Build one fictional packet and run ``_english_edition``. No birth data in the result."""
|
||||
_prepare_process()
|
||||
from scripts.jyotish_engine import build_professional_report_reference_packet, cmd_full_reading
|
||||
from scripts.professional_report_reference import _english_edition, _export_args
|
||||
|
||||
birth = fictional_case(index)
|
||||
reading = cmd_full_reading(type("Args", (), dict(birth))())
|
||||
packet = dict(build_professional_report_reference_packet(reading, _export_args(dict(birth)), []))
|
||||
packet["report_version"] = "pl9_personal_long_report.v3"
|
||||
edition = _english_edition(packet, True)
|
||||
markdown = edition.get("markdown_en") or ""
|
||||
flags = _flags_from_packet(packet)
|
||||
applicability = edition.get("reader_dasha_applicability_en") or ""
|
||||
body = _residue(markdown)
|
||||
extra = _residue(applicability)
|
||||
dump_dir = os.environ.get("REPORT_ENGLISH_SWEEP_DUMP")
|
||||
if dump_dir and markdown:
|
||||
path = Path(dump_dir)
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
(path / f"chart-{index:03d}.md").write_text(markdown, encoding="utf-8")
|
||||
return {
|
||||
"i": index,
|
||||
"status": "ok" if markdown and not edition.get("english_unavailable") else edition.get("english_unavailable") or "empty",
|
||||
"han": body["han"] + extra["han"],
|
||||
"han_chars": (body["han_chars"] + extra["han_chars"])[:40],
|
||||
"glue": sorted(set(body["glue"] + extra["glue"])),
|
||||
**flags,
|
||||
}
|
||||
|
||||
|
||||
def _flags_from_packet(packet: dict) -> dict:
|
||||
rath = (((packet.get("worksheets") or {}).get("timing_and_predictive_systems") or {}).get("narayana_dasha") or {}).get("rath") or {}
|
||||
rule = rath.get("current_md_antardasha_rule") or {}
|
||||
direction = rule.get("direction") if isinstance(rule, dict) else {}
|
||||
advanced = (packet.get("worksheets") or {}).get("advanced_systems") or {}
|
||||
doshas = advanced.get("yogas_doshas") or {}
|
||||
kaal = doshas.get("kaal_sarp_dosha") or {}
|
||||
birth_sheet = (packet.get("worksheets") or {}).get("birth_and_parameters") or {}
|
||||
pack = birth_sheet.get("panchanga") or {}
|
||||
panchanga = pack.get("raw") if isinstance(pack, dict) and isinstance(pack.get("raw"), dict) else pack
|
||||
yoga = advanced.get("yoga") if isinstance(advanced.get("yoga"), dict) else {}
|
||||
rows = [row for row in (yoga.get("detected_yogas") or []) if isinstance(row, dict)]
|
||||
return {
|
||||
"level": rath.get("strength_level_decided"),
|
||||
"direction": rath.get("direction"),
|
||||
"ad_rule": direction.get("rule") if isinstance(direction, dict) else None,
|
||||
"overall": panchanga.get("overall_quality") if isinstance(panchanga, dict) else None,
|
||||
"kaal": kaal.get("type") if isinstance(kaal, dict) else None,
|
||||
"punarphoo": any(str(row.get("strength") or "") == "观察" or "Punarphoo" in str(row.get("name") or "") for row in rows),
|
||||
}
|
||||
|
||||
|
||||
def chart_flags(index: int) -> dict:
|
||||
"""Class flags used to prove the smoke set covers L1–L6. No birth data."""
|
||||
_prepare_process()
|
||||
from scripts.jyotish_engine import build_professional_report_reference_packet, cmd_full_reading
|
||||
from scripts.professional_report_reference import _export_args
|
||||
|
||||
birth = fictional_case(index)
|
||||
reading = cmd_full_reading(type("Args", (), dict(birth))())
|
||||
packet = dict(build_professional_report_reference_packet(reading, _export_args(dict(birth)), []))
|
||||
rath = (((packet.get("worksheets") or {}).get("timing_and_predictive_systems") or {}).get("narayana_dasha") or {}).get("rath") or {}
|
||||
rule = rath.get("current_md_antardasha_rule") or {}
|
||||
direction = rule.get("direction") if isinstance(rule, dict) else {}
|
||||
advanced = (packet.get("worksheets") or {}).get("advanced_systems") or {}
|
||||
doshas = advanced.get("yogas_doshas") or {}
|
||||
kaal = doshas.get("kaal_sarp_dosha") or {}
|
||||
birth_sheet = (packet.get("worksheets") or {}).get("birth_and_parameters") or {}
|
||||
pack = birth_sheet.get("panchanga") or {}
|
||||
panchanga = pack.get("raw") if isinstance(pack, dict) and isinstance(pack.get("raw"), dict) else pack
|
||||
yoga = advanced.get("yoga") if isinstance(advanced.get("yoga"), dict) else {}
|
||||
names = [str(row.get("name") or "") for row in (yoga.get("detected_yogas") or []) if isinstance(row, dict)]
|
||||
strengths = [str(row.get("strength") or "") for row in (yoga.get("detected_yogas") or []) if isinstance(row, dict)]
|
||||
return {
|
||||
"i": index,
|
||||
"level": rath.get("strength_level_decided"),
|
||||
"direction": rath.get("direction"),
|
||||
"ad_rule": direction.get("rule") if isinstance(direction, dict) else None,
|
||||
"overall": panchanga.get("overall_quality") if isinstance(panchanga, dict) else None,
|
||||
"kaal": kaal.get("type") if isinstance(kaal, dict) else None,
|
||||
"punarphoo": "观察" in strengths or any("Punarphoo" in name for name in names),
|
||||
}
|
||||
@@ -91,26 +91,29 @@ def test_full_reading_carries_the_rath_view_and_leaves_the_legacy_result_alone()
|
||||
|
||||
|
||||
def test_reference_edition_shows_both_and_english_terms_cover_the_rath_lines() -> None:
|
||||
from scripts.narayana_legacy_label import rath_report_lines_zh
|
||||
from scripts.pl9_reader_english_terms import PHRASES_EN, REGEX_EN
|
||||
from scripts.narayana_legacy_label import rath_report_lines_en
|
||||
|
||||
packet = _packet(CASES["day"])
|
||||
# 原值:reference 版同时有旧算法句和 Rath 表
|
||||
# 新值:full_data 版同时有这两段
|
||||
# 原因:用户看到的是完整数据版;Rath 并列表必须跟着走(BUG-1218)
|
||||
# 原值:Rath 中文行经 PHRASES_EN 可译干净
|
||||
# 新值:完整数据版英文渲染的 Rath 段无汉字且用英文函数
|
||||
# 原因:完整数据版不走 PHRASES_EN,旧断言测的不是生产路径
|
||||
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
|
||||
assert "| Rath MD | 星座 | 宫主 | 起始年龄 | 结束年龄 | 基本年数 | 修正 | 最终 |" in zh and "| Rath AD | 星座 | 起始年龄 | 结束年龄 |" in zh
|
||||
view = packet["worksheets"]["timing_and_predictive_systems"]["narayana_dasha"]["rath"]
|
||||
for line in rath_report_lines_zh(view):
|
||||
text = line
|
||||
for pattern, target in REGEX_EN:
|
||||
text = pattern.sub(target, text)
|
||||
for source, target in PHRASES_EN:
|
||||
text = text.replace(source, target)
|
||||
assert not re.search(r"[一-鿿]", text), text
|
||||
english_packet = copy.deepcopy(packet)
|
||||
english_packet["report_language"] = "en"
|
||||
en = _pl9_export_markdown_for_edition(english_packet, "full_data")
|
||||
lines = [line for line in rath_report_lines_en(view) if line]
|
||||
assert lines and lines[0].startswith("- Alongside:")
|
||||
assert not re.search(r"[一-鿿]", "\n".join(lines))
|
||||
start = en.index("- Alongside:")
|
||||
section = en[start:start + 2500]
|
||||
assert "level " in section and "[source text retained" not in section
|
||||
assert not re.search(r"[一-鿿]", section)
|
||||
assert "house " not in section.split("\n", 1)[0]
|
||||
|
||||
|
||||
def test_consultation_modules_carry_the_rath_view() -> None:
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
"""Source English for the leaks named in issue #89. Fictional inputs only."""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
from scripts.muhurta import calc_panchanga
|
||||
from scripts.narayana_legacy_label import (
|
||||
DIRECTION_EN,
|
||||
STRENGTH_LEVEL_EN,
|
||||
rath_report_lines_en,
|
||||
rath_report_lines_zh,
|
||||
rath_summary_line_zh,
|
||||
)
|
||||
from scripts.pl9_reader_export import render_pl9_parity_markdown
|
||||
from scripts.yogas_doshas import calc_kaal_sarp_dosha, calc_sade_sati
|
||||
from scripts.yoga_engine import STRENGTH_EN
|
||||
|
||||
HAN = re.compile(r"[㐀-鿿豈-]")
|
||||
|
||||
|
||||
def _rath_view(*, level: int, direction: str, with_divergence: bool) -> dict:
|
||||
rule_en = "Ketu in the MD sign: reversed (Table 14)"
|
||||
direction_block = {
|
||||
"rule_text": "大运星座有计都:反向(Table 14)",
|
||||
"rule_text_en": rule_en,
|
||||
"direction": "reverse",
|
||||
"book_divergence": [
|
||||
{
|
||||
"rule_text": "p.47(J.S. 1.1.34)看大运星座奇偶",
|
||||
"rule_text_en": "p.47 (J.S. 1.1.34) by the odd/even of the MD sign",
|
||||
"direction": "zodiacal",
|
||||
}
|
||||
] if with_divergence else [],
|
||||
}
|
||||
return {
|
||||
"seed_sign": "Libra",
|
||||
"strength_level_decided": level,
|
||||
"strength_level_text": {1: "占据星数", 5: "宫主星座内度数"}.get(level, f"级别{level}"),
|
||||
"strength_level_text_en": STRENGTH_LEVEL_EN[level],
|
||||
"direction": direction,
|
||||
"direction_text": {key: "计都在起运宫:反向(Table 12)" for key in DIRECTION_EN}.get(direction, "中文顺序"),
|
||||
"direction_text_en": DIRECTION_EN[direction],
|
||||
"mahadasha": [{
|
||||
"sign": "Libra", "lord": "Venus", "start_age": 0, "end_age": 7,
|
||||
"base_years": 7, "dignity_adjustment": 0, "years": 7,
|
||||
}],
|
||||
"current_dasha": {"md": {"sign": "Libra", "lord": "Venus"}},
|
||||
"current_md_antardasha": [{"sign": "Libra", "start_age": 0, "end_age": 0.5}],
|
||||
"current_md_antardasha_rule": {
|
||||
"compared": ["Libra", "Aries"],
|
||||
"stronger_sign": "Libra",
|
||||
"strength_level_decided": level,
|
||||
"lord": "Venus",
|
||||
"start_sign": "Libra",
|
||||
"direction": direction_block,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_rath_english_lines_cover_levels_tables_and_book_divergence() -> None:
|
||||
before = rath_summary_line_zh({"seed_sign": "Libra", "strength_level_decided": 5, "strength_level_text": "宫主星座内度数"})
|
||||
for level in range(1, 8):
|
||||
for direction in ("table10_general", "table11_saturn_zodiacal", "table12_ketu_reversed"):
|
||||
view = _rath_view(level=level, direction=direction, with_divergence=True)
|
||||
lines = "\n".join(rath_report_lines_en(view))
|
||||
assert not HAN.search(lines)
|
||||
assert "house " not in lines
|
||||
assert f"level {level}: {STRENGTH_LEVEL_EN[level]}" in lines
|
||||
assert DIRECTION_EN[direction] in lines
|
||||
assert "the book itself disagrees, not applied:" in lines
|
||||
ketu = "\n".join(rath_report_lines_en(_rath_view(level=5, direction="table12_ketu_reversed", with_divergence=True)))
|
||||
assert "level 5: lord's degree in sign" in ketu
|
||||
assert "Ketu in the start sign: reversed (Table 12)" in ketu
|
||||
assert rath_summary_line_zh({"seed_sign": "Libra", "strength_level_decided": 5, "strength_level_text": "宫主星座内度数"}) == before
|
||||
zh = "\n".join(rath_report_lines_zh(_rath_view(level=5, direction="table12_ketu_reversed", with_divergence=True)))
|
||||
assert "- 并列:" in zh and "计都在起运宫:反向(Table 12)" in zh
|
||||
|
||||
|
||||
def test_panchanga_overall_quality_keeps_chinese_and_adds_english() -> None:
|
||||
seen = {}
|
||||
for sun in range(0, 360, 12):
|
||||
for moon in range(0, 360, 15):
|
||||
row = calc_panchanga(float(sun), float(moon), 3, 6.0)
|
||||
seen[row["overall_quality"]] = row["overall_quality_en"]
|
||||
assert row["overall_quality"] in {"吉(Subha)", "中(Mixed)", "凶(Asubha)"}
|
||||
assert seen == {
|
||||
"吉(Subha)": "Auspicious (Subha)",
|
||||
"中(Mixed)": "Mixed",
|
||||
"凶(Asubha)": "Inauspicious (Asubha)",
|
||||
}
|
||||
|
||||
|
||||
def _planets(houses: dict[str, int]) -> dict:
|
||||
return {name: {"house": house, "sign": "Aries"} for name, house in houses.items()}
|
||||
|
||||
|
||||
def test_kaal_sarp_english_summaries_count_planets_in_words() -> None:
|
||||
names = ("Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn")
|
||||
full = _planets({name: 1 + (index % 6) for index, name in enumerate(names)})
|
||||
full["Rahu"] = {"house": 1, "sign": "Aries"}
|
||||
full["Ketu"] = {"house": 7, "sign": "Libra"}
|
||||
full_row = calc_kaal_sarp_dosha(full)
|
||||
assert full_row["summary"].startswith("完全Kaal Sarp Dosha")
|
||||
assert full_row["summary_en"] == "Full Kaal Sarp Dosha — all 7 planets lie between the Rahu (1)-Ketu axis"
|
||||
assert not HAN.search(full_row["summary_en"])
|
||||
|
||||
partial = _planets({name: (1 if index < 5 else 8) for index, name in enumerate(names)})
|
||||
partial["Rahu"] = {"house": 1, "sign": "Aries"}
|
||||
partial["Ketu"] = {"house": 7, "sign": "Libra"}
|
||||
partial_row = calc_kaal_sarp_dosha(partial)
|
||||
assert partial_row["summary"].startswith("部分Kaal Sarp Dosha")
|
||||
assert partial_row["summary_en"] == "Partial Kaal Sarp Dosha — 5 of 7 planets lie between the axis"
|
||||
assert "condition planets" not in partial_row["summary_en"]
|
||||
|
||||
none = _planets({name: 8 for name in names})
|
||||
none["Rahu"] = {"house": 1, "sign": "Aries"}
|
||||
none["Ketu"] = {"house": 7, "sign": "Libra"}
|
||||
none_row = calc_kaal_sarp_dosha(none)
|
||||
assert none_row["summary"] == "无Kaal Sarp Dosha"
|
||||
assert none_row["summary_en"] == "No Kaal Sarp Dosha"
|
||||
|
||||
|
||||
def test_sade_sati_english_summary_has_no_glued_words() -> None:
|
||||
row = calc_sade_sati("Leo")
|
||||
assert row["summary"].startswith("Sade Sati阶段框架:")
|
||||
assert "judge from Saturn's actual position whether it is underway" in row["summary_en"]
|
||||
assert not HAN.search(row["summary_en"])
|
||||
for glued in ("atpresent", "whetherinunderway", "withSaturn"):
|
||||
assert glued not in row["summary_en"]
|
||||
|
||||
|
||||
def test_punarphoo_strength_column_uses_english() -> None:
|
||||
packet = {
|
||||
"report_language": "en",
|
||||
"worksheets": {"advanced_systems": {"yoga": {"detected_yogas": [{
|
||||
"name": "Punarphoo Yoga",
|
||||
"category": "relationship_observation",
|
||||
"strength": "观察",
|
||||
"strength_en": "observational",
|
||||
"combination": "月亮与土星合相",
|
||||
"combination_en": "Moon conjunct Saturn",
|
||||
"effects_en": ["Watch the relationship"],
|
||||
}]}}},
|
||||
}
|
||||
text = render_pl9_parity_markdown(packet)
|
||||
assert "| Punarphoo Yoga | relationship_observation | observational | Moon conjunct Saturn |" in text
|
||||
assert "观察" not in text
|
||||
assert STRENGTH_EN["观察"] == "Watch"
|
||||
@@ -0,0 +1,27 @@
|
||||
"""200 fixed fictional charts: the English edition must come back whole (issue #89).
|
||||
|
||||
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.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from concurrent.futures import ProcessPoolExecutor, as_completed
|
||||
|
||||
from tests.report_english_fictional import CHART_COUNT, render_english
|
||||
|
||||
|
||||
def test_two_hundred_fictional_charts_have_clean_english() -> None:
|
||||
workers = min(4, os.cpu_count() or 1, CHART_COUNT)
|
||||
failures = []
|
||||
with ProcessPoolExecutor(max_workers=workers) as pool:
|
||||
futures = [pool.submit(render_english, index) for index in range(CHART_COUNT)]
|
||||
for future in as_completed(futures):
|
||||
row = future.result()
|
||||
if row["status"] != "ok" or row["han"] or row["glue"]:
|
||||
failures.append(
|
||||
f"#{row['i']} status={row['status']} han={row['han']} chars={row['han_chars']} glue={row['glue']}"
|
||||
)
|
||||
assert failures == []
|
||||
@@ -0,0 +1,46 @@
|
||||
"""Smoke slice of the English sweep, on the quick gate (issue #89 / BUG-1410).
|
||||
|
||||
Indices are from tests.report_english_fictional.fictional_case. Together they
|
||||
hit Rath level 5, an inauspicious Panchanga, Ketu in the start sign, a Table 14
|
||||
sub-period, Punarphoo, and full Kaal Sarp. Replacing the English Rath lines
|
||||
with the Chinese ones makes this file fail.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from concurrent.futures import ProcessPoolExecutor, as_completed
|
||||
|
||||
from tests.report_english_fictional import render_english
|
||||
|
||||
# Filled once the flag scan names a chart for every class. The test checks the
|
||||
# flags, so a stale index fails instead of silently dropping a class.
|
||||
# 1 table12, 4/10/12 level 5, 5 table14 + inauspicious, 6 table12,
|
||||
# 9/12 partial Kaal Sarp, 11/15 table 11, 14 inauspicious,
|
||||
# 32 full Kaal Sarp, 34 Punarphoo. Cheap positions matched the
|
||||
# 0-15 packet Kaal Sarp types before these two were added.
|
||||
SMOKE_INDEXES = (1, 4, 5, 6, 9, 10, 11, 12, 14, 15, 32, 34)
|
||||
|
||||
|
||||
def test_smoke_charts_cover_every_leak_class_and_render_clean_english() -> None:
|
||||
rows = []
|
||||
failures = []
|
||||
with ProcessPoolExecutor(max_workers=min(4, len(SMOKE_INDEXES))) as pool:
|
||||
futures = [pool.submit(render_english, index) for index in SMOKE_INDEXES]
|
||||
for future in as_completed(futures):
|
||||
row = future.result()
|
||||
rows.append(row)
|
||||
if row["status"] != "ok" or row["han"] or row["glue"]:
|
||||
failures.append(
|
||||
f"#{row['i']} status={row['status']} han={row['han']} chars={row['han_chars']} glue={row['glue']}"
|
||||
)
|
||||
levels = {row["level"] for row in rows}
|
||||
directions = {row["direction"] for row in rows}
|
||||
ad_rules = {row["ad_rule"] for row in rows}
|
||||
overall = {row["overall"] for row in rows}
|
||||
kaal = {row["kaal"] for row in rows}
|
||||
assert 5 in levels
|
||||
assert "table12_ketu_reversed" in directions
|
||||
assert "rath_ketu_table14_v1" in ad_rules
|
||||
assert "凶(Asubha)" in overall
|
||||
assert any(row["punarphoo"] for row in rows)
|
||||
assert "full" in kaal
|
||||
assert failures == []
|
||||
Reference in New Issue
Block a user