Skip to content

Commit 93aab5f

Browse files
committed
chore: add migrate_deprecated_categories.py (one-time script)
1 parent afabc28 commit 93aab5f

1 file changed

Lines changed: 133 additions & 0 deletions

File tree

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Migrate papers from deprecated categories (benchmarks, surveys, empirical_studies)
4+
into appropriate functional categories, using the LLM classifier.
5+
Skips papers already present in any other category file.
6+
"""
7+
8+
from __future__ import annotations
9+
import re
10+
import sys
11+
import logging
12+
from pathlib import Path
13+
14+
_REPO = Path(__file__).resolve().parents[1]
15+
sys.path.insert(0, str(_REPO))
16+
17+
from dotenv import load_dotenv
18+
load_dotenv(_REPO / "automation" / ".env")
19+
20+
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)-8s %(message)s")
21+
logger = logging.getLogger(__name__)
22+
23+
import yaml
24+
from automation.config_loader import load_config
25+
from automation.classifier.llm import classify_paper
26+
from automation.crawler.arxiv import fetch_single_paper
27+
from automation.finalizer.yaml_writer import append_paper
28+
29+
DEPRECATED = ["benchmarks", "surveys", "empirical_studies"]
30+
_ARXIV_ID_RE = re.compile(r"arxiv\.org/(?:abs|pdf)/(\d{4}\.\d{4,5})", re.IGNORECASE)
31+
32+
LEGACY_TAG = {
33+
"benchmarks": "benchmark",
34+
"surveys": "survey",
35+
"empirical_studies":"empirical",
36+
}
37+
38+
39+
def load_existing(data_dir: Path) -> tuple[set[str], set[str]]:
40+
"""All paper URLs and titles already recorded in non-deprecated files."""
41+
urls, titles = set(), set()
42+
for f in data_dir.glob("papers_*.yaml"):
43+
if any(f.name == f"papers_{d}.yaml" for d in DEPRECATED):
44+
continue # don't count deprecated files as "existing"
45+
try:
46+
for e in (yaml.safe_load(f.read_text()) or []):
47+
if isinstance(e, dict):
48+
u = (e.get("links") or {}).get("paper", "").strip()
49+
t = e.get("title", "").strip()
50+
if u: urls.add(u)
51+
if t: titles.add(t)
52+
except Exception:
53+
pass
54+
return urls, titles
55+
56+
57+
def fetch_abstract(paper: dict) -> str:
58+
"""Try to get abstract via arXiv API; fall back to title."""
59+
url = (paper.get("links") or {}).get("paper", "")
60+
m = _ARXIV_ID_RE.search(url)
61+
if m:
62+
fetched = fetch_single_paper(m.group(1))
63+
if fetched and fetched.get("abstract"):
64+
return fetched["abstract"]
65+
return paper.get("title", "")
66+
67+
68+
def main():
69+
cfg = load_config()
70+
data_dir = _REPO / "data"
71+
existing_urls, existing_titles = load_existing(data_dir)
72+
73+
logger.info("Non-deprecated existing papers: %d urls, %d titles",
74+
len(existing_urls), len(existing_titles))
75+
76+
migrated = skipped_dup = skipped_irrel = 0
77+
78+
for cat in DEPRECATED:
79+
src = data_dir / f"papers_{cat}.yaml"
80+
if not src.exists():
81+
continue
82+
papers = yaml.safe_load(src.read_text()) or []
83+
logger.info("\n=== %s (%d papers) ===", cat, len(papers))
84+
85+
for paper in papers:
86+
title = paper.get("title", "").strip()
87+
url = (paper.get("links") or {}).get("paper", "").strip()
88+
89+
# Dedup against non-deprecated files
90+
if (url and url in existing_urls) or (title and title in existing_titles):
91+
logger.info(" SKIP dup: %s", title[:70])
92+
skipped_dup += 1
93+
continue
94+
95+
# Fetch abstract for better classification
96+
logger.info(" Fetching: %s", title[:70])
97+
abstract = fetch_abstract(paper)
98+
99+
result = classify_paper(
100+
{"title": title, "abstract": abstract, "arxiv_id": ""},
101+
categories=cfg["categories"],
102+
tags=cfg["tags"],
103+
)
104+
105+
if not result.get("relevant") or not result.get("category"):
106+
logger.info(" SKIP not relevant: %s", title[:70])
107+
skipped_irrel += 1
108+
continue
109+
110+
paper["category"] = result["category"]
111+
# Merge tags: original + classifier + legacy tag, deduped
112+
orig_tags = paper.get("tags") or []
113+
new_tags = result.get("tags") or []
114+
legacy = LEGACY_TAG.get(cat, "")
115+
paper["tags"] = list(dict.fromkeys(
116+
[t for t in orig_tags + new_tags + ([legacy] if legacy else []) if t]
117+
))
118+
119+
if append_paper(paper, data_dir):
120+
existing_urls.add(url)
121+
existing_titles.add(title)
122+
migrated += 1
123+
logger.info(" ✓ → %s %s", result["category"], paper["tags"])
124+
else:
125+
skipped_dup += 1
126+
logger.info(" SKIP append dedup: %s", title[:70])
127+
128+
logger.info("\n=== Result: migrated=%d dup=%d irrelevant=%d ===",
129+
migrated, skipped_dup, skipped_irrel)
130+
131+
132+
if __name__ == "__main__":
133+
main()

0 commit comments

Comments
 (0)