feat(b08): 건설 갈래 펴기 — 산림과 같은 B09 표 읽기 한 벌로 갈래를 가름(111 공종 · 갈래 407) · 갈래 불변 열쇠는 장부(CW-00063#01 · 데스크탑 서브 꼴 그대로) · Z01 건설 공종 997 → 줄 1,293 · 이름을 다듬어도(자간·물결표) 새 번호 0 시험 · 건설 열쇠 장부 갈래 표 이름표(브레인 ④)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
This commit is contained in:
2026-09-17 18:34:17 +09:00
co-authored by Claude Opus 5
parent a371ee0454
commit 7a1f6a01ef
10 changed files with 4571 additions and 356 deletions
@@ -13,7 +13,7 @@
장 파일 경계가 쪽 기준이라(원문 머리 경고) 앞뒤 장 제목도 받고, 파일 첫머리 표는 앞 장 파일 꼬리까지 봄.
⚠ 못 붙인 표는 조용히 버리지 않고 `orphan_tables` 목록.
⚠ 총칙·묶는 마디 덜어내기 안 함(가름만) — 총칙 뿌리는 `axis_policy.json` 건설 축(데스크탑 서브 잣대) · 미확정 둘은 안 넣음.
갈래 키 안 붙임 — B09 표 읽기가 산림 표로만 검증됨(빈칸 = 못 가름).
갈래 = 산림과 같은 B09 표 읽기로 가름(못 가른 공종은 빈칸) · 갈래 불변 열쇠는 장부(`CW-00001#01`).
"""
from __future__ import annotations
@@ -284,8 +284,11 @@ def build_const() -> tuple:
node["tables"].append(entry)
attached += 1
apply_parent_modes(nodes, {}) # 합산형은 사람이 적은 것만 — 건설은 아직 없음(전부 choose_one)
for node in nodes:
node["variant_keys"] = [] # 못 가름 — B09 표 읽기가 산림 표로만 검증됨
# 갈래 — 산림과 **같은 표 읽기 한 벌**(B09 `build_resource_axis`)로 가름 · 못 가른 공종은 빈칸.
# 갈래 불변 열쇠(`CW-00001#01`)는 `finish` 가 장부로 줌(데스크탑 서브 · 2026-09-17 브레인 ④).
from B08_Quantity.B08_Quantity_Build_WorkItemMaster_Variants import attach_variant_keys
attach_variant_keys(nodes, data["effective_date"])
return finish(
data,
source=SOURCE,
@@ -303,7 +306,7 @@ def build_const() -> tuple:
"divisions": list(DIVISIONS),
"toc_source": BOOK_NAME + " 머리 「목 차」",
"section_from_body": True,
"variant_keys_attached": False,
"variant_keys_attached": True, # 산림과 같은 표 읽기 — 못 가른 공종은 빈칸
"general_provision_roots": list(general_roots()),
},
)
@@ -1976,8 +1976,8 @@
"merged_tables": 17,
"merged_columns": 178,
"files": 41,
"tables": 101,
"columns": 540,
"tables": 102,
"columns": 543,
"value_groups": 203,
"unknown": 0,
"value_groups_by_kind": {
@@ -7532,6 +7532,34 @@
"visible": true
}
]
},
{
"key": "variants",
"name_ko": "갈래 열쇠 장부",
"summary": "공종 열쇠 아래 갈래마다 번호를 매긴 것. 사라진 갈래의 번호는 다시 쓰지 않는다.",
"shape": "map",
"rows": 111,
"columns": [
{
"key": "@key",
"name_ko": "갈래를 지닌 건설 공종",
"unit": "",
"visible": true,
"is_row_key": true
},
{
"key": "next_serial",
"name_ko": "다음 갈래 번호",
"unit": "",
"visible": true
},
{
"key": "slots",
"name_ko": "갈래 자리→번호",
"unit": "",
"visible": true
}
]
}
],
"value_groups": [
@@ -1,7 +1,7 @@
{
"schema_version": "1.0",
"dataset_id": "data_work_item_master_manifest",
"generated_at": "2026-09-17T18:21:40+09:00",
"generated_at": "2026-09-17T18:25:05+09:00",
"built_by": "B08_Quantity/B08_Quantity_Build_WorkItemMaster.py",
"source": {
"dataset_id": "pum_forest",
@@ -12,7 +12,7 @@
"files": [
{
"file": "work_item_master_2026-01-01.json",
"sha256": "1cae4b3cdba4b7c13b1d0e7854a2754da3bda9402754263e801287fe20d019c7",
"sha256": "97d4c0a12d5cb811d10d3c4108bc1a20acf97748b572db026847b603804f7598",
"size_bytes": 925084
},
{
@@ -1,7 +1,7 @@
{
"schema_version": "1.0",
"dataset_id": "data_work_item_master_manifest",
"generated_at": "2026-09-17T18:21:40+09:00",
"generated_at": "2026-09-17T18:25:40+09:00",
"built_by": "B08_Quantity/B08_Quantity_Build_WorkItemMaster.py",
"source": {
"dataset_id": "pum_const",
@@ -12,8 +12,8 @@
"files": [
{
"file": "const_work_item_master_2026-01-01.json",
"sha256": "52ac9f352106e61cbb0d548763808f967afb152e17eb6dce4a1cd1dd51f59e7d",
"size_bytes": 3780649
"sha256": "f01928541cd24f8cfa6f46036d8d988479d8649796d6630d5b70179c485001bd",
"size_bytes": 3839561
},
{
"file": "const_form_undetermined_2026-01-01.json",
File diff suppressed because it is too large Load Diff
@@ -10951,5 +10951,969 @@
"first_toc_number": "4-3-3",
"first_name": "배관누수 검사"
}
},
"variants": {
"CW-00063": {
"next_serial": 3,
"slots": {
"5m이하": "01",
"10m이하": "02"
}
},
"CW-00064": {
"next_serial": 3,
"slots": {
"평규준틀": "01",
"귀규준틀": "02"
}
},
"CW-00075": {
"next_serial": 3,
"slots": {
"10m이하": "01",
"10m초과∼20m이하": "02"
}
},
"CW-00077": {
"next_serial": 3,
"slots": {
"높이2m": "01",
"높이4m": "02"
}
},
"CW-00080": {
"next_serial": 3,
"slots": {
"벽용": "01",
"슬래브발코니,난간용": "02"
}
},
"CW-00087": {
"next_serial": 3,
"slots": {
"보행자출입구": "01",
"차량출입구": "02"
}
},
"CW-00116": {
"next_serial": 3,
"slots": {
"설치": "01",
"해체": "02"
}
},
"CW-00117": {
"next_serial": 3,
"slots": {
"H=3.0m이하": "01",
"4.0m이하": "02"
}
},
"CW-00118": {
"next_serial": 5,
"slots": {
"H=3.0m이하": "01",
"H=4.0m이하": "02",
"H=5.0m이하": "03",
"H=6.0m이하": "04"
}
},
"CW-00127": {
"next_serial": 3,
"slots": {
"설치": "01",
"해체": "02"
}
},
"CW-00129": {
"next_serial": 3,
"slots": {
"지상또는건물설치": "01",
"타워크레인설치": "02"
}
},
"CW-00135": {
"next_serial": 4,
"slots": {
"300600㎜": "01",
"700900㎜": "02",
"1,0001,200㎜": "03"
}
},
"CW-00151": {
"next_serial": 3,
"slots": {
"30㎝미만": "01",
"30㎝이상": "02"
}
},
"CW-00154": {
"next_serial": 3,
"slots": {
"우물통발파": "01",
"우물통발파이외": "02"
}
},
"CW-00158": {
"next_serial": 3,
"slots": {
"15cm": "01",
"30cm": "02"
}
},
"CW-00182": {
"next_serial": 4,
"slots": {
"5m미만": "01",
"5m이상∼8m미만": "02",
"8m이상": "03"
}
},
"CW-00210": {
"next_serial": 3,
"slots": {
"쌓기": "01",
"놓기": "02"
}
},
"CW-00231": {
"next_serial": 3,
"slots": {
"토사": "01",
"자갈/호박돌": "02"
}
},
"CW-00233": {
"next_serial": 3,
"slots": {
"=20m이하": "01",
"=20m35m": "02"
}
},
"CW-00236": {
"next_serial": 3,
"slots": {
"외부반입/반출": "01",
"작업구간이동": "02"
}
},
"CW-00239": {
"next_serial": 3,
"slots": {
"요동식": "01",
"전회전식": "02"
}
},
"CW-00247": {
"next_serial": 3,
"slots": {
"무근콘크리트": "01",
"철근콘크리트": "02"
}
},
"CW-00271": {
"next_serial": 3,
"slots": {
"ø12.7㎜": "01",
"ø15.2㎜": "02"
}
},
"CW-00286": {
"next_serial": 5,
"slots": {
"D900": "01",
"D1,200": "02",
"D1,500": "03",
"D1,800": "04"
}
},
"CW-00341": {
"next_serial": 3,
"slots": {
"2": "01",
"1": "02"
}
},
"CW-00342": {
"next_serial": 2,
"slots": {
"2": "01"
}
},
"CW-00343": {
"next_serial": 2,
"slots": {
"2": "01"
}
},
"CW-00371": {
"next_serial": 2,
"slots": {
"㎾": "01"
}
},
"CW-00450": {
"next_serial": 6,
"slots": {
"40㎝": "01",
"45㎝": "02",
"50㎝": "03",
"60㎝": "04",
"70㎝": "05"
}
},
"CW-00454": {
"next_serial": 3,
"slots": {
"식생매트설치": "01",
"복토": "02"
}
},
"CW-00472": {
"next_serial": 3,
"slots": {
"공동구": "01",
"바닥/인버트": "02"
}
},
"CW-00487": {
"next_serial": 3,
"slots": {
"일단": "01",
"양단": "02"
}
},
"CW-00489": {
"next_serial": 3,
"slots": {
"50㎏": "01",
"60㎏": "02"
}
},
"CW-00491": {
"next_serial": 3,
"slots": {
"레일인장법": "01",
"자연대기온도법": "02"
}
},
"CW-00494": {
"next_serial": 4,
"slots": {
"37㎏": "01",
"50㎏": "02",
"60㎏": "03"
}
},
"CW-00497": {
"next_serial": 3,
"slots": {
"조임": "01",
"해체": "02"
}
},
"CW-00527": {
"next_serial": 13,
"slots": {
"800": "01",
"900": "02",
"1,000": "03",
"1,100": "04",
"1,200": "05",
"1,350": "06",
"1,500": "07",
"1.650": "08",
"1,800": "09",
"2,000": "10",
"2,200": "11",
"2,400": "12"
}
},
"CW-00537": {
"next_serial": 7,
"slots": {
"75": "01",
"100": "02",
"150": "03",
"200": "04",
"250": "05",
"300": "06"
}
},
"CW-00547": {
"next_serial": 8,
"slots": {
"50": "01",
"75": "02",
"100": "03",
"150": "04",
"200": "05",
"250": "06",
"300": "07"
}
},
"CW-00548": {
"next_serial": 5,
"slots": {
"8001,100": "01",
"1,2001,800": "02",
"2,0002,200": "03",
"2,4003,000": "04"
}
},
"CW-00553": {
"next_serial": 3,
"slots": {
"80": "01",
"100": "02"
}
},
"CW-00564": {
"next_serial": 2,
"slots": {
"0.1㎥이상": "01"
}
},
"CW-00566": {
"next_serial": 3,
"slots": {
"0.1㎥이상": "01",
"0.03㎥이하": "02"
}
},
"CW-00567": {
"next_serial": 6,
"slots": {
"기초고르기": "01",
"피복석고르기": "02",
"피복석거친고르기": "03",
"내부사석고르기": "04",
"필터사석고르기": "05"
}
},
"CW-00586": {
"next_serial": 3,
"slots": {
"BX": "01",
"NX": "02"
}
},
"CW-00700": {
"next_serial": 7,
"slots": {
"ø16이하": "01",
"ø20이하": "02",
"ø24이하": "03",
"ø28이하": "04",
"ø32이하": "05",
"ø40이하": "06"
}
},
"CW-00703": {
"next_serial": 3,
"slots": {
"판두께1.6㎜": "01",
"판두께2.3㎜": "02"
}
},
"CW-00708": {
"next_serial": 3,
"slots": {
"자동용접": "01",
"수동용접": "02"
}
},
"CW-00715": {
"next_serial": 3,
"slots": {
"1.0B": "01",
"1.5B": "02"
}
},
"CW-00717": {
"next_serial": 5,
"slots": {
"벽돌": "01",
"치장벽돌": "02",
"블록": "03",
"ALC블록": "04"
}
},
"CW-00735": {
"next_serial": 3,
"slots": {
"주택": "01",
"일반": "02"
}
},
"CW-00782": {
"next_serial": 3,
"slots": {
"바닥": "01",
"수직부": "02"
}
},
"CW-00784": {
"next_serial": 3,
"slots": {
"바닥": "01",
"수직부": "02"
}
},
"CW-00801": {
"next_serial": 3,
"slots": {
"1층(회)": "01",
"2층(회)": "02"
}
},
"CW-00802": {
"next_serial": 3,
"slots": {
"바닥": "01",
"수직부": "02"
}
},
"CW-00831": {
"next_serial": 3,
"slots": {
"450×450": "01",
"600×600": "02"
}
},
"CW-00841": {
"next_serial": 3,
"slots": {
"일반철재": "01",
"경량철재": "02"
}
},
"CW-00844": {
"next_serial": 3,
"slots": {
"모래체가름포함": "01",
"모래체가름제외": "02"
}
},
"CW-00850": {
"next_serial": 3,
"slots": {
"3.6m이하": "01",
"3.6m초과∼7.2m이하": "02"
}
},
"CW-00861": {
"next_serial": 5,
"slots": {
"1.0㎡이하": "01",
"1.03.0㎡이하": "02",
"3.06.0㎡이하": "03",
"6.08.0㎡이하": "04"
}
},
"CW-00862": {
"next_serial": 6,
"slots": {
"1.0㎡이하": "01",
"1.03.0㎡이하": "02",
"3.06.0㎡이하": "03",
"6.09.0㎡이하": "04",
"9.012.0㎡이하": "05"
}
},
"CW-00865": {
"next_serial": 6,
"slots": {
"5㎡미만": "01",
"510㎡미만": "02",
"1015㎡미만": "03",
"1520㎡미만": "04",
"2025㎡미만": "05"
}
},
"CW-00871": {
"next_serial": 3,
"slots": {
"일반도어록": "01",
"디지털도어록": "02"
}
},
"CW-00873": {
"next_serial": 2,
"slots": {
"12mm이하": "01"
}
},
"CW-00922": {
"next_serial": 3,
"slots": {
"ø16mm": "01",
"ø20mm": "02"
}
},
"CW-00946": {
"next_serial": 10,
"slots": {
"ø50이하": "01",
"65": "02",
"80": "03",
"100": "04",
"125": "05",
"150": "06",
"200": "07",
"250": "08",
"300": "09"
}
},
"CW-00950": {
"next_serial": 3,
"slots": {
"세대내": "01",
"공용부위": "02"
}
},
"CW-00955": {
"next_serial": 13,
"slots": {
"0.75㎾이하": "01",
"1.5㎾이하": "02",
"2.2㎾이하": "03",
"3.7㎾이하": "04",
"5.5㎾이하": "05",
"7.5㎾이하": "06",
"11㎾이하": "07",
"15㎾이하": "08",
"22㎾이하": "09",
"37㎾이하": "10",
"55㎾이하": "11",
"75㎾이하": "12"
}
},
"CW-00956": {
"next_serial": 7,
"slots": {
"0.75㎾이하": "01",
"1.5㎾이하": "02",
"2.2㎾이하": "03",
"3.7㎾이하": "04",
"5.5㎾이하": "05",
"7.5㎾이하": "06"
}
},
"CW-00957": {
"next_serial": 13,
"slots": {
"0.75㎾이하": "01",
"1.5㎾이하": "02",
"2.2㎾이하": "03",
"3.7㎾이하": "04",
"5.5㎾이하": "05",
"7.5㎾이하": "06",
"11㎾이하": "07",
"15㎾이하": "08",
"22㎾이하": "09",
"37㎾이하": "10",
"55㎾이하": "11",
"75㎾이하": "12"
}
},
"CW-00963": {
"next_serial": 3,
"slots": {
"700㎜이하": "01",
"900㎜이하": "02"
}
},
"CW-00969": {
"next_serial": 7,
"slots": {
"ø15mm": "01",
"ø20mm": "02",
"ø25mm": "03",
"ø32mm": "04",
"ø40mm": "05",
"ø50mm": "06"
}
},
"CW-00972": {
"next_serial": 11,
"slots": {
"ø1525": "01",
"3250": "02",
"65": "03",
"80": "04",
"100": "05",
"125": "06",
"150": "07",
"200": "08",
"250": "09",
"300": "10"
}
},
"CW-00977": {
"next_serial": 4,
"slots": {
"ø1315": "01",
"2032": "02",
"4050": "03"
}
},
"CW-00978": {
"next_serial": 3,
"slots": {
"ø1315mm": "01",
"ø2032mm": "02"
}
},
"CW-00981": {
"next_serial": 3,
"slots": {
"ø1315mm": "01",
"ø2032mm": "02"
}
},
"CW-00982": {
"next_serial": 5,
"slots": {
"ø50mm": "01",
"ø6580mm": "02",
"ø100125mm": "03",
"ø150mm": "04"
}
},
"CW-00986": {
"next_serial": 3,
"slots": {
"거치형": "01",
"벽걸이형": "02"
}
},
"CW-01000": {
"next_serial": 4,
"slots": {
"0.5㎡미만": "01",
"1.0㎡미만": "02",
"1.5㎡미만": "03"
}
},
"CW-01002": {
"next_serial": 4,
"slots": {
"ø50㎜": "01",
"ø75㎜": "02",
"ø100㎜": "03"
}
},
"CW-01011": {
"next_serial": 17,
"slots": {
"유효길이610㎜": "01",
"762〃": "02",
"914〃": "03",
"1,067〃": "04",
"1,219〃": "05",
"1,372〃": "06",
"1,524〃": "07",
"1,676〃": "08",
"1,829〃": "09",
"1,981〃": "10",
"2,134〃": "11",
"2,286〃": "12",
"2,438〃": "13",
"2,591〃": "14",
"2,875〃": "15",
"3,048〃": "16"
}
},
"CW-01014": {
"next_serial": 3,
"slots": {
"실내기": "01",
"실외기": "02"
}
},
"CW-01018": {
"next_serial": 2,
"slots": {
"15,000Kcal/hr": "01"
}
},
"CW-01020": {
"next_serial": 6,
"slots": {
"70×1,000kcal/hr이하": "01",
"120〃": "02",
"150〃": "03",
"240〃": "04",
"360〃": "05"
}
},
"CW-01022": {
"next_serial": 10,
"slots": {
"주철재바닥설치20절이하": "01",
"21절이상": "02",
"벽걸이3절": "03",
"천장달기3절": "04",
"1m길트": "05",
"콘백터길이1m미만": "06",
"베이스보드1단형길이2m미만": "07",
"2m이상": "08",
"강판제및알루미늄제방열기1m미만": "09"
}
},
"CW-01027": {
"next_serial": 7,
"slots": {
"2구": "01",
"3구": "02",
"4구": "03",
"5구": "04",
"6구": "05",
"7구": "06"
}
},
"CW-01029": {
"next_serial": 8,
"slots": {
"100": "01",
"200": "02",
"300": "03",
"400": "04",
"500": "05",
"750": "06",
"1,000": "07"
}
},
"CW-01032": {
"next_serial": 5,
"slots": {
"건타입오일버너0.75㎾": "01",
"1.5": "02",
"(전자동방식)2.2": "03",
"3.7": "04"
}
},
"CW-01035": {
"next_serial": 11,
"slots": {
"ø50": "01",
"65": "02",
"80": "03",
"100": "04",
"125": "05",
"150": "06",
"200": "07",
"250": "08",
"300": "09",
"350": "10"
}
},
"CW-01036": {
"next_serial": 3,
"slots": {
"일반철재": "01",
"경량철재": "02"
}
},
"CW-01045": {
"next_serial": 3,
"slots": {
"지상노출관": "01",
"지하매설관": "02"
}
},
"CW-01053": {
"next_serial": 2,
"slots": {
"소화용구격납상자": "01"
}
},
"CW-01058": {
"next_serial": 2,
"slots": {
"관말시험밸브": "01"
}
},
"CW-01067": {
"next_serial": 2,
"slots": {
"압력공기탱크": "01"
}
},
"CW-01072": {
"next_serial": 2,
"slots": {
"스프링클러헤드": "01"
}
},
"CW-01083": {
"next_serial": 6,
"slots": {
"ø20": "01",
"25": "02",
"32": "03",
"40": "04",
"50": "05"
}
},
"CW-01085": {
"next_serial": 20,
"slots": {
"ø25": "01",
"32": "02",
"40": "03",
"50": "04",
"63": "05",
"75": "06",
"90": "07",
"110": "08",
"125": "09",
"140": "10",
"160": "11",
"180": "12",
"200": "13",
"225": "14",
"250": "15",
"280": "16",
"315": "17",
"355": "18",
"400": "19"
}
},
"CW-01087": {
"next_serial": 13,
"slots": {
"ø2025": "01",
"4050": "02",
"65": "03",
"80": "04",
"100": "05",
"125": "06",
"150": "07",
"200": "08",
"250": "09",
"300": "10",
"350": "11",
"400": "12"
}
},
"CW-01093": {
"next_serial": 7,
"slots": {
"분전반": "01",
"조작반": "02",
"계기반(자립개방)": "03",
"계기반(자립밀폐)": "04",
"계기반(현장)": "05",
"〃(발신기수납상)": "06"
}
},
"CW-01100": {
"next_serial": 4,
"slots": {
"유니온엘보": "01",
"공기압축기시험": "02",
"조작함(설비물)": "03"
}
},
"CW-01114": {
"next_serial": 5,
"slots": {
"ø20(90)": "01",
"25(90)": "02",
"32(110)": "03",
"40(110)": "04"
}
},
"CW-01128": {
"next_serial": 7,
"slots": {
"ø16이하": "01",
"ø20이하": "02",
"ø24이하": "03",
"ø28이하": "04",
"ø32이하": "05",
"ø40이하": "06"
}
},
"CW-01199": {
"next_serial": 4,
"slots": {
"7.5㎾": "01",
"15㎾": "02",
"30㎾": "03"
}
},
"CW-01200": {
"next_serial": 2,
"slots": {
"0.5": "01"
}
},
"CW-01201": {
"next_serial": 3,
"slots": {
"산기분기관제작": "01",
"분기관및산기장치설치": "02"
}
},
"CW-01291": {
"next_serial": 5,
"slots": {
"#8번": "01",
"#10번": "02",
"#12번": "03",
"#15번": "04"
}
},
"CW-01300": {
"next_serial": 3,
"slots": {
"3시간차단": "01",
"4시간차단": "02"
}
},
"CW-01301": {
"next_serial": 3,
"slots": {
"판넬설치(개당)": "01",
"가받침해체및설치(m당)": "02"
}
},
"CW-01302": {
"next_serial": 3,
"slots": {
"궤도정정": "01",
"궤도이설": "02"
}
},
"CW-01306": {
"next_serial": 4,
"slots": {
"A급": "01",
"B급": "02",
"C급": "03"
}
},
"CW-01358": {
"next_serial": 15,
"slots": {
"ø15": "01",
"20": "02",
"25": "03",
"32": "04",
"40": "05",
"50": "06",
"65": "07",
"80": "08",
"100": "09",
"125": "10",
"150": "11",
"200": "12",
"250": "13",
"300": "14"
}
}
}
}
@@ -4,7 +4,7 @@
"effective_date": "2026-01-01",
"pum_edition": "2026-01-01",
"toc_edition": "산림청고시제2025-82호",
"generated_at": "2026-09-17T18:21:40+09:00",
"generated_at": "2026-09-17T18:25:05+09:00",
"dataset_version": {
"dataset_id": "pum_forest",
"effective_date": "2026-01-01",
@@ -38,6 +38,7 @@ def _same_except_time(fresh: dict, saved: dict) -> None:
for doc in (fresh, saved):
doc.pop("generated_at")
doc["stats"].pop("keys_newly_issued") # 첫 뽑기만 발급 · 다시 뽑으면 0
doc["stats"].pop("variant_keys_newly_issued", None) # 갈래 열쇠도 같음
assert fresh == saved
@@ -149,3 +150,28 @@ def test_산림을_고르는_쪽이_건설_파일을_안_집음() -> None:
assert load_master()["dataset_id"] == "work_item_master_forest"
assert not list(OUT.glob("work_item_master_*const*.json"))
def test_건설_갈래도_이름을_다듬어도_같은_열쇠(master: dict) -> None:
"""④ 건설 갈래 펴기(2026-09-17) — 갈래 열쇠는 번호(`CW-00063#01`) · 자간·물결표를 손질해도 새로 안 남."""
import copy
from B08_Quantity.B08_Quantity_Build_WorkItemMaster_Keys import (
assign_variant_keys,
load_registry,
)
items = master["work_items"]
spread = [it for it in items if it.get("variants")]
assert len(spread) > 100 and sum(len(it["variants"]) for it in spread) > 400
registry = load_registry(const.CONST_KEY_REGISTRY, const.KEY_PREFIX)
nodes = copy.deepcopy(spread)
for node in nodes: # 사람이 이름을 다듬은 날 — 자간 한 칸 · 물결표 다른 벌
node["variant_keys"] = [
" ".join(k.split()).replace("", "") for k in node["variant_keys"]
]
assert assign_variant_keys(nodes, registry) == [] # 새 번호 0
for before, after in zip(spread, nodes):
assert [v["variant_key"] for v in after["variants"]] == [
v["variant_key"] for v in before["variants"]
]
+2 -1
View File
@@ -85,7 +85,8 @@ def test_트리_표_id_와_이름표_표_id_는_양쪽으로_같음(client: Test
assert not_labelled == [], not_labelled
assert label_only == [], label_only
# 2026-09-17 92 → 101 — 공종 축 산출 일곱 + 갈래 열쇠 장부 표.
assert sum(len(t) for t in files.values()) == LABELS["counts"]["tables"] == 101
# 2026-09-17 101 → 102 — 건설 열쇠 장부에도 갈래 표(건설 갈래 펴기 ④).
assert sum(len(t) for t in files.values()) == LABELS["counts"]["tables"] == 102
for entry in LABELS["files"]: # 이름이 실제로 붙음 · 줄 수도 이름표 셈과 같음
named = {t["key"]: t for t in entry["tables"]}
for t in files[entry["file_id"]]:
+9 -3
View File
@@ -76,9 +76,11 @@ def test_산림_평평한_잎_목록과_계산_규칙(client: TestClient) -> Non
def test_건설_공종_축도_같은_길(client: TestClient) -> None:
"""건설(2026-09-17 뽑음) — 부문이 뿌리라 경로가 부문부터 · 열쇠 CW- · 계산 규칙은 전부 choose_one."""
"""건설(2026-09-17 뽑음) — 부문이 뿌리라 경로가 부문부터 · 열쇠 CW- · 계산 규칙은 전부 choose_one.
갈래도 산림과 같은 읽기로 ( · 공종 997 1,293 · 갈래 열쇠 `CW-00063#01`)."""
table = _get(client, "work_item_const", size=500, axis_role="")
assert table["total"] == table["stats"]["work_items"] > 900
assert table["stats"] == {"work_items": 997, "units": 1293} and table["total"] == 1293
assert sum(1 for r in table["rows"] if "#" in r["@id"]) > 0
rows = table["rows"]
assert all(r["@id"].startswith("CW-") for r in rows)
assert all(r["name"].split(" ")[0].endswith("부문") for r in rows)
@@ -200,7 +202,11 @@ def test_연결고리_표가_이은_줄에_연결됨_감추지_않고_표시만(
} - confirmed
for kind, prefix in (("work_item_const", "CW-"), ("work_item_forest", "FW-")):
every = _get(client, kind, size=500, axis_role="")
rows = every["rows"] + _get(client, kind, size=500, page=2, axis_role="")["rows"]
rows = [
row
for page in range(1, every["total"] // 500 + 2)
for row in _get(client, kind, size=500, page=page, axis_role="")["rows"]
]
assert len(rows) == every["total"] # 연결 거르기 기본은 「전부」 — 다 보임
marked = {r["@key"] for r in rows if r["link"] == "연결됨"} # 갈래 줄은 공종 열쇠로
listed = {r["@key"] for r in rows} # 표가 안 붙은 묶는 마디(CW-01329 따위)는 목록 밖