# -*- coding: utf-8 -*- """Erzeugt einen HTML-Prüfbericht für die manuelle Sichtprüfung des Katalogs. Stellt alle 575 Fragen mit Lösungen, Musterantworten, hervorgehobenen Kernelementen und Abbildungen dar, damit sie gegen das Original-PDF geprüft werden können. Der Bericht ist selbst tastaturbedienbar und kontraststark. """ from __future__ import annotations import base64 import html import io import json import sys from pathlib import Path sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8") BASE = Path(__file__).resolve().parent.parent KATALOG = BASE / "content" / "katalog" ZIEL = BASE / "docs" / "katalog-pruefbericht.html" def bild_tag(bid: str, bilder: dict) -> str: meta = bilder.get(bid) if not meta: return f'Bild {html.escape(bid)} fehlt' daten = base64.b64encode((KATALOG / "assets" / meta["datei"]).read_bytes()).decode() alt = meta.get("alt") or "" return (f'{html.escape(alt)}') def segmente_html(feld: dict) -> str: teile = [] for s in feld["segmente"]: text = html.escape(s["t"]).replace("\n", "
") teile.append(f"{text}" if s.get("h") else text) return "".join(teile) or '(kein Text)' def main() -> int: data = json.loads((KATALOG / "katalog.json").read_text(encoding="utf-8")) bilder = {b["id"]: b for b in data["bilder"]} meta = data["meta"] abschnitt_titel = {} for kap in data["kapitel"]: for a in kap["abschnitte"]: abschnitt_titel[a["id"]] = a["titel"] abschnitt_titel[kap["id"]] = kap["titel"] zeilen: list[str] = [] aktuelle_gruppe = None for f in data["fragen"]: gruppe = f["abschnitt"] or f["kapitel"] if gruppe != aktuelle_gruppe: aktuelle_gruppe = gruppe titel = abschnitt_titel.get(gruppe, gruppe) zeilen.append(f'

{html.escape(gruppe)} · ' f'{html.escape(titel)}

') typ_label = {"mc": "Multiple Choice", "freitext": "offene Frage", "lueckentext": "Lückentext"}[f["typ"]] kopf = (f'
{html.escape(f["id"])}' f'amtlich {html.escape(f["amtliche_nummer"])}' f'{typ_label}' f'PDF-Seite {f["seite"]}
') koerper = [f'

{segmente_html(f["frage"])}

'] for bid in f["bilder"]: koerper.append(f'

{bild_tag(bid, bilder)}

') if f["typ"] == "mc": richtige = sum(1 for o in f["optionen"] if o["korrekt"]) if richtige > 1: koerper.append(f'

{richtige} richtige Antworten

') koerper.append("") else: koerper.append(f'
Musterantwort' f'{segmente_html(f["musterantwort"])}
') if f.get("warnungen"): hinweise = "; ".join(html.escape(w) for w in f["warnungen"]) koerper.append(f'

Hinweis aus der Extraktion: {hinweise}

') zeilen.append(f'
' f'{kopf}{"".join(koerper)}
') kennzahlen = [ ("Fragen", len(data["fragen"])), ("Multiple Choice", sum(1 for f in data["fragen"] if f["typ"] == "mc")), ("offene Fragen", sum(1 for f in data["fragen"] if f["typ"] != "mc")), ("Antwortoptionen", sum(len(f.get("optionen", [])) for f in data["fragen"])), ("markierte Lösungen", sum(sum(1 for o in f.get("optionen", []) if o["korrekt"]) for f in data["fragen"])), ("Abbildungen", len(data["bilder"])), ] kacheln = "".join(f'
{w}' f'{html.escape(b)}
' for b, w in kennzahlen) nav = "".join( f'
  • {html.escape(g)} · {html.escape(t)}
  • ' for g, t in abschnitt_titel.items() if any((f["abschnitt"] or f["kapitel"]) == g for f in data["fragen"])) ZIEL.parent.mkdir(parents=True, exist_ok=True) ZIEL.write_text(SEITE.format( titel="Prüfbericht Fragenkatalog", quelle=html.escape(meta["quellenangabe"]), stand=html.escape(meta["stand"]), kacheln=kacheln, nav=nav, inhalt="\n".join(zeilen), # LF wie im Archiv verlangt, nicht das CRLF von Windows. ), encoding="utf-8", newline="\n") print(f"Prüfbericht geschrieben: {ZIEL}") print(f"Größe: {ZIEL.stat().st_size / 1024:.0f} kB") return 0 SEITE = """ {titel}

    Prüfbericht Fragenkatalog

    {quelle} — Katalogstand {stand}. Grün markierte Antworten sind im Original angekreuzt; hervorgehobene Stellen sind die Kernelemente der Musterantworten.

    {kacheln}
    {inhalt}
    """ if __name__ == "__main__": raise SystemExit(main())