import json, re, subprocess, sys from collections import Counter from datetime import datetime, timezone prs = json.load(open(sys.argv[1])) closed = set(int(l.split()[0]) for l in open("close_fail.log")) if False else set() done = set(int(m) for m in re.findall(r"close (\d+)", open("cmds.sh").read())) main = set(subprocess.run(["git", "-C", "/Users/arthurzucker/Work/transformers", "ls-tree", "-r", "--name-only", "origin/main"], capture_output=True, text=True).stdout.split()) NOW = datetime(2026, 10, 20, tzinfo=timezone.utc) age = lambda s: (NOW - datetime.fromisoformat(s.replace("Z", "+00:00"))).days PHIL = [(re.compile(r"modeling_(tf|flax)_|_tf_utils|_flax_utils|/tf_|/flax_"), "TF/Flax code (removed in v5)"), (re.compile(r"^examples/research_projects/|^examples/legacy/|^templates/|^examples/(tensorflow|flax)/"), "removed/legacy examples or templates"), (re.compile(r"^src/transformers/models/deprecated/"), "deprecated model"), (re.compile(r"^src/transformers/(benchmark|onnx|commands/(pt_to_tf|convert))/"), "removed subsystem (benchmark/onnx/pt_to_tf)")] out = [] for p in prs: if p["number"] in done: continue files = [f["path"] for f in p["files"]["nodes"]] last_commit = age(p["commits"]["nodes"][0]["commit"]["committedDate"]) if p["commits"]["nodes"] else 9999 upd = age(p["updatedAt"]) # new modeling files added count as present only if PR adds them; missing = existing-path edits that vanished missing = [f for f in files if f not in main and not re.search(r"models/[^/]+/(modeling|modular|configuration|processing|image_processing|tokenization|convert)_|^tests/models/|^docs/", f)] reasons, cats = [], set() if p["baseRefName"] != "main": cats.add("old_branch"); reasons.append(f"base branch is `{p['baseRefName']}`") if files and len(missing) / len(files) >= 0.5: cats.add("old_branch"); reasons.append(f"{len(missing)}/{len(files)} touched files no longer exist on main") for rx, why in PHIL: hits = [f for f in files if rx.search(f)] if hits: cats.add("philosophy"); reasons.append(f"{why}: {hits[0]}" + (f" (+{len(hits)-1})" if len(hits) > 1 else "")) if last_commit > 365 and upd > 90: cats.add("outdated"); reasons.append(f"last commit {last_commit}d ago, last activity {upd}d ago") elif p["mergeable"] == "CONFLICTING" and last_commit > 180 and upd > 60: cats.add("outdated"); reasons.append(f"merge conflicts, last commit {last_commit}d ago") if cats: out.append({"number": p["number"], "title": p["title"], "author": (p["author"] or {}).get("login"), "assoc": p["authorAssociation"], "draft": p["isDraft"], "base": p["baseRefName"], "mergeable": p["mergeable"], "review": p["reviewDecision"], "created": p["createdAt"][:10], "last_commit_days": last_commit, "updated_days": upd, "labels": [l["name"] for l in p["labels"]["nodes"]], "files": files[:15], "nfiles": p["files"]["totalCount"], "cats": sorted(cats), "signals": reasons, "last_comments": [{"by": (c["author"] or {}).get("login"), "assoc": c["authorAssociation"], "at": c["createdAt"][:10], "text": c["body"][:300]} for c in p["comments"]["nodes"]]}) json.dump(out, open(sys.argv[2], "w"), indent=1) print(len(prs), "open PRs ->", len(out), "flagged;", Counter(c for o in out for c in o["cats"])) print("assoc:", Counter(o["assoc"] for o in out))