"""CLI: dump EPRO2 docs to Std-shaped JSON files for downstream consumers. The output is "Option 2" per the downstream colleague's spec: Std envelope with a raw EPRO2 ``objects: {id: payload}`` dict in place of the usual ``shape[]`` tilde-string array. Their ~100-LoC adapter walks ``objects`` and dispatches by ``_type`` to build real Std shapes — see ``docs/sources/epro2_to_std_mapping.md`` for the OPTYPE → Std verb table. Usage: uv run python -m tools.epro2.std --all-pcb --out uv run python -m tools.epro2.std --all-sch --out uv run python -m tools.epro2.std --all --out Output: flat ``.json`` per doc — mirrors Std's own data layout so a downstream pipeline that already iterates ``source/*.json`` works unchanged. """ from __future__ import annotations import argparse import json import sys from pathlib import Path from ..replay import Project, replay_project from .pcb_writer import write_pcb_std from .sch_writer import write_sch_std def _dump(payload: dict, out_path: Path, project_uuid: str) -> None: payload["result"]["puuid"] = project_uuid or "" out_path.write_text( json.dumps(payload, ensure_ascii=False, separators=(",", ":")), encoding="utf-8", ) def _convert_pcbs(proj: Project, out_dir: Path) -> int: uuids = [u for u, d in proj.documents.items() if d.doc_type == "PCB"] if not uuids: return 0 print(f"PCB: dumping {len(uuids)} doc(s) → {out_dir}") for u in uuids: try: payload = write_pcb_std(proj.documents[u]) except Exception as e: # noqa: BLE001 print(f" FAIL {u[:12]}: {e}", file=sys.stderr) continue _dump(payload, out_dir / f"{u}.json", proj.project_uuid or "") s = getattr(write_pcb_std, "last_stats", None) if s: print( f" {u[:12]}.json: objects={s.objects} layers={s.layers_emitted} " f"BBox=({s.bbox_x:g},{s.bbox_y:g},{s.bbox_w:g},{s.bbox_h:g})" ) return len(uuids) def _convert_schs(proj: Project, out_dir: Path) -> int: uuids = [u for u, d in proj.documents.items() if d.doc_type == "SCH_PAGE"] if not uuids: return 0 print(f"SCH: dumping {len(uuids)} doc(s) → {out_dir}") for u in uuids: try: payload = write_sch_std(proj.documents[u]) except Exception as e: # noqa: BLE001 print(f" FAIL {u[:12]}: {e}", file=sys.stderr) continue _dump(payload, out_dir / f"{u}.json", proj.project_uuid or "") s = getattr(write_sch_std, "last_stats", None) if s: print( f" {u[:12]}.json: objects={s.objects} " f"BBox=({s.bbox_x:g},{s.bbox_y:g},{s.bbox_w:g},{s.bbox_h:g})" ) return len(uuids) def main(argv: list[str] | None = None) -> int: ap = argparse.ArgumentParser(description="EPRO2 → EasyEDA Std-shaped JSON dump") ap.add_argument("project_dir", type=Path) g = ap.add_mutually_exclusive_group(required=True) g.add_argument("--all-pcb", action="store_true", help="dump every PCB doc") g.add_argument("--all-sch", action="store_true", help="dump every SCH_PAGE doc") g.add_argument("--all", action="store_true", help="dump both PCB and SCH_PAGE docs") ap.add_argument("--out", type=Path, default=Path("data/processed/std_json")) args = ap.parse_args(argv) proj = replay_project(args.project_dir) args.out.mkdir(parents=True, exist_ok=True) n = 0 if args.all_pcb or args.all: n += _convert_pcbs(proj, args.out) if args.all_sch or args.all: n += _convert_schs(proj, args.out) if n == 0: print("nothing to dump (no PCB / SCH_PAGE docs found)", file=sys.stderr) return 1 return 0 if __name__ == "__main__": raise SystemExit(main())