- 모든 요소·표·로직 줄의 열쇠 → 키(LB000123 꼴) · 옛 열쇠·원문 번호는 원문번호 칸 · 대장 _키대장.json (키 36,840 · 다음 번호)
- 변수 적는 법 키로 — 로직 식·연결·준용·통합·후보·조달 연결 일괄 변환 · {이름} 낀 참조는 ID:원문번호
- 인력 8 파일 → 인력.json(줄마다 조사 · 머리 조사 묶음) · 기계 2 파일 → 기계.json(세부분류)
- 소요량·계수·로직 파일 이름 = 그룹_원문_NN장_장 제목 · 머리 부문·차례
- 엔진(값 찾기 · 알림에 키 옆 이름) · check_master(키 모양·겹침·대장·끊긴 키 · 기계 요소 줄도 본문 결손 대조) · M01 서버(새 줄 키는 대장 다음 번호 · 키 못 고침) · 화면 칸 이름만 맞춤
- 로직 일괄 시험 계산 1,351 줄 — 옮기기 전후 줄마다 결과·금액 같음
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JgUhN55Z3BCYhUJTjwTNfj
100 lines
3.5 KiB
Python
100 lines
3.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""마스터 키 — 테이블ID(영문 대문자 2) + 6자리 일련번호 · 대장 `_키대장.json` (`_틀.md` 2장).
|
|
|
|
한 번 준 키는 안 바뀜 · 새 줄은 대장의 다음 번호. 원문이 준 번호·옛 열쇠는 줄의 「원문번호」.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
from pathlib import Path
|
|
|
|
MASTER = Path(__file__).resolve().parent.parent
|
|
BOOK = MASTER / "_키대장.json"
|
|
KEY = re.compile(r"[A-Z]{2}\d{6}")
|
|
|
|
# (그룹, 원문) → 테이블ID · 인력·기계는 원문 구분 없이 한 테이블
|
|
TABLES = {
|
|
("인력", None): "LB",
|
|
("재료", "시중물가"): "MT",
|
|
("재료", "나라장터자재"): "MN",
|
|
("재료", "오피넷유가"): "MO",
|
|
("재료", "품셈재료"): "MP",
|
|
("기계", None): "EQ",
|
|
("소요량", "산림품셈"): "QF",
|
|
("소요량", "건설품셈"): "QC",
|
|
("계수", "산림품셈"): "CF",
|
|
("계수", "건설품셈"): "CC",
|
|
("환율", "한국은행환율"): "FX",
|
|
("요율", "조달청제비율"): "RT",
|
|
("로직", "산림품셈"): "GF",
|
|
("로직", "건설품셈"): "GC",
|
|
("로직", "자체"): "GX",
|
|
}
|
|
GROUP_OF = {tid: group for (group, _), tid in TABLES.items()}
|
|
IDS = tuple(GROUP_OF)
|
|
|
|
|
|
def table_id(group: str, book: str | None) -> str:
|
|
return TABLES.get((group, None)) or TABLES[(group, book)]
|
|
|
|
|
|
def id_of(ref: str) -> str:
|
|
"""키·「ID:원문번호」 의 테이블ID — 모르면 ""."""
|
|
return ref[:2] if ref[:2] in GROUP_OF else ""
|
|
|
|
|
|
def load_book(path: Path = BOOK) -> dict:
|
|
if path.is_file():
|
|
return json.loads(path.read_text(encoding="utf-8"))
|
|
return {"다음": {}, "키": {}}
|
|
|
|
|
|
def save_book(book: dict, path: Path = BOOK) -> None:
|
|
"""키 한 줄씩 — 머리 「다음」 뒤에 키 차례대로."""
|
|
lines = ["{", ' "다음": ' + json.dumps(book["다음"], ensure_ascii=False) + ",", ' "키": {']
|
|
items = sorted(book["키"].items())
|
|
for i, (key, v) in enumerate(items):
|
|
comma = "," if i < len(items) - 1 else ""
|
|
lines.append(f" {json.dumps(key)}: {json.dumps(v, ensure_ascii=False)}{comma}")
|
|
lines += [" }", "}"]
|
|
path.write_text("\n".join(lines) + "\n", encoding="utf-8", newline="\n")
|
|
|
|
|
|
def issue(book: dict, tid: str, number: str, file: str) -> str:
|
|
"""(테이블ID, 원문번호) 의 키 — 대장에 있으면 그 키 · 없으면(원문번호 빈 새 줄 포함) 다음 번호를 줌."""
|
|
index = book.setdefault("_찾기", {})
|
|
if not index:
|
|
for key, v in book["키"].items():
|
|
index[(key[:2], v["원문번호"])] = key
|
|
key = index.get((tid, number)) if number else None
|
|
if key:
|
|
book["키"][key]["파일"] = file
|
|
return key
|
|
n = book["다음"].get(tid, 1)
|
|
key = f"{tid}{n:06d}"
|
|
book["다음"][tid] = n + 1
|
|
book["키"][key] = {"원문번호": number, "파일": file}
|
|
index[(tid, number)] = key
|
|
return key
|
|
|
|
|
|
def finish(book: dict) -> dict:
|
|
book.pop("_찾기", None)
|
|
return book
|
|
|
|
|
|
def stamp(
|
|
book: dict, tid: str, rows: list[dict], file: str, extra: dict | None = None
|
|
) -> list[dict]:
|
|
"""빌더가 만든 줄(「열쇠」 = 원문번호) → 「키」 · 「원문번호」 · extra 칸을 앞에 둔 줄."""
|
|
out = []
|
|
for row in rows:
|
|
row = dict(row)
|
|
number = str(row.pop("열쇠", row.get("원문번호")))
|
|
row.pop("원문번호", None)
|
|
key = row.pop("키", None) or issue(book, tid, number, file)
|
|
out.append({"키": key, "원문번호": number, **(extra or {}), **row})
|
|
return out
|