fix(report): project annual sections and thicken dasha reading

完整数据版第二轮。年运章写出资料包里已有的节,没有数据的不新算。大运解释补回可能段落。瑜伽作用不再断言寿命。术语替换不进标识符。罗睺和计都不写守护第未列出宫。未合入 staging。
This commit is contained in:
jesse-ux
2026-10-08 22:30:32 +08:00
committed by Jesse_Chen
parent 25d067bf12
commit b8eef75871
14 changed files with 1148 additions and 165 deletions
+334 -112
View File
@@ -226,7 +226,7 @@ def _strip_pl9_user_engineering_markers(markdown: str) -> str:
"neecha_bhanga": "落陷取消",
"dhana": "财富",
"putra": "子女/创造",
"ayur": "寿命/健康",
"ayur": "体质参考",
"auspicious": "吉性",
"nabhasa": "天象格局",
"surya": "太阳类",
@@ -258,8 +258,12 @@ def _strip_pl9_user_engineering_markers(markdown: str) -> str:
"Aquarius": "水瓶座",
"Pisces": "双鱼座",
}
try:
from pl9_language_terms import replace_natural_language_term as _replace_term
except ModuleNotFoundError: # pragma: no cover
from scripts.pl9_language_terms import replace_natural_language_term as _replace_term
for raw, public in sorted(glossary_terms.items(), key=lambda item: len(item[0]), reverse=True):
text = re.sub(rf"(?<![A-Za-z]){re.escape(raw)}(?![A-Za-z])", public, text)
text = _replace_term(text, raw, public)
text = re.sub(r"\b[a-zA-Z_]+_profile\b", "计算口径", text)
text = re.sub(r"\b[a-zA-Z_]+_status\b", "状态", text)
text = re.sub(r"\b[a-zA-Z_]+_producer\b", "计算项", text)
@@ -306,7 +310,6 @@ def _strip_pl9_user_engineering_markers(markdown: str) -> str:
"太阳 and 水星": "太阳与水星",
"金星 and 水星": "金星与水星",
"Lagna": "上升",
"Kendra": "角宫",
"Trikona": "三方宫",
"Trine": "三方宫",
"Navamsa": "九分盘",
@@ -331,6 +334,7 @@ def _strip_pl9_user_engineering_markers(markdown: str) -> str:
}
for raw, public in natural_replacements.items():
text = text.replace(raw, public)
text = _replace_term(text, "Kendra", "角宫")
cleanups = {
"Special 上升s and Points": "特殊上升与派生点",
"Special 上升s": "特殊上升",
@@ -353,7 +357,11 @@ def _strip_pl9_user_engineering_markers(markdown: str) -> str:
text = re.sub(r"(?<=[\u4e00-\u9fff])\s+大运", "大运", text)
text = re.sub(r"大运\s+(?=[\u4e00-\u9fff])", "大运", text)
text = re.sub(r"(太阳|月亮|火星|水星|木星|金星|土星|北交点/罗睺|南交点/计都)\1", r"\1", text)
return text
try:
from pl9_language_terms import strip_han_plural_s
except ModuleNotFoundError: # pragma: no cover
from scripts.pl9_language_terms import strip_han_plural_s
return strip_han_plural_s(text)
def _sanitize_pl9_public_markdown(markdown: str) -> str:
@@ -410,9 +418,6 @@ def _render_pl9_user_markdown(packet: dict) -> str:
"{planets}": "相关行星",
"natural_benefic": "自然吉星",
"natural_malefic": "自然凶星",
"functional_benefic": "功能吉星",
"functional_malefic": "功能凶星",
"functional_neutral": "功能中性",
"functional_role_not_returned": "功能属性未单独返回",
"absolute Virupas; precise Sthana/Dig/Kala/Drik; bounded BPHS Chesta": "以 Virupa 为单位,综合 Sthana、Dig、Kala、Drik 与 BPHS Chesta",
"Chesta remains cross-engine method-conflicted; totals inherit that boundary.": "Chesta Bala 的算法口径会影响总分,阅读时以同一口径内比较为主。",
@@ -428,6 +433,16 @@ def _render_pl9_user_markdown(packet: dict) -> str:
}
for raw, public in replacements.items():
text = text.replace(raw, public)
try:
from pl9_language_terms import replace_natural_language_term
except ModuleNotFoundError: # pragma: no cover
from scripts.pl9_language_terms import replace_natural_language_term
for raw, public in (
("functional_benefic", "功能吉星"),
("functional_malefic", "功能凶星"),
("functional_neutral", "功能中性"),
):
text = replace_natural_language_term(text, raw, public)
text = re.sub(r"^#\s+.*$", f"# {title}", text, count=1, flags=re.MULTILINE)
drop_fragments = (
"本册只编排原版",
@@ -559,15 +574,25 @@ def render_pl9_parity_markdown(packet: dict) -> str:
if field.get('status') in {'blocked', 'not_applicable'}:
return '-'
if field.get('status') == 'conflict':
labels = {'solar_return': '太阳返照', 'tajika': 'Tajika'}
labels = (
{'solar_return': 'solar return', 'tajika': 'Tajika'}
if _lang == 'en'
else {'solar_return': '太阳返照', 'tajika': 'Tajika'}
)
other = 'another method' if _lang == 'en' else '另一口径'
values = []
for item in _list(field.get('values')):
item = _dict(item)
data = _dict(item.get('value'))
value = next((data.get(key) for key in keys if data.get(key)), None)
if value is not None:
values.append(f"{labels.get(item.get('producer'), '另一口径')}:{_token(value)}")
return ';'.join(values) + '(两者不同)' if values else '-'
sep = ': ' if _lang == 'en' else ':'
values.append(f"{labels.get(item.get('producer'), other)}{sep}{_token(value)}")
if not values:
return '-'
if _lang == 'en':
return '; '.join(values) + ' (the two differ)'
return ';'.join(values) + '(两者不同)'
data = _dict(field.get('data') or field)
return _token(next((data.get(key) for key in keys if data.get(key)), None))
@@ -575,13 +600,20 @@ def render_pl9_parity_markdown(packet: dict) -> str:
year_lord = _dict(year_lord)
blocked_detail = _dict(year_lord.get('blocked_detail'))
if year_lord.get('status') == 'blocked' and blocked_detail.get('selection_basis_text'):
# BUG-1223: no stand-in year lord; 「年主暂无法确定(原因)」.
# BUG-1223: no stand-in year lord. Chinese keeps 「年主暂无法确定(原因)」.
# English uses the twin sentence and does not keep the Chinese source line.
if _lang == 'en' and blocked_detail.get('selection_basis_text_en'):
return _cell(blocked_detail['selection_basis_text_en'])
return _cell(blocked_detail['selection_basis_text'])
if year_lord.get('status') == 'conflict':
if _lang == 'en':
return 'The two calculations name different year lords, so they are not merged'
return '两种计算口径给出的年主不同,暂不合并'
data = _dict(year_lord.get('data') or year_lord)
# BUG-1214: the producer's one-sentence Panchadhikari basis (replaces
# the BUG-1212 "Muntha lord for now" stand-in).
if _lang == 'en' and data.get('selection_basis_text_en'):
return _cell(data['selection_basis_text_en'])
if data.get('selection_basis_text'):
return _cell(data['selection_basis_text'])
if data.get('selection_basis'):
@@ -666,18 +698,31 @@ def render_pl9_parity_markdown(packet: dict) -> str:
def _public_report_text(value) -> str:
text = str(value or '').strip()
replacements = {
'parameter_sensitive': '参考',
'blocked': '未列',
'observed_relationships': '已形成',
'observed_empty': '未形成',
'external_replay_pending': '待补充',
'Evidence label': '证据口径',
'证据标签': '证据口径',
}
text = text.replace('blocked_external_callable', '外部回放待校对')
text = text.replace('pyjhora_behavior_only / not_multiengine_parity', '外部行为参考')
text = text.replace('pyjhora_behavior_only', '外部行为参考')
if _lang == 'en':
replacements = {
'blocked_external_callable': 'external replay still to review',
'pyjhora_behavior_only / not_multiengine_parity': 'external behavior note',
'pyjhora_behavior_only': 'external behavior note',
'parameter_sensitive': 'reference',
'blocked': 'not supplied',
'observed_relationships': 'formed',
'observed_empty': 'not formed',
'external_replay_pending': 'to be supplied',
'Evidence label': 'note',
}
else:
replacements = {
'blocked_external_callable': '外部回放待校对',
'pyjhora_behavior_only / not_multiengine_parity': '外部行为参考',
'pyjhora_behavior_only': '外部行为参考',
'parameter_sensitive': '参考',
'blocked': '未列',
'observed_relationships': '已形成',
'observed_empty': '未形成',
'external_replay_pending': '待补充',
'Evidence label': '证据口径',
'证据标签': '证据口径',
}
for raw, public in replacements.items():
text = text.replace(raw, public)
return text
@@ -2047,12 +2092,20 @@ def render_pl9_parity_markdown(packet: dict) -> str:
current_year = _dict(annual.get('pl9_current_year_results'))
current_sections = _list(current_year.get('sections'))
if current_sections:
lines.extend([
'### p139-p141 Varshaphala 年度结果解释',
'',
'| 项目 | 锚点 | 年度主题 |',
'|------|------|----------|',
])
if _lang == 'en':
lines.extend([
'### Varshaphala annual result notes',
'',
'| Item | Anchor | Annual theme |',
'|------|--------|---------------|',
])
else:
lines.extend([
'### p139-p141 Varshaphala 年度结果解释',
'',
'| 项目 | 锚点 | 年度主题 |',
'|------|------|----------|',
])
for section in current_sections:
if not isinstance(section, dict):
continue
@@ -2070,12 +2123,20 @@ def render_pl9_parity_markdown(packet: dict) -> str:
annual_dasha = _dict(annual.get('pl9_annual_dasha_results'))
dasha_rows = _list(annual_dasha.get('rows'))
if dasha_rows:
lines.extend([
'### p144-p145 Annual Dasha 结果解释',
'',
'| 阶段 | 主星 | 年盘宫位 | 日期边界 | 主题 |',
'|------|------|----------|----------|------|',
])
if _lang == 'en':
lines.extend([
'### Annual Dasha result notes',
'',
'| Stage | Lord | Annual house | Date boundary | Theme |',
'|-------|------|---------------|---------------|-------|',
])
else:
lines.extend([
'### p144-p145 Annual Dasha 结果解释',
'',
'| 阶段 | 主星 | 年盘宫位 | 日期边界 | 主题 |',
'|------|------|----------|----------|------|',
])
for row in dasha_rows:
if not isinstance(row, dict):
continue
@@ -2102,6 +2163,20 @@ def render_pl9_parity_markdown(packet: dict) -> str:
f"{_cell(row.get('start'))} | {_cell(row.get('end'))} | {_public_status_note(row.get('boundary_status'))} |"
)
lines.append('')
readings = [str(row.get('reading')).strip() for row in mudda_rows if isinstance(row, dict) and str(row.get('reading') or '').strip()]
if readings:
lines.extend([
'#### Mudda period notes' if _lang == 'en' else '#### Mudda 阶段可能主题',
'',
])
for reading in readings:
lines.append(f"- {_public_report_text(reading)}")
lines.append('')
if _lang == 'en':
lines.append('Day-level rows and a second balance profile are not in this table.')
else:
lines.append('这张表没有日级行,也没有第二套余额口径。')
lines.append('')
patyayini_rows = _list(normalized.get('patyayini_rows'))
if patyayini_rows:
lines.extend([
@@ -2151,9 +2226,15 @@ def render_pl9_parity_markdown(packet: dict) -> str:
if not isinstance(row, dict):
continue
forming = row.get('forming_planets') or row.get('planets') or row.get('raw_relationships')
motion_key = str(row.get('motion') or row.get('aspect_motion') or '-')
motion_labels = (
{'applying': 'applying', 'separating': 'separating'}
if _lang == 'en'
else {'applying': '入相', 'separating': '分离'}
)
lines.append(
f"| {_cell(row.get('yoga') or row.get('name'))} | {_cell(_public_report_text(row.get('observation_status') or row.get('condition_status') or row.get('present')))} | "
f"{_cell(forming)} | {_cell({'applying': '入相', 'separating': '分离'}.get(str(row.get('motion') or row.get('aspect_motion') or '-'), row.get('motion') or row.get('aspect_motion') or '-'))} |"
f"{_cell(forming)} | {_cell(motion_labels.get(motion_key, motion_key))} |"
)
lines.append('')
saham_rows = _list(normalized.get('saham_rows'))
@@ -2175,26 +2256,40 @@ def render_pl9_parity_markdown(packet: dict) -> str:
public_replay = _dict(annual.get('public_panchavargiya_replay'))
replay_coverage = _dict(public_replay.get('coverage'))
if public_replay:
lines.extend([
'### 公开 Panchavargiya 数值复核边界',
'',
'| 项目 | 结果 |',
'|------|------|',
f"| 公开复核状态 | {_cell(public_replay.get('status'))} |",
f"| 覆盖行星 | {_cell(replay_coverage.get('planet_count'))} |",
f"| 已匹配分量 | {_cell(replay_coverage.get('exact_component_match_count'))} / {_cell(replay_coverage.get('component_count'))} |",
f"| 已匹配总分 | {_cell(replay_coverage.get('exact_total_match_count'))} / {_cell(replay_coverage.get('planet_count'))} |",
f"| 可直接用于公式升级 | {_cell(public_replay.get('promotion_allowed'))} |",
'',
])
if _lang == 'en':
lines.extend([
'### Public Panchavargiya value check',
'',
'| Item | Result |',
'|------|--------|',
f"| Public check status | {_cell(public_replay.get('status'))} |",
f"| Planets covered | {_cell(replay_coverage.get('planet_count'))} |",
f"| Components matched | {_cell(replay_coverage.get('exact_component_match_count'))} / {_cell(replay_coverage.get('component_count'))} |",
f"| Totals matched | {_cell(replay_coverage.get('exact_total_match_count'))} / {_cell(replay_coverage.get('planet_count'))} |",
f"| Usable for a formula upgrade | {_cell(public_replay.get('promotion_allowed'))} |",
'',
])
else:
lines.extend([
'### 公开 Panchavargiya 数值复核边界',
'',
'| 项目 | 结果 |',
'|------|------|',
f"| 公开复核状态 | {_cell(public_replay.get('status'))} |",
f"| 覆盖行星 | {_cell(replay_coverage.get('planet_count'))} |",
f"| 已匹配分量 | {_cell(replay_coverage.get('exact_component_match_count'))} / {_cell(replay_coverage.get('component_count'))} |",
f"| 已匹配总分 | {_cell(replay_coverage.get('exact_total_match_count'))} / {_cell(replay_coverage.get('planet_count'))} |",
f"| 可直接用于公式升级 | {_cell(public_replay.get('promotion_allowed'))} |",
'',
])
annual_extra_specs = (
('annual_anchor_rows', 'p126 年度锚点', ('name', 'label', 'value', 'sign', 'degree', 'house')),
('annual_planet_rows', 'p127 年度行星资料', ('planet', 'sign', 'degree', 'longitude', 'nakshatra', 'pada')),
('annual_anchor_rows', 'Annual anchors' if _lang == 'en' else 'p126 年度锚点', ('name', 'label', 'value', 'sign', 'degree', 'house')),
('annual_planet_rows', 'Annual planet data' if _lang == 'en' else 'p127 年度行星资料', ('planet', 'sign', 'degree', 'longitude', 'nakshatra', 'pada')),
('true_solar_return_rows', 'True Solar Return', ('field', 'value', 'date', 'time', 'dt_local')),
('panchadhikari_rows', 'Panchadhikari', ('planet', 'role', 'value', 'sign', 'house')),
('tripataki_placement_rows', 'Tripataki Placement', ('planet', 'sign', 'house', 'degree', 'placement')),
('tripataki_vedha_rows', 'Tripataki Vedha', ('planet', 'target', 'sign', 'house', 'result')),
('current_year_result_rows', 'p139-p141 年度结果', ('title', 'value', 'theme', 'summary', 'anchor')),
('current_year_result_rows', 'Annual result notes' if _lang == 'en' else 'p139-p141 年度结果', ('title', 'value', 'theme', 'summary', 'anchor')),
('tajika_presence_rows', 'Tajika Presence', ('yoga', 'name', 'present', 'forming_planets')),
('tajika_condition_status_rows', 'Tajika Condition', ('yoga', 'name', 'condition', 'present')),
('tajika_forming_pair_rows', 'Tajika Forming Pair', ('yoga', 'name', 'planet_a', 'planet_b', 'forming_planets')),
@@ -2282,12 +2377,20 @@ def render_pl9_parity_markdown(packet: dict) -> str:
monthly_pack = _dict(annual.get('monthly_chart_pack'))
monthly_rows = _list(monthly_pack.get('rows'))
if monthly_rows:
lines.extend([
'### 年度月返照图盘数据',
'',
'| 月份 | 本地时刻 | 年盘上升 | 上升度数 | 行星黄经(度) |',
'|------|----------|----------|----------|-----------------|',
])
if _lang == 'en':
lines.extend([
'### Monthly returns within the year',
'',
'| Month | Local time | Annual ascendant | Ascendant degree | Planet longitudes (deg) |',
'|-------|------------|------------------|------------------|--------------------------|',
])
else:
lines.extend([
'### 年度月返照图盘数据',
'',
'| 月份 | 本地时刻 | 年盘上升 | 上升度数 | 行星黄经(度) |',
'|------|----------|----------|----------|-----------------|',
])
for row in monthly_rows:
if not isinstance(row, dict):
continue
@@ -2311,12 +2414,20 @@ def render_pl9_parity_markdown(packet: dict) -> str:
for row in _list(page.get('rows')) if isinstance(row, dict)
]
if lifetime_rows:
lines.extend([
'### 生命周期年度返照图盘数据',
'',
'| 年龄 | 年份 | 年返时刻 | 上升 | 上升度数 | 行星黄经(度) |',
'|------|------|----------|------|----------|-----------------|',
])
if _lang == 'en':
lines.extend([
'### Lifetime annual return charts',
'',
'| Age | Year | Return time | Ascendant | Ascendant degree | Planet longitudes (deg) |',
'|-----|------|-------------|-----------|------------------|--------------------------|',
])
else:
lines.extend([
'### 生命周期年度返照图盘数据',
'',
'| 年龄 | 年份 | 年返时刻 | 上升 | 上升度数 | 行星黄经(度) |',
'|------|------|----------|------|----------|-----------------|',
])
for row in lifetime_rows:
asc = _dict(row.get('ascendant'))
longitudes = _dict(row.get('planet_longitudes'))
@@ -2332,20 +2443,36 @@ def render_pl9_parity_markdown(packet: dict) -> str:
)
lines.append('')
annual_series = _dict(annual.get('annual_chart_series_pack'))
for series_key, title in (
('birth_place_eight_year_overview', '出生地八年年度总览'),
('local_eight_year_overview', '现居地八年年度总览'),
):
eight_year_titles = (
(
('birth_place_eight_year_overview', 'Eight-year overview at the birthplace'),
('local_eight_year_overview', 'Eight-year overview at the current residence'),
)
if _lang == 'en'
else (
('birth_place_eight_year_overview', '出生地八年年度总览'),
('local_eight_year_overview', '现居地八年年度总览'),
)
)
for series_key, title in eight_year_titles:
series = _dict(annual_series.get(series_key))
series_rows = _list(series.get('rows'))
if not series_rows:
continue
lines.extend([
f'### {title}',
'',
'| 年份 | 年龄 | 年返时刻 | 年盘上升 |',
'|------|------|----------|----------|',
])
if _lang == 'en':
lines.extend([
f'### {title}',
'',
'| Year | Age | Return time | Annual ascendant |',
'|------|-----|-------------|-------------------|',
])
else:
lines.extend([
f'### {title}',
'',
'| 年份 | 年龄 | 年返时刻 | 年盘上升 |',
'|------|------|----------|----------|',
])
for row in series_rows:
if not isinstance(row, dict):
continue
@@ -2358,7 +2485,41 @@ def render_pl9_parity_markdown(packet: dict) -> str:
annual_visual = annual.get('pl9_annual_visual_panels') if isinstance(annual.get('pl9_annual_visual_panels'), dict) else {}
panels = annual_visual.get('panels') if isinstance(annual_visual.get('panels'), list) else []
if panels:
lines.extend(['### p139-p155 年度北印度图盘', ''])
lines.extend([
'### Annual north-Indian chart' if _lang == 'en' else '### p139-p155 年度北印度图盘',
'',
])
else:
occupancy_longitudes = _dict(_dict(annual_chart.get('data')).get('planet_longitudes'))
if occupancy_longitudes:
signs = list(globals().get('SIGNS') or [])
asc_idx = _dict(annual_chart.get('data')).get('asc_sign_idx')
asc_name = _dict(annual_chart.get('data')).get('asc_sign')
if not isinstance(asc_idx, int) and signs and asc_name in signs:
asc_idx = signs.index(asc_name)
lines.extend([
'### Annual north-Indian chart' if _lang == 'en' else '### p139-p155 年度北印度图盘',
'',
(
'Sign occupancy from the annual longitudes already calculated. This is a text table, not a drawn chart.'
if _lang == 'en'
else '下面按已算出的年度黄经,把行星放进年返上升起的整宫。这是文字表,不是另画的图。'
),
'',
'| Planet | Sign | House from annual Lagna |' if _lang == 'en' else '| 行星 | 星座 | 自年返上升的宫 |',
'|--------|------|--------------------------|' if _lang == 'en' else '|------|------|----------------|',
])
if isinstance(asc_idx, int) and signs:
lines.append(f"| Lagna | {_cell(_token(signs[asc_idx % 12]))} | 1 |")
for planet in ('Sun', 'Moon', 'Mars', 'Mercury', 'Jupiter', 'Venus', 'Saturn', 'Rahu', 'Ketu'):
longitude = occupancy_longitudes.get(planet)
if not isinstance(longitude, (int, float)) or isinstance(longitude, bool):
continue
sign_idx = int(float(longitude) // 30) % 12
sign = signs[sign_idx] if signs else '-'
house = ((sign_idx - asc_idx) % 12) + 1 if isinstance(asc_idx, int) else '-'
lines.append(f"| {_cell(_token(planet))} | {_cell(_token(sign))} | {_cell(house)} |")
lines.append('')
for panel in panels:
if not isinstance(panel, dict):
continue
@@ -2378,13 +2539,25 @@ def render_pl9_parity_markdown(packet: dict) -> str:
annual_chart_data = _dict(annual_chart.get('data'))
solar_return = _dict(annual_pack.get('solar_return'))
solar_return_data = _dict(solar_return.get('data') or solar_return)
lines.extend([f'### {year} 年度 Varshaphala / Tajika 细项', ''])
if _lang == 'en':
lines.extend([f'### {year} annual Varshaphala / Tajika detail', ''])
else:
lines.extend([f'### {year} 年度 Varshaphala / Tajika 细项', ''])
if solar_return_data or annual_chart_data:
asc_label = 'Annual ascendant' if _lang == 'en' else '年返上升'
if _lang == 'en':
lines.extend([
'| Item | Value |',
'|------|-------|',
])
else:
lines.extend([
'| 项目 | 数值 |',
'|------|------|',
])
lines.extend([
'| 项目 | 数值 |',
'|------|------|',
f"| Varshapravesha | {_cell(solar_return_data.get('dt_local') or solar_return_data.get('local_time') or solar_return_data.get('date') or '-')} |",
f"| 年返上升 | {_cell(_token(annual_chart_data.get('asc_sign')))} |",
f"| {asc_label} | {_cell(_token(annual_chart_data.get('asc_sign')))} |",
])
year_lord = _dict(annual_pack.get('year_lord'))
year_lord_data = _dict(year_lord.get('data') or year_lord)
@@ -2398,12 +2571,20 @@ def render_pl9_parity_markdown(packet: dict) -> str:
planet_longitudes = _dict(annual_chart_data.get('planet_longitudes'))
if planet_longitudes:
signs = globals().get('SIGNS', [])
lines.extend([
'#### 年度行星资料',
'',
'| 行星 | 星座 | 落座度数 | 黄经 |',
'|------|------|----------|------|',
])
if _lang == 'en':
lines.extend([
'#### Annual planet data',
'',
'| Planet | Sign | Degree in sign | Longitude |',
'|--------|------|----------------|-----------|',
])
else:
lines.extend([
'#### 年度行星资料',
'',
'| 行星 | 星座 | 落座度数 | 黄经 |',
'|------|------|----------|------|',
])
if annual_chart_data.get('asc_sign') is not None:
lines.append(f"| Lagna | {_cell(_token(annual_chart_data.get('asc_sign')))} | - | - |")
for planet in ('Sun', 'Moon', 'Mars', 'Mercury', 'Jupiter', 'Venus', 'Saturn', 'Rahu', 'Ketu'):
@@ -2419,12 +2600,20 @@ def render_pl9_parity_markdown(packet: dict) -> str:
annual_dasha = _dict(annual_pack.get('pl9_annual_dasha_results'))
dasha_rows = _list(annual_dasha.get('rows'))
if dasha_rows:
lines.extend([
'#### Annual Dasha 阶段表',
'',
'| 阶段 | 主星 | 年盘宫位 | 日期边界 | 主题 |',
'|------|------|----------|----------|------|',
])
if _lang == 'en':
lines.extend([
'#### Annual Dasha periods',
'',
'| Stage | Lord | Annual house | Date boundary | Theme |',
'|-------|------|---------------|---------------|-------|',
])
else:
lines.extend([
'#### Annual Dasha 阶段表',
'',
'| 阶段 | 主星 | 年盘宫位 | 日期边界 | 主题 |',
'|------|------|----------|----------|------|',
])
for row in dasha_rows:
if not isinstance(row, dict):
continue
@@ -2435,15 +2624,26 @@ def render_pl9_parity_markdown(packet: dict) -> str:
)
lines.append('')
normalized = _dict(annual_pack.get('normalized_tables'))
future_specs = (
('mudda_rows', 'Mudda / Patyayini 年度 Dasha 表', ('balance_profile', 'lord', 'start', 'end', 'boundary_status')),
('patyayini_rows', 'Patyayini / Patyamsha ending-date 表', ('main', 'sub', 'boundary', 'boundary_status')),
('saham_rows', 'Sahams 点位表', ('name', 'saham', 'sign', 'degree_in_sign')),
('tajika_yoga_rows', 'Tajika Yoga Presence 表', ('yoga', 'name', 'present', 'forming_planets')),
('pyjhora_tajika_yoga_rows', 'Tajika Yoga Presence 表', ('yoga', 'name', 'present', 'forming_planets')),
('annual_planet_rows', '年度行星扩展字段', ('planet', 'sign', 'degree', 'longitude', 'nakshatra', 'pada')),
('annual_anchor_rows', '年度锚点', ('name', 'label', 'value', 'sign', 'degree', 'house')),
)
if _lang == 'en':
future_specs = (
('mudda_rows', 'Mudda dasha table', ('balance_profile', 'lord', 'start', 'end', 'boundary_status')),
('patyayini_rows', 'Patyayini ending dates', ('main', 'sub', 'boundary', 'boundary_status')),
('saham_rows', 'Saham positions', ('name', 'saham', 'sign', 'degree_in_sign')),
('tajika_yoga_rows', 'Tajika yoga presence', ('yoga', 'name', 'present', 'forming_planets')),
('pyjhora_tajika_yoga_rows', 'Tajika yoga presence', ('yoga', 'name', 'present', 'forming_planets')),
('annual_planet_rows', 'Annual planet extras', ('planet', 'sign', 'degree', 'longitude', 'nakshatra', 'pada')),
('annual_anchor_rows', 'Annual anchors', ('name', 'label', 'value', 'sign', 'degree', 'house')),
)
else:
future_specs = (
('mudda_rows', 'Mudda / Patyayini 年度 Dasha 表', ('balance_profile', 'lord', 'start', 'end', 'boundary_status')),
('patyayini_rows', 'Patyayini / Patyamsha ending-date 表', ('main', 'sub', 'boundary', 'boundary_status')),
('saham_rows', 'Sahams 点位表', ('name', 'saham', 'sign', 'degree_in_sign')),
('tajika_yoga_rows', 'Tajika Yoga Presence 表', ('yoga', 'name', 'present', 'forming_planets')),
('pyjhora_tajika_yoga_rows', 'Tajika Yoga Presence 表', ('yoga', 'name', 'present', 'forming_planets')),
('annual_planet_rows', '年度行星扩展字段', ('planet', 'sign', 'degree', 'longitude', 'nakshatra', 'pada')),
('annual_anchor_rows', '年度锚点', ('name', 'label', 'value', 'sign', 'degree', 'house')),
)
rendered_titles = set()
for table_key, title, preferred_keys in future_specs:
rows = [row for row in _list(normalized.get(table_key)) if isinstance(row, dict)]
@@ -2473,12 +2673,20 @@ def render_pl9_parity_markdown(packet: dict) -> str:
special = _dict(annual_pack.get('annual_special_points'))
special_data = _dict(special.get('data'))
if special_data:
lines.extend([
'#### 年度特殊点',
'',
'| 点位 | 星座 | 落座度数 | 黄经 | 守护星 |',
'|------|------|----------|------|--------|',
])
if _lang == 'en':
lines.extend([
'#### Annual special points',
'',
'| Point | Sign | Degree in sign | Longitude | Ruler |',
'|-------|------|----------------|-----------|-------|',
])
else:
lines.extend([
'#### 年度特殊点',
'',
'| 点位 | 星座 | 落座度数 | 黄经 | 守护星 |',
'|------|------|----------|------|--------|',
])
for name, row in special_data.items():
if not isinstance(row, dict):
continue
@@ -2491,12 +2699,20 @@ def render_pl9_parity_markdown(packet: dict) -> str:
monthly_pack = _dict(annual_pack.get('monthly_chart_pack'))
monthly_rows = _list(monthly_pack.get('rows'))
if monthly_rows:
lines.extend([
'#### 年度月返照图盘数据',
'',
'| 月份 | 本地时刻 | 年盘上升 | 上升度数 |',
'|------|----------|----------|----------|',
])
if _lang == 'en':
lines.extend([
'#### Monthly returns within the year',
'',
'| Month | Local time | Annual ascendant | Ascendant degree |',
'|-------|------------|------------------|-------------------|',
])
else:
lines.extend([
'#### 年度月返照图盘数据',
'',
'| 月份 | 本地时刻 | 年盘上升 | 上升度数 |',
'|------|----------|----------|----------|',
])
for row in monthly_rows:
if not isinstance(row, dict):
continue
@@ -2677,10 +2893,16 @@ def render_pl9_parity_markdown(packet: dict) -> str:
effects = row.get('effects') if isinstance(row.get('effects'), list) else []
yoga_name = row.get('name_cn') or row.get('name') or row.get('type')
combination = row.get('combination') or row.get('source_reference') or row.get('reason')
effect_text = _public_report_text(_brief(effects, 3) or row.get('interpretation') or row.get('summary'))
try:
from pl9_language_terms import soften_lifespan_wording
except ModuleNotFoundError: # pragma: no cover
from scripts.pl9_language_terms import soften_lifespan_wording
effect_text = soften_lifespan_wording(str(effect_text or ''), _lang)
lines.append(
f"| {_cell(yoga_name)} | {_cell(row.get('category') or row.get('subtype'))} | "
f"{_cell(row.get('strength'))} | {_cell(combination)} | "
f"{_cell(_public_report_text(_brief(effects, 3) or row.get('interpretation') or row.get('summary')))} |"
f"{_cell(effect_text)} |"
)
lines.append('')
if yoga_pages: