"""옛 일위대가 조합 → 구조물 열마다 일위대가 문서 — 한 번 돌리는 도구(PLAN 39-1b · 설계_정함 1장). 층마다 구조물 열마다 `labor/<열id>.json`: - 열 `일위대가` 키가 있으면 그 조합의 담은로직 · 단위 복사(`본뜬.키` = 옛 키 · 입력값 {} · 수량 null · 자재 []) — 여러 열이 쓰던 조합도 열마다 따로 복사 - 키 없는 열 = 빈 일위대가 · 번호 SU 는 저장소가 줌(도번과 같은 뒷자리) - 몇 번 돌려도 같음 — 이미 그 키를 본뜬 문서 · 사람이 채운 문서(담은로직 · 자재가 있음)는 그대로 옛 조합 파일 · 집계표 열 `일위대가` 칸은 손대지 않음(실무문서 · 은퇴는 39-1b B9). ./venv/Scripts/python.exe M02_MasterTemplete/M02_Labor_Migrate.py # 시스템 층 ./venv/Scripts/python.exe M02_MasterTemplete/M02_Labor_Migrate.py --storage # 회사 · 개인 층 [--dry-run] """ from __future__ import annotations import argparse import json import sys from pathlib import Path from typing import Any sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from M02_MasterTemplete import M02_Labor_Doc as labor # noqa: E402 from M02_MasterTemplete import M02_MasterTemplete_Store as store # noqa: E402 from M02_MasterTemplete import M02_Template_Layers as layers # noqa: E402 def _filled(doc: dict) -> bool: return bool(doc.get("담은로직") or doc.get("자재")) def migrate(folder: Path, dry: bool = False) -> dict[str, list[str]]: """`folder` 층 하나 — `{만듦, 옮김, 그대로, 못함}`(열 id · 못함은 까닭 붙임).""" out: dict[str, list[str]] = {"만듦": [], "옮김": [], "그대로": [], "못함": []} with store.at(folder): for col in store._structure_columns(): cid, key = col["id"], str(col.get("일위대가") or "") path = store._path("labor", cid) have = json.loads(path.read_bytes()) if path.is_file() else None done = have is not None and (have.get("본뜬") or {}).get("키") == key if have is not None and (_filled(have) or not key or done): out["그대로"].append(cid) continue if not key: out["만듦"].append(cid) if not dry: store._ensure("labor", cid) continue try: body = labor.from_combo(key) except store.StoreError as e: out["못함"].append(f"{cid} · {e.detail}") continue out["옮김"].append(f"{cid} ← {key}") if dry: continue store._ensure("labor", cid) # 번호(SU · 같은 뒷자리) got = store.read("labor", cid) store.write("labor", cid, got["판"], {**got["문서"], **body}) return out def layer_folders(storage: bool) -> list[Path]: """시스템 층 하나 · 또는 storage 회사 · 개인 층(구조물집계표가 있는 것 · 프로젝트 빼고).""" if not storage: return [layers.system_dir()] root = layers.storage_root() table = Path("table") / f"{store.STRUCTURE_TABLE}.json" found = [ *root.glob(f"*/{layers.TEMPLATES_DIRNAME}"), *root.glob(f"*/*/{layers.TEMPLATES_DIRNAME}"), ] return sorted(p for p in found if (p / table).is_file()) def main() -> None: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--storage", action="store_true", help="회사 · 개인 층(storage)") parser.add_argument("--dry-run", action="store_true") args = parser.parse_args() report: dict[str, Any] = {} for folder in layer_folders(args.storage): report[str(folder)] = migrate(folder, args.dry_run) print(json.dumps(report, ensure_ascii=False, indent=2)) if __name__ == "__main__": main()