Files
Aislo/M02_MasterTemplete/M02_MasterTemplete_Branch.py
T
eomsangdonandClaude Opus 5.5 54ce67ceb8 refactor(M02): 자재 조합(UM) 지움 — 좌측 컨테이너 · 재료산출 옆 판 · 길(material-combos · 갈라 내기 · 키 적기 자재조합) · 저장소 · 틀 검사 · 복사 길의 UM 줄 · 시스템 층 표 · fixture · 시험
- 자재 고르기 창은 M02_Material_Pick.ts 로 옮겨 남김
- storage 층(회사 · 개인 · 프로젝트) 자재조합.json 은 지우지 않음(읽기만 끊음)
- check_master 틀 검사 · _틀.md 11장 · _화면_계약.md 자재 조합 절 정리

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPJGtfXo6GMbcpiboQC4uK
2026-10-04 17:39:16 +09:00

131 lines
5.5 KiB
Python

"""조합 갈라 내기 — 구조물 열 하나가 가리키는 조합을 고친 내용으로 새로 만들어 그 열만 새 키를
가리키게 함(PLAN 15장 · 검토 `14_조합편집검토.md` 1장).
· 일위대가 조합(UA · UC · UP) — 새 줄은 지금 층 조합 저장소가 만듦(`Combo_Store` · PLAN 27-2c:
마스터 층 = UA · 회사 층 = UC · 개인 층 = UP · 고르기는 세 층 중 이 층이 쓸 수 있는 것)
조합은 여러 열 · 모든 프로젝트가 같이 씀 — 제자리 고치지 않음. 담은 것(차례 · 메모까지)이
이미 있는 조합과 같으면 새로 안 만들고 그 키(원래 키 먼저) · 새 줄의 본뜬키 = 원래 키 · 소유 현장.
"""
from __future__ import annotations
from typing import Any, Callable
from M02_MasterTemplete import M02_MasterTemplete_Combo_Store as combos
from M02_MasterTemplete import M02_MasterTemplete_Store as store
_OWNER = "현장"
def _free_name(name: str, rows: list[dict]) -> str:
taken = {str(r.get("이름") or "") for r in rows}
n, out = 2, name
while out in taken:
out, n = f"{name} ({n})", n + 1
return out
def _branch(
column: str,
version: str,
field: str,
rows: list[dict],
slot: str,
ident: tuple[str, ...],
items: list[dict],
source: str,
make: Callable[[str, list[dict]], dict],
name: str,
) -> dict[str, Any]:
"""집계표 판 검사 → 같은 내용 찾기 · 없으면 `make(이름, 줄들)` 로 새 줄 → 집계표 그 열 `field`.
`slot` = 담은 칸(담은로직) · `ident` = 담은 줄에서 견주는 칸(로직 · 메모)."""
table = _table(column, version)
if not isinstance(items, list) or not items:
raise store.StoreError(400, "담은 것이 비었음")
def body(got) -> tuple: # 차례대로 · 빈 메모 = null
return tuple(
tuple(str(x.get(k) or "") for k in ident) for x in got or [] if isinstance(x, dict)
)
same = [r for r in rows if body(r.get(slot)) == body(items)]
hit = next((r for r in same if source and r.get("키") == source), same[0] if same else None)
if hit:
key, made_name, made = str(hit["키"]), str(hit.get("이름") or ""), False
else:
made_name = _free_name(name, rows)
key, made = make(made_name, rows), True
# ponytail: 조합 쓰기와 집계표 쓰기는 한 묶음 아님 — 사이에 집계표가 바뀌면 409 뒤 주인 없는
# 조합 하나가 남음(다시 하면 같은 내용이라 그 키를 씀)
판 = _point(table, column, field, key)
return {"키": key, "이름": made_name, "새로만듦": made, "집계표판": 판}
def _table(column: str, version: str) -> dict:
"""집계표 판 검사 · 구조물 열 있는지 — 읽은 표(`{판, 문서}`)."""
table = store.read("table", store.STRUCTURE_TABLE)
if version and table["판"] != version:
raise store.StoreError(409, {"stale": [store.STRUCTURE_TABLE], "판": table["판"]})
if not any(c["id"] == column for c in store._structure_columns()):
raise store.StoreError(404, f"{store.STRUCTURE_TABLE}에 없는 구조물 열 「{column}」")
return table
def _point(table: dict, column: str, field: str, key: str) -> str:
"""집계표 그 열 `field` = `key` 로 저장 — 새 판."""
doc = table["문서"]
for c in doc.get("열", []):
if isinstance(c, dict) and c.get("id") == column:
c[field] = key
return store.write("table", store.STRUCTURE_TABLE, table["판"], doc)["판"] # SS 도 여기서 맞춤
def use_combo(column: str, field: str, key: str, version: str = "") -> dict[str, Any]:
"""있는 조합을 이 열에 씀 — `field` 일위대가(UA) · 조합은 그대로(고르기만)."""
if field == "일위대가":
name = _combo(key).get("이름")
else:
raise store.StoreError(400, f"모르는 조합 종류 「{field}」")
판 = _point(_table(column, version), column, field, key)
return {"키": key, "이름": str(name or ""), "새로만듦": False, "집계표판": 판}
def _combo(key: str) -> dict[str, Any]:
"""이 층이 쓸 수 있는 조합(마스터 · 같은 회사 · 내 것) — 없으면 404 · 남의 층 것은 403."""
row = combos.find(key)
if row is None:
raise store.StoreError(404, f"없는 조합 「{key}」")
if not key.startswith(combos.heads()):
raise store.StoreError(
403, f"이 층에서 못 쓰는 조합 「{key}」 — 마스터 · 같은 회사 · 내 것만"
)
return row
def branch_combo(
column: str, source: str, items: list[dict], name: str, version: str = ""
) -> dict[str, Any]:
"""일위대가 조합 갈라 내기 — 구분 · 상세구분 · 단위 · 출처 = 본뜬 것 · 새 줄은 지금 층에."""
was = _combo(source)
items = [
{"로직": str(x.get("로직") or ""), "메모": x.get("메모")}
for x in items or []
if isinstance(x, dict)
]
def make(made_name: str, _rows) -> str:
row = {
"이름": made_name,
**{k: was.get(k) for k in ("구분", "상세구분", "단위")},
"담은로직": items,
"본뜬키": source,
"출처": was.get("출처"),
}
return str(combos.create({**row, "소유": _OWNER})["키"])
name = name.strip() or str(was.get("이름") or source)
rows = combos.rows()
return _branch(
column, version, "일위대가", rows, "담은로직", ("로직", "메모"), items, source, make, name
)