· 손료 = 자재가 × 표 수량 곧장(패널 0.089 · 내부 패널 0.003매/㎡) + 부자재 24/52/79% · 소모자재 5%(주자재비 = 패널 + 내부 패널) — 봉화 2024 호표 재료비 보통 4,500 · 간단 3,697 과 일치 · 임대료 = 「유로폼 임대료 ㎡당」 한 줄 · 부자재 % 는 원문이 안 적어 안 걺 · 둘 다 · 한쪽만 반쯤 · 아무것도 — 안 세우고 사유(부모 12-38 까지 그대로) · 12-38-1 잔존율은 안 곱함 — 표 수량이 이미 12회·25% 몫으로 보임(우리 역산 · 차 2.5% 는 추정이라 비고에 밝힘) · 이중계상 ③ 가드에서 12-38-02 만 뺌(패널이 자재총괄에 줄 없음) · 유로폼이 자재총괄로 가면 빨강 시험 · 합산형 부모 — 갈래 벌이 같으면 여러 단계 갈래끼리 합산(12-38 = 사용수량 + 설치·해체) · 작업조 표 7 은 밑수 없음 목록에서 뺌(1단위당 = 인원 ÷ 시공량) — 12-38-3 설치·해체가 막혀 있던 자리 · 마스터 재생성 — 12-38 · 12-38-02 갈래 키만 바뀜 기본 조립 제목 금액 변화 0 · 프로젝트 내역 변화 0 · 잴 시험 일곱 빨강→초록 · 끄기 넷 빨강 · 전체 시험 1677 + 318 통과 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
86 lines
4.5 KiB
Python
86 lines
4.5 KiB
Python
"""공종 마스터 — **갈래 키(`variant_key`)를 표 읽기가 실제로 가른 대로** 싣는다 (명세 14장 정정).
|
|
|
|
종전 `variant_axis` 는 표 첫 열 값 24개만 담아, 단가표 갈래 제목 294 중 86 만 맞았다
|
|
(2026-09-13 브레인 실측). 머리행 갈래(벌목 「5m이상~8m미만」) · 두 단 머리 · 첫 열이 자원
|
|
이름인 표를 못 가렸다.
|
|
|
|
⚠ 표 모양을 여기서 **다시 판정하지 않는다** — 첫 열 · 열이 자원 · 뭉친 이름+갈래 열 · 축 셋 ·
|
|
두 단 머리 · 작업조 · 공정 합산 · 고르기형 등 모양 가르기는 B09 표 읽기에 이미 있다.
|
|
두 벌로 짜면 한쪽만 고쳐진다. 그 읽기를 **메모리의 이 마스터**에 돌려 표마다 가른 갈래
|
|
이름을 원문 표기 그대로 싣는다(키 비교는 받는 쪽이 `normalize_variant_key` 로).
|
|
⚠ 못 가른 표는 **빈칸** — 자원을 못 맞춘 표 · 계수 · 참조 · 미판정 표에 갈래를 지어내지 않는다.
|
|
|
|
공종마다 `variant_keys` — 표들의 갈래 + 원문이 정했으나 자원 줄로 안 서는 갈래
|
|
(불도저 운반 공식 갈래 · 합산형 단계 잎의 암질 행 · 9-4-1 [주]① 평균 · 유로폼 12-38-2 부자재 요율 머리) · 합산형 부모는 단계 갈래.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
|
|
def _add(labels: list[str], seen: set[str], label: str, normalize: Any) -> None:
|
|
key = normalize(label)
|
|
if key and key not in seen:
|
|
seen.add(key)
|
|
labels.append(label)
|
|
|
|
|
|
def attach_variant_keys(nodes: list[dict[str, Any]], edition: str) -> None:
|
|
"""표마다 `variant_key`, 공종마다 `variant_keys` 를 채운다(자리에서)."""
|
|
from B09_Estimation.B09_Estimation_Euroform import CODE as EUROFORM_CODE
|
|
from B09_Estimation.B09_Estimation_Euroform import RATE_TABLE as EUROFORM_RATE_TABLE
|
|
from B09_Estimation.B09_Estimation_Euroform import rates as euroform_rates
|
|
from B09_Estimation.B09_Estimation_MachineProductivity_Dozer import extract_dozer_factors
|
|
from B09_Estimation.B09_Estimation_ParentSteps import AVERAGE_BASIS, AVERAGE_VARIANT, rock_rows
|
|
from B09_Estimation.B09_Estimation_ResourceAxis import build_resource_axis
|
|
from B09_Estimation.B09_Estimation_ResourceAxis_Sources import load_combined_catalog
|
|
from B09_Estimation.B09_Estimation_UnitPrice import normalize_variant_key as normalize
|
|
|
|
axis = build_resource_axis(
|
|
{"effective_date": edition, "work_items": nodes}, load_combined_catalog()
|
|
)
|
|
by_table: dict[tuple[str, str], tuple[list[str], set[str]]] = {}
|
|
for row in axis.rows:
|
|
if row.variant:
|
|
labels, seen = by_table.setdefault((row.work_item_code, row.pum_table_id), ([], set()))
|
|
_add(labels, seen, row.variant, normalize)
|
|
|
|
steps = {
|
|
str(step["code"])
|
|
for node in nodes
|
|
if node.get("parent_mode") == "sum_steps"
|
|
for step in node.get("steps") or []
|
|
}
|
|
by_code: dict[str, list[str]] = {}
|
|
for node in nodes:
|
|
code = str(node.get("work_item_code") or "")
|
|
keys: list[str] = []
|
|
seen: set[str] = set()
|
|
for table in node.get("tables") or []:
|
|
labels, table_seen = by_table.get((code, str(table.get("pum_table_id"))), ([], set()))
|
|
found = extract_dozer_factors(code, table)
|
|
for label in found if isinstance(found, dict) else {}:
|
|
_add(labels, table_seen, label, normalize)
|
|
if code in steps:
|
|
for rock, _, _ in rock_rows({"tables": [table]}):
|
|
_add(labels, table_seen, rock, normalize)
|
|
if code == EUROFORM_CODE and table.get("pum_table_id") == EUROFORM_RATE_TABLE:
|
|
for head in euroform_rates({"tables": [table]}): # 부자재 요율 머리 갈래
|
|
_add(labels, table_seen, head, normalize)
|
|
table["variant_key"] = labels
|
|
for label in labels:
|
|
_add(keys, seen, label, normalize)
|
|
if code in AVERAGE_BASIS and keys:
|
|
_add(keys, seen, AVERAGE_VARIANT, normalize)
|
|
by_code[code] = keys
|
|
for node in nodes:
|
|
code = str(node.get("work_item_code") or "")
|
|
keys = list(by_code.get(code) or [])
|
|
if node.get("parent_mode") == "sum_steps":
|
|
seen = {normalize(k) for k in keys}
|
|
for step in node.get("steps") or []:
|
|
for label in by_code.get(str(step["code"])) or []:
|
|
_add(keys, seen, label, normalize)
|
|
node["variant_keys"] = keys
|