140 lines
5.8 KiB
Python
140 lines
5.8 KiB
Python
"""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())
|