#!/usr/bin/env python3 """ conversione/validate.py — Validazione batch di tutti gli stem convertiti Legge i report.json prodotti da pipeline.py e stampa una tabella di stato per ogni documento, evidenziando anomalie e problemi residui. Stato per stem: ✅ nessuna anomalia critica ⚠️ anomalie presenti ma documento processabile ❌ struttura non rilevata o problemi gravi Uso: python conversione/validate.py # tutti gli stem python conversione/validate.py analisi1 # stem specifico """ import json import sys from pathlib import Path # ─── Soglie ────────────────────────────────────────────────────────────────── _CRITICO_STRUTTURA = 0 # livello_struttura == 0 → testo piatto, nessun header _CRITICO_BACKTICK = 50 # molti accenti non corretti → testo illeggibile _WARNING_BARE = 1 # anche un solo header senza titolo è sospetto _WARNING_BACKTICK = 1 # qualsiasi backtick residuo va verificato _WARNING_LONG_SECS = 80 # troppe sezioni lunghe indica struttura insufficiente def _status(r: dict) -> str: structure = r.get("structure", {}) anomalie = r.get("anomalie", {}) residui = r.get("residui", {}) livello = structure.get("livello_struttura", -1) backtick = residui.get("backtick", 0) if livello <= _CRITICO_STRUTTURA or backtick >= _CRITICO_BACKTICK: return "❌" if ( anomalie.get("bare_headers", 0) >= _WARNING_BARE or backtick >= _WARNING_BACKTICK or anomalie.get("long_sections", 0) >= _WARNING_LONG_SECS ): return "⚠️ " return "✅" def _fmt(value, width: int) -> str: return str(value).ljust(width) def validate(stems: list[str], project_root: Path) -> None: conv_dir = project_root / "conversione" if stems: paths = [conv_dir / s / "report.json" for s in stems] else: paths = sorted(conv_dir.glob("*/report.json")) if not paths: print("Nessun report.json trovato in conversione/*/") sys.exit(0) rows = [] for path in paths: if not path.exists(): rows.append({"stem": path.parent.name, "_missing": True}) continue r = json.loads(path.read_text(encoding="utf-8")) rows.append(r) # ── Intestazione ───────────────────────────────────────────────────── col_stem = max(len(r.get("stem", "stem")) for r in rows) + 2 header = ( f"{'stem':<{col_stem}}" f"{'h2':>4}{'h3':>5} " f"{'strategia':<20}" f"{'bare':>5}{'corte':>6}{'lunghe':>7}" f"{'backtick':>9}{'dotlead':>8}{'url':>4}" f" {'status'}" ) sep = "─" * len(header) print() print(header) print(sep) # ── Righe ───────────────────────────────────────────────────────────── for r in rows: if r.get("_missing"): print(f"{r['stem']:<{col_stem}} (report.json non trovato)") continue stem = r.get("stem", "?") structure = r.get("structure", {}) anomalie = r.get("anomalie", {}) residui = r.get("residui", {}) h2 = structure.get("n_h2", 0) h3 = structure.get("n_h3", 0) strat = structure.get("strategia_chunking", "?") bare = anomalie.get("bare_headers", 0) corte = anomalie.get("short_sections", 0) lunghe = anomalie.get("long_sections", 0) backtick = residui.get("backtick", 0) dotlead = residui.get("dotleader", 0) url = residui.get("url", 0) status = _status(r) print( f"{stem:<{col_stem}}" f"{h2:>4}{h3:>5} " f"{strat:<20}" f"{bare:>5}{corte:>6}{lunghe:>7}" f"{backtick:>9}{dotlead:>8}{url:>4}" f" {status}" ) print(sep) totali = len(rows) ok = sum(1 for r in rows if not r.get("_missing") and _status(r) == "✅") warn = sum(1 for r in rows if not r.get("_missing") and _status(r).startswith("⚠")) err = sum(1 for r in rows if not r.get("_missing") and _status(r) == "❌") print(f"Totale: {totali} ✅ {ok} ⚠️ {warn} ❌ {err}") print() print("Legenda colonne: bare=header senza titolo corte=sezioni<150ch " "lunghe=sezioni>1500ch backtick=accenti residui") print() if __name__ == "__main__": project_root = Path(__file__).parent.parent stems = sys.argv[1:] validate(stems, project_root)