Files
Aislo/M02_MasterTemplete/M02_Labor_Migrate.py
T
eomsangdonandClaude Sonnet 5.5 e1b17a523c feat(732-1): M02 실무문서 칸 · 복사 탭 · 서버 길 걷음
- 왼쪽 「실무문서」 칸(Practice.ts) · 일위대가 가져오기 「실무문서」 탭 · 복사 원본 고르기 · 낱 CSS · locale 열쇠 걷음
- 서버 `from_layer=practice`(_copy_practice) · `DELETE /api/m02/practice`(drop_practice) · 조합 목록 · 키 하나 읽기 길 걷음 · 조합 길 시스템 관리자 문 걷음(담을 로직 읽기는 그대로)
- 옛 마스터 조합 파일 `일위대가조합.json`(UA 37) = 쓰는 곳 있음(구조물집계표 일위대가 칸 · labor 본뜬키 · 프로젝트 초기값 사본 · 불러오기 맞춤 씨앗) → 파일 · 읽기 길 유지 · 지우지 않음
- 낡은 시험: test_m02_practice 지움(화면 · 길 없음) · 조합 길 읽던 시험(combo · combo_inputs · combo_layers)은 저장소 함수로 · 실무문서 복사 시험(labor_copy · pair_copy) 줄 지움 · 탭 · 차례 단정(layer_screen · structure_options · structure_panel · 720 · precombo_scope · ui_loading) 고침

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-02 13:17:24 +09:00

91 lines
3.8 KiB
Python

"""옛 일위대가 조합 → 구조물 열마다 일위대가 문서 — 한 번 돌리는 도구(PLAN 39-1b · 설계_정함 1장).
층마다 구조물 열마다 `labor/<열id>.json`:
- 열 `일위대가` 키가 있으면 그 조합의 담은로직 · 단위 복사(`본뜬.키` = 옛 키 · 입력값 {} ·
수량 null · 자재 []) — 여러 열이 쓰던 조합도 열마다 따로 복사
- 키 없는 열 = 빈 일위대가 · 번호 SU 는 저장소가 줌(도번과 같은 뒷자리)
- 몇 번 돌려도 같음 — 이미 그 키를 본뜬 문서 · 사람이 채운 문서(담은로직 · 자재가 있음)는 그대로
옛 조합 파일 · 집계표 열 `일위대가` 칸은 손대지 않음.
./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()