新报告用围栏画出大运和事业、财富、婚恋,标题下写名字与日期。旧报告不变。 自测:pytest test_report_full_data_fix.py 10 通过;相关四份报告测试退出码 0;前端相关测试 147 通过、0 失败、0 取消;tsc 退出码 0;lint 0 error。 Co-Authored-By: Grok (agent loop)
54 lines
1.6 KiB
Python
54 lines
1.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Structured opening-focus fence for the full-data report.
|
|
|
|
The engine writes one ``jyotish-focus`` JSON fence. The report page renders
|
|
that fence; it does not recompute dasha lords or house lords.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
from typing import Any
|
|
|
|
FOCUS_FENCE_RE = re.compile(r"```jyotish-focus\n(.*?)\n```", re.DOTALL)
|
|
_FOCUS_BLOCK_RE = re.compile(r"```jyotish-focus\n.*?\n```", re.DOTALL)
|
|
_HOLD_TOKEN = "JYOTISHFOCUSHOLD{index}END"
|
|
|
|
|
|
def build_focus_fence(payload: dict[str, Any]) -> str:
|
|
"""Return a one-line JSON fence. Values are already public words."""
|
|
|
|
body = json.dumps(payload, ensure_ascii=False, separators=(",", ":"))
|
|
return f"```jyotish-focus\n{body}\n```"
|
|
|
|
|
|
def parse_focus_fence(markdown: str) -> dict[str, Any] | None:
|
|
match = FOCUS_FENCE_RE.search(markdown or "")
|
|
if not match:
|
|
return None
|
|
try:
|
|
value = json.loads(match.group(1))
|
|
except json.JSONDecodeError:
|
|
return None
|
|
return value if isinstance(value, dict) else None
|
|
|
|
|
|
def hold_focus_fences(markdown: str) -> tuple[str, list[str]]:
|
|
"""Lift fences out of the glossary pass so planet words stay valid JSON."""
|
|
|
|
slots: list[str] = []
|
|
|
|
def repl(match: re.Match[str]) -> str:
|
|
slots.append(match.group(0))
|
|
return _HOLD_TOKEN.format(index=len(slots) - 1)
|
|
|
|
return _FOCUS_BLOCK_RE.sub(repl, markdown or ""), slots
|
|
|
|
|
|
def restore_focus_fences(markdown: str, slots: list[str]) -> str:
|
|
text = markdown
|
|
for index, slot in enumerate(slots):
|
|
text = text.replace(_HOLD_TOKEN.format(index=index), slot)
|
|
return text
|