fix(report): draw charts in the full-data body and clean Chinese residue

New reports draw one north-Indian chart for each ready divisional in the body, so the trailing duplicate section drops. Chinese full-data prose no longer glues English onto Han or leaves listed, background, or native. The birth table starts with the ascendant. The D9 same-sign column says yes or no.

Self-test: tsc exit 0; npm run lint exit 0 (0 errors, 125 existing warnings); three frontend test files 39 pass, 0 fail, 0 cancelled; pytest test_report_chart_blank_columns.py 16 passed; seven related report files 88 passed, exit 0; privacy markers 63 passed.

Co-Authored-By: Grok (agent loop)
This commit is contained in:
jesse-ux
2026-10-13 14:02:26 +08:00
co-authored by Grok (agent loop)
parent 05f8800531
commit a295afa40f
14 changed files with 3466 additions and 10 deletions
+34 -1
View File
@@ -107,7 +107,13 @@ def _sanitize_pl9_ai_density_markdown(markdown: str, title: str = "印度占星
strip_han_plural_s,
)
text = _sanitize_pl9_public_markdown(markdown)
try:
from pl9_language_terms import translate_zh_glued_phrases
except ModuleNotFoundError: # pragma: no cover - package import compatibility
from scripts.pl9_language_terms import translate_zh_glued_phrases
# Whole phrases and glued planet+dignity tokens before shorter word swaps.
# Chapters appended after the parity strip still pass through here.
text = translate_zh_glued_phrases(_sanitize_pl9_public_markdown(markdown))
replacements = {
"# PL9 对标主册": f"# {title}",
"出生资料与图盘(PL9 p1-p17)": "出生资料与图盘",
@@ -1264,6 +1270,27 @@ def _sanitize_pl9_ai_density_markdown_en(markdown: str, title: str = "Vedic Astr
# Body residue (BUG-1312). Parenthetical Sanskrit names (nakshatra, yoga,
# Dasha) and header abbreviations (SAV, BAV, KP, D1-D60) are separated from
# Han by parentheses or spaces, so they are not "glued". They are not function
# words. Chart fences are structured payload and are not part of the prose count.
_ZH_GLUE_RE = re.compile(r"[\u4e00-\u9fff][A-Za-z]{3,}|[A-Za-z]{3,}[\u4e00-\u9fff]")
_ZH_FUNCTION_WORD_RE = re.compile(
r"(?<![A-Za-z])(?:of|in|the|and|by|from|with|listed|background|native|condition|met|satisfied|occupied)(?![A-Za-z])",
re.IGNORECASE,
)
_ZH_CHART_FENCE_RE = re.compile(r"```jyotish-chart\n.*?\n```", re.DOTALL)
def _zh_body_residue(markdown: str) -> dict[str, int]:
body = (markdown or "").split("## 结构化资料附录", 1)[0]
body = _ZH_CHART_FENCE_RE.sub("", body)
return {
"glued_latin": len(_ZH_GLUE_RE.findall(body)),
"function_words": len(_ZH_FUNCTION_WORD_RE.findall(body)),
}
def _pl9_markdown_hygiene_receipt(markdown: str, *, language: str) -> dict[str, Any]:
"""Check the Markdown before rendering user-facing PDF."""
@@ -1327,6 +1354,12 @@ def _pl9_markdown_hygiene_receipt(markdown: str, *, language: str) -> dict[str,
chinese_chars = len(re.findall(r"[\u4e00-\u9fff]", markdown))
if chinese_chars:
violations.append(f"chinese_characters:{chinese_chars}")
if language == 'zh':
residue = _zh_body_residue(markdown)
if residue["glued_latin"]:
violations.append(f"glued_latin:{residue['glued_latin']}")
if residue["function_words"]:
violations.append(f"function_words:{residue['function_words']}")
language_receipt = language_quality_receipt(markdown, language=language)
violations.extend(language_receipt.get("violations") or [])
orphan_lines = orphan_separator_lines(markdown)