feat(master_data): 기계 갈래 칸 이름 원문으로 · 틀 문서를 지금 데이터 모양에 맞춤

- `기계.json` — 갈래 칸 「세부분류」 → 「원문」(머리 묶음·줄 654) · 줄의 원문 글 칸은 「값원문」 으로(34 줄) · 줄의 `구분`·`상세구분` 과 이름이 겹치지 않게 함
- `check_master` MERGED 는 `master_keys.MERGED` 에서 받음 — 칸 이름을 한 곳에서만 적음 · 머리 「원문」 이 묶음인 파일도 틀 검사가 돎
- `master_keys.MERGED` · `build_기계_건설품셈.py` · `compare_estx.py` — 새 칸 이름
- `M01 서버` — 갈래 칸을 MERGED 에서 찾음(박은 이름 없앰) · 머리 원문이 묶음이면 `book` 은 빈 값
- `_틀.md` — 기계 원문·구분·값원문 · 자재품목 구분 채움과 묶음 문서 · 소요량·계수 「용도」 칸 · 인력 줄 갈래 칸 바로잡음
- `ref/_묶음_기계.md` 새로 — 구분 11 · 상세구분 174 (654 줄)
- `_갱신절차.md` 기계 절 칸 이름
- 검사 — `check_master` 틀 0 · 로직 0 · 시험 83 통과(test_m01_api 25 · test_masterdata_check 58)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JgUhN55Z3BCYhUJTjwTNfj
This commit is contained in:
2026-09-20 22:24:13 +09:00
co-authored by Claude Opus 5
parent 6993c6b95e
commit a6f12fb07f
10 changed files with 784 additions and 719 deletions
+11 -6
View File
@@ -70,6 +70,11 @@ def _hit(row: dict, q: str) -> bool:
)
def _book(data: dict) -> str | None:
"""머리 원문 이름 — 한 테이블 파일(기계)은 머리 「원문」 이 갈래 묶음이라 이름이 묶음 안에."""
return data.get("원문") if isinstance(data.get("원문"), str) else None
def groups() -> list[dict]:
out = {g: {"group": g, "files": 0, "rows": 0} for g in cm.mf.GROUPS}
for data in cm.load(folder=FOLDER).values():
@@ -89,7 +94,7 @@ def files_of(group: str) -> list[dict]:
out.append(
{
"file": path.name,
"book": data.get("원문"),
"book": _book(data),
"chapter": chapter_of(path.name, data),
"edition": data.get(""),
"rows": len(data.get(items_key(data)) or []),
@@ -130,7 +135,7 @@ _STATES = ("공표", "산정", "미공표", "미확보", "추정") # 인력 「
def subs(file: str = "", group: str = "") -> dict:
"""하위 거름 목록 — 한 테이블 파일은 머리 묶음(인력 「구분」 · 기계 「세부분류」) ·
"""하위 거름 목록 — 한 테이블 파일은 머리 묶음(인력 「구분」 · 기계 「원문」) ·
장 그룹(소요량 · 계수 · 로직)은 장 파일에서 모음(구분 = 원문 + 부문 · 상세구분 = 장 · 차례대로)."""
if group:
if group not in cm.mf.GROUPS:
@@ -146,7 +151,7 @@ def subs(file: str = "", group: str = "") -> dict:
slot["details"].append(mk.detail_of(info["file"]))
return {"file": "", "version": "", "slot": "구분", "subs": list(found.values())}
data, version = read(file)
slot = next((s for s in ("구분", "세부분류") if isinstance(data.get(s), dict)), "")
slot = next((s for s in cm.MERGED.values() if isinstance(data.get(s), dict)), "")
lines = data.get(items_key(data)) or []
has_state = any("상태" in r for r in lines)
@@ -184,8 +189,8 @@ def rows(
) -> dict:
data, version = read(file)
hits = [r for r in data.get(items_key(data)) or [] if _hit(r, q)]
if sub: # 인력 구분 · 기계 세부분류
hits = [r for r in hits if sub in (r.get("구분"), r.get("세부분류"))]
if sub: # 인력 구분 · 기계 원문
hits = [r for r in hits if sub in {r.get(s) for s in cm.MERGED.values()}]
if detail: # 인력 상세구분
hits = [r for r in hits if r.get("상세구분") == detail]
if state: # 인력 상태
@@ -527,7 +532,7 @@ def _follow(row: dict, items: list[dict], file: str) -> dict:
def _apply(data: dict, changes: list[dict], file: str, book: dict) -> None:
"""고침 · 더함(키는 대장의 다음 번호 · 원문번호 없으면 빈 글) · 지움 — 키는 바꾸지 않음."""
items = data.setdefault(items_key(data), [])
tid = mk.table_id(data.get("그룹"), data.get("원문"))
tid = mk.table_id(data.get("그룹"), _book(data))
for ch in changes:
op, key, row = ch.get("op"), ch.get("key"), _dec(ch.get("row"))
if op not in ("edit", "add", "delete"):