#!/usr/bin/env python3 """Visual report of a consensus calibration (calibrate --consensus --report X.json). python3 tools/konsens_bericht.py BERICHT.json EPUB [--name konsens-goellmann] Writes vergleich//index.html (not versioned): per formula the original image, the rendered truth, the rendered readings of both models and the verdict (agree+right / agree+WRONG / disagree). """ from __future__ import annotations import argparse import base64 import html import io import json import random import sys import zipfile from pathlib import Path import numpy as np from PIL import Image sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from formula_img2latex.calibrate import _dvipng_jpeg # noqa: E402 from formula_img2latex.render import render_many # noqa: E402 from formula_img2latex.tex import strip_delimiters # noqa: E402 def png_uri(img: Image.Image) -> str: buf = io.BytesIO() img.save(buf, "PNG") return "data:image/png;base64," + base64.b64encode(buf.getvalue()).decode() def orig_img(data: bytes, scale: int) -> Image.Image: im = Image.open(io.BytesIO(data)) if im.mode in ("P", "LA", "RGBA") or "transparency" in im.info: im = im.convert("RGBA") bg = Image.new("RGBA", im.size, "white") im = Image.alpha_composite(bg, im) im = im.convert("RGB") return im.resize((im.width * scale, im.height * scale), Image.NEAREST) def main() -> int: ap = argparse.ArgumentParser() ap.add_argument("report", type=Path) ap.add_argument("epub", type=Path) ap.add_argument("--name", default=None) args = ap.parse_args() rep = json.loads(args.report.read_text(encoding="utf-8")) name = args.name or "konsens-" + args.epub.name.split("_")[0].lower() out = Path(__file__).resolve().parents[1] / "vergleich" / name out.mkdir(parents=True, exist_ok=True) z = zipfile.ZipFile(args.epub) models = rep.get("engines", ["A", "B"]) parts = [f"

Konsensmessung: {html.escape(args.epub.name[:70])}

", f"

Modell A = {html.escape(models[0])}, Modell B = {html.escape(models[1])}. " "Grün = beide einig und richtig, Rot = beide einig, aber falsch, " "Grau = uneinig. Gerendert mit pdflatex; „richtig“ heißt: gleiche Zeichen an gleicher Stelle " "(Abstände, Pfeillängen, kursiv/aufrecht egal).

"] for s in rep["sets"]: res = s["results"] n = max(1, len(res)) parts.append(f"

{html.escape(s['label'])}

{len(res)} Formeln · A richtig {s['a_correct'] / n:.0%} · " f"B richtig {s['b_correct'] / n:.0%} · einig {s['agree'] / n:.0%} · " f"einig, aber falsch: {s['agree_wrong']}

") st = s.get("structural_examples", []) if st: # matches found only by the structural comparison: the truth comparison uses it too parts.append(f"

Nur strukturell gleich ({len(st)}, bitte prüfen):

") order = sorted(res, key=lambda r: (0 if r["agree"] and not r["a_ok"] else 1 if not r["agree"] else 2)) texes = [] for r in order: texes += [(strip_delimiters(r["truth"]), r["display"]), (r["a"].get("tex") or "", r["display"]), (r["b"].get("tex") or "", r["display"])] rendered = render_many([(t or "\\text{–}", d) for t, d in texes], "cm") rng = random.Random(1) for k, r in enumerate(order): if r["name"].endswith("#synth.jpg"): data = _dvipng_jpeg(strip_delimiters(r["truth"]), r["display"], 102) or b"" else: data = z.read(r["name"]) try: o = orig_img(data, 2) except Exception: # noqa: BLE001 continue imgs = [] for j in range(3): rr = rendered[3 * k + j] if rr.ok: g = (rr.rasterize(2.0) * 255).astype(np.uint8) imgs.append(png_uri(Image.fromarray(255 - g))) else: imgs.append("") if r["agree"] and not r["a_ok"]: cls, verdict = "bad", "einig, aber FALSCH" elif r["agree"]: cls, verdict = "good", "einig und richtig" else: cls, verdict = "dis", "uneinig" mark = lambda ok: "✓" if ok else "✗" # noqa: E731 pa, pb = r["a"].get("p_mean"), r["b"].get("p_mean") parts.append( f"
{verdict}
" f"" f"" f"" f"
Original
richtig{html.escape(r['truth'])}
A {mark(r['a_ok'])}{html.escape(r['a'].get('tex') or '–')}" f" p={pa and round(pa, 3)}
B {mark(r['b_ok'])}{html.escape(r['b'].get('tex') or '–')}" f" p={pb and round(pb, 3)}
") del rng rng = random.Random(1) (out / "index.html").write_text( "Konsens" + "".join(parts), encoding="utf-8") print(out / "index.html") return 0 if __name__ == "__main__": sys.exit(main())