diff --git a/B09_Estimation/B09_Estimation_ResourceAxis.py b/B09_Estimation/B09_Estimation_ResourceAxis.py index 09b5f57b..1c9e4525 100644 --- a/B09_Estimation/B09_Estimation_ResourceAxis.py +++ b/B09_Estimation/B09_Estimation_ResourceAxis.py @@ -41,6 +41,13 @@ _CATALOG_SUBPATH = ("resources", "data_cost_input_value") _RE_SPEC = re.compile(r"[((]([^))]+)[))]|(\d+(?:\.\d+)?\s*(?:톤|ton|㎥|m3|㎡|㎜|mm|HP|kW))") _RE_NUMBER = re.compile(r"^-?\d+(?:,\d{3})*(?:\.\d+)?$") +#: 첫 칸이 **분류 딱지**이고 이름이 둘째 칸에 오는 표가 있다. +#: 예 — `['자재', '종 자', '', 'kg', '0.025']` · `['장비', '종자살포기', …]`. +#: 이 표를 첫 칸만 보고 읽으면 **자재·장비가 통째로 빠진다**(2026-09-07 실측 — +#: 씨앗뿜어붙이기에서 종자·비료·피복제·침식안정제·색소·장비 3종이 다 빠지고 +#: 보통인부 한 줄만 남았다). +_GROUP_LABELS = ("자재", "장비", "인력", "노무", "재료", "기계") + #: 표 머리글·소계 행의 첫 칸에 오는 말. 자원이 아니므로 `unmatched` 로도 안 올린다. #: 이것을 안 거르면 못 맞춘 목록이 머리글로 가득 차 **쓸 수 없는 목록**이 된다. #: ⚠ **부분일치로 보면 안 된다.** 「계」를 부분일치로 잡으면 `건설기계운전사`·`비계공`· @@ -447,11 +454,16 @@ def match_table( cells = [str(c) for c in row] if not cells: continue + # 첫 칸이 분류 딱지(「자재」·「장비」)면 **이름은 둘째 칸**이다. name_cell = cells[0] + value_cells = cells[1:] + if _normalize(name_cell) in _GROUP_LABELS and len(cells) > 1: + name_cell = cells[1] + value_cells = cells[2:] # 숫자 셀이 없는 행은 자원 줄이 아니다(제목·설명 행) — 목록에 안 올린다. amount_cell = next( - (parse_amount(c) for c in cells[1:] if parse_amount(c) is not None), None + (parse_amount(c) for c in value_cells if parse_amount(c) is not None), None ) if amount_cell is None: continue @@ -459,7 +471,7 @@ def match_table( # ⚠ **카탈로그 조회를 먼저 한다.** 이름 필터를 앞에 두면 필터가 넓을 때 # 정상 자원이 조용히 사라진다(2026-09-07 실측 — 부분일치 필터가 매칭 14건을 # 지우고 있었음). 카탈로그에 있는 이름은 **정의상 자원**이다. - entry = _resolve_cell(catalog, name_cell, cells) + entry = _resolve_cell(catalog, name_cell, [name_cell, *value_cells]) if entry is None: if is_non_resource_label(name_cell): continue # 머리글·소계 — 못 맞춘 목록에도 안 올린다 diff --git a/resources/data_cost_resource_axis/resource_axis_2026-01-01.json b/resources/data_cost_resource_axis/resource_axis_2026-01-01.json index a1abf61c..5774d43a 100644 --- a/resources/data_cost_resource_axis/resource_axis_2026-01-01.json +++ b/resources/data_cost_resource_axis/resource_axis_2026-01-01.json @@ -211,6 +211,18 @@ "resource_spec": "", "work_item_code": "FP-05-23-03" }, + { + "amount": "0.0007", + "amount_unit": "", + "pum_form": "requirement", + "pum_table_id": "F0135", + "raw_row_index": 8, + "resource_code": "1038", + "resource_kind": "labor", + "resource_name": "조경공", + "resource_spec": "", + "work_item_code": "FP-05-24-01" + }, { "amount": "0.0004", "amount_unit": "", @@ -223,6 +235,18 @@ "resource_spec": "", "work_item_code": "FP-05-24-01" }, + { + "amount": "0.0007", + "amount_unit": "", + "pum_form": "requirement", + "pum_table_id": "F0136", + "raw_row_index": 8, + "resource_code": "1038", + "resource_kind": "labor", + "resource_name": "조경공", + "resource_spec": "", + "work_item_code": "FP-05-24-02" + }, { "amount": "0.0004", "amount_unit": "", @@ -235,6 +259,18 @@ "resource_spec": "", "work_item_code": "FP-05-24-02" }, + { + "amount": "0.002", + "amount_unit": "", + "pum_form": "requirement", + "pum_table_id": "F0139", + "raw_row_index": 1, + "resource_code": "1038", + "resource_kind": "labor", + "resource_name": "조경공", + "resource_spec": "", + "work_item_code": "FP-05-25" + }, { "amount": "0.0007", "amount_unit": "", @@ -415,6 +451,18 @@ "resource_spec": "", "work_item_code": "FP-09-05-01" }, + { + "amount": "0.02", + "amount_unit": "", + "pum_form": "requirement", + "pum_table_id": "F0251", + "raw_row_index": 0, + "resource_code": "1012", + "resource_kind": "labor", + "resource_name": "용접공", + "resource_spec": "", + "work_item_code": "FP-09-08-01" + }, { "amount": "0.08", "amount_unit": "", @@ -559,6 +607,30 @@ "resource_spec": "", "work_item_code": "FP-09-13-18" }, + { + "amount": "0.019", + "amount_unit": "", + "pum_form": "requirement", + "pum_table_id": "F0290", + "raw_row_index": 0, + "resource_code": "1002", + "resource_kind": "labor", + "resource_name": "보통인부", + "resource_spec": "", + "work_item_code": "FP-09-19-02" + }, + { + "amount": "0.0328", + "amount_unit": "", + "pum_form": "requirement", + "pum_table_id": "F0291", + "raw_row_index": 0, + "resource_code": "1002", + "resource_kind": "labor", + "resource_name": "보통인부", + "resource_spec": "", + "work_item_code": "FP-09-19-03" + }, { "amount": "0.80", "amount_unit": "", @@ -943,6 +1015,18 @@ "resource_spec": "", "work_item_code": "FP-12-18" }, + { + "amount": "0.034", + "amount_unit": "", + "pum_form": "requirement", + "pum_table_id": "F0368", + "raw_row_index": 1, + "resource_code": "1026", + "resource_kind": "labor", + "resource_name": "방수공", + "resource_spec": "", + "work_item_code": "FP-12-21" + }, { "amount": "0.04", "amount_unit": "", @@ -955,6 +1039,18 @@ "resource_spec": "", "work_item_code": "FP-12-21" }, + { + "amount": "0.003", + "amount_unit": "", + "pum_form": "requirement", + "pum_table_id": "F0370", + "raw_row_index": 2, + "resource_code": "1002", + "resource_kind": "labor", + "resource_name": "보통인부", + "resource_spec": "", + "work_item_code": "FP-12-23" + }, { "amount": "4.0", "amount_unit": "", @@ -991,6 +1087,30 @@ "resource_spec": "", "work_item_code": "FP-12-24-01" }, + { + "amount": "0.004", + "amount_unit": "", + "pum_form": "requirement", + "pum_table_id": "F0384", + "raw_row_index": 1, + "resource_code": "1002", + "resource_kind": "labor", + "resource_name": "보통인부", + "resource_spec": "", + "work_item_code": "FP-12-33" + }, + { + "amount": "3.8", + "amount_unit": "", + "pum_form": "requirement", + "pum_table_id": "F0387", + "raw_row_index": 1, + "resource_code": "1008", + "resource_kind": "labor", + "resource_name": "철근공", + "resource_spec": "", + "work_item_code": "FP-12-34-03" + }, { "amount": "2.2", "amount_unit": "", @@ -1288,12 +1408,12 @@ "sha256": "111208fd482dcfc3b8c8dd58004b153382fc46555d0d00985c19b45ebbc2e0dd" }, "stats": { - "rows": 106, + "rows": 116, "skipped_forms": { "coefficient": 19, "reference": 94, "undetermined": 83 }, - "unmatched": 344 + "unmatched": 348 } } \ No newline at end of file diff --git a/resources/data_cost_resource_axis/unmatched_2026-01-01.json b/resources/data_cost_resource_axis/unmatched_2026-01-01.json index 5143c743..915d08fc 100644 --- a/resources/data_cost_resource_axis/unmatched_2026-01-01.json +++ b/resources/data_cost_resource_axis/unmatched_2026-01-01.json @@ -417,7 +417,7 @@ "work_item_code": "FP-05-22-04" }, { - "cell": "자재", + "cell": "종 자", "pum_table_id": "F0135", "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", "work_item_code": "FP-05-24-01" @@ -446,6 +446,12 @@ "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", "work_item_code": "FP-05-24-01" }, + { + "cell": "종자살포기", + "pum_table_id": "F0135", + "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", + "work_item_code": "FP-05-24-01" + }, { "cell": "트 럭", "pum_table_id": "F0135", @@ -459,7 +465,7 @@ "work_item_code": "FP-05-24-01" }, { - "cell": "자재", + "cell": "종 자", "pum_table_id": "F0136", "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", "work_item_code": "FP-05-24-02" @@ -488,6 +494,12 @@ "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", "work_item_code": "FP-05-24-02" }, + { + "cell": "종자살포기", + "pum_table_id": "F0136", + "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", + "work_item_code": "FP-05-24-02" + }, { "cell": "트 럭", "pum_table_id": "F0136", @@ -501,7 +513,7 @@ "work_item_code": "FP-05-24-02" }, { - "cell": "자재", + "cell": "거 적", "pum_table_id": "F0139", "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", "work_item_code": "FP-05-25" @@ -999,7 +1011,7 @@ "work_item_code": "FP-09-04-01" }, { - "cell": "자재", + "cell": "폭 약", "pum_table_id": "F0243", "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", "work_item_code": "FP-09-05-01" @@ -1016,6 +1028,18 @@ "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", "work_item_code": "FP-09-05-01" }, + { + "cell": "화 약 공", + "pum_table_id": "F0243", + "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", + "work_item_code": "FP-09-05-01" + }, + { + "cell": "착 암 기", + "pum_table_id": "F0243", + "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", + "work_item_code": "FP-09-05-01" + }, { "cell": "공기압축기", "pum_table_id": "F0243", @@ -1047,7 +1071,7 @@ "work_item_code": "FP-09-06-01" }, { - "cell": "자재", + "cell": "아세틸렌", "pum_table_id": "F0251", "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", "work_item_code": "FP-09-08-01" @@ -1079,7 +1103,7 @@ { "cell": "아스팔트", "pum_table_id": "F0255", - "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", + "reason": "규격이 없어 같은 이름 여럿 중 고를 수 없음", "work_item_code": "FP-09-10-02" }, { @@ -1478,6 +1502,18 @@ "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", "work_item_code": "FP-09-19-01" }, + { + "cell": "유압식백호우 (무한궤도,0.7㎥)", + "pum_table_id": "F0290", + "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", + "work_item_code": "FP-09-19-02" + }, + { + "cell": "공기압축기(3.5㎥/min)", + "pum_table_id": "F0291", + "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", + "work_item_code": "FP-09-19-03" + }, { "cell": "소형브레이커", "pum_table_id": "F0291", @@ -1601,19 +1637,19 @@ { "cell": "각 재", "pum_table_id": "F0336", - "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", + "reason": "규격이 없어 같은 이름 여럿 중 고를 수 없음", "work_item_code": "FP-12-04" }, { "cell": "철 선", "pum_table_id": "F0336", - "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", + "reason": "규격이 없어 같은 이름 여럿 중 고를 수 없음", "work_item_code": "FP-12-04" }, { "cell": "박 리 제", "pum_table_id": "F0336", - "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", + "reason": "규격이 없어 같은 이름 여럿 중 고를 수 없음", "work_item_code": "FP-12-04" }, { @@ -1737,13 +1773,13 @@ "work_item_code": "FP-12-18" }, { - "cell": "자재", + "cell": "아스팔트(㏊-500)", "pum_table_id": "F0368", - "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", + "reason": "규격이 없어 같은 이름 여럿 중 고를 수 없음", "work_item_code": "FP-12-21" }, { - "cell": "자재", + "cell": "부 직 포", "pum_table_id": "F0370", "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", "work_item_code": "FP-12-23" @@ -1796,12 +1832,6 @@ "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", "work_item_code": "FP-12-24-01" }, - { - "cell": "자재", - "pum_table_id": "F0376", - "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", - "work_item_code": "FP-12-27-02" - }, { "cell": "접착제", "pum_table_id": "F0376", @@ -1815,13 +1845,7 @@ "work_item_code": "FP-12-27-02" }, { - "cell": "재료", - "pum_table_id": "F0384", - "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", - "work_item_code": "FP-12-33" - }, - { - "cell": "자재", + "cell": "결속선(R-0.9mm)", "pum_table_id": "F0387", "reason": "카탈로그에 없는 이름 (기계·자재 카탈로그 미확보 포함)", "work_item_code": "FP-12-34-03"