"""Regression harness for drop_masthead_regions. drop_masthead_regions runs inside main() BEFORE political_result.json is saved, so the saved fixtures never capture its effect. This harness replays it directly over every cached page's regions.json, mirroring the real call (drop_masthead_regions(regions, paper, page_num, page_img_path)). The cached fixtures carry no meta.json, so the paper is recovered from the page-1 image height (verified against the printed masthead nameplate): 7053/7055 -> sakshi 6833/6549 -> andhra_jyothi 6422 -> namaste_telangana Two views: • SHIPPED (gated): what the function actually drops in production. Expect the Namaste Telangana page-1 masthead to be dropped (incl. a doc_title nameplate, the mis-group seed) and NOTHING dropped on inner pages or other papers. • UNGATED PROBE: the same geometric rule with the paper/page gate removed, run on every page-1. Confirms the gate is the only thing keeping the rule off other papers AND that the rule would never strip a genuine page-1 headline. """ import glob import json import sys from collections import Counter from pathlib import Path from PIL import Image import smart_extractor from smart_extractor import drop_masthead_regions OUT = Path("output") def paper_of(run_dir): png = run_dir / "pages" / "page_001.png" if png.exists(): try: h = Image.open(png).height if 7040 <= h <= 7060: return "sakshi" if h in (6833, 6549): return "andhra_jyothi" if h == 6422: return "namaste_telangana" except Exception: pass return "unknown" def page_num_of(reg_path): # page_001.regions.json -> 1 stem = reg_path.name.split(".")[0] # page_001 return int(stem.split("_")[1]) def types_of(regions, ids): m = {r["id"]: r.get("type") for r in regions} return Counter(m.get(i) for i in ids) def run(): dirs = sorted([d for d in OUT.iterdir() if d.is_dir()]) shipped = [] # rows: (paper, page, run, dropped_ids, dropped_types) probe = [] # ungated, page-1 only fired = Counter() # paper -> #page-1 runs where shipped drop fired p1_total = Counter() # paper -> #page-1 runs false_pos = [] # shipped drops on non-NT or page>1 (should be empty) for d in dirs: pages_dir = d / "pages" if not pages_dir.exists(): continue paper = paper_of(d) for reg_path in sorted(pages_dir.glob("page_*.regions.json")): page_num = page_num_of(reg_path) png = pages_dir / reg_path.name.replace(".regions.json", ".png") regions = json.loads(reg_path.read_text())["regions"] # ---- SHIPPED (gated) ---- kept = drop_masthead_regions(list(regions), paper, page_num, str(png)) kept_ids = {r["id"] for r in kept} dropped = sorted(r["id"] for r in regions if r["id"] not in kept_ids) if dropped: shipped.append((paper, page_num, d.name, dropped, types_of(regions, dropped))) if not (paper == "namaste_telangana" and page_num == 1): false_pos.append((paper, page_num, d.name, dropped)) if page_num == 1: p1_total[paper] += 1 if paper == "namaste_telangana" and dropped: fired[paper] += 1 # ---- UNGATED PROBE (force the rule to run) ---- probe_kept = drop_masthead_regions(list(regions), "namaste_telangana", 1, str(png)) probe_kept_ids = {r["id"] for r in probe_kept} pdropped = sorted(r["id"] for r in regions if r["id"] not in probe_kept_ids) if pdropped: probe.append((paper, d.name, pdropped, types_of(regions, pdropped))) return shipped, probe, fired, p1_total, false_pos def report(shipped, probe, fired, p1_total, false_pos): print("\n=== SHIPPED (gated) — Namaste Telangana page-1 masthead drop ===") nt = [s for s in shipped if s[0] == "namaste_telangana" and s[1] == 1] print(f"NT page-1 runs: {p1_total['namaste_telangana']} | masthead fired in: {fired['namaste_telangana']}") seedless = [] for paper, page, run, dropped, types in sorted(nt, key=lambda x: x[2]): has_title = types.get("doc_title", 0) > 0 flag = "" if has_title else " ⚠️ no doc_title dropped (seed not removed?)" if not has_title: seedless.append(run) print(f" {run}: dropped {dropped} types={dict(types)}{flag}") misses = p1_total['namaste_telangana'] - fired['namaste_telangana'] print(f"\n NT page-1 misses (nothing dropped): {misses}") print(f" NT page-1 drops WITHOUT a doc_title nameplate seed: {len(seedless)} {seedless}") print("\n=== FALSE POSITIVES (shipped drops on non-NT, or page>1) ===") if false_pos: for paper, page, run, dropped in false_pos: print(f" ⚠️ {paper} p{page} {run}: dropped {dropped}") else: print(" none — gate held: 0 drops on inner pages and on Sakshi/Andhra Jyothi.") print("\n=== UNGATED PROBE — would the bare geometric rule strip a real headline? ===") by_paper = Counter(p[0] for p in probe) print(f"page-1 runs where the ungated rule drops something, by paper: {dict(by_paper)}") for paper in ("sakshi", "andhra_jyothi"): hits = [p for p in probe if p[0] == paper] print(f"\n --- {paper}: {len(hits)} page-1 run(s) would drop something ungated ---") for _, run, dropped, types in hits[:12]: print(f" {run}: {dropped} types={dict(types)}") if not hits: print(" none — rule is inert on this paper's page-1 even ungated (safe).") if __name__ == "__main__": report(*run())