diff --git a/B09_Estimation/B09_Estimation_MachineCost.py b/B09_Estimation/B09_Estimation_MachineCost.py index ca1615f1..3c1b5576 100644 --- a/B09_Estimation/B09_Estimation_MachineCost.py +++ b/B09_Estimation/B09_Estimation_MachineCost.py @@ -132,6 +132,32 @@ MASHED_MACHINE_FIXES: dict[str, tuple[str, str]] = { #: (0230) 표의 「시간당 계」 — 규격이 달라도 같은 값이다(원문 여섯 줄 모두 6,601). MASHED_LOSS_COEFFICIENT = Decimal("0.0006601") +#: ⚠ **같은 파싱 병의 나머지** (2026-09-14 · 축 C 1장 ① 브레인 차례). +#: 원천이 쪽 끝 기종의 이름 칸에 **다음 쪽 머리글 + 다음 분류 이름**을 붙여 넣어 ① 그 기종은 +#: 이름에 표 머리글이 붙고 규격이 비고 ② 다음 분류 첫 기종들은 **이름이 빔**. 이름으로 찾아지지 +#: 않고 화면에도 빈 이름으로 선다. 이름·규격은 **건설공사 표준품셈 제8장 표**(`pum_const_2026.json` +#: 기종 목록 줄 · 붙은 꼬리 「(분류번호) 이름」)에서 읽음 — 취득가·손료계수는 원천 그대로. +#: ⚠ 원천이 바로 실으면 이 표는 지운다(위 표와 같은 약속). +GLUED_MACHINE_FIXES: dict[str, tuple[str, str]] = { + "5220-0015": ("소형브레이커(전기식)", "1.5㎾"), + "6532-0220": ("진동파일 해머(유압식)", "162㎾"), + "7101-0450": ("고성능 착정기", "335.70㎾"), + "7120-0746": ("버킷식준설기", "7.46kW"), + "7995-0050": ("배관파이프", "ø50-2.6m"), +} +#: 이름이 빈 분류 — 분류번호(코드 앞 넷) → 원문 이름. 규격은 원천 값 그대로. +EMPTY_NAME_BY_GROUP: dict[str, str] = { + "0240": "유압식 진동콤팩터(굴착기 부착용)", + "3611": "콘크리트 피니셔(중앙분리대용)", + "5330": "드릴웨곤", + "6540": "워터젯트", + "6802": "파일천공전용장비", + "7202": "자동세륜기(롤 타입)", + "7930": "모터", + "8201": "3D GNSS 머신 가이던스(굴착기용)", + "9070": "이우선(비자항)", +} + class MachineCostError(LookupError): """기계경비를 세울 수 없는 경우. 0 으로 때우지 않고 멈춘다.""" @@ -205,6 +231,11 @@ def load_machine_catalog(file_name: str = "mach_base_2026.json") -> MachineCatal # 뭉개진 줄 — 원문으로 이름·규격을 되살리고 손료계수를 채운다. row = {**row, "machine_name": fixed[0], "specification": fixed[1]} coefficient = {**coefficient, "loss_coefficient_per_hour": MASHED_LOSS_COEFFICIENT} + elif code in GLUED_MACHINE_FIXES: + name, spec = GLUED_MACHINE_FIXES[code] + row = {**row, "machine_name": name, "specification": spec} + elif not str(row.get("machine_name") or "").strip() and code[:4] in EMPTY_NAME_BY_GROUP: + row = {**row, "machine_name": EMPTY_NAME_BY_GROUP[code[:4]]} catalog.machines[code] = MachineSpec( machine_code=code, name=row["machine_name"], diff --git a/resources/tester/test_b09_machine_catalog_names.py b/resources/tester/test_b09_machine_catalog_names.py new file mode 100644 index 00000000..5feaf783 --- /dev/null +++ b/resources/tester/test_b09_machine_catalog_names.py @@ -0,0 +1,60 @@ +"""기계 카탈로그 이름 — 원천 파싱 병(쪽 끝 이름에 다음 쪽 머리글이 붙고 다음 분류 이름이 빔) 되살림. + +2026-09-14 축 C 1장 ① (브레인 차례) · 원문 = 건설공사 표준품셈 제8장 기종 목록 줄. + ① 표 머리글(「시 간 당」·「(10-7)」)이 붙은 이름이 하나도 없음 + ② 빈 이름이 하나도 없음 + ③ 되살린 이름·규격이 원문 차례와 같음 · 취득가는 원천 그대로 +""" + +from __future__ import annotations + +import json +import sys +from decimal import Decimal +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(ROOT)) + +from B09_Estimation.B09_Estimation_MachineCost import load_machine_catalog # noqa: E402 + +CATALOG = load_machine_catalog() + + +def test_표_머리글이_붙은_이름이_없다() -> None: + glued = [ + m.machine_code + for m in CATALOG.machines.values() + if "시 간 당" in m.name or "(10-7)" in m.name + ] + assert glued == [] + + +def test_빈_이름이_없다() -> None: + assert [m.machine_code for m in CATALOG.machines.values() if not m.name.strip()] == [] + + +def test_되살린_이름과_규격() -> None: + expected = { + "5220-0015": ("소형브레이커(전기식)", "1.5㎾"), + "7101-0450": ("고성능 착정기", "335.70㎾"), + "0240-0007": ("유압식 진동콤팩터(굴착기 부착용)", "0.7"), + "3611-0142": ("콘크리트 피니셔(중앙분리대용)", "105.9"), + "6802-0100": ("파일천공전용장비", "100"), + "9070-0020": ("이우선(비자항)", ""), + } + for code, (name, spec) in expected.items(): + machine = CATALOG.machines[code] + assert (machine.name, machine.specification) == (name, spec), code + + +def test_취득가는_원천_그대로() -> None: + raw = json.loads( + (ROOT / "resources/data_cost_input_value/mach_base_2026.json").read_text(encoding="utf-8") + ) + prices = { + r["machine_code"]: r["price_thousand_krw"] + for r in raw["variables"]["mach_price"]["records"] + } + for code in ("5220-0015", "0240-0007", "6802-0040", "7930-0100"): + assert CATALOG.machines[code].price_thousand_krw == Decimal(str(prices[code]))