From e15e9fc2235d695f1e30f79c49d77d9caba85891 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Tue, 15 Sep 2026 02:47:59 +0900 Subject: [PATCH] =?UTF-8?q?fix(b09):=202=EB=8B=A8=20=EB=A8=B8=EB=A6=AC=20?= =?UTF-8?q?=ED=91=9C=EA=B0=80=20=EB=B0=91=EC=88=98=EB=A1=9C=20=EC=95=88=20?= =?UTF-8?q?=EB=82=98=EB=88=84=EB=8D=98=20=EA=B2=83=20=E2=80=94=20=EA=B0=88?= =?UTF-8?q?=EB=9E=98=20=EC=9D=B4=EB=A6=84=EC=9D=98=20=E3=80=8C(Nha?= =?UTF-8?q?=EB=8B=B9)=E3=80=8D=20=EB=B0=91=EC=88=98=EB=A1=9C=20=EB=82=98?= =?UTF-8?q?=EB=88=94(8-6-1=20=EC=9C=A0=EC=9D=B8=ED=97=AC=EA=B8=B0=20160?= =?UTF-8?q?=EB=B0=B0=C2=B7400=EB=B0=B0=20=EB=B6=80=ED=92=82)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit · _match_two_row_table 이 표 밑수로 아예 안 나눠 제목 단위 ha 에 160ha·400ha 몫이 그대로 앉아 있었음 · 표 밑수가 한 벌뿐이라 갈래마다 다른 밑수(소형 160ha · 대형 400ha)도 못 가르던 병 → 갈래 이름이 제 밑수를 적으면 그것으로, 없으면 표 밑수로 · 그 길을 지나는 표 다섯 중 밑수가 1 이 아닌 것 8-6-1 하나 · 전체에서 갈래마다 밑수가 다른 표도 8-6-1 하나 8-6-1 소형 1,591,855 → 9,949원/ha · 대형 2,780,567 → 6,951 · 나머지 제목 그대로 · 잴 시험 빨강→초록 · 끄기 빨강 · 전체 시험 통과 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn --- .../B09_Estimation_ResourceAxis_Transposed.py | 13 +++++- resources/tester/test_b09_variant_basis.py | 40 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 resources/tester/test_b09_variant_basis.py diff --git a/B09_Estimation/B09_Estimation_ResourceAxis_Transposed.py b/B09_Estimation/B09_Estimation_ResourceAxis_Transposed.py index 79b87bf4..d8595726 100644 --- a/B09_Estimation/B09_Estimation_ResourceAxis_Transposed.py +++ b/B09_Estimation/B09_Estimation_ResourceAxis_Transposed.py @@ -46,6 +46,8 @@ def _normalize_label(text: str) -> str: #: 「계」 열 — **가공 + 조립을 이미 더한 값**이다. 같이 읽으면 두 번 센다(㉤ 열 방향). _SUM_GROUP_LABELS = ("계", "합계", "소계", "총계") +#: 갈래 이름에 적힌 제 밑수 — 「대형헬기 (400ha당)」. +_RE_VARIANT_BASIS = re.compile(r"\(\s*(\d[\d,]*(?:\.\d+)?)\s*(?:ha|㏊|㎡|㎥|m|본|개소)\s*당\s*\)") def _sum_group_positions(headers: list, resource_count: int) -> set: @@ -143,6 +145,7 @@ def _match_two_row_table( unit: str, ordinal: list, skip_rows: int, + basis_quantity: Decimal | None = None, ) -> bool: """2단 표 — **숫자 칸을 순서대로** 자원에 맞춘다. @@ -180,9 +183,15 @@ def _match_two_row_table( ) ) continue + # ⚠ 밑수로 나눔 — 갈래 이름이 「(400ha당)」 처럼 제 밑수를 적으면 그것으로(8-6-1 유인헬기가 + # 160배·400배 부풀어 있던 자리 · 2026-09-15). 표 밑수가 한 벌뿐이라 갈래 밑수를 못 가르던 병. + found = _RE_VARIANT_BASIS.search(variant) + divisor = Decimal(found.group(1).replace(",", "")) if found else basis_quantity for (order, entry, blocked), amount in zip(ordinal, numbers): if blocked: continue # 「계」 묶음 — 이미 더한 값이다 + if divisor not in (None, 0, Decimal(1)): + amount = amount / divisor result.rows.append( ResourceRow( work_item_code=work_item_code, @@ -497,7 +506,9 @@ def match_transposed_table( # 자원 이름이 **둘째 줄**에 오는 2단 표일 수 있다. ordinal, skip_rows = second_row_columns(table, catalog) if ordinal: - return _match_two_row_table(node, table, catalog, result, unit, ordinal, skip_rows) + return _match_two_row_table( + node, table, catalog, result, unit, ordinal, skip_rows, basis_quantity + ) if not columns: return False diff --git a/resources/tester/test_b09_variant_basis.py b/resources/tester/test_b09_variant_basis.py new file mode 100644 index 00000000..836db1b4 --- /dev/null +++ b/resources/tester/test_b09_variant_basis.py @@ -0,0 +1,40 @@ +"""갈래마다 밑수가 다른 표 — 2026-09-15 브레인(헬기 착수 전 바로잡기). + +8-6-1 유인헬기방제 표(F0225)는 「소형헬기 (160ha당)」·「대형헬기 (400ha당)」 갈래마다 밑수가 다른데, +2단 머리 표 읽기(`_match_two_row_table`)가 **밑수로 아예 안 나눠** 제목 단위 「ha」 에 160ha·400ha 몫이 +그대로 앉아 있었음(160배·400배 부풂). 표 밑수가 한 벌(160ha)로만 잡혀 대형 400ha 도 못 가름. +⇒ 갈래 이름의 「(N단위당)」 이 있으면 그 밑수로, 없으면 표 밑수로 나눔. +그 길을 지나는 표 다섯 중 밑수가 1 이 아닌 표는 8-6-1 하나 · 갈래마다 밑수가 다른 표도 전체에서 8-6-1 하나. +""" + +from __future__ import annotations + +from decimal import Decimal + +from B09_Estimation.B09_Estimation_UnitPrice import cached_build + + +def _labor(book, title: str) -> dict[str, Decimal]: + return { + book.titles[r.ref_code].name: r.quantity + for r in book.details[title] + if r.ref_code in ("1002", "1003") + } + + +def test_유인헬기_인력은_갈래_밑수로_나눈_ha당() -> None: + book = cached_build().book + small = _labor(book, "B-FP-08-06-01#소형헬기(160ha당)") + assert ( + small["특별인부"] == Decimal("0.8") / 160 and small["보통인부"] == Decimal("8.2") / 160 + ), small + large = _labor(book, "B-FP-08-06-01#대형헬기(400ha당)") + assert ( + large["특별인부"] == Decimal("2.1") / 400 and large["보통인부"] == Decimal("13.4") / 400 + ), large + + +def test_밑수_1_인_2단_표는_그대로() -> None: + book = cached_build().book + rows = [r for t in book.titles if t.startswith("B-FP-12-03#") for r in book.details[t]] + assert rows and all(r.quantity < 20 for r in rows) # 철근 가공·조립 ton당 — 나누기 전과 같음