""" Renders catalog.json (from catalog_schema.py) into a Markdown appendix: one section per source file, one subsection per table, with a column table. Excluded (scratch/template/report) tables are marked and collapsed to a one-liner rather than a full column dump, since they carry no data worth documenting column-by-column. Usage: python render_catalog_md.py catalog.json --output appendix.md """ from __future__ import annotations import argparse import json from pathlib import Path SOURCE_TITLES = { "utilities": "UTILITIES.accdb", "seguros": "SEGUROS 16_be.mdb", "scothia": "SCOTHIA.mdb", } def render_table(name: str, entry: dict) -> str: lines = [f"#### `{name}`"] if entry.get("excluded"): lines.append("") lines.append(f"**Excluded from migration** — {entry.get('row_count', '?')} rows. Not detailed here; see the exclusion rationale table above.") lines.append("") return "\n".join(lines) if "error" in entry: lines.append("") lines.append(f"**Could not read this table:** `{entry['error']}`") lines.append("") return "\n".join(lines) lines.append("") lines.append(f"Rows: {entry['row_count']} | Columns: {entry['column_count']}") lines.append("") lines.append("| Column | Type | Nullable |") lines.append("|---|---|---|") for col in entry["columns"]: type_label = col["access_type"] or f"_(inferred: {col['pandas_dtype']})_" nullable = "yes" if col["nullable"] else ("no" if col["nullable"] is False else "—") col_name = col["name"].replace("|", "\\|") lines.append(f"| `{col_name}` | {type_label} | {nullable} |") lines.append("") return "\n".join(lines) def render_source(source_name: str, source_data: dict) -> str: title = SOURCE_TITLES.get(source_name, source_name) lines = [f"### {title}", ""] tables = source_data["tables"] included = sorted(t for t, e in tables.items() if not e.get("excluded")) excluded = sorted(t for t, e in tables.items() if e.get("excluded")) lines.append(f"{len(tables)} tables total — {len(included)} included in migration, {len(excluded)} excluded (scratch/template/report tables).") lines.append("") for table_name in included: lines.append(render_table(table_name, tables[table_name])) if excluded: lines.append("#### Excluded tables") lines.append("") lines.append("| Table | Rows |") lines.append("|---|---|") for table_name in excluded: entry = tables[table_name] lines.append(f"| `{table_name}` | {entry.get('row_count', '?')} |") lines.append("") return "\n".join(lines) def main() -> None: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("catalog", type=Path) parser.add_argument("--output", type=Path, default=Path("appendix.md")) args = parser.parse_args() catalog = json.loads(args.catalog.read_text(encoding="utf-8")) sections = ["## Appendix: full table catalog", "", "Generated by `migration/catalog_schema.py` + `migration/render_catalog_md.py`. Regenerate after any change to the source files.", ""] for source_name in ["utilities", "seguros", "scothia"]: sections.append(render_source(source_name, catalog[source_name])) args.output.write_text("\n".join(sections), encoding="utf-8") print(f"Wrote {args.output}") if __name__ == "__main__": main()