diff --git a/resources/data_master_labels/labels_2026-01-01.json b/resources/data_master_labels/labels_2026-01-01.json index 561f62cc1..1a560ad02 100644 --- a/resources/data_master_labels/labels_2026-01-01.json +++ b/resources/data_master_labels/labels_2026-01-01.json @@ -1931,13 +1931,13 @@ "counts": { "merged_tables": 17, "merged_columns": 172, - "files": 34, - "tables": 92, - "columns": 471, - "value_groups": 177, + "files": 41, + "tables": 100, + "columns": 532, + "value_groups": 202, "unknown": 0, "value_groups_by_kind": { - "doc": 111, + "doc": 136, "value": 66 } }, @@ -7038,6 +7038,675 @@ "kind": "doc" } ] + }, + { + "file_id": "work_item_master_const", + "path": "resources/data_work_item_master/const_work_item_master_2026-01-01.json", + "name_ko": "건설 공종 마스터", + "summary": "건설공사 표준품셈 목차를 나무로 세운 벌. 줄의 열쇠는 `work_item_key`(CW-…)이고 목차 코드(CP-…)·번호·판은 칸이다.", + "kind": "logic", + "tables": [ + { + "key": "work_items", + "name_ko": "공종", + "summary": "건설 공종 나무. 부문 다섯이 뿌리이고, 부문마다 장 번호가 1부터 다시 시작한다.", + "shape": "list", + "rows": 1367, + "columns": [ + { + "key": "work_item_code", + "name_ko": "공종코드", + "unit": "", + "visible": true + }, + { + "key": "number", + "name_ko": "품셈 번호", + "unit": "", + "visible": true + }, + { + "key": "name", + "name_ko": "이름", + "unit": "", + "visible": true + }, + { + "key": "division", + "name_ko": "부문", + "unit": "", + "visible": true + }, + { + "key": "level", + "name_ko": "단계", + "unit": "", + "visible": true + }, + { + "key": "parent_code", + "name_ko": "상위 공종코드", + "unit": "", + "visible": true + }, + { + "key": "sort_order", + "name_ko": "정렬 차례", + "unit": "", + "visible": false + }, + { + "key": "tables", + "name_ko": "딸린 품셈 표", + "unit": "", + "visible": true + }, + { + "key": "parent_mode", + "name_ko": "하위 고르는 방식", + "unit": "", + "visible": true + }, + { + "key": "variant_keys", + "name_ko": "갈래 키", + "unit": "", + "visible": true + }, + { + "key": "work_item_key", + "name_ko": "공종 열쇠", + "unit": "", + "visible": true + }, + { + "key": "toc_code", + "name_ko": "그 판 목차 코드", + "unit": "", + "visible": true + }, + { + "key": "toc_number", + "name_ko": "그 판 목차 번호", + "unit": "", + "visible": true + }, + { + "key": "toc_edition", + "name_ko": "목차 판(고시)", + "unit": "", + "visible": false + }, + { + "key": "path_name", + "name_ko": "전체 경로 이름", + "unit": "", + "visible": true + }, + { + "key": "axis_role", + "name_ko": "줄 구실", + "unit": "", + "visible": true + } + ] + } + ], + "value_groups": [ + { + "key": "toc_edition", + "name_ko": "목차 판(고시)", + "summary": "목차 번호가 묶인 고시 판. 값이 아니라 **판을 가리키는 표**다.", + "kind": "doc" + }, + { + "key": "policy", + "name_ko": "방침", + "summary": "이 벌을 다룰 때 지킬 것 — 지어내지 않기·빈칸 안 두기 같은 것.", + "kind": "doc" + }, + { + "key": "stats", + "name_ko": "집계", + "summary": "몇 줄이 섰고 몇 줄이 빠졌는지 센 것.", + "kind": "doc" + }, + { + "key": "toc_corrections", + "name_ko": "목차 오기 바로잡음", + "summary": "목차 번호가 본문과 다른 자리를 본문 번호로 고친 기록.", + "kind": "doc" + }, + { + "key": "toc_duplicate_rows", + "name_ko": "목차 번호가 겹친 줄", + "summary": "목차가 같은 번호를 두 번 적었는데 본문 번호로도 못 가른 자리. 비어 있어야 정상 — 줄이 생기면 표가 딴 줄에 붙는다.", + "kind": "doc" + }, + { + "key": "orphan_tables", + "name_ko": "공종에 못 붙인 표", + "summary": "목차 어디에도 못 붙인 품셈 표.", + "kind": "doc" + } + ] + }, + { + "file_id": "const_form_undetermined", + "path": "resources/data_work_item_master/const_form_undetermined_2026-01-01.json", + "name_ko": "건설 형태 미판정 표", + "summary": "생산량형인지 소요량형인지 못 정한 건설 표. 사람이 보고 확정할 자리다.", + "kind": "byproduct", + "tables": [ + { + "key": "items", + "name_ko": "형태 미판정 표", + "summary": "생산량형인지 소요량형인지 못 정한 표.", + "shape": "list", + "rows": 881, + "columns": [ + { + "key": "pum_table_id", + "name_ko": "품셈 표 번호", + "unit": "", + "visible": true + }, + { + "key": "section", + "name_ko": "품셈 절", + "unit": "", + "visible": true + }, + { + "key": "headers", + "name_ko": "표 머리", + "unit": "", + "visible": true + }, + { + "key": "first_rows", + "name_ko": "첫 줄들", + "unit": "", + "visible": true + }, + { + "key": "reason", + "name_ko": "사유", + "unit": "", + "visible": true + } + ] + } + ], + "value_groups": [ + { + "key": "note", + "name_ko": "설명", + "summary": "이 벌이 무엇이고 어떻게 쓰는지 적은 글.", + "kind": "doc" + } + ] + }, + { + "file_id": "const_basis_missing", + "path": "resources/data_work_item_master/const_basis_missing_2026-01-01.json", + "name_ko": "건설 밑수 미확보 표", + "summary": "「10㎡당」 같은 기준 수량을 못 찾은 건설 표. ⚠ 1 단위당으로 단정하면 곱셈이 10배·100배 틀린다.", + "kind": "byproduct", + "tables": [ + { + "key": "items", + "name_ko": "밑수 미확보 표", + "summary": "기준 수량을 못 찾은 표.", + "shape": "list", + "rows": 447, + "columns": [ + { + "key": "pum_table_id", + "name_ko": "품셈 표 번호", + "unit": "", + "visible": true + }, + { + "key": "section", + "name_ko": "품셈 절", + "unit": "", + "visible": true + }, + { + "key": "pum_form", + "name_ko": "품셈 표 형태", + "unit": "", + "visible": true + }, + { + "key": "line", + "name_ko": "원문 줄 번호", + "unit": "", + "visible": false + } + ] + } + ], + "value_groups": [ + { + "key": "note", + "name_ko": "설명", + "summary": "이 벌이 무엇이고 어떻게 쓰는지 적은 글.", + "kind": "doc" + } + ] + }, + { + "file_id": "work_item_keys", + "path": "resources/data_work_item_master/work_item_keys.json", + "name_ko": "산림 공종 열쇠 장부", + "summary": "산림 공종의 불변 열쇠(FW-…) 장부. 판이 바뀌어도 이 열쇠는 그 공종 것이다.", + "kind": "logic", + "tables": [ + { + "key": "keys", + "name_ko": "열쇠 발급 내역", + "summary": "열쇠마다 처음 낸 판·날짜·목차 코드·이름.", + "shape": "map", + "rows": 477, + "columns": [ + { + "key": "@key", + "name_ko": "산림 공종 열쇠", + "unit": "", + "visible": true, + "is_row_key": true + }, + { + "key": "issued_edition", + "name_ko": "처음 낸 판", + "unit": "", + "visible": true + }, + { + "key": "issued_on", + "name_ko": "처음 낸 날", + "unit": "", + "visible": true + }, + { + "key": "first_toc_code", + "name_ko": "처음 목차 코드", + "unit": "", + "visible": true + }, + { + "key": "first_toc_number", + "name_ko": "처음 목차 번호", + "unit": "", + "visible": true + }, + { + "key": "first_name", + "name_ko": "처음 이름", + "unit": "", + "visible": true + }, + { + "key": "relinked", + "name_ko": "손으로 이은 자리", + "unit": "", + "visible": true + } + ] + } + ], + "value_groups": [ + { + "key": "note", + "name_ko": "메모", + "summary": "이 벌이 무엇인지 적어 둔 글.", + "kind": "doc" + }, + { + "key": "policy", + "name_ko": "방침", + "summary": "이 벌을 다룰 때 지킬 것.", + "kind": "doc" + }, + { + "key": "key_prefix", + "name_ko": "열쇠 앞꼬리", + "summary": "산림 FW · 건설 CW.", + "kind": "doc" + }, + { + "key": "key_digits", + "name_ko": "열쇠 자릿수", + "summary": "다섯 자리.", + "kind": "doc" + }, + { + "key": "next_serial", + "name_ko": "다음 일련번호", + "summary": "다음에 낼 열쇠 번호.", + "kind": "doc" + }, + { + "key": "editions", + "name_ko": "판별 자리→열쇠", + "summary": "고시 판마다 목차 자리와 열쇠를 이은 사전. 낱값이 아니라 이음표다.", + "kind": "doc" + } + ] + }, + { + "file_id": "work_item_keys_const", + "path": "resources/data_work_item_master/work_item_keys_const.json", + "name_ko": "건설 공종 열쇠 장부", + "summary": "건설 공종의 불변 열쇠(CW-…) 장부. 산림 장부와 일련번호를 섞지 않는다.", + "kind": "logic", + "tables": [ + { + "key": "keys", + "name_ko": "열쇠 발급 내역", + "summary": "열쇠마다 처음 낸 판·날짜·목차 코드·이름.", + "shape": "map", + "rows": 1367, + "columns": [ + { + "key": "@key", + "name_ko": "건설 공종 열쇠", + "unit": "", + "visible": true, + "is_row_key": true + }, + { + "key": "issued_edition", + "name_ko": "처음 낸 판", + "unit": "", + "visible": true + }, + { + "key": "issued_on", + "name_ko": "처음 낸 날", + "unit": "", + "visible": true + }, + { + "key": "first_toc_code", + "name_ko": "처음 목차 코드", + "unit": "", + "visible": true + }, + { + "key": "first_toc_number", + "name_ko": "처음 목차 번호", + "unit": "", + "visible": true + }, + { + "key": "first_name", + "name_ko": "처음 이름", + "unit": "", + "visible": true + } + ] + } + ], + "value_groups": [ + { + "key": "note", + "name_ko": "메모", + "summary": "이 벌이 무엇인지 적어 둔 글.", + "kind": "doc" + }, + { + "key": "policy", + "name_ko": "방침", + "summary": "이 벌을 다룰 때 지킬 것.", + "kind": "doc" + }, + { + "key": "key_prefix", + "name_ko": "열쇠 앞꼬리", + "summary": "산림 FW · 건설 CW.", + "kind": "doc" + }, + { + "key": "key_digits", + "name_ko": "열쇠 자릿수", + "summary": "다섯 자리.", + "kind": "doc" + }, + { + "key": "next_serial", + "name_ko": "다음 일련번호", + "summary": "다음에 낼 열쇠 번호.", + "kind": "doc" + }, + { + "key": "editions", + "name_ko": "판별 자리→열쇠", + "summary": "고시 판마다 목차 자리와 열쇠를 이은 사전. 낱값이 아니라 이음표다.", + "kind": "doc" + } + ] + }, + { + "file_id": "work_item_axis_policy", + "path": "resources/data_work_item_master/axis_policy.json", + "name_ko": "공종 축 줄 구실 잣대", + "summary": "어느 줄이 공종이고 어느 줄이 총칙·묶는 마디인지 가르는 잣대. ⚠ 지우는 잣대가 아니라 **가르는** 잣대다.", + "kind": "logic", + "tables": [ + { + "key": "roles", + "name_ko": "줄 구실 넷", + "summary": "공종·묶는 마디·총칙·빈 잎 — 각각 무엇이고 남기는지.", + "shape": "map", + "rows": 4, + "columns": [ + { + "key": "@key", + "name_ko": "구실 이름", + "unit": "", + "visible": true, + "is_row_key": true + }, + { + "key": "name_ko", + "name_ko": "한글 이름", + "unit": "", + "visible": true + }, + { + "key": "rule", + "name_ko": "가르는 규칙", + "unit": "", + "visible": true + }, + { + "key": "keep", + "name_ko": "남기나", + "unit": "", + "visible": true + }, + { + "key": "why", + "name_ko": "까닭", + "unit": "", + "visible": true + } + ] + }, + { + "key": "axes", + "name_ko": "갈래별 잣대", + "summary": "산림·건설 각각의 코드 꼴·열쇠 앞꼬리·총칙 뿌리.", + "shape": "map", + "rows": 2, + "columns": [ + { + "key": "@key", + "name_ko": "갈래 이름", + "unit": "", + "visible": true, + "is_row_key": true + }, + { + "key": "name_ko", + "name_ko": "한글 이름", + "unit": "", + "visible": true + }, + { + "key": "settled", + "name_ko": "확정됐나", + "unit": "", + "visible": true + }, + { + "key": "source", + "name_ko": "출처", + "unit": "", + "visible": true + }, + { + "key": "toc_source", + "name_ko": "계층 근거", + "unit": "", + "visible": true + }, + { + "key": "code_prefix", + "name_ko": "목차 코드 앞꼬리", + "unit": "", + "visible": true + }, + { + "key": "key_prefix", + "name_ko": "열쇠 앞꼬리", + "unit": "", + "visible": true + }, + { + "key": "general_provision_roots", + "name_ko": "총칙 뿌리", + "unit": "", + "visible": true + }, + { + "key": "general_provision_basis", + "name_ko": "총칙 뿌리 근거", + "unit": "", + "visible": true + }, + { + "key": "code_shape", + "name_ko": "목차 코드 꼴", + "unit": "", + "visible": true + }, + { + "key": "code_shape_why", + "name_ko": "코드 꼴 까닭", + "unit": "", + "visible": true + }, + { + "key": "general_provision_pending", + "name_ko": "총칙 미확정", + "unit": "", + "visible": true + }, + { + "key": "known_defects", + "name_ko": "아는 흠", + "unit": "", + "visible": true + }, + { + "key": "roots_are", + "name_ko": "뿌리를 적는 꼴", + "unit": "", + "visible": true + }, + { + "key": "counts", + "name_ko": "줄 구실 셈", + "unit": "", + "visible": true + } + ] + } + ], + "value_groups": [ + { + "key": "note", + "name_ko": "메모", + "summary": "이 벌이 무엇인지 적어 둔 글.", + "kind": "doc" + }, + { + "key": "policy", + "name_ko": "방침", + "summary": "이 벌을 다룰 때 지킬 것.", + "kind": "doc" + }, + { + "key": "decision_order", + "name_ko": "가르는 차례", + "summary": "총칙 → 공종 → 묶는 마디 → 빈 잎 차례로 본다.", + "kind": "doc" + } + ] + }, + { + "file_id": "manifest_work_item_master_const", + "path": "resources/data_work_item_master/_manifest_const.json", + "name_ko": "건설 공종 축 폴더 지문", + "summary": "건설 공종 축을 어느 판·어느 원문으로 지었는지 못박는 표.", + "kind": "fingerprint", + "tables": [ + { + "key": "files", + "name_ko": "실린 파일", + "summary": "이 폴더에 든 파일과 지문.", + "shape": "list", + "rows": 3, + "columns": [ + { + "key": "file", + "name_ko": "파일 이름", + "unit": "", + "visible": true + }, + { + "key": "sha256", + "name_ko": "파일 지문(SHA-256)", + "unit": "", + "visible": false + }, + { + "key": "size_bytes", + "name_ko": "파일 크기", + "unit": "byte", + "visible": false + } + ] + } + ], + "value_groups": [ + { + "key": "built_by", + "name_ko": "만든 자리", + "summary": "이 벌을 지은 코드나 사람.", + "kind": "doc" + }, + { + "key": "source", + "name_ko": "출처", + "summary": "어느 원문에서 왔는지.", + "kind": "doc" + } + ] } ], "columns": { diff --git a/resources/data_work_item_master/axis_policy.json b/resources/data_work_item_master/axis_policy.json index a31d3d829..96a9f4b07 100644 --- a/resources/data_work_item_master/axis_policy.json +++ b/resources/data_work_item_master/axis_policy.json @@ -53,7 +53,7 @@ "FP-02" ], "general_provision_basis": "FP-01 적용기준 51줄 · FP-02 소요재료 및 기계손료 27줄. ⚠ 지우면 안 됨 — `B09_Estimation_Consumables.py` 가 FP-02-01-01·FP-02-01-08(연료 F0042·F0043·F0058)과 FP-02-02-01/02/04/05/06(손료계수 F0064~F0069)을 읽는다.", - "counts_2025_82": { + "counts": { "rows": 477, "work_item": 304, "group": 82, @@ -96,15 +96,15 @@ "밑수 미확보 447 — 곱하면 안 되는 줄이라 받는 쪽이 가려야 함", "갈래 키 비어 있음 — 랩탑 메인이 뒤로 미룸" ], - "counts_2026": { + "roots_are": "**장 파일 이름**(「01_공통부문/제1장_적용기준.md」). 뽑는 코드가 목차 코드(`CP-01-01`)로 바꿔 준다 — 관리자가 부문 번호를 셀 필요가 없다.", + "counts": { "rows": 1367, "work_item": 997, "group": 270, "general_provision": 0, "empty": 100, "note": "총칙 표시 전 셈(랩탑 메인 첫 산출). 뿌리를 붙이면 `general_provision` 으로 옮겨 간다 — 줄이 줄지는 않는다." - }, - "roots_are": "**장 파일 이름**(「01_공통부문/제1장_적용기준.md」). 뽑는 코드가 목차 코드(`CP-01-01`)로 바꿔 준다 — 관리자가 부문 번호를 셀 필요가 없다." + } } } } diff --git a/resources/tester/test_master_labels_cover.py b/resources/tester/test_master_labels_cover.py index efcfa4489..d8f95f193 100644 --- a/resources/tester/test_master_labels_cover.py +++ b/resources/tester/test_master_labels_cover.py @@ -11,6 +11,7 @@ from __future__ import annotations import json +import re from pathlib import Path import pytest @@ -21,7 +22,13 @@ LABELS_PATH = ROOT / "resources" / "data_master_labels" / "labels_2026-01-01.jso #: 브레인 갈래 나눔표(2026-09-15). 폴더 장부(_manifest)는 그 31 밖이라 따로 센다. #: 2026-09-16 사용자 판정 — `structure_unit_observed`(울진소광 한 현장 관찰값)와 #: `revetment_sabang`(교본값에 설명 글 섞임)은 기초데이터가 아니라 **참고 사례**다. -BRAIN_KIND_COUNTS = {"logic": 15, "base_value": 9, "byproduct": 4, "seed": 1, "reference": 2} +BRAIN_KIND_COUNTS = { + "logic": 19, + "base_value": 9, + "byproduct": 6, + "reference": 2, + "seed": 1, +} MANIFEST_PREFIX = "manifest_" META_KEYS = { @@ -130,7 +137,9 @@ def test_갈래_나눔이_브레인_표와_같다(labels): continue counted[entry["kind"]] = counted.get(entry["kind"], 0) + 1 assert counted == BRAIN_KIND_COUNTS - assert sum(counted.values()) == 31 + # 2026-09-17 31 → 37 — 공종 축 산출 일곱을 이름표에 올림(건설 마스터·미판정·밑수 · 열쇠 장부 둘 · + # 구실 잣대 · 건설 폴더 지문). 지문은 manifest 라 이 셈에서 빠진다. + assert sum(counted.values()) == 37 def test_갈래_이름이_다_풀려_있다(labels): @@ -192,6 +201,31 @@ def test_표_이름이_비지_않았다(labels): assert not blank, f"한글 이름이 빈 표: {blank}" +def test_이름이_영문으로_떨어진_칸이_없다(labels): + """⚠ 빈칸이 아니라 **영문 키가 그대로 이름 자리에 앉은** 것을 잡는다(2026-09-17 데스크탑 서브). + + 새 파일을 이름표에 올릴 때 이름을 못 지으면 키를 그대로 베끼기 쉬운데, 그러면 화면에 + `general_provision_roots` 같은 영문이 뜬다. 한글이 한 글자도 없으면 이름을 안 지은 것이다. + """ + hangul = re.compile(r"[가-힣]") + bad = [] + for entry in labels["files"]: + for table in entry["tables"]: + for column in table["columns"]: + if not hangul.search(column["name_ko"]): + bad.append(f"{entry['file_id']}::{table['key']}/{column['key']}") + if not hangul.search(table["name_ko"]): + bad.append(f"{entry['file_id']}::{table['key']}") + for group in entry["value_groups"]: + if not hangul.search(group["name_ko"]): + bad.append(f"{entry['file_id']}::{group['key']}") + for merged in labels["merged_tables"]: + for column in merged["columns"]: + if not hangul.search(column["name_ko"]): + bad.append(f"{merged['key']}/{column['key']}") + assert not bad, f"영문으로 뜨는 칸: {bad}" + + def test_값_묶음_이름이_비지_않았다(labels): blank = [ f"{entry['file_id']}::{group['key']}" diff --git a/resources/tester/test_z01_master_data.py b/resources/tester/test_z01_master_data.py index a23ef2fe8..2a68430a8 100644 --- a/resources/tester/test_z01_master_data.py +++ b/resources/tester/test_z01_master_data.py @@ -50,12 +50,13 @@ def test_갈래는_이름표_차례_파일_34_강우_캐시는_없음(client: Te assert [g["key"] for g in groups] == [k for k in LABELS["kinds"]] counts = {g["key"]: len(g["files"]) for g in groups} # 2026-09-16 사용자 판정 — 한 현장 관찰값·교본 설명 섞임 둘은 참고 사례로 옮겼다. + # 2026-09-17 공종 축 산출 일곱을 올림 — logic 15 → 19 · byproduct 4 → 6 · fingerprint 3 → 4 assert counts == { - "logic": 15, + "logic": 19, "base_value": 9, - "byproduct": 4, + "byproduct": 6, "reference": 2, - "fingerprint": 3, + "fingerprint": 4, "seed": 1, } assert {g["key"]: g["label"] for g in groups}["base_value"] == "기초값" @@ -83,8 +84,8 @@ def test_트리_표_id_와_이름표_표_id_는_양쪽으로_같음(client: Test label_only += [f"{entry['file_id']}::{i}" for i in sorted(label_ids - tree_ids)] assert not_labelled == [], not_labelled assert label_only == [], label_only - # 2026-09-17 91 → 92 — 공종 마스터가 `toc_duplicate_rows`(목차 번호가 겹친 줄) 를 새로 냄. - assert sum(len(t) for t in files.values()) == LABELS["counts"]["tables"] == 92 + # 2026-09-17 92 → 100 — 공종 축 산출 일곱을 이름표에 올림. + assert sum(len(t) for t in files.values()) == LABELS["counts"]["tables"] == 100 for entry in LABELS["files"]: # 이름이 실제로 붙음 · 줄 수도 이름표 셈과 같음 named = {t["key"]: t for t in entry["tables"]} for t in files[entry["file_id"]]: