- 넣을 값 카드(M02_Labor_Values)는 담은 로직 입력 전부를 보임(자리 「선택」 감싸기 없앰) · 이름 「넣을 값」 - 고르기 모달 면적산출 알약 = 복사본 일위대가 알약(가져온 때 그대로) - 입력값.json · 고른자재.json · /calc 는 그대로 · storage 층 inputs/ 파일은 안 건드림(읽기만 끊음) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NPJGtfXo6GMbcpiboQC4uK
111 lines
4.8 KiB
Python
111 lines
4.8 KiB
Python
"""프로젝트에서 개인 층으로 — [개인에 저장](save-as) · 프로젝트에서 새 항목(new) (PLAN 26-2b).
|
|
|
|
개인 층 새 항목 = 구조물집계표 새 열 하나 + 빈 도면 · 면적산출(번호 · SS 키는 저장소가
|
|
줌). [개인에 저장]은 구조물 복사본의 열 정의 · 도면 · 면적을 담음 —
|
|
단가 묶음 · 입력값 · 고른자재는 프로젝트 몫이라 안 가져감. 쓰기는 지금 층 저장소 함수(`store.at` ·
|
|
`store.write`) — 나무 고침(`M02_MasterTemplete_Tree`)과
|
|
같은 집계표 저장 한 줄기(경로 · 머리 줄 수 맞춤 · 번호 · SS 장부)를 탐(PLAN 26-2a).
|
|
권한(본인 개인 층 · 회사 없음 409)은 부르는 쪽 몫.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from common_util.common_util_json import atomic_write_json
|
|
from M02_MasterTemplete import M02_MasterTemplete_Items as items_store
|
|
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
|
from M02_MasterTemplete import M02_MasterTemplete_Tree as tree
|
|
from M02_MasterTemplete import M02_Template_Layers as layers
|
|
from M02_MasterTemplete.M02_Item_Source import read_json
|
|
|
|
#: 복사본 파일 → 층 양식 종류
|
|
PART_KINDS = (("도면.json", "structure"), ("면적.json", "area"))
|
|
|
|
|
|
def heads_of(path: list[str], name: str | None) -> list[str]:
|
|
"""경로 + 이름 → 항목 경로(깊이 자유 · 빈 칸은 뺌) — 머리 줄 수는 집계표 저장이 맞춤."""
|
|
full = [str(p).strip() for p in [*path, name or ""] if str(p or "").strip()]
|
|
if not full:
|
|
raise ValueError("새 항목의 경로 · 이름이 비어 있습니다.")
|
|
return full
|
|
|
|
|
|
def _free_path(doc: dict[str, Any], full: list[str]) -> list[str]:
|
|
"""같은 경로 항목이 이미 있으면 마지막 이름을 「이름 (2)」 로
|
|
(나무는 같은 경로 두 항목을 안 둠)."""
|
|
taken = [tree.path_of(c) for c in doc.get("열", []) if tree.is_item(c)]
|
|
out, n = list(full), 2
|
|
while out in taken:
|
|
out, n = [*full[:-1], f"{full[-1]} ({n})"], n + 1
|
|
return out
|
|
|
|
|
|
def create_item(
|
|
folder: Path,
|
|
path: list[str],
|
|
name: str | None,
|
|
unit: str | None,
|
|
copy: Path | None = None,
|
|
origin: dict[str, Any] | None = None,
|
|
) -> dict[str, Any]:
|
|
"""개인 층 `folder` 에 새 항목 — `copy` = 구조물 복사본 폴더(있으면 그 내용을 담음).
|
|
|
|
돌려줌 `{key, 열, 이름}`."""
|
|
heads = heads_of(path, name)
|
|
total = read_json(copy / "집계.json") if copy else None
|
|
base_col = dict((total or {}).get("열") or {})
|
|
base_row = (total or {}).get("줄") or {}
|
|
items_store.ensure(folder)
|
|
with store.at(folder):
|
|
table = store.read("table", store.STRUCTURE_TABLE)
|
|
doc = table["문서"]
|
|
used = {c.get("id") for c in doc.get("열", []) if isinstance(c, dict)}
|
|
new_id = items_store._fresh_id(str(base_col.get("id") or "item").split("@")[0], used)
|
|
heads = _free_path(doc, heads)
|
|
column = {
|
|
**json.loads(json.dumps(base_col)),
|
|
"id": new_id,
|
|
"머리": heads,
|
|
"경로": heads,
|
|
"꼴": "수",
|
|
"단위": unit or base_col.get("단위") or base_row.get("단위"),
|
|
"일위대가": base_col.get("일위대가") or base_row.get("일위대가"),
|
|
}
|
|
column.pop("본뜬열", None)
|
|
column.pop("자재조합", None) # 옛 자재 조합 칸은 안 옮김
|
|
if origin:
|
|
column["본뜬"] = origin
|
|
tree._place(doc, column) # 같은 앞마디 형제 곁(제목행 병합이 이어지게)
|
|
store.write("table", store.STRUCTURE_TABLE, table["판"], doc) # 빈 세 파일 · 번호 · SS
|
|
for file, kind in PART_KINDS if copy else ():
|
|
got = read_json(copy / file)
|
|
if not isinstance(got, dict):
|
|
continue
|
|
number = store._KEY[kind]
|
|
have = store.read(kind, new_id)["문서"]
|
|
body = {k: v for k, v in got.items() if k != "계산값"}
|
|
body = {**body, "열": new_id, number: have.get(number)}
|
|
if kind in store.SHEETS:
|
|
body = store._server_values(body)
|
|
with store._LOCK:
|
|
atomic_write_json(store._path(kind, new_id), body)
|
|
ss = next(r for r in store.read_combos()["줄"] if r.get("열") == new_id)
|
|
return {"key": ss["키"], "열": new_id, "이름": tree.name_of(heads)}
|
|
|
|
|
|
def personal_item(company: int, user: int, made: dict[str, Any]) -> dict[str, Any]:
|
|
"""새로 만든 개인 층 항목의 `summary_item`."""
|
|
return {
|
|
"layer": "personal",
|
|
"owner": f"{company}/{user}",
|
|
"key": made["key"],
|
|
"name": made["이름"],
|
|
}
|
|
|
|
|
|
def personal_folder(company: int, user: int) -> Path:
|
|
return layers.personal_dir(company, user)
|