Files
Jyotisha/scripts/report_render/sections_misc.py
T
jesse-uxandGrok (agent loop) dbba127e71 拆分报告渲染函数的第一阶段,把 34 个无外层依赖的辅助函数搬到 report_render。
同一份真实盘包在屏蔽与不屏蔽 PyJHora 下新旧渲染 sha 都一致,各 24 项且退出码 0。指定 pytest 233 通过、3 跳过。

Co-Authored-By: Grok (agent loop)
2026-10-15 20:59:11 +08:00

128 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Helpers moved unchanged out of render_pl9_markdown (phase 1)."""
try:
from scripts.report_render.text import _md_cell
except ModuleNotFoundError: # pragma: no cover - direct scripts/ execution
from report_render.text import _md_cell
def _render_boundary_packet_summary_line(packet: dict, summary_pairs: list[tuple[str, str]]) -> str:
summary = packet.get('summary') if isinstance(packet.get('summary'), dict) else {}
bits = [_md_cell(packet.get('packet_mode'))]
for key, suffix in summary_pairs:
value = summary.get(key)
if value not in (None, '', []):
bits.append(f"{_md_cell(value)}{suffix}")
return f"参考包摘要:{';'.join(bits)}。"
def _status_from_report_pack_section(section: dict) -> str:
if not isinstance(section, dict):
return 'blocked'
audit = section.get('audit') if isinstance(section.get('audit'), dict) else {}
if section.get('status'):
return str(section.get('status'))
if audit.get('status'):
return str(audit.get('status'))
if section:
return 'partial_verified'
return 'blocked'
def _section_evidence_boundary(section: dict) -> str:
if not isinstance(section, dict):
return 'section_missing'
exports = section.get('exports') if isinstance(section.get('exports'), dict) else {}
ai_bundle = exports.get('ai_evidence_bundle') if isinstance(exports.get('ai_evidence_bundle'), dict) else {}
audit = section.get('audit') if isinstance(section.get('audit'), dict) else {}
boundaries = []
if ai_bundle.get('contains_private_pl9_text') is False:
boundaries.append('no_private_pl9_text')
if ai_bundle.get('claim_policy'):
boundaries.append(str(ai_bundle.get('claim_policy')))
if audit.get('must_not_claim'):
boundaries.append('must_not_claim=' + ','.join(map(str, audit.get('must_not_claim')[:3])))
if section.get('reason'):
boundaries.append(str(section.get('reason')))
return '; '.join(boundaries) if boundaries else 'evidence_labeled'
def _domi_pl9_current_gap_appendix() -> list[str]:
return []
def _chart_planets_from_positions(positions: dict) -> dict:
chart_planets = {}
if not isinstance(positions, dict):
return chart_planets
for planet_name in ('Sun', 'Moon', 'Mars', 'Mercury', 'Jupiter', 'Venus', 'Saturn', 'Rahu', 'Ketu'):
planet = positions.get(planet_name)
if not isinstance(planet, dict):
continue
sign = planet.get('sign') or planet.get('rashi') or planet.get('sign_name')
if not sign:
continue
degree = planet.get('degree_in_sign', planet.get('degree', planet.get('longitude_in_sign', 0)))
chart_planets[planet_name] = {'sign': sign, 'degree': degree or 0}
return chart_planets
def _kp_house_focus_text(value) -> str:
house_label_map = {
2: '收入与资源',
5: '机会与投入',
6: '职责与压力',
7: '合作与伴侣',
9: '运气与扩张',
10: '事业与位置',
11: '回报与支持',
}
parts = []
if isinstance(value, (list, tuple)):
raw_parts = [str(item).strip() for item in value if str(item).strip()]
else:
raw_parts = [part.strip() for part in str(value or '').replace('/', ',').split(',') if part.strip()]
for part in raw_parts:
try:
house_no = int(part)
except ValueError:
parts.append(part)
continue
parts.append(f"{house_no}宫[{house_label_map.get(house_no, '-')}]")
return '、'.join(parts) if parts else '-'
def _high_value_theme_authority_sentence(domain: str) -> str | None:
domain_label_map = {
'career': '事业',
'wealth': '财务',
'relationship': '关系',
}
action_map = {
'career': '优先排序事业推进顺序、外部舞台和月份节奏',
'wealth': '优先排序资源承接、现金流节奏和收益兑现顺序',
'relationship': '优先排序合作推进、联结温度和关系节奏',
}
return (
"主结论加厚:Narayana 第二时间轴、力量兑现结构、年度 highlights / field briefs、Patyayini 年内换挡、Graha Padas 这几层现在直接参与"
+ f"{domain_label_map.get(domain, domain)}章主结论,"
+ f"用来{action_map.get(domain, '补强主题排序')};"
+ "它们仍保留 parameter_sensitive / unclosed_divisional_chart 边界,但不再只停留在附录或登记表。"
)
def _kp_conflict_adjudication_sentence(domain: str) -> str:
domain_label_map = {
'career': '事业',
'wealth': '财务',
'relationship': '关系',
}
focus_map = {
'career': '外部舞台、职位变化和项目推进',
'wealth': '资源承接、现金流和回收节奏',
'relationship': '合作推进、联结温度和承诺节奏',
}
return (
f"如果 Vimshottari、Narayana 与年度专题同向,而 KP 月度支持也落在同一主题上,就把这个月份优先当作{domain_label_map.get(domain, domain)}线的可执行窗口。"
f"如果 KP 单独活跃、但本命结构或双大运没有接力,KP 只负责提示 {focus_map.get(domain, '主题变化')} 的月份排序,不改写主题事实。"
f"如果 KP 与年度专题或双大运出现冲突,先以本命结构 + Vimshottari + Narayana 定主轴,KP 退回到月度提醒与核对层,继续保持 parameter_sensitive。"
)
def _prashna_exact_cusp_boundary_sentence() -> str | None:
return (
"如果下一步要把月份继续缩到更细日期,仍需要补 question_time / place / timezone / question category;"
"Prashna ruling planets 与 exact cusp oracle 在这份本命个人报告里继续保持 blocked,只作为后续缩窄入口,不冒充 timing truth。"
)