Add the interpretation pages from fields the engine already computed, project the annual tables the reader prints, and clear English Han on the four measured charts. Patyayini is not implemented. The generate button copy is unchanged. This commit stays on the feature branch and is not pushed to staging.
219 lines
8.4 KiB
Python
219 lines
8.4 KiB
Python
"""Report completion for the full-data edition (TASK-report-full-data-fix-20261008).
|
|
|
|
Template wording, annual field projection, the opening focus, and the two
|
|
mistranslations. Chart numbers come from the packet. These tests do not
|
|
calculate a new dasha or import PyJHora.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import copy
|
|
import re
|
|
|
|
from scripts.pl9_full_data_export import _sanitize_pl9_ai_density_markdown, render_full_data_markdown
|
|
from scripts.pl9_interpretation_packs import (
|
|
attach_full_data_interpretation,
|
|
missing_field_count,
|
|
opening_focus_block,
|
|
section_coverage,
|
|
)
|
|
from scripts.pl9_reader_english import HAN
|
|
|
|
FORBIDDEN_ZH = (
|
|
"再婚",
|
|
"几段关系",
|
|
"早夭",
|
|
"一定会",
|
|
"必然",
|
|
"Neecha Bhanga Raja Yoga",
|
|
"落陷取消王",
|
|
"疾病诊断",
|
|
"疾病",
|
|
)
|
|
FORBIDDEN_EN = (
|
|
"divorce",
|
|
"miscarriage",
|
|
"infertility",
|
|
"guarantee",
|
|
"guaranteed",
|
|
)
|
|
HALF_TRANSLATED = re.compile(r"[A-Za-z]+\s*[\u4e00-\u9fff]+s")
|
|
|
|
|
|
def _planet(sign: str, house: int, nakshatra: str, status: str = "Own Sign") -> dict:
|
|
return {"sign": sign, "house": house, "status": status, "nakshatra": nakshatra}
|
|
|
|
|
|
def _packet(language: str = "zh") -> dict:
|
|
planets = {
|
|
"Sun": _planet("Aries", 10, "Ashwini", "入旺(Exalted)"),
|
|
"Moon": _planet("Taurus", 2, "Rohini"),
|
|
"Mars": _planet("Capricorn", 7, "Exalted"),
|
|
"Mercury": _planet("Pisces", 6, "Debilitated"),
|
|
"Jupiter": _planet("Cancer", 1, "Exalted"),
|
|
"Venus": _planet("Libra", 4, "Own Sign"),
|
|
"Saturn": _planet("Aquarius", 8, "Own Sign"),
|
|
"Rahu": _planet("Gemini", 12, "Ardra", ""),
|
|
"Ketu": _planet("Sagittarius", 6, "Mula", ""),
|
|
}
|
|
lords = ["Mars", "Venus", "Mercury", "Moon", "Sun", "Mercury", "Venus", "Mars", "Jupiter", "Saturn", "Saturn", "Jupiter"]
|
|
houses = {f"house_{index}": {"lord": lord, "cusp_sign": "Aries"} for index, lord in enumerate(lords, start=1)}
|
|
timeline = []
|
|
for lord in ("Sun", "Moon", "Mars"):
|
|
current = lord == "Moon"
|
|
timeline.append({
|
|
"lord": lord,
|
|
"start": "2000-01-01",
|
|
"end": "2006-01-01",
|
|
"is_current": current,
|
|
"antardasha_timeline": [{
|
|
"lord": "Mars",
|
|
"start": "2001-01-01",
|
|
"end": "2002-01-01",
|
|
"is_current": current,
|
|
"pratyantar_dasha_timeline": [{
|
|
"lord": "Mercury",
|
|
"start": "2001-02-01",
|
|
"end": "2001-04-01",
|
|
"is_current": current,
|
|
}],
|
|
}],
|
|
})
|
|
return {
|
|
"report_language": language,
|
|
"birth_info": {"name": "虚构甲" if language == "zh" else "Fictional"},
|
|
"worksheets": {
|
|
"d1_rasi_bhava": {"planets": planets, "houses": houses, "ascendant": {"sign": "Cancer"}},
|
|
"strengths_and_scores": {
|
|
"functional_benefic_malefic": {
|
|
"functional_benefics": ["Jupiter", "Mars"],
|
|
"functional_malefics": ["Saturn"],
|
|
"functional_neutrals": ["Moon"],
|
|
},
|
|
"shadbala": {"planets": {"Sun": {"total": 1.25}}},
|
|
},
|
|
"timing_and_predictive_systems": {
|
|
"dasha": {"timeline": timeline, "current_dasha": timeline[1]},
|
|
"annual_tajika_pack": {
|
|
"solar_return": {"status": "partial_verified", "data": {"dt_local": "2026-04-07T09:00:00"}},
|
|
"mudda_dasha": {
|
|
"status": "partial_verified",
|
|
"periods": [{"lord": "Sun", "start": "2026-04-07", "end": "2026-05-07", "months": 1}],
|
|
},
|
|
"patyayini_dasha": {"status": "blocked", "reason": "not_in_engine"},
|
|
"sahams": {"status": "partial_verified", "data": [{"name": "Punya", "sign": "Aries", "degree_in_sign": 1.2}]},
|
|
"tajika_yogas": {
|
|
"status": "partial_verified",
|
|
"data": {"yogas": [{"name": "Ithasala", "present": True, "planets": ["Sun", "Moon"]}]},
|
|
},
|
|
"monthly_windows": [{
|
|
"index": 1,
|
|
"lord": "Sun",
|
|
"duration_months": 1,
|
|
"start": "2026-04-07",
|
|
"end": "2026-05-07",
|
|
}],
|
|
},
|
|
},
|
|
"advanced_systems": {
|
|
"yoga": {
|
|
"detected_yogas": [{
|
|
"name": "Gajakesari Yoga",
|
|
"name_cn": "象鹿瑜伽",
|
|
"category": "raja",
|
|
"effects": ["名声可见"],
|
|
"effects_en": ["visible reputation"],
|
|
"planets": ["Moon", "Jupiter"],
|
|
"combination": "Moon and Jupiter",
|
|
}, {
|
|
"name": "Neecha Bhanga Raja Yoga",
|
|
"name_cn": "落陷取消王瑜伽",
|
|
"category": "neecha_bhanga",
|
|
"effects": ["传统条件成立"],
|
|
"effects_en": ["the cancellation conditions hold"],
|
|
"planets": ["Mercury"],
|
|
}],
|
|
},
|
|
"yogas_doshas": {"mangal_dosha": {"has_dosha": False, "mars_house": 7, "severity": "none"}},
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
def test_t5_repairs_bhava_bala_and_planets_suffix() -> None:
|
|
raw = "\n".join([
|
|
"# 印度占星星盘报告",
|
|
"",
|
|
"### p31-p33 Shadbala / Bhava Bala 摘要",
|
|
"",
|
|
"### p32 Bhava Bala 十二宫力量",
|
|
"",
|
|
"### p125 Natal / KP Ruling Planets",
|
|
"",
|
|
"### Upagraha and Sub-Planets Points",
|
|
"",
|
|
"### Planetary Friendship Matrix",
|
|
"",
|
|
])
|
|
text = _sanitize_pl9_ai_density_markdown(raw)
|
|
assert "Bhava 年龄状态" not in text
|
|
assert "六维力量 / Bhava Bala" in text
|
|
assert "Bhava Bala 十二宫力量" in text
|
|
assert "行星s" not in text
|
|
assert "本命 / KP 征象星" in text
|
|
assert HALF_TRANSLATED.search(text) is None
|
|
assert "年龄状态" not in text.split("Graha Avastha", 1)[-1] or "Baladi" not in raw
|
|
|
|
|
|
def test_templates_cover_required_sections_and_forbidden_wording() -> None:
|
|
packet = _packet("zh")
|
|
attach_full_data_interpretation(packet)
|
|
focus = opening_focus_block(packet)
|
|
assert focus.startswith("重点:")
|
|
assert "MD" in focus and "AD" in focus and "PD" in focus
|
|
assert "第10宫" in focus and "第2宫" in focus and "第7宫" in focus
|
|
text = render_full_data_markdown(packet)
|
|
for heading in ("大运解释", "行星、星宿与宫位解释", "宫主逐宫解释", "Dosha 逐项事实与解释", "瑜伽解释"):
|
|
assert heading in text
|
|
assert "Patyayini 没有实现" in text
|
|
assert "Mudda 月段窗口" in text
|
|
assert "blocked" not in text.lower()
|
|
for phrase in FORBIDDEN_ZH:
|
|
assert phrase not in text
|
|
assert "落陷取消(Neecha Bhanga" in text
|
|
assert "Raja Yoga" not in text
|
|
assert HALF_TRANSLATED.search(text) is None
|
|
assert "行星s" not in text
|
|
coverage = {row["section"]: row["status"] for row in section_coverage(packet)}
|
|
assert coverage["sthira"] == "out_of_scope"
|
|
assert "Sthira Dasha" not in text
|
|
assert missing_field_count(packet) <= 10
|
|
|
|
|
|
def test_english_focus_and_new_sections_have_no_han() -> None:
|
|
packet = _packet("en")
|
|
text = render_full_data_markdown(packet)
|
|
assert "Focus: current Maha Dasha MD" in text
|
|
assert "AD" in text and "PD" in text
|
|
assert "Dasha Interpretation" in text
|
|
assert "House-lord interpretation" in text
|
|
assert "Yoga interpretation" in text
|
|
assert "Patyayini is not implemented" in text
|
|
assert "Monthly mudda windows" in text
|
|
for phrase in FORBIDDEN_EN:
|
|
assert phrase not in text.lower()
|
|
assert "Neecha Bhanga Raja Yoga" not in text
|
|
assert "not a Raja Yoga" in text
|
|
leaked = HAN.findall(text)
|
|
assert leaked == [], leaked[:12]
|
|
|
|
|
|
def test_chinese_render_stays_stable_when_english_uses_a_copy() -> None:
|
|
packet = _packet("zh")
|
|
before = render_full_data_markdown(copy.deepcopy(packet))
|
|
english = copy.deepcopy(packet)
|
|
english["report_language"] = "en"
|
|
render_full_data_markdown(english)
|
|
after = render_full_data_markdown(copy.deepcopy(packet))
|
|
assert after == before
|