feat(b08): 표머리 몫꼴에서 밑수 읽기 — 꼴마다 한 번씩 · 산림 18 · 건설 23 이 한꺼번에 채워짐
안티그래비티 자리 명세(2dab2ce2)가 준 것은 **값이 아니라 읽는 규칙**이었음(155 중 142가
「표머리 명시 단위」 자리표시). 그래서 하나씩 넣지 않고 꼴을 코드로 짬.
- 본문 쪽 몫꼴(`SOURCE_BASIS_RATIO_RE`)은 이미 있었으나 **표머리는 안 보고 있었음** —
`인/ha`·`인/100kg`·`소요인력(인/본당)`·`재료비(1km 소요량기준)` 을 읽게 함. **분모가 밑수**
- ⚠ 반쪽 진실보다 「미확보」 — 거절하는 자리 둘
· 분모가 둘인 몫(`ℓ/일,대`) — 대수를 안 곱해 조용히 적게 섬
· 표머리끼리 어긋나는 표(`주연료 (ℓ/hr)` + `조종원 (인/일)`) — 먼저 걸린 것을 고르면 반은 틀림
- 차례가 뜻 — 표 안 「…당」이 먼저, 표머리 몫꼴은 그 뒤. **덮어쓴 것 0 · 사라진 것 0**
밑수 확보 산림 204 → 222(미확보 115 → 99) · 건설 515 → 538(447 → 439)
지문 한 번 갱신 — 값이 움직인 것이 아니라 **곱하면 안 되던 줄이 곱할 수 있게 된 것**
이름표 줄 수도 맞춤(basis_missing 115→99 · const 447→439)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsGw1Dz9HmhuAxGisxmDPF
This commit is contained in:
@@ -312,6 +312,49 @@ def basis_from_name(name: str) -> tuple[float | None, str | None]:
|
||||
return None, None
|
||||
|
||||
|
||||
#: 밑수가 될 수 있는 **세는 단위**(분모 자리).
|
||||
_DENOM = "㎥|m3|㎡|m2|㏊|ha|㎞|km|m|매|본|개소|개|인|공|kg|㎏|톤|ton|주|식|대|일|시간|hr"
|
||||
#: 값이 붙는 **재는 단위**(분자 자리). 이쪽이 단위꼴이어야 「몫」이지 규격이 아니다.
|
||||
_NUMER = "인|공|ℓ|L|l|㎏|kg|톤|ton|m|㎝|cm|매|본|개|주|대|시간|hr|㎥|m3|㎡|m2"
|
||||
|
||||
#: ⚠ **표머리에 「당」 없이 몫으로만 적힌 밑수** — `인/ha` · `ℓ/일,대` · `m/본` (명세 꼴 B).
|
||||
#: 본문 쪽은 `SOURCE_BASIS_RATIO_RE` 가 이미 보는데 **표머리는 안 보고 있었다.**
|
||||
#: ⚠ 좁게 잡는다 — **양쪽이 다 단위꼴일 때만**. 안 그러면 `(무한궤도,0.7㎥)` 같은
|
||||
#: 규격 칸이 밑수로 둔갑한다(옛날에 실제로 다섯 건 걸렸다).
|
||||
HEADER_RATIO_RE = re.compile(
|
||||
rf"(?:^|[((\s])(?:단위\s*[::]\s*)?(?:{_NUMER})\s*/\s*([\d,]*\.?\d*)\s*({_DENOM})\s*당?(?:$|[,))\s])"
|
||||
)
|
||||
#: `재료비(1km 소요량기준)` 꼴 — 「당」 대신 「소요량기준」으로 적은 표머리.
|
||||
HEADER_BASIS_NOTE_RE = re.compile(rf"([\d,]*\.?\d*)\s*({_DENOM})\s*소요량\s*기준")
|
||||
|
||||
|
||||
#: 분모가 둘인 몫 — `ℓ/일,대` · `인/㎥·일`. 밑수가 하나로 안 정해진다.
|
||||
COMPOUND_DENOM_RE = re.compile(rf"/\s*[\d,]*\.?\d*\s*(?:{_DENOM})\s*[,,·]\s*(?:{_DENOM})")
|
||||
|
||||
|
||||
def basis_from_header(cell: str) -> tuple[float | None, str | None]:
|
||||
"""표머리 한 칸에서 몫꼴 밑수를 읽는다. 못 읽으면 `(None, None)`.
|
||||
|
||||
⚠ 분모가 밑수다 — `인/ha` 는 「1ha당 몇 인」이라 밑수는 `ha` 다. 분자(인)를 밑수로
|
||||
읽으면 뜻이 뒤집힌다.
|
||||
"""
|
||||
# ⚠ **분모가 둘인 몫은 거절한다** — `ℓ/일,대` 는 「하루에 한 대당」이라 밑수가 둘이다.
|
||||
# 한쪽(일)만 적으면 대수를 안 곱해 조용히 적게 선다. 반쪽 진실보다 「미확보」가 정직하다.
|
||||
if COMPOUND_DENOM_RE.search(cell):
|
||||
return None, None
|
||||
for pattern in (HEADER_RATIO_RE, HEADER_BASIS_NOTE_RE):
|
||||
if m := pattern.search(cell):
|
||||
raw = (m.group(1) or "").replace(",", "")
|
||||
unit = m.group(2)
|
||||
if not person_basis_ok(raw, unit):
|
||||
continue
|
||||
try:
|
||||
return (float(raw) if raw else 1.0), unit
|
||||
except ValueError:
|
||||
return 1.0, unit
|
||||
return None, None
|
||||
|
||||
|
||||
def detect_basis(table: dict[str, Any]) -> tuple[float | None, str | None]:
|
||||
"""「100㎥당」 같은 밑수. 없으면 `(None, None)` — 단위당 1 로 단정하지 않는다.
|
||||
|
||||
@@ -336,6 +379,17 @@ def detect_basis(table: dict[str, Any]) -> tuple[float | None, str | None]:
|
||||
return float(m.group(1).replace(",", "")), m.group(2)
|
||||
except ValueError:
|
||||
return None, m.group(2)
|
||||
# 「당」이 없는 몫꼴은 **표머리에서만** 본다(줄 값에는 규격이 섞여 있다).
|
||||
# ⚠ 표머리가 **서로 다른 분모**를 말하면 거절한다 — 건설 8장 기계경비표가 한 표 안에서
|
||||
# `주연료 (ℓ/hr)` 와 `조종원 (인/일)` 을 함께 적는다. 먼저 걸린 것을 고르면 반은 틀린다.
|
||||
found = {
|
||||
basis_from_header(cell)
|
||||
for cell in (norm(h) for h in table.get("headers", []) or [])
|
||||
if cell
|
||||
}
|
||||
found = {hit for hit in found if hit[1]}
|
||||
if len(found) == 1:
|
||||
return found.pop()
|
||||
return None, None
|
||||
|
||||
|
||||
|
||||
@@ -6327,7 +6327,7 @@
|
||||
"name_ko": "밑수 못 찾은 표",
|
||||
"summary": "기준 수량을 못 읽은 품셈 표. 1 단위당으로 단정하면 곱셈이 틀린다.",
|
||||
"shape": "list",
|
||||
"rows": 115,
|
||||
"rows": 99,
|
||||
"columns": [
|
||||
{
|
||||
"key": "pum_table_id",
|
||||
@@ -7315,7 +7315,7 @@
|
||||
"name_ko": "밑수 미확보 표",
|
||||
"summary": "기준 수량을 못 찾은 표.",
|
||||
"shape": "list",
|
||||
"rows": 447,
|
||||
"rows": 439,
|
||||
"columns": [
|
||||
{
|
||||
"key": "pum_table_id",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"dataset_id": "data_work_item_master_manifest",
|
||||
"generated_at": "2026-09-17T18:22:17+09:00",
|
||||
"generated_at": "2026-09-17T18:29:07+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": "ffa114016725768042a7b59f4daf6ab7188595e8c649208c659dcd9787c5ca5d",
|
||||
"size_bytes": 925084
|
||||
"sha256": "ef9a9d0424a2b602b74640e6bdb49da3ccdd31c873367d404a09d1ede7efd882",
|
||||
"size_bytes": 925160
|
||||
},
|
||||
{
|
||||
"file": "form_undetermined_2026-01-01.json",
|
||||
@@ -22,8 +22,8 @@
|
||||
},
|
||||
{
|
||||
"file": "basis_missing_2026-01-01.json",
|
||||
"sha256": "d57f26c736829ef40c50dc616d96358db785295d1aa0d5959aa7bc9eaa9be842",
|
||||
"size_bytes": 15845
|
||||
"sha256": "15306da88ffa36bbe33b65c0f290fcaab94888e494e61546b1b6a2949d814a90",
|
||||
"size_bytes": 13680
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"dataset_id": "data_work_item_master_manifest",
|
||||
"generated_at": "2026-09-17T18:22:18+09:00",
|
||||
"generated_at": "2026-09-17T18:29:08+09:00",
|
||||
"built_by": "B08_Quantity/B08_Quantity_Build_WorkItemMaster.py",
|
||||
"source": {
|
||||
"dataset_id": "pum_const",
|
||||
@@ -12,8 +12,8 @@
|
||||
"files": [
|
||||
{
|
||||
"file": "const_work_item_master_2026-01-01.json",
|
||||
"sha256": "9744ac236de8f08582a3aa36b50125aef76acac18094bd06d23d442183fffe0c",
|
||||
"size_bytes": 3780649
|
||||
"sha256": "f81019fc9699d01c074f55fc1e8074d16ce7b0a81c0291fcbb63fb5094b3c9b5",
|
||||
"size_bytes": 3780743
|
||||
},
|
||||
{
|
||||
"file": "const_form_undetermined_2026-01-01.json",
|
||||
@@ -22,8 +22,8 @@
|
||||
},
|
||||
{
|
||||
"file": "const_basis_missing_2026-01-01.json",
|
||||
"sha256": "0ae14342e37ba5f394c36cc4dd8cea1b505b13d6520e0a12979c21676d6ffcdb",
|
||||
"size_bytes": 74228
|
||||
"sha256": "65f401c717fcbb97323d60240f8207a1d8e7598c2f8a5f6e1b57e2f0b637bd85",
|
||||
"size_bytes": 72968
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -16,12 +16,6 @@
|
||||
"pum_form": "requirement",
|
||||
"line": 1328
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0049",
|
||||
"section": "2-1-4. 페인트 및 마킹테이프",
|
||||
"pum_form": "requirement",
|
||||
"line": 1402
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0050",
|
||||
"section": "2-1-4. 페인트 및 마킹테이프",
|
||||
@@ -52,36 +46,12 @@
|
||||
"pum_form": "requirement",
|
||||
"line": 1579
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0075",
|
||||
"section": "3-1. 경계표시",
|
||||
"pum_form": "requirement",
|
||||
"line": 1731
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0077",
|
||||
"section": "3-3. 작업로 설치",
|
||||
"pum_form": "requirement",
|
||||
"line": 1754
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0078",
|
||||
"section": "3-4-3. 임산물 운반로 및 작업로 보수비 산정",
|
||||
"pum_form": "requirement",
|
||||
"line": 1815
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0080",
|
||||
"section": "3-6. 산물 임내정리",
|
||||
"pum_form": "requirement",
|
||||
"line": 1867
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0081",
|
||||
"section": "3-7. 재해산물 수집",
|
||||
"pum_form": "requirement",
|
||||
"line": 1887
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0082",
|
||||
"section": "3-8. 드론 영상 촬영",
|
||||
@@ -106,12 +76,6 @@
|
||||
"pum_form": "requirement",
|
||||
"line": 1957
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0088",
|
||||
"section": "4-3. 위험목 베기",
|
||||
"pum_form": "requirement",
|
||||
"line": 2012
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0089",
|
||||
"section": "4-4. 가지정리",
|
||||
@@ -184,12 +148,6 @@
|
||||
"pum_form": "requirement",
|
||||
"line": 2919
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0153",
|
||||
"section": "6-1. 비료주기",
|
||||
"pum_form": "requirement",
|
||||
"line": 3062
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0154",
|
||||
"section": "6-2-1. 둘레베기",
|
||||
@@ -304,12 +262,6 @@
|
||||
"pum_form": "requirement",
|
||||
"line": 3556
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0181",
|
||||
"section": "7-7-2. 숲가꾸기, 병해충방제",
|
||||
"pum_form": "requirement",
|
||||
"line": 3580
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0182",
|
||||
"section": "7-8-1. 가선설치",
|
||||
@@ -328,30 +280,6 @@
|
||||
"pum_form": "requirement",
|
||||
"line": 3631
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0185",
|
||||
"section": "7-9-1. 수확",
|
||||
"pum_form": "requirement",
|
||||
"line": 3656
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0186",
|
||||
"section": "7-9-2. 숲가꾸기, 소나무재선충병방제",
|
||||
"pum_form": "requirement",
|
||||
"line": 3673
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0189",
|
||||
"section": "7-11. 동력상하차기(우드그래플) 집재-수확",
|
||||
"pum_form": "productivity",
|
||||
"line": 3723
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0190",
|
||||
"section": "7-11. 동력상하차기(우드그래플) 집재-수확",
|
||||
"pum_form": "productivity",
|
||||
"line": 3731
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0191",
|
||||
"section": "7-11. 동력상하차기(우드그래플) 집재-수확",
|
||||
@@ -508,12 +436,6 @@
|
||||
"pum_form": "requirement",
|
||||
"line": 4583
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0235",
|
||||
"section": "8-9. 잔가지줍기",
|
||||
"pum_form": "requirement",
|
||||
"line": 4623
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0236",
|
||||
"section": "8-10. 그물망 피복",
|
||||
@@ -532,18 +454,6 @@
|
||||
"pum_form": "requirement",
|
||||
"line": 4675
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0241",
|
||||
"section": "9-4-1. 암파쇄",
|
||||
"pum_form": "productivity",
|
||||
"line": 4719
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0244",
|
||||
"section": "9-5-2. 깎기(90%)",
|
||||
"pum_form": "productivity",
|
||||
"line": 4759
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0251",
|
||||
"section": "9-8-1. T=30㎝ 미만",
|
||||
@@ -664,12 +574,6 @@
|
||||
"pum_form": "requirement",
|
||||
"line": 7415
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0430",
|
||||
"section": "13-10-2. 나무 말뚝박기",
|
||||
"pum_form": "requirement",
|
||||
"line": 7491
|
||||
},
|
||||
{
|
||||
"pum_table_id": "F0437",
|
||||
"section": "13-12-1. 뭉기기",
|
||||
|
||||
@@ -166,12 +166,6 @@
|
||||
"pum_form": "requirement",
|
||||
"line": 211
|
||||
},
|
||||
{
|
||||
"pum_table_id": "C0176",
|
||||
"section": "3-3-6 암발파(대규모발파 TYPE-Ⅵ)('20, '26년 보완)(㎥당)",
|
||||
"pum_form": "productivity",
|
||||
"line": 229
|
||||
},
|
||||
{
|
||||
"pum_table_id": "C0183",
|
||||
"section": "3-4 쌓기3-4-1 흙쌓기('25년 신설)",
|
||||
@@ -220,12 +214,6 @@
|
||||
"pum_form": "requirement",
|
||||
"line": 660
|
||||
},
|
||||
{
|
||||
"pum_table_id": "C0217",
|
||||
"section": "3-10 개간3-10-1 답면고르기('03년 신설)",
|
||||
"pum_form": "productivity",
|
||||
"line": 722
|
||||
},
|
||||
{
|
||||
"pum_table_id": "C0218",
|
||||
"section": "3-11 스마트 토공3-11-1 머신 가이던스(MG) 굴착기('23년 신설, '24, '26년 보완)",
|
||||
@@ -760,18 +748,6 @@
|
||||
"pum_form": "requirement",
|
||||
"line": 2415
|
||||
},
|
||||
{
|
||||
"pum_table_id": "C0618",
|
||||
"section": "8-3-5 [40]콘크리트기계(4108) 콘크리트 배치플랜트",
|
||||
"pum_form": "productivity",
|
||||
"line": 2422
|
||||
},
|
||||
{
|
||||
"pum_table_id": "C0619",
|
||||
"section": "8-3-5 [40]콘크리트기계(4108) 콘크리트 배치플랜트",
|
||||
"pum_form": "productivity",
|
||||
"line": 2431
|
||||
},
|
||||
{
|
||||
"pum_table_id": "C0620",
|
||||
"section": "8-3-5 [40]콘크리트기계(4108) 콘크리트 배치플랜트",
|
||||
@@ -796,12 +772,6 @@
|
||||
"pum_form": "productivity",
|
||||
"line": 2479
|
||||
},
|
||||
{
|
||||
"pum_table_id": "C0625",
|
||||
"section": "8-3-5 [40]콘크리트기계(4108) 콘크리트 배치플랜트",
|
||||
"pum_form": "productivity",
|
||||
"line": 2486
|
||||
},
|
||||
{
|
||||
"pum_table_id": "C0640",
|
||||
"section": "8-3-6 [50]골재생산기계 등(5105) 크러셔(이동식) ('11년 보완)",
|
||||
@@ -1006,12 +976,6 @@
|
||||
"pum_form": "requirement",
|
||||
"line": 512
|
||||
},
|
||||
{
|
||||
"pum_table_id": "C0849",
|
||||
"section": "3-2-2 기계굴착의 능력('07, '20년 보완)",
|
||||
"pum_form": "productivity",
|
||||
"line": 83
|
||||
},
|
||||
{
|
||||
"pum_table_id": "C0856",
|
||||
"section": "3-2-5 터널굴착 1발파당 작업인원('07, '20년 보완)(1발파당)",
|
||||
@@ -2212,24 +2176,12 @@
|
||||
"pum_form": "requirement",
|
||||
"line": 2092
|
||||
},
|
||||
{
|
||||
"pum_table_id": "C1788",
|
||||
"section": "13-6-6 Stop-Log 설치",
|
||||
"pum_form": "requirement",
|
||||
"line": 2280
|
||||
},
|
||||
{
|
||||
"pum_table_id": "C1789",
|
||||
"section": "13-6-6 Stop-Log 설치",
|
||||
"pum_form": "requirement",
|
||||
"line": 2300
|
||||
},
|
||||
{
|
||||
"pum_table_id": "C1792",
|
||||
"section": "13-6-7 수문 Hoist 설치",
|
||||
"pum_form": "requirement",
|
||||
"line": 2325
|
||||
},
|
||||
{
|
||||
"pum_table_id": "C1796",
|
||||
"section": "80713-6-8 Spiral Casing 설치",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"effective_date": "2026-01-01",
|
||||
"pum_edition": "2026-01-01",
|
||||
"toc_edition": "2026-01-01",
|
||||
"generated_at": "2026-09-17T18:22:18+09:00",
|
||||
"generated_at": "2026-09-17T18:29:08+09:00",
|
||||
"dataset_version": {
|
||||
"dataset_id": "pum_const",
|
||||
"effective_date": "2026-01-01",
|
||||
@@ -39,8 +39,8 @@
|
||||
"tables_attached": 2192,
|
||||
"tables_orphan": 0,
|
||||
"form_undetermined": 881,
|
||||
"basis_found": 515,
|
||||
"basis_missing": 447,
|
||||
"basis_found": 538,
|
||||
"basis_missing": 439,
|
||||
"basis_grouped": 54,
|
||||
"axis_roles": {
|
||||
"group": 258,
|
||||
@@ -790,9 +790,9 @@
|
||||
"source_line": 197,
|
||||
"pum_form": "reference",
|
||||
"form_basis": "품셈 제1장(적용기준)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "㎥",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -825,9 +825,9 @@
|
||||
"source_line": 203,
|
||||
"pum_form": "reference",
|
||||
"form_basis": "품셈 제1장(적용기준)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "㎥",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -1099,9 +1099,9 @@
|
||||
"source_line": 273,
|
||||
"pum_form": "reference",
|
||||
"form_basis": "품셈 제1장(적용기준)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "hr",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -11829,9 +11829,9 @@
|
||||
"source_line": 229,
|
||||
"pum_form": "productivity",
|
||||
"form_basis": "헤더 '㎥/hr'",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "hr",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -14878,9 +14878,9 @@
|
||||
"source_line": 722,
|
||||
"pum_form": "productivity",
|
||||
"form_basis": "헤더 '㎡/hr'",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "hr",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -32589,9 +32589,9 @@
|
||||
"source_line": 783,
|
||||
"pum_form": "undetermined",
|
||||
"form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "hr",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [
|
||||
@@ -33351,9 +33351,9 @@
|
||||
"source_line": 936,
|
||||
"pum_form": "undetermined",
|
||||
"form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "m",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -33530,9 +33530,9 @@
|
||||
"source_line": 982,
|
||||
"pum_form": "undetermined",
|
||||
"form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "m",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -33618,9 +33618,9 @@
|
||||
"source_line": 996,
|
||||
"pum_form": "undetermined",
|
||||
"form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "m",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -33763,9 +33763,9 @@
|
||||
"source_line": 1019,
|
||||
"pum_form": "undetermined",
|
||||
"form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "m",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -41847,9 +41847,9 @@
|
||||
"source_line": 2330,
|
||||
"pum_form": "undetermined",
|
||||
"form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "hr",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -42586,9 +42586,9 @@
|
||||
"source_line": 2422,
|
||||
"pum_form": "productivity",
|
||||
"form_basis": "헤더 '㎥/hr'",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "hr",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -42643,9 +42643,9 @@
|
||||
"source_line": 2431,
|
||||
"pum_form": "productivity",
|
||||
"form_basis": "헤더 '㎥/hr'",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "hr",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -43069,9 +43069,9 @@
|
||||
"source_line": 2486,
|
||||
"pum_form": "productivity",
|
||||
"form_basis": "헤더 '㎥/hr'",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "hr",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -43198,9 +43198,9 @@
|
||||
"source_line": 2501,
|
||||
"pum_form": "undetermined",
|
||||
"form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "m",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -43274,9 +43274,9 @@
|
||||
"source_line": 2508,
|
||||
"pum_form": "undetermined",
|
||||
"form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "hr",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -59146,9 +59146,9 @@
|
||||
"source_line": 83,
|
||||
"pum_form": "productivity",
|
||||
"form_basis": "헤더 '㎥/hr'",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "hr",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -137687,9 +137687,9 @@
|
||||
"source_line": 978,
|
||||
"pum_form": "undetermined",
|
||||
"form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "hr",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -145817,9 +145817,9 @@
|
||||
"source_line": 2122,
|
||||
"pum_form": "undetermined",
|
||||
"form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "ton",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -145938,9 +145938,9 @@
|
||||
"source_line": 2149,
|
||||
"pum_form": "undetermined",
|
||||
"form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "ton",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -146441,9 +146441,9 @@
|
||||
"source_line": 2254,
|
||||
"pum_form": "undetermined",
|
||||
"form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "ton",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -146592,9 +146592,9 @@
|
||||
"source_line": 2280,
|
||||
"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": [],
|
||||
@@ -146829,9 +146829,9 @@
|
||||
"source_line": 2325,
|
||||
"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": [],
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"effective_date": "2026-01-01",
|
||||
"pum_edition": "2026-01-01",
|
||||
"toc_edition": "산림청고시제2025-82호",
|
||||
"generated_at": "2026-09-17T18:22:17+09:00",
|
||||
"generated_at": "2026-09-17T18:29:07+09:00",
|
||||
"dataset_version": {
|
||||
"dataset_id": "pum_forest",
|
||||
"effective_date": "2026-01-01",
|
||||
@@ -25,9 +25,9 @@
|
||||
"tables_attached": 453,
|
||||
"tables_orphan": 22,
|
||||
"form_undetermined": 14,
|
||||
"basis_found": 204,
|
||||
"basis_missing": 115,
|
||||
"basis_grouped": 48,
|
||||
"basis_found": 222,
|
||||
"basis_missing": 99,
|
||||
"basis_grouped": 49,
|
||||
"axis_roles": {
|
||||
"general_provision": 78,
|
||||
"group": 85,
|
||||
@@ -4635,9 +4635,9 @@
|
||||
"source_line": 1402,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "'소요량'",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "km",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -6196,9 +6196,9 @@
|
||||
"source_line": 1731,
|
||||
"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": [],
|
||||
@@ -6298,9 +6298,9 @@
|
||||
"source_line": 1754,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "km",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -6560,9 +6560,9 @@
|
||||
"source_line": 1867,
|
||||
"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": [],
|
||||
@@ -6632,9 +6632,9 @@
|
||||
"source_line": 1887,
|
||||
"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": [],
|
||||
@@ -7260,9 +7260,9 @@
|
||||
"source_line": 2012,
|
||||
"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": [],
|
||||
@@ -12388,9 +12388,9 @@
|
||||
"source_line": 3062,
|
||||
"pum_form": "requirement",
|
||||
"form_basis": "직종 표기((인)·인부·공)",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 100.0,
|
||||
"basis_unit": "kg",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -13043,9 +13043,9 @@
|
||||
"source_line": 3234,
|
||||
"pum_form": "reference",
|
||||
"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": [],
|
||||
@@ -14808,9 +14808,9 @@
|
||||
"source_line": 3580,
|
||||
"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": [],
|
||||
@@ -15212,9 +15212,9 @@
|
||||
"source_line": 3656,
|
||||
"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": [],
|
||||
@@ -15306,9 +15306,9 @@
|
||||
"source_line": 3673,
|
||||
"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": [],
|
||||
@@ -15382,9 +15382,9 @@
|
||||
"source_line": 3692,
|
||||
"pum_form": "coefficient",
|
||||
"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": [],
|
||||
@@ -15529,9 +15529,9 @@
|
||||
"source_line": 3723,
|
||||
"pum_form": "productivity",
|
||||
"form_basis": "사람 판정 — 표 바로 위 「(㎥/1대, 1일)」 — 대당 일 작업량",
|
||||
"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": [],
|
||||
@@ -15602,9 +15602,9 @@
|
||||
"source_line": 3731,
|
||||
"pum_form": "productivity",
|
||||
"form_basis": "사람 판정 — 위와 같음(최대집재거리 80m 이하)",
|
||||
"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": [],
|
||||
@@ -20625,9 +20625,9 @@
|
||||
"source_line": 4623,
|
||||
"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": [],
|
||||
@@ -21111,9 +21111,9 @@
|
||||
"source_line": 4719,
|
||||
"pum_form": "productivity",
|
||||
"form_basis": "헤더 '㎥/hr'",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "hr",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -21446,9 +21446,9 @@
|
||||
"source_line": 4759,
|
||||
"pum_form": "productivity",
|
||||
"form_basis": "헤더 '㎥/hr'",
|
||||
"basis_quantity": null,
|
||||
"basis_unit": null,
|
||||
"basis_source": null,
|
||||
"basis_quantity": 1.0,
|
||||
"basis_unit": "hr",
|
||||
"basis_source": "표 안",
|
||||
"resource_shares": {},
|
||||
"partial_ratio": false,
|
||||
"expression_cells": [],
|
||||
@@ -37895,9 +37895,9 @@
|
||||
"source_line": 7491,
|
||||
"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": [],
|
||||
|
||||
@@ -41,14 +41,16 @@ from B08_Quantity.B08_Quantity_Build_WorkItemMaster_Keys import ( # noqa: E402
|
||||
MASTER = OUT_DIR / "work_item_master_2026-01-01.json"
|
||||
|
||||
#: 열쇠를 얹기 **전** 벌(`work_items`)의 지문. 2026-09-17 데스크탑_서브가 뜬 값.
|
||||
#: ⚠ 이 값이 흔들리면 열쇠 작업이 값을 건드렸다는 뜻이다 — 지문을 고치지 말고 원인을 찾을 것.
|
||||
#: ⚠ 이 값이 흔들리면 값이 움직였다는 뜻이다 — 지문을 고치지 말고 **원인을 먼저 찾을 것.**
|
||||
#: 2026-09-17 한 번 갱신 — 표머리 몫꼴(`인/ha`·`인/100kg`)에서 **밑수 18개를 새로 읽음.**
|
||||
#: ⚠ **덮어쓴 것 0 · 사라진 것 0**(더하기만) — 곱하면 안 되던 줄이 곱할 수 있게 된 것뿐이다.
|
||||
#: 2026-09-17 랩탑 메인 — 목차 오기 둘을 본문 번호로 바로잡아(브레인 지시) **네 줄만** 바뀌어 새로 뜸
|
||||
#: (옛 f8b8fba1…). 네 줄 모습은 `test_목차_오기는_본문_번호로_바로잡고_열쇠는_그대로` 가 잼 ·
|
||||
#: 미판정·밑수 목록 파일은 한 글자도 안 바뀜.
|
||||
#: 2026-09-17 랩탑 메인 둘째 — 헛단위 뺌(인용을 제목으로 읽은 13-3 표 셋 · 부록 사례 번호 겹침 일곱)으로 다시 뜸
|
||||
#: (옛 be88f730…). 바뀐 줄은 13-3·4-1·4-2·2-1·2-2·3-1·3-2·3-3·13-4-1·13-5-2 의 표 귀속뿐 —
|
||||
#: `test_work_item_master_toc` 가 자리를 잼 · 단가표 대조는 그 파일 머리.
|
||||
BASELINE_WORK_ITEMS_SHA = "2accafe5e366c32eb6e95123642b4330e28537b2199dee851247e996fcd839da"
|
||||
BASELINE_WORK_ITEMS_SHA = "40746aa3b4a2ac6a78323590069242088f1fb44f4662dc2f240afe303f385978"
|
||||
|
||||
#: 열쇠 작업이 새로 얹은 칸. 지문을 잴 때만 떼어낸다.
|
||||
ADDED_FIELDS = (
|
||||
@@ -327,6 +329,38 @@ def test_갈래_문자열이_열쇠라_흔들림을_못박음(master: dict) -> N
|
||||
assert len(messy) == 6, f"공백·물결표만 다른 짝이 {len(messy)} — 6 이던 것"
|
||||
|
||||
|
||||
def test_표머리_몫꼴_밑수_읽기() -> None:
|
||||
"""「당」 없이 몫으로만 적힌 표머리에서 밑수를 읽는다 — 분모가 밑수다(명세 꼴 B).
|
||||
|
||||
⚠ 반쪽만 읽느니 「미확보」가 낫다 — 분모가 둘이거나(`ℓ/일,대`) 표머리끼리 어긋나면 거절한다.
|
||||
"""
|
||||
from B08_Quantity.B08_Quantity_Build_WorkItemMaster_Table import basis_from_header, detect_basis
|
||||
|
||||
assert basis_from_header("(단위 : 인/ha)") == (1.0, "ha")
|
||||
assert basis_from_header("소요인력 (인/100kg)") == (100.0, "kg")
|
||||
assert basis_from_header("소요인력(인/본당)") == (1.0, "본")
|
||||
assert basis_from_header("재료비(1km 소요량기준)") == (1.0, "km")
|
||||
assert basis_from_header("주연료 (ℓ/일,대)") == (None, None) # 분모가 둘 — 거절
|
||||
assert basis_from_header("기계명(주재료)") == (None, None) # 몫이 아님
|
||||
# 표머리끼리 어긋나면 표 전체를 미확보로 둔다(건설 8장 기계경비표)
|
||||
표 = {"headers": ["분류번호", "주연료 (ℓ/hr)", "조종원 (인/일)"], "rows": []}
|
||||
assert detect_basis(표) == (None, None)
|
||||
# 하나로 모이면 읽는다
|
||||
표2 = {"headers": ["구 분", "정리산물(㎥/ha)", "비고"], "rows": []}
|
||||
assert detect_basis(표2) == (1.0, "ha")
|
||||
|
||||
|
||||
def test_밑수_채움은_더하기만_함(master: dict) -> None:
|
||||
"""⚠ 이미 있던 밑수를 덮지 않는다 — 덮으면 금액이 조용히 움직인다.
|
||||
|
||||
표 안 칸(`BASIS_RE`)이 먼저고, 표머리 몫꼴은 **그 뒤**에만 본다.
|
||||
"""
|
||||
from B08_Quantity.B08_Quantity_Build_WorkItemMaster_Table import detect_basis
|
||||
|
||||
표 = {"headers": ["(단위 : 인/ha)", "100㎡당"], "rows": []}
|
||||
assert detect_basis(표) == (100.0, "㎡") # 「당」이 붙은 쪽이 이긴다
|
||||
|
||||
|
||||
def test_어느_구실도_지우지_않음() -> None:
|
||||
"""브레인 승인(2026-09-17) — 「덜어내기」가 아니라 「가름만」."""
|
||||
import json as _json
|
||||
|
||||
Reference in New Issue
Block a user