Files
CMPlanning/scripts/inventory_dxf.py
T
Claude cc381b9d50 feat: add DXF inventory script for main-line (본선) drawings
Scans 21 main-line DXF files (02 평면·종단, 03 본선굴착, 04 본선기타) for
header, layers, entity counts, extents, 3D flag and text samples. Strict
load failures fall back to ezdxf recover mode and are recorded.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A5Cun3RHQKna9qxE6ybZ4A
2026-10-20 23:25:05 +00:00

86 lines
3.2 KiB
Python

#!/usr/bin/env python3
"""Inventory the main-line (본선) DXF drawings: header, layers, entity counts,
extents, 3D flag and text samples. Read-only on the source RAG folder."""
import collections
import glob
import json
import os
import sys
import ezdxf
from ezdxf import bbox, recover
RAG_DIR = sys.argv[1]
OUT_JSON = sys.argv[2]
# 본선 scope (approved 2026-10-21): 02 평면·종단, 03 본선굴착, 04 본선기타
SUBDIRS = ["02_평면 및 종단면도", "03_본선터널굴착", "04_본선터널기타"]
def inventory(path):
rec = {"file": os.path.basename(path), "ok": False}
rec["recovered"] = False
try:
doc = ezdxf.readfile(path)
except Exception as exc: # strict load failed: retry in recover mode and record it
strict_error = f"{type(exc).__name__}: {exc}"
try:
doc, _auditor = recover.readfile(path)
rec["recovered"] = True
rec["strict_error"] = strict_error
except Exception as exc2:
rec["error"] = f"{strict_error} | recover: {type(exc2).__name__}: {exc2}"
return rec
hdr = doc.header
rec["dxfversion"] = doc.dxfversion
rec["insunits"] = hdr.get("$INSUNITS")
rec["codepage"] = hdr.get("$DWGCODEPAGE")
rec["layers"] = len(doc.layers)
msp = doc.modelspace()
counts = collections.Counter(e.dxftype() for e in msp)
rec["entity_counts"] = dict(counts.most_common())
rec["blocks"] = len([b for b in doc.blocks if not b.name.startswith("*")])
z_vals = []
texts = []
for e in msp:
t = e.dxftype()
if t == "LINE":
z_vals += [e.dxf.start.z, e.dxf.end.z]
elif t in ("TEXT", "MTEXT"):
s = e.dxf.text if t == "TEXT" else e.plain_text()
if s and len(texts) < 40:
texts.append(s.strip()[:80])
elif t in ("POINT", "INSERT", "CIRCLE", "ARC"):
z_vals.append(e.dxf.location.z if t == "POINT" else e.dxf.insert.z if t == "INSERT" else e.dxf.center.z)
rec["z_min"] = round(min(z_vals), 3) if z_vals else None
rec["z_max"] = round(max(z_vals), 3) if z_vals else None
rec["has_3d_geometry"] = bool(z_vals) and (max(z_vals) - min(z_vals)) > 1e-6
try:
ext = bbox.extents(msp)
rec["extents_min"] = [round(v, 2) for v in ext.extmin]
rec["extents_max"] = [round(v, 2) for v in ext.extmax]
except Exception as exc:
rec["extents_error"] = str(exc)
rec["text_sample"] = texts
rec["ok"] = True
return rec
def main():
paths = []
for sub in SUBDIRS:
paths += sorted(glob.glob(os.path.join(RAG_DIR, sub, "*.dxf")))
results = [inventory(p) for p in paths]
os.makedirs(os.path.dirname(OUT_JSON), exist_ok=True)
with open(OUT_JSON, "w", encoding="utf-8") as fh:
json.dump(results, fh, ensure_ascii=False, indent=2)
ok = sum(r["ok"] for r in results)
print(f"files={len(results)} ok={ok} failed={len(results) - ok}")
for r in results:
status = "OK " if r["ok"] else "ERR"
detail = r.get("error") or f"ents={sum(r['entity_counts'].values())} 3D={r['has_3d_geometry']} units={r['insunits']} cp={r['codepage']} recovered={r['recovered']}"
print(f"{status} {r['file'][:60]:60s} {detail}")
if __name__ == "__main__":
main()