fix(axis): 표 읽기 — 단끊기 5-16-1 공정 줄 합산 · 「(인/100m당)」 꼴 밑수 22표
단끊기는 공정 네 줄을 토질 갈래마다 더하고 [주]⑤ 작업반장 1인/보통인부 20인을 더함. 원문 합계 줄은 절취를 ㎥당으로 더한 값이라 안 씀(까닭을 적은 공종만 섬). 같은 모양 뭉기기 13-12-1 은 안 더함. 「단위:」 없이 분모만 적은 본문 밑수를 읽어 조림·시비·떼심기 22표가 밑수 없음에서 풀림. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
This commit is contained in:
@@ -276,8 +276,9 @@ BASIS_RE = re.compile(r"(\d[\d,.]*)\s*(㎥|m3|㎡|m2|㏊|ha|km|㎞|m|인|본|개
|
||||
# ⚠ **「당」 또는 「단위:」 가 있어야 밑수다.** 둘 다 없으면 규격일 뿐이다 —
|
||||
# 만들다 실제로 걸렸다: `(무한궤도,0.7㎥)` 를 「0.7㎥당」으로 읽어 5건이 잘못 잡혔다.
|
||||
#: ⚠ `(단위: 인/㎡당)` 꼴 — **분모가 밑수**다. 값의 단위(인)를 밑수로 읽으면 뜻이 뒤집힌다.
|
||||
#: ⚠ 「단위:」 없이 `(인/100m당)` 으로만 적은 표가 22 곳이다(조림 5장·단끊기 5-16-1 등, 2026-09-13).
|
||||
SOURCE_BASIS_RATIO_RE = re.compile(
|
||||
r"[((]\s*단위\s*[::]\s*[^))/]+/\s*([\d,]*\.?\d*)\s*"
|
||||
r"[((]\s*(?:단위\s*[::]\s*)?[^))/::]+/\s*([\d,]*\.?\d*)\s*"
|
||||
r"(㎥|m3|㎡|m2|㏊|ha|㎞|km|m|매|본|개소|개|인|공|kg|㎏|톤|ton|주|식|대)\s*당\s*[))]"
|
||||
)
|
||||
|
||||
|
||||
@@ -441,6 +441,12 @@ def match_table(
|
||||
# 「구 분 | 콘크리트공(인) | 보통인부(인)」처럼 **열이 자원**이고 행은 규격 갈래
|
||||
# (무근·철근·소형구조물)다. 행을 자원으로 읽는 길로 보내면 통째로 못 맞춘다 —
|
||||
# 콘크리트 타설(12-1)이 그래서 하나도 안 서고 있었다.
|
||||
# 공정 줄을 더해 한 품이 되는 표(단끊기 5-16-1) — 사람이 적은 공종만. 아래 길은 표째 버린다.
|
||||
from B09_Estimation.B09_Estimation_ResourceAxis_ProcessSum import match_process_sum_table
|
||||
|
||||
if match_process_sum_table(node, table, catalog, result, basis_quantity, unit):
|
||||
return
|
||||
|
||||
from B09_Estimation.B09_Estimation_ResourceAxis_Transposed import match_transposed_table
|
||||
|
||||
if match_transposed_table(node, table, catalog, result, basis_quantity, unit):
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
"""B09 원가계산 — **공정 줄을 더해 한 품이 되는 표** 읽기 (자원 축 보조, 2026-09-13).
|
||||
|
||||
공정별 | 보통인부(인) | 비고
|
||||
보통토사 | 경질ㆍ고사점토 및 자갈섞인 점토 | 호박돌 섞인 토사
|
||||
절 취 | 2.4 | 3.3 | 5.4
|
||||
수평잡기 및 단정리 | 0.34 | 0.34 | 0.34
|
||||
…
|
||||
합계 | 2.03 | 2.09 | 2.23
|
||||
|
||||
행은 공정, 열은 토질 갈래, 자원은 머리 한 칸. 갈래마다 공정 줄을 **더한 것**이 한 품이다.
|
||||
지금은 「숫자 칸이 자원 열보다 많다」로 표째 버려져 단끊기 5-16-1 이 한 줄도 안 섰다.
|
||||
|
||||
⚠ **모양만으로는 「더하는 표」인지 못 가른다** — 뭉기기 13-12-1 은 같은 모양인데 줄마다 단위가
|
||||
달라(절취 ㎥ · 면고르기 시간/㎡) 더하면 틀린다. 그래서 **사람이 적은 공종만** 더한다.
|
||||
⚠ 원문 합계 줄은 안 읽는다 — 줄 합과 다르면 **까닭을 적어 둔 공종만** 서고, 아니면 막는다.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from decimal import Decimal
|
||||
from typing import Any
|
||||
|
||||
from B09_Estimation.B09_Estimation_ResourceAxis import (
|
||||
AxisResult,
|
||||
ResourceCatalog,
|
||||
ResourceRow,
|
||||
UnmatchedRow,
|
||||
parse_amount,
|
||||
)
|
||||
|
||||
_TOTAL_LABELS = ("계", "합계", "소계", "총계")
|
||||
|
||||
#: 공종 → 근거 · 원문 합계 줄과 다를 때의 까닭 · [주]가 더하라는 직종(코드, 보통인부 몇 인당 1인).
|
||||
PROCESS_SUM: dict[str, dict[str, Any]] = {
|
||||
"FP-05-16-01": {
|
||||
"basis": (
|
||||
"품셈 5-16-1 표 머리 (인/100m당) · [주]④ 「수평잡기 및 단정리, 잡석 및 뿌리정리,"
|
||||
" 성토면고르기, 고르기품은 100m당 품」 · [주]③ 절취량 0.15㎥/m 기준"
|
||||
),
|
||||
"total_mismatch": (
|
||||
"원문 합계 줄(2.03·2.09·2.23)은 절취를 ㎥당(2.4÷15=0.16)으로 더한 값이라 줄 합과 다름"
|
||||
" — 100m당 줄을 더함(STmate 단끊기 산출과 같음)"
|
||||
),
|
||||
"per_workers": ("1001", Decimal(20), "[주]⑤ 작업반장 1인/보통인부 20인 가산"),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _tight(text: Any) -> str:
|
||||
return "".join(str(text or "").split())
|
||||
|
||||
|
||||
def match_process_sum_table(
|
||||
node: dict[str, Any],
|
||||
table: dict[str, Any],
|
||||
catalog: ResourceCatalog,
|
||||
result: AxisResult,
|
||||
basis_quantity: Decimal | None,
|
||||
unit: str,
|
||||
) -> bool:
|
||||
"""적어 둔 공종의 공정 합산 표를 읽는다. 그 공종이 아니면 `False`."""
|
||||
from B09_Estimation.B09_Estimation_ResourceAxis_Transposed import transposed_columns
|
||||
|
||||
code = str(node.get("work_item_code") or "")
|
||||
spec = PROCESS_SUM.get(code)
|
||||
if spec is None:
|
||||
return False
|
||||
table_id = str(table.get("pum_table_id", ""))
|
||||
rows = [[str(c).strip() for c in row] for row in (table.get("raw_row") or [])]
|
||||
columns = transposed_columns(table, catalog)
|
||||
|
||||
def block(cell: str, why: str) -> bool:
|
||||
result.unmatched.append(UnmatchedRow(code, table_id, cell, why))
|
||||
result.partial_items[code] = why
|
||||
return True
|
||||
|
||||
labels = [cell for cell in (rows[0] if rows else []) if cell]
|
||||
if len(columns) != 1 or len(labels) < 2 or basis_quantity in (None, 0):
|
||||
return block(code, "공정 합산 표 모양이 아닙니다(자원 한 칸 · 갈래 줄 · 밑수)")
|
||||
sums = [Decimal(0)] * len(labels)
|
||||
totals: list[Decimal] | None = None
|
||||
for cells in rows[1:]:
|
||||
numbers = [value for value in (parse_amount(c) for c in cells[1:]) if value is not None]
|
||||
if not cells or not cells[0] or not numbers:
|
||||
continue
|
||||
if len(numbers) != len(labels):
|
||||
return block(cells[0], f"값 {len(numbers)} 개가 갈래 {len(labels)} 개와 안 맞습니다")
|
||||
if _tight(cells[0]) in _TOTAL_LABELS:
|
||||
totals = numbers
|
||||
continue
|
||||
sums = [total + value for total, value in zip(sums, numbers)]
|
||||
if totals is not None and totals != sums and not spec.get("total_mismatch"):
|
||||
return block("합계", f"원문 합계 {totals} 가 줄 합 {sums} 와 다릅니다")
|
||||
|
||||
entry = columns[0][1]
|
||||
members = [(entry, Decimal(1))]
|
||||
if spec.get("per_workers"):
|
||||
chief_code, workers, _note = spec["per_workers"]
|
||||
chief = next((e for e in catalog.entries if e.code == chief_code), None)
|
||||
if chief is None:
|
||||
return block(chief_code, "[주]가 더하라는 직종이 카탈로그에 없습니다")
|
||||
members.append((chief, Decimal(1) / workers))
|
||||
for label, total in zip(labels, sums):
|
||||
for member, ratio in members:
|
||||
result.rows.append(
|
||||
ResourceRow(
|
||||
work_item_code=code,
|
||||
pum_table_id=table_id,
|
||||
pum_form=str(table.get("pum_form", "")),
|
||||
resource_kind=member.kind,
|
||||
resource_code=member.code,
|
||||
resource_name=member.name,
|
||||
resource_spec=member.spec,
|
||||
amount=total * ratio / basis_quantity,
|
||||
amount_unit=unit,
|
||||
raw_row_index=0,
|
||||
variant=label,
|
||||
)
|
||||
)
|
||||
return True
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"dataset_id": "data_work_item_master_manifest",
|
||||
"generated_at": "2026-09-13T18:54:39+09:00",
|
||||
"generated_at": "2026-09-13T19:52:49+09:00",
|
||||
"built_by": "B08_Quantity/B08_Quantity_Build_WorkItemMaster.py",
|
||||
"source": {
|
||||
"dataset_id": "pum_forest",
|
||||
@@ -12,8 +12,8 @@
|
||||
"files": [
|
||||
{
|
||||
"file": "work_item_master_2026-01-01.json",
|
||||
"sha256": "11c49ecde0a611307a82fd98b3625ee6577c01c2f77db6e1f515b41bf83ce685",
|
||||
"size_bytes": 861050
|
||||
"sha256": "6d1b46ce4e619f0f828dbf111e05a50c0bc22cff969fca1e38d5736f6e7dd440",
|
||||
"size_bytes": 861142
|
||||
},
|
||||
{
|
||||
"file": "form_undetermined_2026-01-01.json",
|
||||
@@ -22,8 +22,8 @@
|
||||
},
|
||||
{
|
||||
"file": "basis_missing_2026-01-01.json",
|
||||
"sha256": "cb97edff0f47183173e23549c77f98f0509441edecc291bfcabba23254e60e9d",
|
||||
"size_bytes": 18770
|
||||
"sha256": "bc3b92c991e03ddd6a2ca837e4a03a0729e3022fb783f741e6c504f5cb3a2813",
|
||||
"size_bytes": 15905
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -130,24 +130,6 @@
|
||||
"pum_form": "requirement",
|
||||
"line": 2062
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0092",
|
||||
"section": "5-1-1. 관목굴취",
|
||||
"pum_form": "requirement",
|
||||
"line": 2081
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0093",
|
||||
"section": "5-1-2. 교목굴취(나무높이)",
|
||||
"pum_form": "requirement",
|
||||
"line": 2099
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0094",
|
||||
"section": "5-1-3. 교목굴취(근원직경)",
|
||||
"pum_form": "requirement",
|
||||
"line": 2125
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0095",
|
||||
"section": "5-1-3. 교목굴취(근원직경)",
|
||||
@@ -160,108 +142,18 @@
|
||||
"pum_form": "requirement",
|
||||
"line": 2177
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0098",
|
||||
"section": "5-2. 뿌리돌림",
|
||||
"pum_form": "requirement",
|
||||
"line": 2194
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0099",
|
||||
"section": "5-3-1. 나무식재",
|
||||
"pum_form": "requirement",
|
||||
"line": 2219
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0102",
|
||||
"section": "5-3-2. 관목식재(단식)",
|
||||
"pum_form": "requirement",
|
||||
"line": 2254
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0103",
|
||||
"section": "5-3-3. 관목식재(군식)",
|
||||
"pum_form": "requirement",
|
||||
"line": 2272
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0104",
|
||||
"section": "5-3-4. 교목식재(나무높이)",
|
||||
"pum_form": "requirement",
|
||||
"line": 2291
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0106",
|
||||
"section": "5-3-5. 교목식재(흉고직경)",
|
||||
"pum_form": "requirement",
|
||||
"line": 2322
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0108",
|
||||
"section": "5-3-5. 교목식재(흉고직경)",
|
||||
"pum_form": "requirement",
|
||||
"line": 2355
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0109",
|
||||
"section": "5-4. 파종조림",
|
||||
"pum_form": "requirement",
|
||||
"line": 2366
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0110",
|
||||
"section": "5-5. 천연하종갱신",
|
||||
"pum_form": "requirement",
|
||||
"line": 2383
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0111",
|
||||
"section": "5-6. 움싹갱신",
|
||||
"pum_form": "requirement",
|
||||
"line": 2396
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0112",
|
||||
"section": "5-7. 생태보완조림",
|
||||
"pum_form": "requirement",
|
||||
"line": 2409
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0113",
|
||||
"section": "5-8. 큰나무 공익조림",
|
||||
"pum_form": "requirement",
|
||||
"line": 2426
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0114",
|
||||
"section": "5-9. 해안조림",
|
||||
"pum_form": "requirement",
|
||||
"line": 2439
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0116",
|
||||
"section": "5-11. 사초심기",
|
||||
"pum_form": "requirement",
|
||||
"line": 2475
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0117",
|
||||
"section": "5-12. 떼붙임(재배잔디)",
|
||||
"pum_form": "requirement",
|
||||
"line": 2496
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0118",
|
||||
"section": "5-13. 떼심기",
|
||||
"pum_form": "requirement",
|
||||
"line": 2511
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0121",
|
||||
"section": "5-16-1. 단끊기",
|
||||
"pum_form": "requirement",
|
||||
"line": 2565
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0126",
|
||||
"section": "5-19-1. 표토절취 및 모으기",
|
||||
@@ -364,24 +256,6 @@
|
||||
"pum_form": "requirement",
|
||||
"line": 3244
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0164",
|
||||
"section": "6-6. 가지치기 및 수형교정",
|
||||
"pum_form": "requirement",
|
||||
"line": 3276
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0165",
|
||||
"section": "6-7-1. 교목 시비",
|
||||
"pum_form": "requirement",
|
||||
"line": 3304
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0166",
|
||||
"section": "6-7-2. 관목 시비",
|
||||
"pum_form": "requirement",
|
||||
"line": 3318
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0170",
|
||||
"section": "7-1-1. 수확",
|
||||
@@ -490,12 +364,6 @@
|
||||
"pum_form": "requirement",
|
||||
"line": 3763
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0201",
|
||||
"section": "8-1-1. 약제주입기",
|
||||
"pum_form": "requirement",
|
||||
"line": 3945
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0202",
|
||||
"section": "8-1-2. 약제주입병",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"schema_version": "1.0",
|
||||
"dataset_id": "work_item_master_forest",
|
||||
"effective_date": "2026-01-01",
|
||||
"generated_at": "2026-09-13T18:54:39+09:00",
|
||||
"generated_at": "2026-09-13T19:52:49+09:00",
|
||||
"dataset_version": {
|
||||
"dataset_id": "pum_forest",
|
||||
"effective_date": "2026-01-01",
|
||||
@@ -21,9 +21,9 @@
|
||||
"tables_attached": 456,
|
||||
"tables_orphan": 19,
|
||||
"form_undetermined": 77,
|
||||
"basis_found": 182,
|
||||
"basis_missing": 138,
|
||||
"basis_grouped": 37
|
||||
"basis_found": 204,
|
||||
"basis_missing": 116,
|
||||
"basis_grouped": 48
|
||||
},
|
||||
"orphan_tables": [
|
||||
{
|
||||
@@ -12716,7 +12716,7 @@
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": 100.0,
|
||||
"basis_unit": "본",
|
||||
"basis_source": "절 이름",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -12873,7 +12873,7 @@
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": 1000.0,
|
||||
"basis_unit": "㎡",
|
||||
"basis_source": "절 이름",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -13185,9 +13185,9 @@
|
||||
"source_line": 2081,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 10.0,
|
||||
"basis_unit": "본",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -13247,9 +13247,9 @@
|
||||
"source_line": 2099,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "본",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -13351,9 +13351,9 @@
|
||||
"source_line": 2125,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "본",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -13737,9 +13737,9 @@
|
||||
"source_line": 2194,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "본",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -13893,9 +13893,9 @@
|
||||
"source_line": 2219,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1000.0,
|
||||
"basis_unit": "본",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -14088,9 +14088,9 @@
|
||||
"source_line": 2254,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 10.0,
|
||||
"basis_unit": "본",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -14150,9 +14150,9 @@
|
||||
"source_line": 2272,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 10.0,
|
||||
"basis_unit": "본",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -14212,9 +14212,9 @@
|
||||
"source_line": 2291,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "본",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -14389,9 +14389,9 @@
|
||||
"source_line": 2322,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "본",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -14647,9 +14647,9 @@
|
||||
"source_line": 2366,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "ha",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -14722,9 +14722,9 @@
|
||||
"source_line": 2383,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "ha",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -14777,9 +14777,9 @@
|
||||
"source_line": 2396,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "ha",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -14822,9 +14822,9 @@
|
||||
"source_line": 2409,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "ha",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -14881,9 +14881,9 @@
|
||||
"source_line": 2426,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "본",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -14928,9 +14928,9 @@
|
||||
"source_line": 2439,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 100.0,
|
||||
"basis_unit": "본",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -15062,9 +15062,9 @@
|
||||
"source_line": 2475,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 100.0,
|
||||
"basis_unit": "본",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -15158,9 +15158,9 @@
|
||||
"source_line": 2511,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 100.0,
|
||||
"basis_unit": "㎡",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -15362,9 +15362,9 @@
|
||||
"source_line": 2565,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 100.0,
|
||||
"basis_unit": "m",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -18216,9 +18216,9 @@
|
||||
"source_line": 3276,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 100.0,
|
||||
"basis_unit": "본",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -18331,9 +18331,9 @@
|
||||
"source_line": 3304,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 10.0,
|
||||
"basis_unit": "주",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -18396,9 +18396,9 @@
|
||||
"source_line": 3318,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 100.0,
|
||||
"basis_unit": "㎡",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -21434,9 +21434,9 @@
|
||||
"source_line": 3945,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "ha",
|
||||
"basis_source": "본문",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
|
||||
@@ -105,3 +105,42 @@ def test_단목베기_5m_미만이_실무값_근처로_선다():
|
||||
total = build.book.resolve("B-FP-04-02-02#5m미만").total
|
||||
# 영월 설계내역 1.9.2 「잡관목제거 벌목(5m미만)」 @882 — 노임 연도 차이로 ±5 % 안이면 같은 읽기
|
||||
assert Decimal("838") <= total <= Decimal("926"), total
|
||||
|
||||
|
||||
def test_단위_글자_없는_분모형_밑수도_본문에서_읽는다():
|
||||
"""「(인/100m당)」 — 「단위:」 없이 분모만 적은 22 곳(2026-09-13). 「인」은 밑수가 아니다."""
|
||||
from B08_Quantity.B08_Quantity_Build_WorkItemMaster import basis_from_source
|
||||
|
||||
assert basis_from_source(["(인/100m당)", "", "| 공정별 |"], 3) == (100.0, "m")
|
||||
assert basis_from_source(["(인/본당)", "| 규격 |"], 2) == (1.0, "본")
|
||||
assert basis_from_source(["(무한궤도/0.7㎥)", "| 규격 |"], 2) == (None, None) # 「당」 없음
|
||||
|
||||
|
||||
_단끊기 = {
|
||||
"pum_table_id": "F-단끊기",
|
||||
"pum_form": "requirement",
|
||||
"basis_quantity": 100.0,
|
||||
"basis_unit": "m",
|
||||
"condition_note": ["공정별", "보통인부(인)", "비고"],
|
||||
"raw_row": [
|
||||
["보통토사", "경질ㆍ고사점토 및 자갈섞인 점토", "호박돌 섞인 토사", "", ""],
|
||||
["절 취", "2.4", "3.3", "5.4", ""],
|
||||
["수평잡기 및 단정리", "0.34", "0.34", "0.34", ""],
|
||||
["잡석 및 뿌리 정리", "0.36", "0.36", "0.36", ""],
|
||||
["절·성토면 고르기", "1.17", "1.17", "1.17", ""],
|
||||
["합계", "2.03", "2.09", "2.23", ""],
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def test_단끊기는_공정_줄을_더하고_작업반장을_20인당_1인_더한다():
|
||||
catalog = ResourceCatalog(entries=[*MACHINES, CatalogEntry("1001", "작업반장", "labor")])
|
||||
result = AxisResult()
|
||||
match_table({"work_item_code": "FP-05-16-01"}, _단끊기, catalog, result)
|
||||
assert _codes(result, "보통토사") == {"1002": Decimal("0.0427"), "1001": Decimal("0.002135")}
|
||||
assert _codes(result, "호박돌 섞인 토사")["1002"] == Decimal("0.0727")
|
||||
assert "FP-05-16-01" not in result.partial_items
|
||||
# 적어 두지 않은 공종은 같은 모양이어도 더하지 않는다(뭉기기 13-12-1 은 줄마다 단위가 다름)
|
||||
other = AxisResult()
|
||||
match_table({"work_item_code": "FP-13-12-01"}, _단끊기, catalog, other)
|
||||
assert not other.rows
|
||||
|
||||
Reference in New Issue
Block a user