"""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。" )