"""사람 길 만들 때 물음(PLAN 733-2 · 요구서_572 3장 3) — 같은 층 같은 내용 문서가 있으면 409. 409 detail = `{같은것: [{키, 이름, 층, 주인}], 까닭}` → 다시 보냄 몸 `새로: true` = 그래도 새로 · `씀: <키>` = 그것을 씀(새로 안 만듦 · 그 키를 돌려줌 · 같은 내용이 아니면 다시 409). 지문 = `M02_Library_Same`(일위대가 SC · 단가산출 SK) · 구조물 조합(labor) = 같은 꼴 + 줄 수량 넣음. 빈 문서(지문 없음) = 안 물음 · 같은 층 [복사] · 붙여넣기 = 안 물음(복사가 뜻). 부르는 쪽 = 지금 층(`store.at` 안). """ from __future__ import annotations import json from pathlib import Path from typing import Any from common_util.common_util_derived_store import derived_key from M02_MasterTemplete import M02_Labor_Ref as refs from M02_MasterTemplete import M02_Library_Same as same from M02_MasterTemplete import M02_MasterTemplete_Store as store from M02_MasterTemplete import M02_MasterTemplete_Tree as tree WHY = "같은 층에 같은 내용 문서가 있음 — 그것을 씀 · 그래도 새로" def _cleaned(kind: str, doc: dict[str, Any]) -> dict[str, Any]: """저장될 꼴로 견줌(서버 정리) — 틀린 문서는 그대로(만들 때 까닭이 남).""" try: return refs.clean_doc(kind, {**doc, "키": refs.next_key(kind)}) except store.StoreError: return doc def _refuse(rows: list[dict[str, Any]], why: str = WHY) -> store.StoreError: return store.StoreError(409, {"같은것": rows, "까닭": why}) def _pick(found: list[str], rows: list[dict[str, Any]], new: bool, use: str | None) -> str | None: if use: if use in found: return use raise _refuse(rows, f"고른 「{use}」 가 같은 내용이 아님 — 다시 고름") if found and not new: raise _refuse(rows) return None def ask( kind: str, doc: dict[str, Any], new: bool = False, use: str | None = None, s: same.Same | None = None, ) -> str | None: """서고 문서(SC · SK) — 씀 키 | None(새로 만듦) · 같은 것 있고 물음 없으면 409 · `s` = 미리 채운 지문 한 벌(층 밖 참조 지문 등).""" folder = store.root() found = (s or same.Same()).find_same(folder, kind, _cleaned(kind, doc)) at = refs.where(folder) rows = [ { "키": key, "이름": (refs.read_ref({"층": at[0], "주인": at[1], "키": key}) or {}).get("이름"), } for key in found ] return _pick(found, [{**r, "층": at[0], "주인": at[1]} for r in rows], new, use) def labor_finger(doc: Any) -> str | None: """구조물 조합 일위대가 지문 — 담은로직 · 자재 · 단위 · 규격(줄 수량 넣음 · 차례 무시).""" if not isinstance(doc, dict): return None s = same.Same() mats = [same.norm(m) for m in doc.get("자재") or []] body = { "단위": same.unit(doc.get("단위")), "담은로직": same._sorted([s._line(x, (), True) for x in doc.get("담은로직") or []]), "자재": same._sorted(mats), "규격": same.norm(doc.get("규격")), } body = {k: v for k, v in body.items() if v not in same.EMPTY} if not body.get("담은로직") and not body.get("자재"): return None return f"SU:{derived_key(body)}" def ask_item( folder: Path, doc: dict[str, Any], new: bool = False, use: str | None = None ) -> dict[str, Any] | None: """구조물 조합 항목(실무 호표 [구조물 조합으로]) — 씀 = `{key, 열, 이름}` | None(새로) · 409.""" from M02_MasterTemplete import M02_Labor_Doc as labor try: doc = {**labor.clean(doc), "규격": doc.get("규격")} # 저장될 꼴로 견줌 except store.StoreError: pass want = labor_finger(doc) if not want: return None at = refs.where(folder) with store.at(folder): table = store.read("table", store.STRUCTURE_TABLE)["문서"] cols = {c["id"]: c for c in table.get("열") or [] if tree.is_item(c)} found, rows = [], [] for key, col in cols.items(): try: have = json.loads((Path(folder) / "labor" / f"{key}.json").read_bytes()) except (OSError, ValueError): continue if labor_finger(have) == want: name = " / ".join(tree.path_of(col)) found.append(key) rows.append({"키": key, "이름": name, "층": at[0], "주인": at[1]}) got = _pick(found, rows, new, use) if got is None: return None return {"key": got, "열": got, "이름": rows[found.index(got)]["이름"], "씀": True} def create(kind: str, doc: dict[str, Any], new: bool = False, use: str | None = None) -> dict: """[새로 만들기](733-2) — 같은 것 물음 뒤 `refs.create` · 씀 = `{이름: 그 키, 씀: true}`.""" got = ask(kind, doc, new, use) return {"이름": got, "씀": True} if got else refs.create(kind, doc)