#!/usr/bin/env python3 """WP44 — generate manuscript/tables/numbers.tex: every number quoted in the prose is a LaTeX macro produced here from result files (never typed by hand). Macro names use letters only (no digits: `\\nCoreEligible`, not `\\n2026`). Sources: data/metadata/prisma_counts.json, linkage_report.json, results/bibliometrics/*.csv, results/econometrics/*.json, taxonomy/validation/metrics.json, results/emerging/metrics_h12.json (each optional: missing → macro undefined → compile error, which is intended: the prose cannot cite a number that does not exist).""" import json, os, sys, csv, re R = sys.argv[1] if len(sys.argv) > 1 else os.path.dirname(os.path.dirname(os.path.abspath(__file__))) out = [] WORDS = {"0": "Zero", "1": "One", "2": "Two", "3": "Three", "4": "Four", "5": "Five", "6": "Six", "7": "Seven", "8": "Eight", "9": "Nine"} def name(s): s = re.sub(r"[^A-Za-z0-9]+", " ", s).title().replace(" ", "") return "".join(WORDS[ch] if ch.isdigit() else ch for ch in s) def fmt(v): if isinstance(v, bool): return str(v) if isinstance(v, int): return f"{v:,}".replace(",", "{,}") if isinstance(v, float): return f"{v:,.3f}".replace(",", "{,}") if abs(v) < 100 else f"{v:,.1f}".replace(",", "{,}") return str(v).replace("%", "\\%").replace("_", "\\_") def emit(k, v): out.append(f"\\newcommand{{\\{name(k)}}}{{{fmt(v)}}}") def walk(prefix, obj): if isinstance(obj, dict): for k, v in obj.items(): walk(f"{prefix} {k}", v) elif isinstance(obj, (int, float, str, bool)) and obj is not None: emit(prefix, obj) p = os.path.join(R, "data/metadata/prisma_counts.json") if os.path.exists(p): walk("prisma", json.load(open(p))) p = os.path.join(R, "data/metadata/linkage_report.json") if os.path.exists(p): walk("link", json.load(open(p))) for f in ["volume_yearly", "citation_windows_yearly", "international_collab_yearly", "industry_share_yearly", "venue_hhi_yearly"]: p = os.path.join(R, "results/bibliometrics", f + ".csv") if os.path.exists(p): for row in csv.DictReader(open(p)): y = row.get("year") or row.get("publication_year") for k, v in row.items(): if k in ("year", "publication_year") or v in ("", None): continue try: v = int(v) if re.fullmatch(r"-?\d+", v) else float(v) except ValueError: pass emit(f"{f} {y} {k}", v) for f, key in [("results/econometrics/its_results.json", "its"), ("taxonomy/validation/metrics.json", "val"), ("results/emerging/metrics_h12.json", "emerg"), ("results/emerging/metrics_h24.json", "emergTwoFour")]: p = os.path.join(R, f) if os.path.exists(p): d = json.load(open(p)) walk(key, {k: v for k, v in d.items() if not isinstance(v, (list,))}) for f, key in [("data/metadata/dblp_parse_report.json", "dblp"), ("data/metadata/acl_parse_report.json", "acl"), ("data/metadata/api_increment_ledger.json", "increment"), ("data/metadata/increment_report.json", "incrementIngest")]: p = os.path.join(R, f) if os.path.exists(p): d = json.load(open(p)) if key == "increment": sl = d.get("slices", {}); emit("increment api calls", d.get("calls", 0)); emit("increment records retrieved", sum(v.get("records", 0) for v in sl.values())) emit("increment truncated slices", sum(1 for v in sl.values() if v.get("truncated"))); emit("increment slices done", sum(1 for v in sl.values() if v.get("done"))); emit("increment cost usd", round(d.get("cost_usd_total", 0), 4)) elif key == "incrementIngest": emit("increment new works", d.get("new_works", 0)); emit("increment already in snapshot", d.get("already_in_snapshot", 0)) elif key == "dblp": emit("dblp parsed records", d.get("rows_written", 0)); [emit(f"dblp type {k}", v) for k, v in d.get("counts_by_type", {}).items()] elif key == "acl": emit("acl parsed entries", d.get("entries", 0)) for f, key in [("reports/classification_report.json", "classification"), ("literature_synthesis/landmarks/selection_report.json", "selection"), ("data/metadata/linkage_sample_summary.json", "linkagesample")]: p = os.path.join(R, f) if os.path.exists(p): walk(key, json.load(open(p))) p = os.path.join(R, "results/emerging/known_set_lead_times.csv") if os.path.exists(p): for row in csv.DictReader(open(p)): for k in ("total_works", "detection_month", "peak_growth_month", "lead_months"): v = row.get(k) if v in ("", None): continue try: v = int(float(v)) if re.fullmatch(r"-?\d+(\.0)?", v) else v except ValueError: pass emit(f"known {row['concept']} {k}", v) leads = [int(float(r["lead_months"])) for r in csv.DictReader(open(p)) if r.get("lead_months") not in ("", None)] if leads: import statistics; emit("known set median lead months", int(statistics.median(leads))); emit("known set n detected", len(leads)); emit("known set n positive lead", sum(1 for l in leads if l > 0)) open(os.path.join(R, "manuscript/tables/numbers.tex"), "w").write("% machine-generated by tables/make_numbers.py — do not edit\n" + "\n".join(out) + "\n") print(len(out), "macros")