mirror of
https://github.com/harvard-edge/cs249r_book.git
synced 2026-11-09 10:39:41 -06:00
interviews/ held StaffML and nothing else, so the directory now carries the product's name. Three names inside it repeated that prefix, and now line up with vault/, vault-cli/, and paper/: interviews/staffml/ -> staffml/app/ interviews/staffml-vault-worker/ -> staffml/vault-worker/ interviews/staffml-vault-types/ -> staffml/vault-types/ Every repository path moved with it: workflow paths: triggers and variable fallbacks, pre-commit hooks, .gitignore, contributor tooling, vault-cli path joins, the app's tsconfig mapping to the shared types, and the docs. Left alone on purpose: - the published /interviews/ redirect to /staffml/ and the gh-pages subsite lists that protect it; those are URLs, not repository paths - package and worker names (staffml-vault-worker, @staffml/vault-types) and the vitest job name, which branch protection may key on - question YAMLs and generated track JSON, which are content-addressed. Three questions carry an older corruption, "recall @interviews/vault/ questions/...", that predates this change - the applied D1 migration 0001 and the vault CHANGELOG, which are history codegen-hashes.txt is re-recorded; the three pinned artifacts changed only in path strings. vault-cli: 97 passed, codegen --check clean. interviewer.py's __main__ loader was one parent short and pointed at a corpus.json that never existed; it now resolves staffml/app/src/data/ corpus.json. tools/validate_playbook.py and generate_interview_flashcard.py still expect per-track directories that no longer exist; they point at the new root but remain dead. The GitHub repository variables STAFFML_ROOT, VAULT_DIR, and VAULT_CLI_DIR still name the old paths and must change when this reaches origin.
70 lines
2.2 KiB
Python
70 lines
2.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Validate bibliography entries in paper.bbl against CrossRef.
|
|
|
|
Reads paper.bbl from the paper directory, extracts each \\bibitem's rough
|
|
title, queries CrossRef, and reports whether a match was found. Useful as a
|
|
spot-check after large bibliography edits to catch typos, wrong years, or
|
|
silently-renamed works.
|
|
|
|
Run: python3 staffml/paper/scripts/validate_refs.py
|
|
Reads: ../paper.bbl (generated by LaTeX from references.bib)
|
|
Writes: stdout — [OK] / [WARN] / [ERR] per citation key
|
|
"""
|
|
|
|
import json
|
|
import re
|
|
import time
|
|
import urllib.parse
|
|
import urllib.request
|
|
from pathlib import Path
|
|
|
|
SCRIPTS_DIR = Path(__file__).parent
|
|
PAPER_DIR = SCRIPTS_DIR.parent
|
|
BBL_FILE = PAPER_DIR / "paper.bbl"
|
|
|
|
with open(BBL_FILE, "r") as f:
|
|
bbl = f.read()
|
|
|
|
# Extract bibitems
|
|
entries = re.split(r"\\bibitem\[.*?\]\{(.*?)\}", bbl)[1:]
|
|
keys = entries[0::2]
|
|
blocks = entries[1::2]
|
|
|
|
print(f"Checking {len(keys)} cited references...")
|
|
|
|
results = []
|
|
for i, (key, block) in enumerate(zip(keys, blocks)):
|
|
# Very rough extraction: grab text after \newblock up to a period
|
|
m = re.search(r"\\newblock (.*?)\.", block.replace("\n", " "))
|
|
title = m.group(1) if m else key
|
|
title = re.sub(r"\\[a-zA-Z]+", "", title)
|
|
title = re.sub(r"[\{\}\[\]\$\^~]", "", title)
|
|
title = title.strip()
|
|
|
|
if len(title) < 5:
|
|
title = key
|
|
|
|
try:
|
|
url = (
|
|
"https://api.crossref.org/works?query.title="
|
|
+ urllib.parse.quote(title)
|
|
+ "&select=title,author,URL&rows=1"
|
|
)
|
|
req = urllib.request.Request(
|
|
url, headers={"User-Agent": "mailto:vj@eecs.harvard.edu"}
|
|
)
|
|
with urllib.request.urlopen(req) as response:
|
|
data = json.loads(response.read().decode())
|
|
if data["message"]["items"]:
|
|
top_hit = data["message"]["items"][0]
|
|
crossref_title = top_hit.get("title", [""])[0]
|
|
results.append(f"[OK] {key}: Found '{crossref_title}'")
|
|
else:
|
|
results.append(f"[WARN] {key}: No CrossRef hit for '{title}'")
|
|
except Exception as e:
|
|
results.append(f"[ERR] {key}: {str(e)} for '{title}'")
|
|
time.sleep(0.1)
|
|
|
|
for r in results:
|
|
print(r)
|