From 078af37d331f22ee9566a72b25af190fdab2a066 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Tue, 15 Sep 2026 04:11:25 +0900 Subject: [PATCH] =?UTF-8?q?fix(b09):=20=EB=AC=BC=ED=83=B1=ED=81=AC(?= =?UTF-8?q?=EC=82=B4=EC=88=98=EC=B0=A8)=20=EC=9A=B4=EC=A0=84=EA=B2=BD?= =?UTF-8?q?=EB=B9=84=20=EB=90=98=EC=82=B4=EB=A6=BC=20=E2=80=94=208-4-8=20?= =?UTF-8?q?=EC=85=80=20=EB=B3=91=ED=95=A9=EC=9C=BC=EB=A1=9C=20=EC=A4=84?= =?UTF-8?q?=EC=A7=B8=20=EB=B2=84=EB=A0=A4=EC=A7=80=EB=8D=98=207204=20?= =?UTF-8?q?=EB=8B=A4=EC=84=AF=EC=9D=84=20=EC=9B=90=EB=AC=B8=20=EA=B0=92?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=C2=B7=20=EC=82=B4=EC=88=98=EC=B0=A8=20?= =?UTF-8?q?=EC=A1=B0=EC=A2=85=EC=9B=90=EC=9D=80=208-1-2=205=ED=98=B8=20?= =?UTF-8?q?=ED=99=94=EB=AC=BC=EC=B0=A8=EC=9A=B4=EC=A0=84=EC=82=AC(?= =?UTF-8?q?=EC=B4=88=EB=A5=98=EC=A2=85=EC=9E=90=EC=82=B4=ED=8F=AC=20?= =?UTF-8?q?=E2=91=A2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn --- .../B09_Estimation_MachineOperating.py | 34 +++++++++++++- .../tester/test_b09_water_tank_operating.py | 47 +++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 resources/tester/test_b09_water_tank_operating.py diff --git a/B09_Estimation/B09_Estimation_MachineOperating.py b/B09_Estimation/B09_Estimation_MachineOperating.py index 06e7bcd2..7e4e802a 100644 --- a/B09_Estimation/B09_Estimation_MachineOperating.py +++ b/B09_Estimation/B09_Estimation_MachineOperating.py @@ -43,11 +43,29 @@ _RE_FUEL_WITH_KIND = re.compile(r"^(휘발유|중유|경유)?(\d+(?:\.\d+)?)$") #: 조종원 직종 — 품셈 표는 「인/일」 수만 주고 직종명을 안 준다. #: TODO(미결 PLAN 9-6): 기종별 직종이 품셈 다른 장에 있다. 아래는 `aliases` 기반 **잠정**이며 #: 결과에 `operator_mapping_is_provisional: true` 로 드러난다. -_OPERATOR_TRUCK_WORDS = ("덤프트럭", "트럭", "트레일러", "화물") +#: 「살수차」 — 8-1-2 5호 화물차운전사 줄이 이름으로 적음(원문 또렷 · 물탱크(살수차) 7204 · 2026-09-15 ③). +_OPERATOR_TRUCK_WORDS = ("덤프트럭", "트럭", "트레일러", "화물", "살수차") _OPERATOR_ALIAS_TRUCK = "labor_op_truck" _OPERATOR_ALIAS_CONSTRUCTION = "labor_op_const" +#: ⚠ **셀 병합으로 줄째 버려지는 표에서 원문 값을 되살린다** (2026-09-15 초류종자살포 ③ 브레인). +#: 8-4-8 기타기계(원문 L3458)는 36기종이 한 칸에 뭉치고 자동세륜기 「-」 하나가 두 기종 몫이라 잡재료 35 · +#: 조종원 34 개로 짝이 안 맞아 줄째 버려짐. 양끝 맞추기는 우연 일치로 틀린 값이 붙어 못 씀 → 공종이 쓰는 +#: 물탱크(살수차) 다섯만 손으로(주연료 칸 · 잡재료 30 · 조종원 1 — 앞 기종 수가 칸마다 같아 자리가 또렷). +#: 버려진 줄 셋 81기종 중 공종이 쓰는 것은 이 다섯뿐(나머지는 막는 공종 0 이라 미룸). +#: 거창 실무 5,500ℓ 호표 「경유 9.3ℓ · 잡품 30% · 화물차운전사 1인」 일치. ⚠ 원천이 바로 실으면 이 표는 지운다. +MERGED_CELL_OPERATING_FIXES: dict[str, str] = { + "7204-0018": "8.2", + "7204-0038": "8.6", + "7204-0055": "9.3", + "7204-0065": "9.4", + "7204-0160": "12.9", +} +_WATER_TANK_MISC_PERCENT = Decimal(30) +_WATER_TANK_OPERATORS = Decimal(1) + + class OperatingCostError(ValueError): """운전경비 표를 못 읽은 경우. 어긋난 채로 짝짓지 않는다.""" @@ -278,6 +296,20 @@ def load_operating_records( aliases = _read_json(*_CATALOG_SUBPATH, labor_file)["variables"].get("aliases", {}) catalog = load_machine_catalog() parsed = parse_operating_tables(pum, aliases, set(catalog.machines)) + read = {record.machine_code for record in parsed.records} + for code, liters in MERGED_CELL_OPERATING_FIXES.items(): + if code not in read: + parsed.records.append( + OperatingRecord( + machine_code=code, + machine_name="", + specification="", + fuel_liters_per_hour=Decimal(liters), + fuel_kind="경유", + misc_material_percent=_WATER_TANK_MISC_PERCENT, + operator_person_days=_WATER_TANK_OPERATORS, + ) + ) return enrich_with_catalog(parsed, catalog, aliases) diff --git a/resources/tester/test_b09_water_tank_operating.py b/resources/tester/test_b09_water_tank_operating.py new file mode 100644 index 00000000..b702a38e --- /dev/null +++ b/resources/tester/test_b09_water_tank_operating.py @@ -0,0 +1,47 @@ +"""물탱크(살수차) 운전경비 되살림 — 2026-09-15 브레인 ③ (초류종자살포 5-24 를 막던 넷 중 하나). + +건설품셈 8-4-8 기타기계(원문 L3458)는 36기종이 한 칸에 뭉치고 자동세륜기 「-」 하나가 두 기종 몫(셀 병합)이라 +잡재료 35 · 조종원 34 개로 짝이 안 맞아 **줄째 버려짐** → 물탱크 7204 다섯의 운전경비 레코드가 없어 기계 층이 안 섬. +양끝 맞추기는 우연 일치로 틀린 값이 붙어(자동세륜기 0010 조종원이 「1」 로 읽힘) 못 씀 → 원문 값을 손으로 되살림. +원문: 1,800ℓ 8.2 · 3,800ℓ 8.6 · 5,500ℓ 9.3 · 6,500ℓ 9.4 · 16,000ℓ 12.9 · 잡재료 30 · 조종원 1 +조종원 직종 — 8-1-2 5호 「화물차운전사 … 살수차」(원문 또렷) · 거창 실무 5,500ℓ 호표도 화물차운전사 · 경유 9.3 · 잡품 30 +""" + +from __future__ import annotations + +from decimal import Decimal + +from B09_Estimation.B09_Estimation_MachineOperating import ( + _CATALOG_SUBPATH, + _read_json, + load_operating_records, +) +from B09_Estimation.B09_Estimation_UnitPrice import cached_build + +WATER_TANKS = { + "7204-0018": "8.2", + "7204-0038": "8.6", + "7204-0055": "9.3", + "7204-0065": "9.4", + "7204-0160": "12.9", +} + + +def test_물탱크_다섯은_원문_연료_잡재료_조종원으로_선다() -> None: + records = {r.machine_code: r for r in load_operating_records().records} + truck = _read_json(*_CATALOG_SUBPATH, "labor_const_2026-01-01.json")["variables"]["aliases"][ + "labor_op_truck" + ] + for code, liters in WATER_TANKS.items(): + record = records[code] + assert record.fuel_liters_per_hour == Decimal(liters), record + assert record.fuel_kind == "경유" + assert record.misc_material_percent == Decimal(30) + assert record.operator_person_days == Decimal(1) + assert record.operator_occupation_code == truck, record # 8-1-2 5호 살수차 + + +def test_살수차_기계_층이_서고_막힌_기계에서_빠짐() -> None: + build = cached_build() + assert "X-7204-0055" in build.book.titles + assert not any(m.startswith("7204-0055") for m in build.incomplete_machines)