"""Renderer fuer templates/kimi-trend-report.html. Fuellt das mzone-ui-1.1.0-Report-Template aus einem JSON-Kontext. Naechstes Mal nur noch Kontext-JSON schreiben und: python render_template.py templates/kimi-trend-report.html kontext.json out/index.html Kontext-Schema (siehe templates/kimi-trend-report.context.example.json): page_title, kicker, title, subtitle meta: { "": "", ... } stats: [ { "label": str, "value": str|int, "unit": str?, "variant": "ok|warn|danger|none"? } ] (genau 4) sections: [ { "title": str, "cards": [ { "label": str, "name": str, "url": str, "pills": [ {"text": str, "variant": "accent|info|ok|warn|danger|none"} ], "desc": str (HTML erlaubt), "note": str? } ] } ] delta: { "lead": str (HTML), "text": str (HTML), "rows": [ {"name": str, "url": str, "before": str, "after": str, "delta": str, "top": bool?} ] } (optional; erzeugt Ueberschrift, Lead, Chart und Tabelle) findings: [ {"id": "F1", "pill": str, "variant": "warn|ok|danger", "body": str (HTML)} ] (optional) crossrefs: [ {"href": str, "text": str} ] footer: str (HTML) """ import json, sys from pathlib import Path def pill(p): v = p.get("variant", "none") cls = "mz-pill" + ("" if v == "none" else f" mz-pill--{v}") return f'{p["text"]}' def stat_tile(s): v = s.get("variant", "none") cls = "mz-stat" + ("" if v == "none" else f" mz-stat--{v}") unit = f'{s["unit"]}' if s.get("unit") else "" return (f'
{s["label"]}
' f'
{s["value"]}{unit}
') def card(c): pills = "\n ".join(pill(p) for p in c.get("pills", [])) note = f'\n

{c["note"]}

' if c.get("note") else "" return f'''
{c["label"]}

{c["name"]} {pills}

{c["desc"]}

{note}
''' def section(s): cards = "\n\n".join(card(c) for c in s["cards"]) return f'

{s["title"]}

\n\n{cards}' def delta_row(r): flat = r["delta"] in ("±0", "+0", "0") if flat: p = '±0' else: variant = "mz-pill mz-pill--ok" + (" mz-pill--solid" if r.get("top") else "") p = f'{r["delta"]}' return (f' {r["name"]}' f'{r["before"]}{r["after"]}{p}') def render(template_path, ctx_path, out_path): ctx = json.loads(Path(ctx_path).read_text(encoding="utf-8")) tpl = Path(template_path).read_text(encoding="utf-8") meta_rows = "\n".join( f'
{k}
{v}
' for k, v in ctx["meta"].items()) stat_tiles = "\n".join(stat_tile(s) for s in ctx["stats"]) sections = "\n\n".join(section(s) for s in ctx["sections"]) delta_block, chart_script = "", "" if ctx.get("delta"): d = ctx["delta"] rows = "\n".join(delta_row(r) for r in d["rows"]) delta_block = f'''

{d["title"]}

{d["lead"]}

{d["text"]}

{rows}
Repo{d.get("col_before", "vorher")}{d.get("col_after", "nachher")}{d.get("col_delta", "Δ")}
''' labels = json.dumps(d["chart_labels"], ensure_ascii=False) data = json.dumps(d["chart_data"]) chart_script = f''' applyMzoneChartTheme(Chart); new Chart(document.getElementById('delta-chart'), {{ type: 'bar', data: {{ labels: {labels}, datasets: [{{ label: '{d.get("chart_label", "Δ")}', data: {data}, ...mzPalette.series(0) }}] }}, options: mzChartOptions() }}); magnify('#delta-chart-wrap', {{ zoom: 2.2, radius: 90, wheelZoom: true, label: true }});''' findings_block = "" if ctx.get("findings"): items = "\n".join( f'''
  • {f["id"]}{f["pill"]}
    {f["body"]}
  • ''' for f in ctx["findings"]) findings_block = f'''

    {ctx.get("findings_title", "Aussortiert")}

      {items}
    ''' crossrefs = "\n".join( f' {c["text"]}' for c in ctx.get("crossrefs", [])) html = (tpl .replace("{{PAGE_TITLE}}", ctx["page_title"]) .replace("{{KICKER}}", ctx["kicker"]) .replace("{{TITLE}}", ctx["title"]) .replace("{{SUBTITLE}}", ctx["subtitle"]) .replace("{{META_ROWS}}", meta_rows) .replace("{{STAT_TILES}}", stat_tiles) .replace("{{SECTIONS}}", sections) .replace("{{DELTA_BLOCK}}", delta_block) .replace("{{FINDINGS_BLOCK}}", findings_block) .replace("{{CROSSREFS}}", crossrefs) .replace("{{FOOTER}}", ctx["footer"]) .replace("{{CHART_SCRIPT}}", chart_script)) out = Path(out_path) out.parent.mkdir(parents=True, exist_ok=True) out.write_text(html, encoding="utf-8") print(f"OK {out} ({len(html)} bytes)") if __name__ == "__main__": render(sys.argv[1], sys.argv[2], sys.argv[3])