fix(review): address PR #16 code-review findings
#1 ingest: normalize the pinned caller id (_norm_cited_id) so a non-bare DOI caller cannot store a URL-form papers.id that defeats DQ-5 idempotency / the startswith(10.) recovery gates. #2 quality.section_label: collapse to a controlled bucket only for an EXACT canonical heading; descriptive titles ('Abstract Nonsense...') keep their real title instead of being mislabelled. #4 ra_grobid_backfill: release the read connection before the slow GROBID network loop, fresh connection for the write (no idle-in-transaction across the loop over the flaky tunnel). #5/#10 tex: flatten_inputs strips unresolved input/include at the depth cap (no literal leak on cycles); _norm_texkey strips only a single leading ./ . #6/#7 arxiv.fetch_source: keep non-.tex members resolvable for input; pick primary on an UN-commented documentclass line. #13 is_arxiv_id: also exclude http:// and arXiv-DOI forms. Tests added/updated for each. Left as deliberate decisions: #3 (pre-section text drop is pre-existing in extract_sections; abstract stored separately), #8 (script normalizer is intentionally self-contained, already documented), #9/#11/#12 (no current trigger / tightening the gzip heuristic would reject valid old LaTeX like documentstyle / title truncation is by design on a write-only column). ruff + mypy clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -142,6 +142,10 @@ def main(argv: list[str] | None = None) -> int:
|
||||
pdf_dir = Path(args.pdf_dir)
|
||||
pdfs = {p.stem: p for p in pdf_dir.glob("*.pdf")}
|
||||
|
||||
# Read the target list in a short-lived connection, then release it: the GROBID
|
||||
# extraction below is a slow per-paper network loop and must NOT hold an
|
||||
# idle-in-transaction connection open across it (a mid-loop SSH-tunnel drop would
|
||||
# otherwise abort the whole batch — see docs/infra/jetson-ssh-stability.md).
|
||||
with get_conn() as conn:
|
||||
rows = conn.execute(
|
||||
"""
|
||||
@@ -152,46 +156,49 @@ def main(argv: list[str] | None = None) -> int:
|
||||
"""
|
||||
).fetchall()
|
||||
|
||||
targets: list[tuple[str, Path]] = []
|
||||
for r in rows:
|
||||
if args.only and r["id"] != args.only:
|
||||
continue
|
||||
if args.zero_edge_only and r["out_edges"] != 0:
|
||||
continue
|
||||
stem = Path(r["source_path"]).stem if r["source_path"] else None
|
||||
pdf = pdfs.get(stem) if stem else None
|
||||
if pdf is None:
|
||||
logger.warning("no PDF for %s (stem=%s) — skipping", r["id"], stem)
|
||||
continue
|
||||
targets.append((r["id"], pdf))
|
||||
if args.limit is not None:
|
||||
targets = targets[: args.limit]
|
||||
targets: list[tuple[str, Path]] = []
|
||||
for r in rows:
|
||||
if args.only and r["id"] != args.only:
|
||||
continue
|
||||
if args.zero_edge_only and r["out_edges"] != 0:
|
||||
continue
|
||||
stem = Path(r["source_path"]).stem if r["source_path"] else None
|
||||
pdf = pdfs.get(stem) if stem else None
|
||||
if pdf is None:
|
||||
logger.warning("no PDF for %s (stem=%s) — skipping", r["id"], stem)
|
||||
continue
|
||||
targets.append((r["id"], pdf))
|
||||
if args.limit is not None:
|
||||
targets = targets[: args.limit]
|
||||
|
||||
logger.info(
|
||||
"GROBID=%s | papers to process=%d | mode=%s",
|
||||
grobid_url,
|
||||
len(targets),
|
||||
"WRITE" if args.write else "DRY-RUN",
|
||||
)
|
||||
logger.info(
|
||||
"GROBID=%s | papers to process=%d | mode=%s",
|
||||
grobid_url,
|
||||
len(targets),
|
||||
"WRITE" if args.write else "DRY-RUN",
|
||||
)
|
||||
|
||||
all_citations: list[Citation] = []
|
||||
for paper_id, pdf in targets:
|
||||
try:
|
||||
cits = build_grobid_citations(
|
||||
paper_id, pdf, grobid_url=grobid_url, timeout=args.timeout
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.warning("GROBID extraction failed for %s (%s): %s", paper_id, pdf.name, exc)
|
||||
continue
|
||||
logger.info("%-38s %3d refs (%s)", paper_id, len(cits), pdf.name)
|
||||
all_citations.extend(cits)
|
||||
# GROBID extraction — no DB connection held during this slow network loop.
|
||||
all_citations: list[Citation] = []
|
||||
for paper_id, pdf in targets:
|
||||
try:
|
||||
cits = build_grobid_citations(
|
||||
paper_id, pdf, grobid_url=grobid_url, timeout=args.timeout
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.warning("GROBID extraction failed for %s (%s): %s", paper_id, pdf.name, exc)
|
||||
continue
|
||||
logger.info("%-38s %3d refs (%s)", paper_id, len(cits), pdf.name)
|
||||
all_citations.extend(cits)
|
||||
|
||||
logger.info("total candidate citations: %d", len(all_citations))
|
||||
logger.info("total candidate citations: %d", len(all_citations))
|
||||
|
||||
if not args.write:
|
||||
logger.info("DRY-RUN — no rows written. Re-run with --write to apply.")
|
||||
return 0
|
||||
if not args.write:
|
||||
logger.info("DRY-RUN — no rows written. Re-run with --write to apply.")
|
||||
return 0
|
||||
|
||||
# Fresh connection for the write — the read connection was already released.
|
||||
with get_conn() as conn:
|
||||
before = _coverage(conn)
|
||||
with conn.cursor() as cur:
|
||||
cur.executemany(
|
||||
@@ -204,15 +211,15 @@ def main(argv: list[str] | None = None) -> int:
|
||||
)
|
||||
conn.commit()
|
||||
after = _coverage(conn)
|
||||
logger.info(
|
||||
"coverage papers=%d citing %d->%d edges %d->%d (+%d)",
|
||||
after[0],
|
||||
before[1],
|
||||
after[1],
|
||||
before[2],
|
||||
after[2],
|
||||
after[2] - before[2],
|
||||
)
|
||||
logger.info(
|
||||
"coverage papers=%d citing %d->%d edges %d->%d (+%d)",
|
||||
after[0],
|
||||
before[1],
|
||||
after[1],
|
||||
before[2],
|
||||
after[2],
|
||||
after[2] - before[2],
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user