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:
jesse-ux
2026-10-13 14:02:26 +08:00
co-authored by Grok (agent loop)
parent 05f8800531
commit a295afa40f
14 changed files with 3466 additions and 10 deletions
+120
View File
@@ -11,6 +11,126 @@ import re
from typing import Any
# Bilingual planet tokens from yoga combination text, longest first.
# Dignity is glued on with no space ("木星Jupiterexalted"), so the long form
# has to be translated before the short planet name.
_BILINGUAL_PLANETS = (
("南交点Ketu", "南交点"),
("北交点Rahu", "北交点"),
("水星Mercury", "水星"),
("木星Jupiter", "木星"),
("金星Venus", "金星"),
("土星Saturn", "土星"),
("火星Mars", "火星"),
("月亮Moon", "月亮"),
("太阳Sun", "太阳"),
)
_GLUED_DIGNITY = {
"exalted": "入旺",
"debilitated": "落陷",
"moolatrikona": "本垣",
"own": "入庙",
"strong": "强",
}
_BILINGUAL_PLANET_RE = re.compile(
"(" + "|".join(re.escape(raw) for raw, _public in _BILINGUAL_PLANETS) + ")"
+ "(exalted|debilitated|moolatrikona|own|strong)?"
)
_BILINGUAL_PLANET_ZH = dict(_BILINGUAL_PLANETS)
# Whole formation sentences and status tokens. Longer phrases stay above the
# short-word glossary so "of" / "in" are not left stuck to Chinese.
_ZH_FORMATION_PHRASES = (
(
"| Bhava Number | Bhava Arambha / House beginning | Bhava Madhya / Middle of House | Bhava Antya / House ending |",
"| 宫位编号 | 宫位起点 | 宫位中点 | 宫位终点 |",
),
(
"Lord of sign occupied in navamsa by 2nd/5th/11th lord, exalted and joins the 9th lord",
"第2、第5或第11宫主在九分盘所落星座的宫主入旺,并与第9宫主会合",
),
(
"Lord of navamsa occupied by 4th lord in 12th",
"第4宫主在九分盘所落星座的宫主位于第12宫",
),
("Learned, eloquent, wealthy from knowledge", "有学识,善表达,学问带来资源"),
("Loss or destruction of property/house", "房屋或产业有损耗"),
(
"Lagna lord, his dispositor, the latter's dispositor in rasi and navamsa, all in kendras/trines/exaltation",
"上升主星、它的定位星,以及定位星的定位星,在本命盘和九分盘都落在角宫、三方宫或入旺",
),
("Fulfills all desires, great king, supreme wealth", "愿望容易得到回应,传统上对应较高的地位与丰厚资源"),
("Eloquent, skilled in debate, good communicator", "善表达,辩论清楚,沟通能力强"),
("Enjoyment of food, pleasure in dining, hospitality", "饮食安稳,待客从容"),
("Courage, initiative, status and practical drive", "有行动力,做事务实"),
("Fear of deception, theft; deceptive nature", "容易担心受骗或失物,需要再核对事实"),
(
"Lagna malefic/Gulika in trine OR Gulika with Kendra/Trine lords OR L1 with Rahu/Sat/Ket",
"凶星或吉利伽在三方宫,或吉利伽与角宫、三方宫主同在,或上升主与罗睺、土星、计都同在",
),
("或BVR精神扰动变体", "或(BVR)精神扰动变体"),
("very_rare", "极少"),
("Not supplied by source", "源数据未提供"),
("Crux of Vedic Astrology", "《吠陀占星精要》"),
("Viparita Raja Yoga", "颠倒王者格"),
("Dhana Yoga", "财富瑜伽"),
("Raj Yoga", "王者格"),
("Custom condition met", "满足自定义条件"),
("custom condition satisfied", "满足自定义条件"),
("自定义条件满足", "满足自定义条件"),
("hemmed in by malefics", "被凶星夹制"),
("hemmed in by benefics", "被吉星夹制"),
("occupied in navamsa", "在九分盘所落"),
("occupied in navamsha", "在九分盘所落"),
("Mutual aspect", "互照"),
("mutual aspect", "互照"),
("drig_rath_aspect_sequence_v1", "Drig 相位顺序 v1"),
("Ashtakavarga八分法(BPHS/PVR书例校准v2.1)", "Ashtakavarga 八分法(BPHS / PVR 书例校准 v2.1)"),
("Sade Sati阶段框架", "Sade Sati 阶段框架"),
("灵魂星AK", "灵魂星(AK)"),
("事业星AmK", "事业星(AmK)"),
("兄弟星BK", "兄弟星(BK)"),
("母亲星MK", "母亲星(MK)"),
("子女星PK", "子女星(PK)"),
("障碍星GK", "障碍星(GK)"),
("配偶星DK", "配偶星(DK)"),
("父亲星PiK", "父亲星(PiK)"),
)
def translate_zh_glued_phrases(text: str) -> str:
"""Translate whole bilingual tokens before any shorter English word."""
if not text:
return text
def planet(match: re.Match) -> str:
name = _BILINGUAL_PLANET_ZH[match.group(1)]
dignity = _GLUED_DIGNITY.get(match.group(2) or "", "")
return name + dignity
text = _BILINGUAL_PLANET_RE.sub(planet, text)
for raw, public in _ZH_FORMATION_PHRASES:
text = text.replace(raw, public)
# House-lord combinations are printed as H6_Lord(Venus) in H12. Translate
# the whole pattern before the short word "in" is left beside Chinese.
text = re.sub(
r"H(\d+)_Lord\(([^)]+)\) in H(\d+)",
r"第\1宫主(\2)在第\3宫",
text,
)
text = re.sub(r"H(\d+)_Lord\(([^)]+)\)", r"第\1宫主(\2)", text)
# Status tokens printed as bare English words. Longer phrases above already
# consumed "not listed" / "Custom condition met", so these are the leftovers.
for raw, public in (
("listed", "已列出"),
("background", "背景"),
("native", "本命"),
):
text = re.sub(rf"(?<![A-Za-z]){raw}(?![A-Za-z])", public, text)
return text
def replace_natural_language_term(text: str, raw: str, public: str) -> str:
"""Replace one glossary term in prose only.