"""Render a bounded birth-time sensitivity appendix section.""" from __future__ import annotations from typing import Any, Mapping try: from flexible_birth_time_profile import _candidate_window_authority_violation except ImportError: # pragma: no cover - package import from scripts.flexible_birth_time_profile import _candidate_window_authority_violation SCHEMA_VERSION = "jyotish.flexible_birth_time_full_report_projection.v1" class FlexibleBirthTimeReportSectionError(ValueError): """Raised when a sensitivity projection cannot be rendered safely.""" def render_flexible_birth_time_report_section(projection: Mapping[str, Any]) -> str: packet = _validate_projection(projection) window = packet.get("window") or {} stable = packet.get("stable_structure_section") or {} sensitive = packet.get("minute_sensitive_section") or {} lines = [ "### 出生时间敏感度", "", f"- 可信区间:{window.get('start_time')}–{window.get('end_time')}", f"- 代表分钟:{window.get('representative_time')}", f"- 候选分钟数:{window.get('candidate_count')}", "", "#### 窗口内稳定层", ] for key in stable.get("evidence_keys") or []: lines.append(f"- `{key}`:{_render_value((stable.get('layers') or {}).get(key))}") lines.extend(["", "#### 窗口内敏感层"]) for key in sensitive.get("evidence_keys") or []: lines.append(f"- `{key}`:{_render_value((sensitive.get('layers') or {}).get(key))}") lines.extend([ "", "#### 使用边界", "", "- 被标记为敏感的主题只能作条件性解读,不能据此认定唯一出生分钟。", ]) return "\n".join(lines).rstrip() + "\n" def _validate_projection(value: Mapping[str, Any]) -> dict[str, Any]: violation = _candidate_window_authority_violation(value) if violation: raise FlexibleBirthTimeReportSectionError(f"candidate_window_authority_forbidden:{violation}") if not isinstance(value, Mapping) or value.get("schema_version") != SCHEMA_VERSION: raise FlexibleBirthTimeReportSectionError("flexible_birth_time_full_report_projection_schema_invalid") if value.get("status") != "candidate_window_only": raise FlexibleBirthTimeReportSectionError("projection_must_remain_candidate_window_only") return dict(value) def _render_value(value: Any) -> str: if isinstance(value, Mapping): return " / ".join(f"{key}={_render_value(item)}" for key, item in value.items()) if isinstance(value, list): return " / ".join(_render_value(item) for item in value) return str(value)