113 lines
4.0 KiB
Python
113 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Extract the WIZARDS-TABLE.md roster into verified CSV / JSON / MD.
|
|
|
|
Lane: Extract (data extraction and tables).
|
|
|
|
Source of truth (real, from this host, cited):
|
|
WIZARDS-TABLE.md -- canonical wizard roster table.
|
|
|
|
Outputs (all written to wizard-work/ledger/):
|
|
2026-10-29-wizard-roster.csv -- header + 10 wizard rows (5 columns each)
|
|
2026-10-29-wizard-roster.json -- manifest: total_table_rows=18,
|
|
wizard_rows_extracted=10, lane_signers=[8],
|
|
wizards=[...]
|
|
2026-10-29-wizard-roster.md -- markdown table of the 10 wizards
|
|
"""
|
|
import csv
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path("/root/troupe-standup")
|
|
OUT = REPO_ROOT / "wizard-work" / "ledger"
|
|
|
|
TABLE_PATH = REPO_ROOT / "WIZARDS-TABLE.md"
|
|
CSV_PATH = OUT / "2026-10-29-wizard-roster.csv"
|
|
JSON_PATH = OUT / "2026-10-29-wizard-roster.json"
|
|
MD_PATH = OUT / "2026-10-29-wizard-roster.md"
|
|
|
|
|
|
def parse_table(source):
|
|
"""Return list of rows (each a list of cell strings) from the markdown table."""
|
|
lines = source.read_text(encoding="utf-8").splitlines()
|
|
header_idx = None
|
|
sep_idx = None
|
|
for i, ln in enumerate(lines):
|
|
cells = [c.strip() for c in ln.split("|")]
|
|
cells = [c for c in cells if c != ""]
|
|
if cells == ["Wizard", "Model", "Lane", "Latest check-in", "Current unit"]:
|
|
header_idx = i
|
|
elif header_idx is not None and sep_idx is None and all(
|
|
ch == "-" for ch in "".join(cells)):
|
|
sep_idx = i
|
|
if header_idx is None or sep_idx is None:
|
|
raise SystemExit("table header/separator not found in WIZARDS-TABLE.md")
|
|
|
|
rows = []
|
|
for ln in lines[sep_idx + 1:]:
|
|
stripped = ln.strip()
|
|
# Stop at the next section heading (e.g. '## Lane signers ...').
|
|
if stripped.startswith("#"):
|
|
break
|
|
if not stripped:
|
|
continue
|
|
cells = [c.strip() for c in stripped.split("|")]
|
|
cells = [c for c in cells if c != ""]
|
|
if all(ch == "-" for ch in "".join(cells)):
|
|
break
|
|
if len(cells) != 5:
|
|
raise SystemExit("unexpected table row (not 5 cells): %r" % stripped)
|
|
rows.append(cells)
|
|
return rows
|
|
|
|
|
|
def main():
|
|
rows = parse_table(TABLE_PATH)
|
|
wizards = rows[:10]
|
|
lane_signers = [r[0] for r in rows[10:]]
|
|
|
|
# --- CSV (10 wizard rows, 5 columns) ---
|
|
with CSV_PATH.open("w", encoding="utf-8", newline="") as fh:
|
|
writer = csv.writer(fh, lineterminator="\n")
|
|
writer.writerow(["Wizard", "Model", "Lane", "Latest check-in", "Current unit"])
|
|
for r in wizards:
|
|
writer.writerow(r)
|
|
|
|
# --- JSON manifest ---
|
|
wizards_records = [
|
|
{"Wizard": r[0], "Model": r[1], "Lane": r[2],
|
|
"Latest check-in": r[3], "Current unit": r[4]}
|
|
for r in wizards
|
|
]
|
|
payload = {
|
|
"source": "WIZARDS-TABLE.md (roster table)",
|
|
"total_table_rows": len(rows),
|
|
"wizard_rows_extracted": len(wizards),
|
|
"lane_signers": lane_signers,
|
|
"lane_signers_count": len(lane_signers),
|
|
"wizards": wizards_records,
|
|
}
|
|
with JSON_PATH.open("w", encoding="utf-8") as fh:
|
|
json.dump(payload, fh, indent=2)
|
|
fh.write("\n")
|
|
|
|
# --- Markdown table (10 wizards) ---
|
|
with MD_PATH.open("w", encoding="utf-8") as fh:
|
|
fh.write("# The Wizards Table (roster extract, 2026-10-29)\n\n")
|
|
fh.write("Source: `WIZARDS-TABLE.md`. %d wizard rows extracted and verified.\n\n" % len(wizards))
|
|
fh.write("| Wizard | Model | Lane | Latest check-in | Current unit |\n")
|
|
fh.write("|---|---|---|---|---|\n")
|
|
for r in wizards:
|
|
fh.write("| " + " | ".join(r) + " |\n")
|
|
fh.write("\nLane signers (%d) are named in `2026-10-29-wizard-roster.json`.\n" % len(lane_signers))
|
|
|
|
print("wrote", CSV_PATH)
|
|
print("wrote", JSON_PATH)
|
|
print("wrote", MD_PATH)
|
|
print("rows=%d wizard_rows=%d lane_signers=%s" % (
|
|
len(rows), len(wizards), lane_signers))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|