""" Renders objects.json (from catalog_objects.py) into Markdown: Reports and Forms as name lists (DAO's catalog interface doesn't expose RecordSource or layout, only names/metadata - see catalog_objects.py docstring), real (non-hidden) Queries with full SQL text since that's where the actual business logic lives, hidden ~sq_ queries collapsed to a count. Usage: python render_objects_md.py objects.json --output objects.md """ from __future__ import annotations import argparse import json from pathlib import Path SOURCE_TITLES = { "utilities": "UTILITIES.accdb", "seguros_frontend": "SEGUROS 16.mdb (frontend)", "seguros_backend": "SEGUROS 16_be.mdb (backend)", "scothia": "SCOTHIA.mdb", } QUERY_TYPE_LABELS = { 0: "Select", 16: "Select (linked ODBC)", 32: "Crosstab", 48: "Delete", 64: "Update", 80: "Append", 96: "Make-Table", 112: "DDL", 128: "SQL Pass-Through", 144: "SQL Pass-Through (bulk)", 160: "Union", 176: "Procedure", } def render_name_list(title: str, names: list[str]) -> str: lines = [f"##### {title} ({len(names)})", ""] if not names: lines.append("_none_") lines.append("") return "\n".join(lines) for name in sorted(names): lines.append(f"- `{name}`") lines.append("") return "\n".join(lines) def render_queries(queries: list[dict]) -> str: real = sorted((q for q in queries if not q["hidden"]), key=lambda q: q["name"]) hidden = [q for q in queries if q["hidden"]] lines = [f"##### Queries ({len(queries)} total — {len(real)} real, {len(hidden)} hidden internal subqueries)", ""] if hidden: lines.append(f"_{len(hidden)} hidden `~sq_`-prefixed queries omitted — these are Access-generated internal subquery caches, not authored business logic._") lines.append("") for q in real: type_label = QUERY_TYPE_LABELS.get(q["type"], f"type {q['type']}") lines.append(f"###### `{q['name']}` ({type_label})") lines.append("") lines.append("```sql") lines.append(q["sql"].strip() if q["sql"] else "-- (empty)") lines.append("```") 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}", ""] lines.append(render_name_list("Reports", source_data["reports"])) lines.append(render_name_list("Forms", source_data["forms"])) lines.append(render_queries(source_data["queries"])) 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("objects_appendix.md")) args = parser.parse_args() catalog = json.loads(args.catalog.read_text(encoding="utf-8")) sections = [ "## Appendix: Reports, Forms & Queries", "", "Generated by `migration/catalog_objects.py` + `migration/render_objects_md.py` via DAO COM automation (not visible through plain ODBC). Reports and Forms are listed by name only — DAO's catalog interface doesn't expose RecordSource or visual layout without full MS Access installed. Queries include full SQL text, which is where the real computational business logic lives (billing math, renewal batching, filters).", "", ] for source_name in ["utilities", "seguros_frontend", "seguros_backend", "scothia"]: if source_name in catalog: 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()