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
@@ -107,7 +107,13 @@ def _sanitize_pl9_ai_density_markdown(markdown: str, title: str = "印度占星
|
||||
strip_han_plural_s,
|
||||
)
|
||||
|
||||
text = _sanitize_pl9_public_markdown(markdown)
|
||||
try:
|
||||
from pl9_language_terms import translate_zh_glued_phrases
|
||||
except ModuleNotFoundError: # pragma: no cover - package import compatibility
|
||||
from scripts.pl9_language_terms import translate_zh_glued_phrases
|
||||
# Whole phrases and glued planet+dignity tokens before shorter word swaps.
|
||||
# Chapters appended after the parity strip still pass through here.
|
||||
text = translate_zh_glued_phrases(_sanitize_pl9_public_markdown(markdown))
|
||||
replacements = {
|
||||
"# PL9 对标主册": f"# {title}",
|
||||
"出生资料与图盘(PL9 p1-p17)": "出生资料与图盘",
|
||||
@@ -1264,6 +1270,27 @@ def _sanitize_pl9_ai_density_markdown_en(markdown: str, title: str = "Vedic Astr
|
||||
|
||||
|
||||
|
||||
# Body residue (BUG-1312). Parenthetical Sanskrit names (nakshatra, yoga,
|
||||
# Dasha) and header abbreviations (SAV, BAV, KP, D1-D60) are separated from
|
||||
# Han by parentheses or spaces, so they are not "glued". They are not function
|
||||
# words. Chart fences are structured payload and are not part of the prose count.
|
||||
_ZH_GLUE_RE = re.compile(r"[\u4e00-\u9fff][A-Za-z]{3,}|[A-Za-z]{3,}[\u4e00-\u9fff]")
|
||||
_ZH_FUNCTION_WORD_RE = re.compile(
|
||||
r"(?<![A-Za-z])(?:of|in|the|and|by|from|with|listed|background|native|condition|met|satisfied|occupied)(?![A-Za-z])",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
_ZH_CHART_FENCE_RE = re.compile(r"```jyotish-chart\n.*?\n```", re.DOTALL)
|
||||
|
||||
|
||||
def _zh_body_residue(markdown: str) -> dict[str, int]:
|
||||
body = (markdown or "").split("## 结构化资料附录", 1)[0]
|
||||
body = _ZH_CHART_FENCE_RE.sub("", body)
|
||||
return {
|
||||
"glued_latin": len(_ZH_GLUE_RE.findall(body)),
|
||||
"function_words": len(_ZH_FUNCTION_WORD_RE.findall(body)),
|
||||
}
|
||||
|
||||
|
||||
def _pl9_markdown_hygiene_receipt(markdown: str, *, language: str) -> dict[str, Any]:
|
||||
"""Check the Markdown before rendering user-facing PDF."""
|
||||
|
||||
@@ -1327,6 +1354,12 @@ def _pl9_markdown_hygiene_receipt(markdown: str, *, language: str) -> dict[str,
|
||||
chinese_chars = len(re.findall(r"[\u4e00-\u9fff]", markdown))
|
||||
if chinese_chars:
|
||||
violations.append(f"chinese_characters:{chinese_chars}")
|
||||
if language == 'zh':
|
||||
residue = _zh_body_residue(markdown)
|
||||
if residue["glued_latin"]:
|
||||
violations.append(f"glued_latin:{residue['glued_latin']}")
|
||||
if residue["function_words"]:
|
||||
violations.append(f"function_words:{residue['function_words']}")
|
||||
language_receipt = language_quality_receipt(markdown, language=language)
|
||||
violations.extend(language_receipt.get("violations") or [])
|
||||
orphan_lines = orphan_separator_lines(markdown)
|
||||
|
||||
@@ -864,11 +864,14 @@ def build_yoga_interpretation_pack(
|
||||
if name in seen:
|
||||
continue
|
||||
seen.add(name)
|
||||
combination = str(raw.get("combination_en") or raw.get("combination") or raw.get("reason") or "")
|
||||
if language == "en":
|
||||
combination = str(raw.get("combination_en") or raw.get("name") or combination)
|
||||
combination = str(raw.get("combination_en") or raw.get("name") or raw.get("combination") or "")
|
||||
if re.search(r"[\u4e00-\u9fff]", combination):
|
||||
combination = "combination text is not listed separately"
|
||||
else:
|
||||
# Chinese pages keep the Chinese combination. combination_en is the
|
||||
# English twin and must not leak into the Chinese report.
|
||||
combination = str(raw.get("combination") or raw.get("reason") or "")
|
||||
planets = [str(item) for item in _list(raw.get("planets"))]
|
||||
overlap = sorted(set(planets) & set(active))
|
||||
if language == "zh":
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -35,6 +35,29 @@ _PHRASES: dict[str, str] = {
|
||||
"特殊上升与派生点补充": "Special Lagnas and Points (supplement)",
|
||||
"敏感点与特殊落点": "Sensitive Points and Special Placements",
|
||||
"本命与分盘北印度图盘": "Natal and Divisional Charts (North Indian)",
|
||||
# Chart fences in the parity volume. The Chinese label is the key; the
|
||||
# English edition looks these up instead of keeping a second title list.
|
||||
"D1 本命盘": "D1 natal chart",
|
||||
"月亮盘": "Moon chart",
|
||||
"D2 财富分盘": "D2 wealth chart",
|
||||
"D3 手足分盘": "D3 siblings chart",
|
||||
"D4 居所分盘": "D4 home chart",
|
||||
"D5 声望分盘": "D5 reputation chart",
|
||||
"D6 压力分盘": "D6 pressure chart",
|
||||
"D7 子女分盘": "D7 children chart",
|
||||
"D8 变化分盘": "D8 change chart",
|
||||
"D9 九分盘": "D9 navamsha",
|
||||
"D10 事业分盘": "D10 career chart",
|
||||
"D11 收益分盘": "D11 gains chart",
|
||||
"D12 父母分盘": "D12 parents chart",
|
||||
"D16 享受分盘": "D16 comforts chart",
|
||||
"D20 灵修分盘": "D20 practice chart",
|
||||
"D24 学习分盘": "D24 learning chart",
|
||||
"D27 内在分盘": "D27 inner chart",
|
||||
"D30 困难分盘": "D30 difficulty chart",
|
||||
"D40 母系分盘": "D40 maternal chart",
|
||||
"D45 父系分盘": "D45 paternal chart",
|
||||
"D60 深层分盘": "D60 deep chart",
|
||||
"分盘对出生时间敏感,原始计算供核对,不单独增加结论的确定性。":
|
||||
"Divisional charts are sensitive to birth time. The raw calculations are here for checking and do not by themselves make a conclusion more certain.",
|
||||
" 数值位置表": " Position Table",
|
||||
|
||||
@@ -111,7 +111,11 @@ def _pl9_report_language(packet: dict) -> str:
|
||||
def _strip_pl9_user_engineering_markers(markdown: str) -> str:
|
||||
"""Remove internal audit/profile vocabulary from user-facing report text."""
|
||||
|
||||
text = markdown
|
||||
try:
|
||||
from scripts.pl9_language_terms import translate_zh_glued_phrases
|
||||
except ModuleNotFoundError: # pragma: no cover - direct scripts/ execution
|
||||
from pl9_language_terms import translate_zh_glued_phrases
|
||||
text = translate_zh_glued_phrases(markdown)
|
||||
replacements = {
|
||||
"parameter_sensitive / unverified": "参考",
|
||||
"parameter_sensitive": "参考",
|
||||
@@ -481,6 +485,149 @@ def _render_pl9_user_markdown(packet: dict) -> str:
|
||||
return cleaned
|
||||
|
||||
|
||||
# Drawable charts in the parity "north Indian charts" section. Bhava (Sripati)
|
||||
# has no chart id (its numbers stay in the Sripati table). Navamsha is not
|
||||
# listed again: it is the same id as D9. English titles live in
|
||||
# pl9_reader_english_terms, keyed by these Chinese labels.
|
||||
_PARITY_CHARTS = (
|
||||
("D1", "D1 本命盘", "d1"),
|
||||
("MOON", "月亮盘", "moon"),
|
||||
("D2", "D2 财富分盘", ("D2_Hora", "D2")),
|
||||
("D3", "D3 手足分盘", ("D3_Drekkana", "D3")),
|
||||
("D4", "D4 居所分盘", ("D4_Turyamsa", "D4_Chaturthamsa", "D4")),
|
||||
("D5", "D5 声望分盘", ("D5_Panchamsa", "D5")),
|
||||
("D6", "D6 压力分盘", ("D6_Shashthamsa", "D6")),
|
||||
("D7", "D7 子女分盘", ("D7_Saptamsa", "D7")),
|
||||
("D8", "D8 变化分盘", ("D8_Ashtamsa", "D8")),
|
||||
("D9", "D9 九分盘", ("D9_Navamsa", "D9")),
|
||||
("D10", "D10 事业分盘", ("D10_Dasamsa", "D10")),
|
||||
("D11", "D11 收益分盘", ("D11_Rudramsa", "D11")),
|
||||
("D12", "D12 父母分盘", ("D12_Dwadashamsa", "D12_Dvadasamsa", "D12")),
|
||||
("D16", "D16 享受分盘", ("D16_Shodasamsa", "D16")),
|
||||
("D20", "D20 灵修分盘", ("D20_Vimsamsa", "D20")),
|
||||
("D24", "D24 学习分盘", ("D24_Siddhamsa", "D24_Chaturvimsamsa", "D24")),
|
||||
("D27", "D27 内在分盘", ("D27_Bhamsa", "D27")),
|
||||
("D30", "D30 困难分盘", ("D30_Trimsamsa", "D30")),
|
||||
("D40", "D40 母系分盘", ("D40_Khavedamsa", "D40")),
|
||||
("D45", "D45 父系分盘", ("D45_Akshavedamsa", "D45")),
|
||||
("D60", "D60 深层分盘", ("D60_Shashtyamsa", "D60_Shashtiamsa", "D60")),
|
||||
)
|
||||
|
||||
|
||||
def _parity_chart_title(zh: str, language: str) -> str:
|
||||
if language != "en":
|
||||
return zh
|
||||
try:
|
||||
from scripts.pl9_reader_english_terms import PHRASES_EN
|
||||
except ModuleNotFoundError: # pragma: no cover - direct scripts/ execution
|
||||
from pl9_reader_english_terms import PHRASES_EN
|
||||
return dict(PHRASES_EN).get(zh, zh)
|
||||
|
||||
|
||||
def _parity_chart_source(kind, core_chart, worksheets, divisional, varga_full):
|
||||
if kind == "d1":
|
||||
planets = core_chart.get("planets") if isinstance(core_chart.get("planets"), dict) else {}
|
||||
ascendant = core_chart.get("ascendant") if isinstance(core_chart.get("ascendant"), dict) else {}
|
||||
return planets, ascendant
|
||||
if kind == "moon":
|
||||
d1_sheet = worksheets.get("d1_rasi_bhava") if isinstance(worksheets.get("d1_rasi_bhava"), dict) else {}
|
||||
moon = d1_sheet.get("moon_chart") if isinstance(d1_sheet.get("moon_chart"), dict) else {}
|
||||
if not moon and isinstance(divisional.get("moon_chart"), dict):
|
||||
moon = divisional["moon_chart"]
|
||||
raw = moon.get("raw") if isinstance(moon.get("raw"), dict) else {}
|
||||
if not raw and isinstance(moon.get("data"), dict):
|
||||
raw = moon["data"]
|
||||
if not raw:
|
||||
raw = moon
|
||||
if isinstance(raw.get("raw"), dict):
|
||||
raw = raw["raw"]
|
||||
ascendant = {}
|
||||
for key in ("Ascendant", "ascendant", "Asc"):
|
||||
if isinstance(raw.get(key), dict):
|
||||
ascendant = raw[key]
|
||||
break
|
||||
planets = raw.get("planets") if isinstance(raw.get("planets"), dict) else {}
|
||||
return planets, ascendant
|
||||
chart = {}
|
||||
for key in kind:
|
||||
candidate = varga_full.get(key) if isinstance(varga_full, dict) else None
|
||||
if isinstance(candidate, dict) and candidate:
|
||||
chart = candidate
|
||||
break
|
||||
ascendant = {}
|
||||
for key in ("Ascendant", "ascendant", "Asc"):
|
||||
if isinstance(chart.get(key), dict):
|
||||
ascendant = chart[key]
|
||||
break
|
||||
return chart, ascendant
|
||||
|
||||
|
||||
def _append_parity_chart_fences(lines, *, language, core_chart, worksheets, divisional, varga_full) -> None:
|
||||
"""One north-Indian fence per chart that has a sign to draw. No empty heading."""
|
||||
try:
|
||||
from scripts.report_chart_block import PLANET_ORDER, build_chart_block
|
||||
except ModuleNotFoundError: # pragma: no cover - direct scripts/ execution
|
||||
from report_chart_block import PLANET_ORDER, build_chart_block
|
||||
natal = core_chart.get("planets") if isinstance(core_chart.get("planets"), dict) else {}
|
||||
retrograde = {
|
||||
name: bool(row.get("retrograde")) if isinstance(row, dict) else False
|
||||
for name in PLANET_ORDER
|
||||
for row in [natal.get(name)]
|
||||
}
|
||||
seen: set[str] = set()
|
||||
for chart_id, title_zh, kind in _PARITY_CHARTS:
|
||||
if chart_id in seen:
|
||||
continue
|
||||
positions, ascendant = _parity_chart_source(kind, core_chart, worksheets, divisional, varga_full)
|
||||
title = _parity_chart_title(title_zh, language)
|
||||
try:
|
||||
block = build_chart_block(chart_id, title, positions, ascendant, retrograde)
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
seen.add(chart_id)
|
||||
lines.extend([f"##### {title}", "", block, ""])
|
||||
|
||||
|
||||
def _ascendant_report_fields(asc: dict) -> dict:
|
||||
"""Longitude, nakshatra and lords for the report's ascendant row. No new dignity."""
|
||||
longitude = None
|
||||
for key in ("lon", "longitude", "degree_raw"):
|
||||
value = asc.get(key)
|
||||
if isinstance(value, (int, float)):
|
||||
longitude = float(value) % 360.0
|
||||
break
|
||||
if longitude is None:
|
||||
sign = asc.get("sign")
|
||||
degree = asc.get("degree_in_sign")
|
||||
if not isinstance(degree, (int, float)):
|
||||
degree = asc.get("degree")
|
||||
signs = globals().get("SIGNS") or []
|
||||
if sign in signs and isinstance(degree, (int, float)):
|
||||
longitude = signs.index(sign) * 30.0 + float(degree)
|
||||
fields = {}
|
||||
if longitude is not None:
|
||||
try:
|
||||
from scripts.jyotish_engine import _nakshatra_fields
|
||||
except ModuleNotFoundError: # pragma: no cover - direct scripts/ execution
|
||||
from jyotish_engine import _nakshatra_fields
|
||||
fields = _nakshatra_fields(longitude)
|
||||
degree = asc.get("degree_in_sign")
|
||||
if not isinstance(degree, (int, float)):
|
||||
degree = asc.get("degree")
|
||||
pada = fields.get("nakshatra_pada") if fields else None
|
||||
if pada is None:
|
||||
pada = asc.get("nakshatra_pada") or asc.get("pada")
|
||||
return {
|
||||
"sign": asc.get("sign"),
|
||||
"degree": degree,
|
||||
"longitude": longitude,
|
||||
"nakshatra": fields.get("nakshatra") or asc.get("nakshatra"),
|
||||
"pada": pada,
|
||||
"nakshatra_lord": fields.get("nakshatra_lord") or asc.get("nakshatra_lord"),
|
||||
"sign_lord": asc.get("lord") or asc.get("rasi_lord"),
|
||||
}
|
||||
|
||||
|
||||
def render_pl9_parity_markdown(packet: dict) -> str:
|
||||
"""Render only PL9 page-family material admitted to the parity main volume."""
|
||||
_bind_engine_symbols()
|
||||
@@ -544,6 +691,18 @@ def render_pl9_parity_markdown(packet: dict) -> str:
|
||||
elif isinstance(value, (list, tuple)):
|
||||
value = ';'.join(str(item) for item in value if item not in (None, '', [], {}))
|
||||
text = str(value if value not in (None, '') else '-').replace('\n', ' ').strip()
|
||||
if _lang != 'en':
|
||||
# Whole status tokens. Longer phrases are handled in the glossary
|
||||
# before these short words, so a half-translated fragment is not
|
||||
# cleaned up by deleting letters here.
|
||||
text = {
|
||||
'active': '当前运行',
|
||||
'background': '背景',
|
||||
'listed': '已列出',
|
||||
'native': '本命',
|
||||
'Custom condition met': '满足自定义条件',
|
||||
'custom condition satisfied': '满足自定义条件',
|
||||
}.get(text, text)
|
||||
return text.replace('|', '/')
|
||||
|
||||
def _brief(items, limit=2) -> str:
|
||||
@@ -668,6 +827,10 @@ def render_pl9_parity_markdown(packet: dict) -> str:
|
||||
def _public_status_note(value) -> str:
|
||||
zh_mapping = {
|
||||
'partial_verified': '已生成',
|
||||
'listed': '已列出',
|
||||
'active': '当前运行',
|
||||
'background': '背景',
|
||||
'native': '本命',
|
||||
'parameter_sensitive': '参数敏感',
|
||||
'blocked': '需补资料',
|
||||
'verified': '已核验',
|
||||
@@ -856,6 +1019,14 @@ def render_pl9_parity_markdown(packet: dict) -> str:
|
||||
'| Planet | R/C | Sign | Degree | Longitude | House | Nakshatra | Pada | Speed |',
|
||||
'|--------|-----|------|--------|-----------|-------|-----------|------|-------|',
|
||||
])
|
||||
ascendant = _dict(core_chart.get('ascendant'))
|
||||
if ascendant:
|
||||
info = _ascendant_report_fields(ascendant)
|
||||
asc_label = 'Ascendant' if _lang == 'en' else '上升'
|
||||
lines.append(
|
||||
f"| {_cell(asc_label)} | - | {_cell(_token(info['sign']))} | {_cell(_degree(info['degree']))} | "
|
||||
f"{_cell(_degree(info['longitude']))} | 1 | {_cell(info['nakshatra'])} | {_cell(info['pada'])} | - |"
|
||||
)
|
||||
for planet in ('Sun', 'Moon', 'Mars', 'Mercury', 'Jupiter', 'Venus', 'Saturn', 'Rahu', 'Ketu'):
|
||||
row = _dict(planets.get(planet))
|
||||
if not row:
|
||||
@@ -875,6 +1046,14 @@ def render_pl9_parity_markdown(packet: dict) -> str:
|
||||
'| 行星 | 星座主 | 星宿主 | 分主 | 分分主 | 尊贵状态 | 力量比 |',
|
||||
'|------|--------|--------|------|--------|----------|--------|',
|
||||
])
|
||||
if ascendant:
|
||||
info = _ascendant_report_fields(ascendant)
|
||||
asc_label = 'Ascendant' if _lang == 'en' else '上升'
|
||||
dignity = 'not applicable' if _lang == 'en' else '不适用'
|
||||
lines.append(
|
||||
f"| {_cell(asc_label)} | {_cell(_token(info['sign_lord']))} | {_cell(_token(info['nakshatra_lord']))} | "
|
||||
f"- | - | {_cell(dignity)} | - |"
|
||||
)
|
||||
for planet in ('Sun', 'Moon', 'Mars', 'Mercury', 'Jupiter', 'Venus', 'Saturn', 'Rahu', 'Ketu'):
|
||||
row = _dict(planets.get(planet))
|
||||
if not row:
|
||||
@@ -1078,8 +1257,14 @@ def render_pl9_parity_markdown(packet: dict) -> str:
|
||||
ready_natal_specs = [spec for spec in natal_specs if spec[2]]
|
||||
if ready_natal_specs:
|
||||
lines.extend(['### p6-p17 本命与分盘北印度图盘', '', '分盘对出生时间敏感,原始计算供核对,不单独增加结论的确定性。', ''])
|
||||
for page, label, _ready in ready_natal_specs:
|
||||
lines.extend([f'##### p{page} · {label}', ''])
|
||||
_append_parity_chart_fences(
|
||||
lines,
|
||||
language=_lang,
|
||||
core_chart=core_chart,
|
||||
worksheets=worksheets,
|
||||
divisional=divisional,
|
||||
varga_full=varga_full,
|
||||
)
|
||||
# The original PL9 charts are visual, but their sign placements are
|
||||
# still part of the visible evidence. Keep the chart drawing and add
|
||||
# a compact numeric transcription so an AI reader can recover the
|
||||
|
||||
Reference in New Issue
Block a user