"""Reproduce reviewed public candidate facts and the national contest inventory. NC: official state-only November 3 list, report dated September 21, 2026, pages 1–3. Ballot names and parties only; no contact/address information. Re-running does not fetch or imply a more recent review. """ import csv import json import pathlib import re ROOT = pathlib.Path(__file__).resolve().parents[1] SOURCE = { "name": "North Carolina State Board of Elections — November 2026 candidate list, pages 1–3", "url": "https://s3.amazonaws.com/dl.ncsbe.gov/Elections/2026/Candidate%20Filing/2026_general_candidate_list_by_contest_federal_and_state.pdf", "retrieved_at": "2026-10-04T00:00:00Z", } # Reviewed transcription. Party order follows the official list. CONTESTS = { "senate": "GRE:Michael Dublin|DEM:Roy Cooper|LIB:Shannon W. Bray|REP:Michael Whatley", "house-01": "DEM:Don Davis|REP:Laurie Buckhout|LIB:Tom Bailey", "house-02": "REP:Eugene F. Douglass|DEM:Deborah K. Ross|LIB:Matthew Laszacs", "house-03": "LIB:Daniel Cavender|DEM:Raymond Smith|REP:Greg Murphy", "house-04": "DEM:Valerie P. Foushee|LIB:Guy Meilleur|REP:Mahesh (Max) Ganorkar", "house-05": "REP:Virginia Foxx|LIB:Robert B. Luffman|DEM:Chuck Hubbard", "house-06": "REP:Addison McDowell|DEM:Cyril Jefferson", "house-07": "LIB:Maad Abu-Ghazalah|REP:David Rouzer|DEM:Kimberly Hardy", "house-08": "GRE:Bo Whitehead|DEM:Colby Watson|REP:Mark Harris", "house-09": "DEM:Richard N. Ojeda II|REP:Richard Hudson", "house-10": "LIB:Steven Feldman|DEM:Ashley Bell|REP:Pat Harrigan", "house-11": "REP:Jennifer Capps Balkcom|DEM:Jamie Ager|LIB:Travis Groo", "house-12": "REP:Jack Codiga|DEM:Alma S. Adams", "house-13": "DEM:Paul Barringer|LIB:Steven Swinton|REP:Brad Knott", "house-14": "DEM:Lakesha Womack|REP:Tim Moore", } SEATS = dict(zip("AL AK AZ AR CA CO CT DE FL GA HI ID IL IN IA KS KY LA ME MD MA MI MN MS MO MT NE NV NH NJ NM NY NC ND OH OK OR PA RI SC SD TN TX UT VT VA WA WV WI WY".split(), [7, 1, 9, 4, 52, 8, 5, 1, 28, 14, 2, 2, 17, 9, 4, 4, 6, 6, 2, 8, 9, 13, 8, 4, 8, 2, 3, 4, 2, 12, 3, 26, 14, 1, 15, 5, 6, 17, 2, 7, 1, 9, 38, 4, 1, 11, 10, 2, 8, 1])) def write(path, value): path.write_text(json.dumps(value, indent=2, ensure_ascii=False) + "\n") def main(): catalog = {"schema_version": 1, "kind": "catalog", "id": "nc-2026-10-04", "mode": "real", "label": "North Carolina official list · partial US catalog", "as_of": SOURCE["retrieved_at"], "source": SOURCE, "complete": False, "races": [], "candidates": [], "notes": "Reviewed from official PDF pages 1–3, report dated 2026-09-21. Retrieval has day precision; midnight is a date placeholder. Federal contests only; NC has no scheduled 2026 governor race. Refresh against official changes before freezing."} for contest, candidates in CONTESTS.items(): race = "2026-nc-" + contest + "-general" catalog["races"].append({"id": race, "state": "NC", "office": contest.split("-")[0], "round": "general", "name": "North Carolina " + contest.replace("-", " ").title()}) for row in candidates.split("|"): party, name = row.split(":", 1) candidate_id = race + "-" + re.sub("[^a-z0-9]+", "-", name.lower()).strip("-") catalog["candidates"].append({"id": candidate_id, "race_id": race, "name": name, "party": party, "affiliation": {"DEM": "democrat", "REP": "republican"}.get(party, "other"), "source": SOURCE, "qualified": True, "ballot_lines": [party], "provider_ids": {}}) write(ROOT / "data/real/nc-2026-10-04.json", catalog) combined = json.loads((ROOT / "data/real/mi-2026-10-04.json").read_text()) combined.update(id="us-2026-10-04-partial", label="Michigan + North Carolina · partial US catalog", notes="Combined reviewed official lists. Each candidacy retains its original source and ID. Retrieval timestamps have day precision. 48 states still need imports; no live result feed is verified.", sources=[combined["source"], SOURCE]) combined["races"] += catalog["races"] combined["candidates"] += catalog["candidates"] write(ROOT / "data/real/us-2026-10-04-partial.json", combined) rows = list(csv.DictReader((ROOT / "data/coverage_2026.csv").open())) inventory = [] for row in rows: if row["state"] == "NC" and row["office"] in ("house", "senate"): row["catalog"] = "official_partial" if row["scheduled_2026"] != "yes": continue state, office = row["state"], row["office"] for district in range(1, SEATS[state] + 1) if office == "house" else [None]: seat = f"-{district:02}" if district else "" special = office == "senate" and state in ("FL", "OH") inventory.append({"id": f"2026-{state.lower()}-{office}{seat}-{'special' if special else 'general'}", "state": state, "office": office, "district": district, "election_date": "2026-11-03", "contest_type": "special" if special else "general", "scoring_round": "november_first"}) with (ROOT / "data/coverage_2026.csv").open("w", newline="") as file: writer = csv.DictWriter(file, fieldnames=rows[0].keys(), lineterminator="\n") writer.writeheader() writer.writerows(rows) assert len(inventory) == 506, len(inventory) write(ROOT / "data/contests_2026.json", {"reviewed_at": "2026-10-04", "scope": "50 states; 435 House, 35 Senate (including FL/OH special), 36 governor; November contests only", "sources": ["https://www.census.gov/data/tables/2020/dec/2020-apportionment-data.html", "https://www.senate.gov/senators/Class_II.htm", "https://about.usps.com/what/government-services/election-mail/pdf/2026-election-mail-kit.pdf"], "contests": inventory}) lines = ['#pragma once', '#include ', '#include ', '', 'namespace fpol {', 'inline constexpr auto national_races = std::array{'] lines += [' std::string_view{"' + race["id"] + '"},' for race in inventory] lines += ['};', '}'] (ROOT / "src/national_races.hpp").write_text("\n".join(lines) + "\n") if __name__ == "__main__": main()