knowledge(마스터): 소요량·계수·로직에 구분·상세구분 — 표·줄마다 칸 · 갈래 목록은 서버가 줌

- 장 파일 137 개의 표 3,028 · 로직 1,351 줄에 구분(원문 + 부문 · 「건설품셈 공통」) · 상세구분(「03장 토공사」) 칸을 직접 둠 · 파일 머리의 부문·차례는 목록 차례로 남김
- 열 차례 — 표는 키 · 원문번호 · 구분 · 상세구분 · 이름 · 기준 · 출처 · 비고 · 조건 · 범위규칙 · 값칸 · 줄 · 주 · 그룹(맨 뒤) · 로직은 소유 다음에 비고
- 서버 — GET /subs?group= 이 구분·상세구분 목록(장 차례대로) · GET /tables 가 파일을 가로질러 구분·상세구분·찾기로 거르고 쪽 나눔 · GET /logics 도 구분·상세구분으로 거름 · 새 줄은 서버가 갈래를 채움 · 죽은 book 칸 걷어냄
- check_master 가 구분·상세구분이 파일 머리·이름과 맞는지 봄 · adopt 가 빌더 줄에 갈래를 찍어 되돌리기 시험 글자까지 같음 · _틀.md · 화면 계약 · 화면 트리 · 시험 같이 고침

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 16:30:48 +09:00
co-authored by Claude Opus 5
parent 9de61eb251
commit 10418ccbd4
154 changed files with 8227 additions and 8046 deletions
+42 -2
View File
@@ -65,8 +65,32 @@ _SOURCE = re.compile(
_IDENT = re.compile(r"\d+-\d+(?:-\d+)*")
ROW_KEYS = {
"요소": ("", "원문번호", "이름", "", "출처"),
"": ("", "원문번호", "이름", "기준", "출처", "조건", "값칸", "", ""),
"로직": ("", "원문번호", "이름", "결과단위", "출처", "소유", "입력", "중간", "끝수"),
"": (
"",
"원문번호",
"구분",
"상세구분",
"이름",
"기준",
"출처",
"조건",
"값칸",
"",
"",
),
"로직": (
"",
"원문번호",
"구분",
"상세구분",
"이름",
"결과단위",
"출처",
"소유",
"입력",
"중간",
"끝수",
),
"품셈재료": ("", "원문번호", "이름", "규격", "단위", "요구절", "연결"),
}
# 한 테이블 파일 — 줄의 갈래 칸 · 머리의 갈래 묶음
@@ -124,6 +148,11 @@ COLS = {
COLS["한국은행환율"] = COLS["오피넷유가"]
def _detail(name: str) -> str:
"""장 파일 이름 → 상세구분 「03장 토공사」."""
return name.removesuffix(".json").split("_", 2)[2].replace("_", " ")
def load(names: list[str] | None = None, folder: Path = MASTER) -> dict[str, dict]:
"""첫 층 JSON 모두(밑줄 파일 빼고) — 수는 Decimal. `folder` = 마스터 폴더(M01 시험은 사본)."""
out = {}
@@ -205,6 +234,12 @@ def check_form(name: str, data: dict) -> list[str]:
if not isinstance(rows, list):
return out + [f"{name} · 머리 「{'' if table else ''}」 목록 없음"]
division = data.get("부문") if kind != "요소" else None
# 장 파일 줄의 갈래 — 구분 = 원문 + 부문 · 상세구분 = 「NN장 장 제목」(파일 이름 그대로)
want = (
(" ".join(x for x in (data.get("원문"), division) if x), _detail(name))
if m.group(4)
else None
)
for row in rows:
key, number = str(row.get("")), str(row.get("원문번호"))
where = f"{name} · {key} {row.get('이름') or number}"
@@ -214,6 +249,11 @@ def check_form(name: str, data: dict) -> list[str]:
out.append(f"{where} · {merged}{row.get(merged)}」 이 머리에 없음")
if division and not number.startswith(division + " "):
out.append(f"{where} · 원문번호 앞에 부문 「{division}」 없음")
if want and (row.get("구분"), row.get("상세구분")) != want:
out.append(
f"{where} · 구분·상세구분 「{row.get('구분')} · {row.get('상세구분')}"
f" 이 파일 머리·이름 「{want[0]} · {want[1]}」 과 다름"
)
cols = COLS.get(data.get("원문"))
for need in cols or ROW_KEYS[kind]:
if need not in row: