- 새 프로젝트 씨앗(KINDS)에서도 빠짐 · 새 열은 구조물 도면 · 면적산출만 만듦 - storage 층(회사 · 개인 · 프로젝트) material/ 파일은 지우지 않음(읽기만 끊음) - 장부 재료번 칸은 다음 집계표 [저장] 때 빠짐 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NPJGtfXo6GMbcpiboQC4uK
106 lines
4.4 KiB
Python
106 lines
4.4 KiB
Python
"""여러 항목 · 폴더를 층 사이 붙여넣기(PLAN 30-2) — 구조물 라이브러리 나무의 [복사] → [붙여넣기].
|
|
|
|
붙일 폴더(`dest`) 아래 새 항목 — 대상 층 새 키(SC · SP) · 열 `본뜬` = 원본(층 · 주인 · 키) ·
|
|
같은 이름은 「이름 (2)」 · 폴더는 안의 항목 · 빈 폴더까지(폴더 이름이 겹치면 「폴더 (2)」).
|
|
파일(도면 · 면적 · 입력 연결)은 `Items.copy_item` 이 옮김. 권한은 부르는 쪽 몫.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
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
|
|
|
|
|
|
def _under(path: list[str], folder: list[str]) -> bool:
|
|
return path[: len(folder)] == folder
|
|
|
|
|
|
def outer(targets: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
"""고른 것 가운데 다른 고른 폴더 안에 든 것은 뺌(폴더가 안의 것까지 다룸) —
|
|
옮기기 · 지우기 · 붙여넣기."""
|
|
folders = [list(t.get("경로") or []) for t in targets if not t.get("키")]
|
|
|
|
def inside(t: dict[str, Any]) -> bool:
|
|
path = list(t.get("경로") or [])
|
|
return any(_under(path, f) and (t.get("키") or path != f) for f in folders if f)
|
|
|
|
return [t for t in targets if not inside(t)]
|
|
|
|
|
|
def _table(folder: Path) -> dict[str, Any]:
|
|
with store.at(folder):
|
|
return tree.normalize_table(store.read("table", store.STRUCTURE_TABLE)["문서"])
|
|
|
|
|
|
def _items(doc: dict[str, Any]) -> list[dict[str, Any]]:
|
|
return [c for c in doc.get("열") or [] if tree.is_item(c)]
|
|
|
|
|
|
def _unique(path: list[str], taken: list[list[str]]) -> list[str]:
|
|
n, out = 2, path
|
|
while out in taken:
|
|
out, n = [*path[:-1], f"{path[-1]} ({n})"], n + 1
|
|
return out
|
|
|
|
|
|
def paste(
|
|
source: Path,
|
|
target: Path,
|
|
targets: list[dict[str, Any]],
|
|
dest: list[str],
|
|
origin: dict[str, Any],
|
|
) -> dict[str, Any]:
|
|
"""`targets` = `[{경로, 키?}]`(키 = 항목 · 없으면 폴더 경로) → `target` 층 `dest` 아래 ·
|
|
돌려줌 알림 `{op: "paste", 경로, 열: [새 열 id]}`. `origin` = `{layer, owner}`(본뜬)."""
|
|
src = _table(source)
|
|
with store.at(source):
|
|
keys = {str(r.get("열")): str(r.get("키")) for r in store.read_combos().get("줄", [])}
|
|
src_items = [(tree.path_of(c), keys.get(c["id"])) for c in _items(src)]
|
|
src_folders = tree.folders_of(src)
|
|
items_store.ensure(target)
|
|
dst = _table(target)
|
|
taken = [tree.path_of(c) for c in _items(dst)]
|
|
taken_folders = tree.folders_of(dst)
|
|
plan: list[tuple[str, list[str]]] = [] # (원본 키, 새 경로)
|
|
new_folders: list[list[str]] = []
|
|
|
|
def busy(path: list[str]) -> bool:
|
|
paths = [*taken, *(p for _, p in plan)]
|
|
return path in [*taken_folders, *new_folders] or any(_under(p, path) for p in paths)
|
|
|
|
for one in outer(targets):
|
|
if one.get("키"):
|
|
key = str(one["키"])
|
|
found = next((p for p, k in src_items if k == key), None)
|
|
if found is None:
|
|
raise store.StoreError(404, f"없는 항목 「{key}」")
|
|
plan.append((key, _unique([*dest, found[-1]], [*taken, *(p for _, p in plan)])))
|
|
continue
|
|
folder = list(one.get("경로") or [])
|
|
if not folder or (
|
|
folder not in src_folders and not any(_under(p, folder) for p, _ in src_items)
|
|
):
|
|
raise store.StoreError(404, f"없는 폴더 「{' '.join(folder)}」")
|
|
top, n = [*dest, folder[-1]], 2
|
|
while busy(top):
|
|
top, n = [*dest, f"{folder[-1]} ({n})"], n + 1
|
|
new_folders.append(top)
|
|
new_folders += [[*top, *f[len(folder) :]] for f in src_folders if _under(f, folder)]
|
|
plan += [(k, [*top, *p[len(folder) :]]) for p, k in src_items if _under(p, folder) and k]
|
|
|
|
made = [
|
|
items_store.copy_item(source, target, key, {**origin, "key": key}, path)["열"]
|
|
for key, path in plan
|
|
]
|
|
if new_folders: # 빈 폴더도 남게 — 집계표 `폴더`
|
|
with store.at(target):
|
|
got = store.read("table", store.STRUCTURE_TABLE)
|
|
doc = got["문서"]
|
|
doc["폴더"] = [*(doc.get("폴더") or []), *new_folders]
|
|
store.write("table", store.STRUCTURE_TABLE, got["판"], doc)
|
|
return {"op": "paste", "경로": dest, "열": made}
|