From bba23376f17cd719b834bfb005874939bbbbf295 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 14 Sep 2026 13:15:09 +0900 Subject: [PATCH] =?UTF-8?q?fix(axis):=209-19-3=20=EB=B9=84=ED=83=88?= =?UTF-8?q?=EB=A9=B4=20=EA=B3=A0=EB=A5=B4=EA=B8=B0(=EB=B0=9C=ED=8C=8C?= =?UTF-8?q?=EC=95=94)=20=EC=86=8C=ED=98=95=EB=B8=8C=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=BB=A4=20=3D=205210-0010(1.0=E3=8E=A5/min)=20=E2=80=94=20?= =?UTF-8?q?=EA=B0=92=20=EC=B6=9C=EC=B2=98=209-19-1=20=ED=91=9C=C2=B7[?= =?UTF-8?q?=EC=A3=BC]=E2=91=A0=20=EC=9D=B4=20=EA=B7=9C=EA=B2=A9=EC=9D=84?= =?UTF-8?q?=20=EC=A0=95=ED=95=A8(=EB=B8=8C=EB=A0=88=EC=9D=B8=20=EC=8A=B9?= =?UTF-8?q?=EC=9D=B8)=20=C2=B7=20=EB=B2=94=EC=9C=84=20=EB=B3=84=EC=B9=AD?= =?UTF-8?q?=EC=9D=B4=20=EC=BD=94=EB=93=9C=EB=A5=BC=20=EC=A0=81=EC=9C=BC?= =?UTF-8?q?=EB=A9=B4=20=EA=B7=B8=20=ED=95=AD=EB=AA=A9=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EA=B3=A7=EC=9E=A5=20=EC=9D=B4=EC=9D=8C=20=C2=B7=20=EC=96=B4?= =?UTF-8?q?=EC=96=B4=ED=98=B8=EC=8A=A4=EB=8A=94=20=EC=B9=B4=ED=83=88?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=20=EC=97=86=EC=96=B4=20=EA=B7=B8=EB=8C=80?= =?UTF-8?q?=EB=A1=9C?= 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/B09_Estimation_ResourceAxis.py | 5 ++++- .../B09_Estimation_ResourceAxis_Join.py | 16 ++++++++++++++++ resources/data_aliases/aliases_2026-01-01.json | 8 ++++++++ resources/tester/test_b09_table_reading_2.py | 18 ++++++++++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/B09_Estimation/B09_Estimation_ResourceAxis.py b/B09_Estimation/B09_Estimation_ResourceAxis.py index b964d3d1..f001cf5f 100644 --- a/B09_Estimation/B09_Estimation_ResourceAxis.py +++ b/B09_Estimation/B09_Estimation_ResourceAxis.py @@ -574,8 +574,10 @@ def match_table( # 정상 자원이 조용히 사라진다(2026-09-07 실측 — 부분일치 필터가 매칭 14건을 # 지우고 있었음). 카탈로그에 있는 이름은 **정의상 자원**이다. # 범위 별칭 — 그 공종 범위 안에서만 카탈로그 쪽 이름으로 바꾼다(「화약공」 → 화약취급공). + # 별칭이 코드를 적었으면 그 항목으로 곧장(규격이 조인 키인 기계가 다시 흐려지지 않게). + entry = scoped_alias_entry(catalog, name_cell, node["work_item_code"]) name_cell = apply_scoped_alias(catalog, name_cell, node["work_item_code"]) - entry = _resolve_cell(catalog, name_cell, [name_cell, *value_cells]) + entry = entry or _resolve_cell(catalog, name_cell, [name_cell, *value_cells]) if entry is None: if is_non_resource_label(name_cell): continue # 머리글·소계 — 못 맞춘 목록에도 안 올린다 @@ -699,6 +701,7 @@ from B09_Estimation.B09_Estimation_ResourceAxis_Sources import ( # noqa: E402 from B09_Estimation.B09_Estimation_ResourceAxis_Join import ( # noqa: E402 apply_scoped_alias, pick_by_spec, + scoped_alias_entry, resolve_family, text_spec_candidates, unmatched_reason, diff --git a/B09_Estimation/B09_Estimation_ResourceAxis_Join.py b/B09_Estimation/B09_Estimation_ResourceAxis_Join.py index 778506f4..8716e524 100644 --- a/B09_Estimation/B09_Estimation_ResourceAxis_Join.py +++ b/B09_Estimation/B09_Estimation_ResourceAxis_Join.py @@ -107,6 +107,22 @@ def load_scoped_aliases() -> list[dict[str, str]]: return load_aliases("resource") +def scoped_alias_entry( + catalog: ResourceCatalog, name_cell: str, work_item_code: str +) -> CatalogEntry | None: + """범위 별칭이 가리키는 **항목 그 자체**(코드로 곧장) — 없으면 `None`. + + ⚠ 이름만 돌려주면 규격이 조인 키인 기계(소형브레이커 공압식 넷)는 다시 「규격 미정」 으로 + 흐려짐 — 별칭이 코드를 적었으면 그 코드가 곧 답(2026-09-14 · 9-19-3 소형브레이커). + """ + wanted = _normalize(name_cell) + for row in catalog.scoped_aliases: + if _normalize(row["from"]) != wanted or not in_scope(work_item_code, row["scope"]): + continue + return next((entry for entry in catalog.entries if entry.code == row["to"]), None) + return None + + def apply_scoped_alias(catalog: ResourceCatalog, name_cell: str, work_item_code: str) -> str: """범위 안이면 카탈로그 쪽 **이름**으로 바꾼다. 범위 밖이거나 대상 코드가 없으면 원문 그대로.""" wanted = _normalize(name_cell) diff --git a/resources/data_aliases/aliases_2026-01-01.json b/resources/data_aliases/aliases_2026-01-01.json index 22ba4693..d1bfac04 100644 --- a/resources/data_aliases/aliases_2026-01-01.json +++ b/resources/data_aliases/aliases_2026-01-01.json @@ -11,6 +11,14 @@ "pum_edition": "2026-01-01", "basis": "노임 표준 직종명은 「화약취급공」(노임 카탈로그 1016) — 품셈 9-5 표가 줄여 적음. 2026-09-13 사용자 위임 판정(브레인)" }, + { + "axis": "resource", + "from": "소형브레이커", + "to": "5210-0010", + "scope": "FP-09-19-03", + "pum_edition": "2026-01-01", + "basis": "9-19-3 비탈면 고르기(발파암) 표가 규격을 안 적음 — 그 값 「(2.45+3.05)/2/10㎡」 은 9-19-1 토사면 고르기 표(연암 2.45·보통암+경암 3.05)에서 왔고 9-19-1 [주]① 「소형브레이커는 1㎥/분」 → 소형브레이커(공압식) 1.0㎥/min(9-19-3 ← 9-19-1 표 ← [주]①, 값의 출처가 규격을 정함). 2026-09-14 브레인 승인" + }, { "axis": "variant", "from": "리핑암", diff --git a/resources/tester/test_b09_table_reading_2.py b/resources/tester/test_b09_table_reading_2.py index d6fa7a81..6fe7209c 100644 --- a/resources/tester/test_b09_table_reading_2.py +++ b/resources/tester/test_b09_table_reading_2.py @@ -40,3 +40,21 @@ def test_육상_발파암_1_3m_들어내기는_0_1m_로_읽고_고른_값_버린 assert not left, left source = build.factor_sources.get(code, "") assert "육상 발파암(0~1m)" in source and "자기 참조" in source, source + + +def test_비탈면_고르기_발파암_소형브레이커는_1_0_규격으로_코드에_이어짐() -> None: + """④ 9-19-3 값 「(2.45+3.05)/2/10㎡」 ← 9-19-1 표 ← 9-19-1 [주]① 「소형브레이커는 1㎥/분」 — + 값의 출처가 규격을 정함(2026-09-14 브레인 승인). 범위 별칭이 **코드로** 곧장 이음(이름만 돌려주면 + 규격 후보 넷이 다시 흐려짐). 「어어호스」 는 원문 오기 + 카탈로그에 호스 없음 → 못 붙은 줄 그대로.""" + from B09_Estimation.B09_Estimation_ResourceAxis import ( + build_resource_axis, + load_combined_catalog, + ) + + axis = build_resource_axis(load_work_item_master(), load_combined_catalog()) + rows = [ + (r.resource_code, str(r.amount)) for r in axis.rows if r.work_item_code == "FP-09-19-03" + ] + assert ("5210-0010", "0.275") in rows, rows + left = [u.cell for u in axis.unmatched if u.work_item_code == "FP-09-19-03"] + assert left == ["어어호스(3/4인치)"], left