1#!/usr/bin/env python32"""Merge manuscript/references.bib (main, Agent B-lit) with the writers' references_part*.bib into references_all.bib.3First occurrence wins (main bib first); duplicate keys are reported in references_merge_report.txt with a flag when the4DOI differs (a real conflict to resolve by hand). main.tex uses \\bibliography{references_all}."""5import glob, re, os6H = os.path.dirname(os.path.abspath(__file__))7files = [os.path.join(H, "references.bib")] + sorted(glob.glob(os.path.join(H, "references_part*.bib")) + sorted(glob.glob(os.path.join(H, "references_app*.bib"))))8seen = {}; out = []; dups = []9for f in files:10 txt = open(f, encoding="utf-8").read()11 for m in re.finditer(r"@(\w+)\s*\{\s*([^,\s]+)\s*,", txt):12 start = m.start(); depth = 0; i = m.end() - 113 # find matching closing brace of the entry14 j = txt.find("{", start); depth = 0; k = j15 while k < len(txt):16 if txt[k] == "{": depth += 117 elif txt[k] == "}":18 depth -= 119 if depth == 0: break20 k += 121 entry = txt[start:k + 1]; key = m.group(2)22 doi = re.search(r"doi\s*=\s*[{\"]([^}\"]+)", entry, re.I)23 doi = doi.group(1).strip().lower() if doi else None24 if key in seen:25 dups.append((key, os.path.basename(f), "DOI_CONFLICT" if (doi and seen[key] and doi != seen[key]) else "same"))26 continue27 # verification notes live in the CSVs; strip them from the printed bibliography (aer.bst prints `note`)28 entry = re.sub(r",\s*note\s*=\s*[{\"]verified[^}\"]*[}\"]", "", entry)29 seen[key] = doi; out.append(entry)30open(os.path.join(H, "references_all.bib"), "w", encoding="utf-8").write("% merged by merge_bibs.py — do not edit; edit the source bib files\n\n" + "\n\n".join(out) + "\n")31with open(os.path.join(H, "references_merge_report.txt"), "w") as fh:32 fh.write(f"entries: {len(out)} from {len(files)} files\n"); [fh.write(f"DUP {k} in {f}: {st}\n") for k, f, st in dups]33print(len(out), "entries;", len(dups), "duplicates;", sum(1 for d in dups if d[2] == "DOI_CONFLICT"), "DOI conflicts")34