"""CLI: convert EPRO2 SCH_PAGE docs to KiCad ``.kicad_sch`` files. Usage: uv run python -m tools.epro2.kicad --doc --out uv run python -m tools.epro2.kicad --all-sch --out The ``--all-sch`` form converts every SCH_PAGE in the project, naming each output by its document title (or doc_uuid prefix as fallback). """ from __future__ import annotations import argparse import re import sys from pathlib import Path from ..replay import Project, replay_project from .sch_writer import write_sch_page _SAFE_CHARS = re.compile(r"[^A-Za-z0-9._\-一-鿿]+") def _safe_filename(s: str) -> str: s = _SAFE_CHARS.sub("_", s).strip("_") return s or "untitled" def _convert_one(proj: Project, doc_uuid: str, out_dir: Path) -> Path: if doc_uuid not in proj.documents: candidates = [u for u in proj.documents if u.startswith(doc_uuid)] if len(candidates) != 1: raise SystemExit(f"no unique match for {doc_uuid!r} (candidates: {candidates[:5]})") doc_uuid = candidates[0] doc = proj.documents[doc_uuid] if doc.doc_type != "SCH_PAGE": raise SystemExit(f"doc {doc_uuid} is {doc.doc_type!r}, not SCH_PAGE") text = write_sch_page(doc) title = (doc.objects.get("META") or {}).get("title") or doc_uuid[:12] out_path = out_dir / f"{_safe_filename(title)}_{doc_uuid[:8]}.kicad_sch" out_path.write_text(text, encoding="utf-8") stats = getattr(write_sch_page, "last_stats", None) if stats: print( f" {out_path.name}: wires={stats.wires} symbols={stats.symbol_placements} " f"text={stats.text} skipped={stats.skipped}" ) return out_path def main(argv: list[str] | None = None) -> int: ap = argparse.ArgumentParser(description="EPRO2 → KiCad schematic exporter (Phase 1)") ap.add_argument("project_dir", type=Path) g = ap.add_mutually_exclusive_group(required=True) g.add_argument("--doc", help="SCH_PAGE doc uuid (or unique prefix) to convert") g.add_argument("--all-sch", action="store_true", help="convert every SCH_PAGE") ap.add_argument("--out", type=Path, default=Path("data/processed/kicad_sch")) args = ap.parse_args(argv) proj = replay_project(args.project_dir) args.out.mkdir(parents=True, exist_ok=True) if args.doc: _convert_one(proj, args.doc, args.out) return 0 targets = [u for u, d in proj.documents.items() if d.doc_type == "SCH_PAGE"] if not targets: print("no SCH_PAGE docs in this project", file=sys.stderr) return 1 print(f"Converting {len(targets)} SCH_PAGE docs → {args.out}") for u in targets: try: _convert_one(proj, u, args.out) except Exception as e: # noqa: BLE001 print(f" FAIL {u[:12]}: {e}", file=sys.stderr) return 0 if __name__ == "__main__": raise SystemExit(main())