diff --git a/B09_Estimation/B09_Estimation_Bill_Library.py b/B09_Estimation/B09_Estimation_Bill_Library.py index 538f200ff..a53d71108 100644 --- a/B09_Estimation/B09_Estimation_Bill_Library.py +++ b/B09_Estimation/B09_Estimation_Bill_Library.py @@ -340,24 +340,43 @@ def copies(project_root: str | Path) -> list[tuple[Path, dict[str, Any]]]: return out -def used_folders(user: dict[str, Any]) -> set[str]: +def used_folders(user: dict[str, Any], project_root: str | Path | None = None) -> set[str]: """설계내역서 덮개 줄 · 거리이정표 운반 연결(251-5)이 잇는 서고 사본 폴더 + - 일위대가 복사본(703-2 `@copy`)의 원본 사본.""" - copies = [ - str(lines["@copy"].get("from") or "") - for lines in (user.get("daega_cover") or {}).values() + 일위대가 복사본(703-2 `@copy`)의 원본 사본 — 복사본의 복사본(`K:`)은 맨 처음 원본까지 · + 사전조합 원본(`C:참조`) = 그 참조를 담은 사본 폴더(`project_root` 줄 때).""" + covers = user.get("daega_cover") or {} + + def origin(src: str) -> str: + seen: set[str] = set() + while src.startswith("K:") and src not in seen: + seen.add(src) + src = str(((covers.get(src) or {}).get("@copy") or {}).get("from") or "") + return src + + srcs = { + origin(str(lines["@copy"].get("from") or "")) + for lines in covers.values() if isinstance(lines, dict) and isinstance(lines.get("@copy"), dict) - ] - return { - str(ref["lib"]) - for row in ref_rows(user) - if isinstance(ref := row.get("ref"), dict) and ref.get("tab") == "dg" and ref.get("lib") - } | {src[2:] for src in copies if src.startswith("L:")} + } + pre = {src[2:] for src in srcs if src.startswith("C:")} + return ( + { + str(ref["lib"]) + for row in ref_rows(user) + if isinstance(ref := row.get("ref"), dict) and ref.get("tab") == "dg" and ref.get("lib") + } + | {src[2:] for src in srcs if src.startswith("L:")} + | { + folder.name + for folder, manifest in (copies(project_root) if pre and project_root else []) + if refs.ref_id((manifest.get("원본") or {}).get("참조") or {}) in pre + } + ) def prune(project_root: str | Path, user: dict[str, Any]) -> list[str]: """[저장] 뒤 — 줄이 안 잇는 사본 폴더를 지움(다시 고르면 새로 들여옴) · 지운 이름.""" - keep = used_folders(user) + keep = used_folders(user, project_root) base = library_dir(project_root) gone = [] for child in sorted(base.iterdir()) if base.is_dir() else []: diff --git a/B09_Estimation/B09_Estimation_Router.py b/B09_Estimation/B09_Estimation_Router.py index 2e6424a3a..b414a1727 100644 --- a/B09_Estimation/B09_Estimation_Router.py +++ b/B09_Estimation/B09_Estimation_Router.py @@ -210,7 +210,7 @@ async def _demand(root: Path, draft: dict[str, Any] | None = None) -> list[str]: """단가 수요 키 — 연결된 사본 + 설계내역서 손 줄 로직(166-2) + 거리이정표 운반 연결(251-5) · draft = 화면 캐시(296 · 서고 사본 = 그 줄이 잇는 것만).""" user = await asyncio.to_thread(calc_user, root, draft) # 운반 값 사본 먼저(263-2) - keep = None if draft is None else library.used_folders(user) + keep = None if draft is None else library.used_folders(user, root) keys = await asyncio.to_thread(linked_resource_keys, root, keep) hand = await asyncio.to_thread(sangeun.hand_resource_keys, user) hand = [*hand, *added_keys(user.get("daega_cover"))] # 705-2 붙인 줄 이음 diff --git a/resources/tester/test_703_b09_daega_copy.py b/resources/tester/test_703_b09_daega_copy.py index b1f272386..c4930ef5c 100644 --- a/resources/tester/test_703_b09_daega_copy.py +++ b/resources/tester/test_703_b09_daega_copy.py @@ -96,6 +96,28 @@ def test_서고_사본_원본_지킴(): assert library.used_folders(user) == {"combo@system@-@SS1"} +def test_사전조합_원본_복사본_사본_지킴(tmp_path): + """통합 점검 — 사전조합(`C:참조`) 복사본 · 복사본의 복사본 = 원본 사본 폴더 지킴(업데이트 수요 · 저장 정리).""" + base = library.library_dir(tmp_path) + for name, ref in ( + ("precombo@personal@1-3@SC-000001", {"층": "personal", "주인": "1/3", "키": "SC-000001"}), + ("precombo@personal@1-3@SC-000002", {"층": "personal", "주인": "1/3", "키": "SC-000002"}), + ): + (base / name).mkdir(parents=True) + manifest = {"종류": "precombo", "원본": {"참조": ref}} + (base / name / "manifest.json").write_text(json.dumps(manifest), encoding="utf-8") + src = "C:참조|personal|1/3|SC-000001" + user = { + "daega_cover": { + "K:aa": {"@copy": {"from": src, "name": "복사"}}, + "K:bb": {"@copy": {"from": "K:aa", "name": "복사의 복사"}}, + } + } + assert library.used_folders(user) == set() # 폴더 모름 = 옛 꼴 + assert library.used_folders(user, tmp_path) == {"precombo@personal@1-3@SC-000001"} + assert library.prune(tmp_path, user) == ["precombo@personal@1-3@SC-000002"] + + # ── 내 자료집(개인 층 사전조합 · 임시 storage) ─────────────────────────────