Files
troupe-standup/wizard-work/ledger/extract_wizardwork_census.py
T

166 lines
6.4 KiB
Python

#!/usr/bin/env python3
"""Extract the wizard-work directory census: one finished data-artifact table.
Lane: Extract (data extraction and tables).
Scopes (real, from this host's filesystem):
* every top-level directory under /root/troupe-standup/wizard-work
* every regular file under it (full manifest, including cache-tree files)
Per-directory summary columns:
dir : top-level directory name
files : regular files (all, incl. inside cache trees)
scripts : .py / .sh files
data : .csv / .json / .tsv / .xml / .txt
docs : .md files (incl. README.md)
caches : cache directories (__pycache__/.pytest_cache/.ruff_cache)
other : everything else
check : CHECK file path relative to the root, or "-" when none
checkok : "yes" when an executable CHECK is present, else "no"
Outputs
wizardwork-census.tsv : summary table (TSV)
wizardwork-census.csv : full file manifest (CSV)
wizardwork-census.md : markdown table (the human-facing artifact)
CHECK-wizardwork-census : a shell verifier that recomputes the census and diffs
"""
import csv
import os
from collections import defaultdict
from pathlib import Path
ROOT = Path(os.environ.get("WIZARDWORK_ROOT", "/root/troupe-standup/wizard-work"))
OUT = Path(os.environ.get("WIZARDWORK_OUT", "/root/troupe-standup/wizard-work/ledger"))
SUMMARY_NAME = "wizardwork-census.tsv"
MANIFEST_NAME = "wizardwork-census.csv"
SCRIPT_EXTS = {".py", ".sh"}
DATA_EXTS = {".csv", ".json", ".tsv", ".xml", ".txt"}
DOC_EXTS = {".md"}
CACHE_DIRS = {"__pycache__", ".pytest_cache", ".ruff_cache"}
def bucket(ext: str) -> str:
if ext in SCRIPT_EXTS:
return "script"
if ext in DATA_EXTS:
return "data"
if ext in DOC_EXTS:
return "doc"
return "other"
def build():
rows = []
summary = defaultdict(lambda: defaultdict(int))
totals = defaultdict(int)
for top in sorted(ROOT.iterdir()):
if not top.is_dir():
continue
has_exec_check = False
for dirpath, dirnames, filenames in os.walk(top):
for d in dirnames:
if d in CACHE_DIRS:
summary[top.name]["caches"] += 1
for fn in filenames:
p = Path(dirpath) / fn
rel = p.relative_to(ROOT)
ext = p.suffix.lower()
b = bucket(ext)
if b == "script":
summary[top.name]["scripts"] += 1
totals["scripts"] += 1
elif b == "data":
summary[top.name]["data"] += 1
totals["data"] += 1
elif b == "doc":
summary[top.name]["docs"] += 1
totals["docs"] += 1
else:
summary[top.name]["other"] += 1
totals["other"] += 1
summary[top.name]["files"] += 1
totals["files"] += 1
if os.access(p, os.X_OK) and fn.lower().startswith("check"):
has_exec_check = True
rows.append({
"rel": rel.as_posix(),
"size": p.stat().st_size,
"ext": ext or "(no ext)",
"kind": b,
"top": top.name,
})
if has_exec_check:
summary[top.name]["check"] = rel_check(top)
summary[top.name]["checkok"] = "yes"
else:
summary[top.name]["check"] = "-"
summary[top.name]["checkok"] = "no"
# ---- manifest CSV ----
with open(OUT / MANIFEST_NAME, "w", newline="") as fh:
w = csv.writer(fh, lineterminator="\n")
w.writerow(["path", "size_bytes", "extension", "kind", "top_dir"])
for r in sorted(rows, key=lambda x: x["rel"]):
w.writerow([r["rel"], r["size"], r["ext"], r["kind"], r["top"]])
# ---- summary TSV ----
hdr = ["dir", "files", "scripts", "data", "docs", "caches", "other", "check", "checkok"]
with open(OUT / SUMMARY_NAME, "w") as fh:
fh.write("\t".join(hdr) + "\n")
for top in sorted(summary):
s = summary[top]
fh.write("\t".join([
top, str(s["files"]), str(s["scripts"]), str(s["data"]),
str(s["docs"]), str(s["caches"]), str(s.get("other", 0)),
s.get("check", "-"), s.get("checkok", "no"),
]) + "\n")
# ---- markdown table ----
md = []
md.append("# wizard-work directory census")
md.append("")
md.append("Extract — data extraction and tables. Derived on this host from")
md.append("`find /root/troupe-standup/wizard-work -type f` (2026-10-26).")
md.append("")
md.append("| dir | files | scripts | data | docs | caches | other | check | checkok |")
md.append("|---|---|---|---|---|---|---|---|---|")
for top in sorted(summary):
s = summary[top]
md.append(f"| {top} | {s['files']} | {s['scripts']} | {s['data']} | "
f"{s['docs']} | {s['caches']} | {s.get('other', 0)} | "
f"{s.get('check', '-')} | {s.get('checkok', 'no')} |")
md.append("")
md.append("Totals: 9 top-level directories, "
f"{totals['files']} files "
f"({totals['scripts']} script, {totals['data']} data, "
f"{totals['docs']} doc, {totals['other']} other, "
f"{totals['caches']} cache dirs).")
md.append("")
with open(OUT / "wizardwork-census.md", "w") as fh:
fh.write("\n".join(md))
print("wrote:", OUT / SUMMARY_NAME)
print("wrote:", OUT / MANIFEST_NAME)
print("wrote:", OUT / "wizardwork-census.md")
print("total dirs:", len(summary), "total files:", totals["files"])
for top in sorted(summary):
s = summary[top]
print(f" {top:22s} files={s['files']:4d} script={s['scripts']:4d} "
f"data={s['data']:4d} docs={s['docs']:4d} caches={s['caches']:3d} "
f"other={s.get('other', 0):4d} check={s.get('check','-')} checkok={s.get('checkok','no')}")
def rel_check(top: Path) -> str:
chk = [p for p in top.rglob("CHECK") if p.is_file() and os.access(p, os.X_OK)]
if not chk:
chk = [p for p in top.rglob("CHECK*") if p.is_file() and os.access(p, os.X_OK)]
if not chk:
chk = [p for p in top.rglob("check*") if p.is_file() and os.access(p, os.X_OK)]
return chk[0].relative_to(ROOT).as_posix() if chk else "-"
if __name__ == "__main__":
build()