174 lines
6.6 KiB
Python
174 lines
6.6 KiB
Python
"""Regression harness for _grow_article_downward.
|
|
|
|
Runs the grow function over the saved fixtures of EVERY cached run, with IDENTICAL
|
|
auxiliary inputs (claimed = union of OTHER articles' members on the page,
|
|
dateline_starts = None). Because old and new code receive the same inputs, a diff
|
|
of the two output snapshots isolates exactly what the code change does.
|
|
|
|
Records per article: the grown member-id set and its union bbox, tagged with the
|
|
source newspaper (from meta.json) so results can be broken down per paper.
|
|
|
|
Usage:
|
|
python3 _regress_grow.py snapshot.json # snapshot current code
|
|
python3 _regress_grow.py --diff a.json b.json # compare two snapshots
|
|
"""
|
|
import json
|
|
import re
|
|
import sys
|
|
from collections import Counter
|
|
from pathlib import Path
|
|
|
|
OUT = Path("output")
|
|
|
|
|
|
def union_bbox(member_ids, region_map):
|
|
bs = [region_map[r]["bbox"] for r in member_ids if r in region_map]
|
|
if not bs:
|
|
return None
|
|
return [min(b[0] for b in bs), min(b[1] for b in bs),
|
|
max(b[2] for b in bs), max(b[3] for b in bs)]
|
|
|
|
|
|
def paper_of(run_dir):
|
|
meta = run_dir / "meta.json"
|
|
if meta.exists():
|
|
try:
|
|
fn = json.loads(meta.read_text()).get("filename", "")
|
|
name = re.split(r"[_0-9]", fn)[0].strip()
|
|
if name:
|
|
return name
|
|
except Exception:
|
|
pass
|
|
# Cached fixtures carry no meta.json; identify the paper by page-1 image height,
|
|
# which is distinct per edition (verified against the printed masthead nameplate).
|
|
png = run_dir / "pages" / "page_001.png"
|
|
if png.exists():
|
|
try:
|
|
from PIL import Image
|
|
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 run():
|
|
from smart_extractor import _grow_article_downward
|
|
|
|
runs = sorted([d for d in OUT.iterdir() if d.is_dir()],
|
|
key=lambda d: d.stat().st_mtime, reverse=True)
|
|
snapshot = {}
|
|
papers = Counter()
|
|
for run_dir in runs:
|
|
pages_dir = run_dir / "pages"
|
|
if not pages_dir.exists():
|
|
continue
|
|
paper = paper_of(run_dir)
|
|
for reg_path in sorted(pages_dir.glob("page_*.regions.json")):
|
|
pol_path = reg_path.with_name(reg_path.name.replace(".regions.json",
|
|
".political_result.json"))
|
|
if not pol_path.exists():
|
|
continue
|
|
regions = json.loads(reg_path.read_text())["regions"]
|
|
region_map = {r["id"]: r for r in regions}
|
|
political = json.loads(pol_path.read_text()).get("political_articles", [])
|
|
|
|
# claimed = every member of every article (mirrors the real caller's
|
|
# global claimed set built before growth).
|
|
all_members = set()
|
|
for a in political:
|
|
all_members.update(a.get("member_region_ids", []))
|
|
|
|
for i, art in enumerate(political):
|
|
members = art.get("member_region_ids", []) or []
|
|
if not members:
|
|
continue
|
|
# claimed for THIS article = everything claimed by OTHER articles.
|
|
claimed = set(all_members) - set(members)
|
|
grown, added = _grow_article_downward(
|
|
members, region_map, regions, claimed, dateline_starts=None
|
|
)
|
|
key = f"{run_dir.name}/{reg_path.stem}/art{i}"
|
|
snapshot[key] = {
|
|
"paper": paper,
|
|
"headline": art.get("headline_english", "")[:50],
|
|
"in_members": sorted(members),
|
|
"grown_members": sorted(grown),
|
|
"added": sorted(added),
|
|
"in_bbox": union_bbox(members, region_map),
|
|
"grown_bbox": union_bbox(grown, region_map),
|
|
}
|
|
papers[paper] += 1
|
|
print(f" papers: {dict(papers)}")
|
|
return snapshot
|
|
|
|
|
|
def diff(path_a, path_b):
|
|
"""Compare snapshot A (baseline/OLD) against B (NEW). Reports per-paper how many
|
|
articles changed their grown member set, plus a sample of the changes."""
|
|
a = json.loads(Path(path_a).read_text())
|
|
b = json.loads(Path(path_b).read_text())
|
|
keys = sorted(set(a) | set(b))
|
|
|
|
per_paper_total = Counter()
|
|
per_paper_changed = Counter()
|
|
grew = Counter() # NEW added regions OLD didn't
|
|
shrank = Counter() # NEW dropped regions OLD had
|
|
samples = []
|
|
|
|
for k in keys:
|
|
ra, rb = a.get(k), b.get(k)
|
|
paper = (rb or ra).get("paper", "unknown")
|
|
per_paper_total[paper] += 1
|
|
if ra is None or rb is None:
|
|
per_paper_changed[paper] += 1
|
|
samples.append((k, paper, "ONLY IN ONE SNAPSHOT", None, None))
|
|
continue
|
|
sa, sb = set(ra["grown_members"]), set(rb["grown_members"])
|
|
if sa != sb:
|
|
per_paper_changed[paper] += 1
|
|
added = sorted(sb - sa)
|
|
removed = sorted(sa - sb)
|
|
if added:
|
|
grew[paper] += 1
|
|
if removed:
|
|
shrank[paper] += 1
|
|
samples.append((k, paper, rb["headline"], added, removed))
|
|
|
|
print("\n=== REGRESSION DIFF (A=baseline/OLD → B=new) ===")
|
|
print(f"{'paper':<18}{'articles':>10}{'changed':>9}{'grew':>7}{'shrank':>8}")
|
|
for paper in sorted(per_paper_total):
|
|
print(f"{paper:<18}{per_paper_total[paper]:>10}{per_paper_changed[paper]:>9}"
|
|
f"{grew[paper]:>7}{shrank[paper]:>8}")
|
|
tot = sum(per_paper_total.values())
|
|
chg = sum(per_paper_changed.values())
|
|
print(f"{'TOTAL':<18}{tot:>10}{chg:>9}{sum(grew.values()):>7}{sum(shrank.values()):>8}")
|
|
|
|
if samples:
|
|
print(f"\n--- {len(samples)} changed article(s) (showing up to 40) ---")
|
|
for k, paper, hl, added, removed in samples[:40]:
|
|
print(f"\n[{paper}] {k}")
|
|
print(f" headline: {hl}")
|
|
if added:
|
|
print(f" + added : {added}")
|
|
if removed:
|
|
print(f" - removed: {removed}")
|
|
else:
|
|
print("\nNo differences — grown member sets are identical across all fixtures.")
|
|
return chg
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) >= 4 and sys.argv[1] == "--diff":
|
|
diff(sys.argv[2], sys.argv[3])
|
|
else:
|
|
out_path = sys.argv[1] if len(sys.argv) > 1 else "/tmp/grow_snapshot.json"
|
|
snap = run()
|
|
Path(out_path).write_text(json.dumps(snap, indent=2, ensure_ascii=False))
|
|
print(f"wrote {len(snap)} article snapshots to {out_path}")
|