拆分报告渲染函数的第一阶段,把 34 个无外层依赖的辅助函数搬到 report_render。

同一份真实盘包在屏蔽与不屏蔽 PyJHora 下新旧渲染 sha 都一致,各 24 项且退出码 0。指定 pytest 233 通过、3 跳过。

Co-Authored-By: Grok (agent loop)
This commit is contained in:
jesse-ux
2026-10-15 20:59:11 +08:00
co-authored by Grok (agent loop)
parent 32b238cf86
commit dbba127e71
8 changed files with 1050 additions and 412 deletions
+127
View File
@@ -0,0 +1,127 @@
"""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。"
)