"""B07 구조물 도면(376 · 요구 241-1) — B05 · B06 배치에 연결된 구조물 조합 도면 목록 · 단건. 조합 키 = 항목 사본 폴더 이름(`snapshot_folder_name`) — 같은 조합을 여러 배치가 써도 한 장 · 더한장은 장마다 · id `structure_{키}[_{장id}]` · 차례 = 도번 · 도면 = 프로젝트 사본 `도면.json` + 얹는 도각(M02 `structure_frame` · 도번 · 도면명). 고치기는 M02 · B08 — 여기는 읽기만. """ from __future__ import annotations import logging import re from pathlib import Path from typing import Any from B07_DesignDetail.B07_DesignDetail_Schema import DesignDrawingItem from common_util.common_util_summary_item import snapshot_folder_name from M02_MasterTemplete import M02_Item_Snapshot as snapshot from M02_MasterTemplete.M02_Item_Source import read_json logger = logging.getLogger(__name__) STRUCTURE_PREFIX = "structure_" # M02 `structure_frame` 도각 층 — 그 모듈은 B07 라우터를 불러 여기서 못 부름(같은 값 유지) STRUCTURE_FRAME_LAYER = "m02-structure-frame" _SHEET_ID = re.compile(r"^[A-Za-z0-9-]{1,40}$") def _sheet_number(number: str, index: int) -> str: """장 도번 — 1장 = 도번 · n 째 장 = 도번-n(M02 `sheetNumber` · B09 인쇄와 같음).""" return "" if not number else number if index == 0 else f"{number}-{index + 1}" def structure_sheets(project_root: Path) -> list[dict[str, Any]]: """구조물 조합 도면 장 `[{id, label, number, drawing}]` — 도번 차례(도번 없음 = 뒤).""" docs: dict[str, tuple[str, dict[str, Any]]] = {} for copy in snapshot.linked_snapshots(project_root).values(): item = copy.get("항목") or {} if "도면" not in copy["부분"] or not item.get("key"): continue key = snapshot_folder_name(item) if key in docs: continue doc = read_json(snapshot.structures_dir(project_root) / copy["폴더"] / "도면.json") if isinstance(doc, dict) and isinstance(doc.get("도면"), dict): docs[key] = (str(item.get("name") or key), doc) sheets: list[dict[str, Any]] = [] order = sorted(docs, key=lambda k: (not docs[k][1].get("도번"), str(docs[k][1].get("도번")), k)) for key in order: name, doc = docs[key] number = str(doc.get("도번") or "") pages = [{"장id": "", "이름": "", "도면": doc["도면"]}, *(doc.get("더한장") or [])] for index, page in enumerate(pages): if not isinstance(page, dict) or not isinstance(page.get("도면"), dict): continue sheet_id = str(page.get("장id") or "") suffix = "" if index == 0 else f"_{sheet_id if _SHEET_ID.match(sheet_id) else index}" sheets.append( { "id": f"{STRUCTURE_PREFIX}{key}{suffix}", "label": f"{name} · {page['이름']}" if page.get("이름") else name, "number": _sheet_number(number, index), "drawing": page["도면"], } ) return sheets def structure_items( project_root: Path, manifest_drawings: dict[str, Any] ) -> list[DesignDrawingItem]: """좌측 「구조물 도면」 단추 — 자료가 깨졌으면 빈 목록(나머지 도면은 남김).""" try: sheets = structure_sheets(project_root) except (OSError, ValueError, KeyError) as exc: logger.warning("B07 구조물 도면 목록 실패 — 빼고 싣는다: %s", exc) return [] return [ DesignDrawingItem( id=sheet["id"], kind="structure", label=sheet["label"], confirmed=bool(manifest_drawings.get(sheet["id"], {}).get("confirmed")), ) for sheet in sheets ] def read_structure_drawing( project_root: Path, drawing_id: str ) -> tuple[str, str, dict[str, Any], bool, None]: """구조물 도면 한 장 + 도각 — `_read_drawing` 과 같은 (kind, label, drawing, confirmed, 표).""" # M02 라우터가 B07 라우터를 부름 — 여기서 늦게 불러 순환을 피함(B09 인쇄와 같음) from M02_MasterTemplete.M02_MasterTemplete_Router_Drawing import structure_frame sheet = next((s for s in structure_sheets(project_root) if s["id"] == drawing_id), None) if sheet is None: raise FileNotFoundError("요청한 구조물 도면을 찾을 수 없습니다.") drawing = sheet["drawing"] frame = structure_frame(sheet["number"] or None, sheet["label"]) layers = [*(drawing.get("layers") or []), *([frame["layer"]] if frame["layer"] else [])] framed = { **drawing, "entities": [*frame["entities"], *(drawing.get("entities") or [])], "layers": layers, } return "structure", sheet["label"], framed, False, None