Files
Jyotisha/scripts/flexible_birth_time_report_section.py
T
Jesse_Chen e4d16b7545 fix(report): keep monthly KP headings and readable timing sources
Heading-ensure only covered empty packets, so present KP months still dropped MD/AD titles and sanitizer blanked source_path. Also lock the quality gate onto the new contracts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-06 23:00:01 +08:00

79 lines
3.2 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.
"""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(["", "#### 校时附录·分钟排行", ""])
candidate_times = window.get("candidate_times") if isinstance(window.get("candidate_times"), list) else []
if candidate_times:
lines.extend([
"下列分钟只是窗口内候选顺序,不是已确认出生分钟。",
"",
"| 序号 | 候选分钟 |",
"|------|----------|",
])
for idx, time in enumerate(candidate_times, start=1):
lines.append(f"| {idx} | {time} |")
else:
lines.extend([
"`blocked` / `candidate_times_missing`",
])
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)