- 대응표 master_unit_text.RULES(규칙마다 원문 근거 절) · 빌더 build_단위표기_801_5(--check · --list · 셈 지문 전후 같을 때만 씀) - M01 로직 53 개 · 칸 79 개 글만 바꿈(변수 입력 · 중간 단위 · 결과단위) · 계수 · 소요량 표 값칸 · 호표 줄 단위 · 원문이 두 꼴을 다 쓰는 단위(㎞ · km · 분 · min)는 안 건드림 - M02 사본 새로 씀 도구 M02_Copy_Units(M02_Copy_Symbols 의 fill 만 바꿔 끼움) — 사본 16 문서 28 칸 · 금액 전후 같음 - 검토 목록 docs/raw/요구_1104/_목록_801-5_단위표기.xlsx - 옛 시험 기대 고침: test_796_3 굴착기 cm 단위 초 → sec(원문 표 머리 「싸이클시간(Sec)」) - 새 test_801_5_units 6 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
116 lines
5.6 KiB
Python
116 lines
5.6 KiB
Python
"""옛 로직 사본 변수표 새로 쓰기 — 한 번 돌리는 도구(PLAN 800-5 · 요구 660-1 · 659-1).
|
|
|
|
옛 사본(795-5 이전에 찍힌 것) 줄은 변수표(이름 · 기호 · 아래 · 위)가 비었거나 한글 그대로 →
|
|
수식 영역에 한글 기호가 보임. 지금 M01 줄의 변수표로 그 줄 변수만 채움:
|
|
· 사본 줄에 이미 정한 기호(영문 · 그리스 · 사용자가 고친 것)가 있는 칸 = 그대로
|
|
· 기호가 비었거나 열쇠 그대로(한글)인 칸 = M01 칸(이름 · 기호 · 아래 · 위)으로
|
|
· M01 에 그 줄 · 그 변수가 없으면 건드리지 않음(= 목록 「못닿음」)
|
|
금액 · 셈 = 변수표는 보이기만 → 전후 금액 대조(같을 때만 씀) · 사본 `판`은 줄 지문으로 다시 셈
|
|
(M01 줄과 같아지면 `M01새판` 표지도 걷힘).
|
|
되돌릴 길 = 쓰기 전 파일 그대로 `--backup` 폴더에(`M02_Copy_Migrate --restore` 로 덮음).
|
|
프로젝트 층 문서는 건드리지 않음(굳힌 사본).
|
|
|
|
./venv/Scripts/python.exe -m M02_MasterTemplete.M02_Copy_Symbols # 보기만
|
|
./venv/Scripts/python.exe -m M02_MasterTemplete.M02_Copy_Symbols --apply --backup tmp/800_5
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import shutil
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from common_util.common_util_json import atomic_write_json
|
|
from M02_MasterTemplete import M02_Copy_Migrate as mig
|
|
from M02_MasterTemplete import M02_Sangeun_Copy as skc
|
|
|
|
FIELD = "변수표" # master_equation.FIELD 와 같은 칸
|
|
LOOK = ("이름", "기호", "아래", "위")
|
|
|
|
|
|
def _open(entry: Any, name: str) -> bool:
|
|
"""사본 칸이 채울 자리 — 없음 · 기호 없음 · 열쇠 그대로(한글 기본)."""
|
|
sign = str((entry or {}).get("기호") or "")
|
|
return not sign or sign == name
|
|
|
|
|
|
def fill(copy: dict, whole: Any) -> tuple[dict, int, int]:
|
|
"""(새 사본, 채운 칸 수, 못 닿은 변수 수) — 줄 `변수표`만 고치고 `판` 다시 셈."""
|
|
rows, done, lost = [], 0, 0
|
|
for row in copy["로직"]:
|
|
key = str(row.get("키"))
|
|
live = whole.index.get(key[:2], {}).get(key) or {}
|
|
src = live.get(FIELD) if isinstance(live.get(FIELD), dict) else {}
|
|
mine = dict(row.get(FIELD) or {})
|
|
for part in ("입력", "중간"):
|
|
for spec in row.get(part) or []:
|
|
name = str(spec.get("이름"))
|
|
new = (src or {}).get(name)
|
|
if not _open(mine.get(name), name):
|
|
continue
|
|
if not isinstance(new, dict) or not new.get("기호"):
|
|
lost += 1
|
|
continue
|
|
mine[name] = {**(mine.get(name) or {}), **{k: new[k] for k in LOOK if k in new}}
|
|
done += 1
|
|
rows.append({**row, FIELD: mine} if mine != (row.get(FIELD) or {}) else row)
|
|
return {**copy, "로직": rows, "판": skc._fp(rows)}, done, lost
|
|
|
|
|
|
def check(kind: str, path: Path, fill_fn=None) -> dict[str, Any]:
|
|
"""문서 하나 — {상태: 있음 | 채울것없음 | 같음 | 다름 | 못셈, 채움, 못닿음, 사본?} · `fill_fn` = 다른 고침(801-5 단위 글)."""
|
|
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
|
|
|
doc = json.loads(path.read_bytes())
|
|
if not skc.has(doc):
|
|
return {"상태": "사본없음"}
|
|
try:
|
|
with store.at(path.parent.parent):
|
|
live = skc.whole_()
|
|
new, done, lost = (fill_fn or fill)(doc[skc.KEY], live)
|
|
if not done:
|
|
return {"상태": "채울것없음", "못닿음": lost}
|
|
clean = mig._clean(kind, doc, path.stem)
|
|
before = mig._money(kind, clean, skc.over({skc.KEY: doc[skc.KEY]}, live))
|
|
after = mig._money(kind, clean, skc.over({skc.KEY: new}, live))
|
|
except Exception as e: # noqa: BLE001 — 못 셈 = 목록(통째로 멈추지 않음)
|
|
return {"상태": "못셈", "까닭": str(e) or type(e).__name__}
|
|
row = {"상태": "같음" if before == after else "다름", "채움": done, "못닿음": lost}
|
|
return {**row, "사본": new} if before == after else {**row, "전": before, "후": after}
|
|
|
|
|
|
def run(apply: bool = False, backup: Path | None = None, fill_fn=None) -> list[dict[str, Any]]:
|
|
"""마스터 · 회사 · 개인 층 문서마다 {어디, 종류, 키, 상태, 씀, …} · `apply` = 같음만 씀."""
|
|
if apply and backup is None:
|
|
raise ValueError("--apply 는 --backup 폴더가 있어야 함")
|
|
out = []
|
|
for where_, kind, path, project in mig._files():
|
|
if project:
|
|
continue
|
|
got = check(kind, path, fill_fn)
|
|
copy = got.pop("사본", None)
|
|
row = {"어디": where_, "종류": kind, "키": path.stem, **got, "씀": False}
|
|
if apply and copy:
|
|
keep = mig._backup_path(path, backup)
|
|
keep.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copy2(path, keep)
|
|
atomic_write_json(path, {**json.loads(path.read_bytes()), skc.KEY: copy})
|
|
row["씀"] = True
|
|
out.append(row)
|
|
return out
|
|
|
|
|
|
if __name__ == "__main__": # 브레인 지시 뒤만 --apply
|
|
args = sys.argv[1:]
|
|
at = Path(args[args.index("--backup") + 1]) if "--backup" in args else None
|
|
rows = run("--apply" in args, at)
|
|
for one in rows:
|
|
if one["상태"] not in ("사본없음", "채울것없음", "같음"):
|
|
print(json.dumps(one, ensure_ascii=False, default=str))
|
|
count: dict[str, int] = {}
|
|
for one in rows:
|
|
count[one["상태"]] = count.get(one["상태"], 0) + 1
|
|
print(json.dumps(count, ensure_ascii=False), "채움", sum(r.get("채움", 0) for r in rows))
|