# -*- coding: utf-8 -*- """642-2(PLAN 785-2) 옵션값 읽기 · 검산 · 판정 — 북부청 임도 예시 1 · 2 · 3 + 단산46 · 39 기대표 (설계 642-A 2-10 · 5-3) · 출처 꼬리표 · 지어 넣은 값 0 · 입력 필요 · 품 같음 · master_auto.defaults. 읽기만(엑셀 · M01) · storage 안 씀.""" from decimal import Decimal from pathlib import Path import pytest from M02_MasterTemplete import M02_Hopyo_Bind as hb from M02_MasterTemplete import M02_Hopyo_Excel as hx from M02_MasterTemplete import M02_Hopyo_Solve as hs NORTH = ( Path(__file__).resolve().parents[1] / "knowledge/original/실무문서/2026_실무일위대가 모음" / "2026년_북부지방산림청_임도사업_설계내역서(공통단가).xls" ) PATHS = {"B1", "B2", "B3", "B4", "B5", "B6"} # 후보 = 785-1 Clause 몫(조항 색인) — 여기선 설계 2-10 후보 열 그대로 CANDS = { 10: ["GC001140", "GC001141", "GC001142"], 24: ["GC000211"], 46: ["GC000198"], 47: [{"키": "GC000206", "절": "1"}, {"키": "GC000207", "절": "2"}], 39: ["GF000224", "GF000225"], } @pytest.fixture(scope="module") def master(): return hb.m01.cm.master() @pytest.fixture(scope="module") def book(): return hx.read_book(NORTH.name, NORTH.read_bytes()) @pytest.fixture(scope="module") def solved(master, book): blocks = {b["번"]: b for b in book["단산"]} out = {} for no, cands in CANDS.items(): hopyo = next( h for h in book["호표"] if any((x.get("참조") or {}).get("번") == no for x in h["줄"]) ) line = next(x for x in hopyo["줄"] if (x.get("참조") or {}).get("번") == no) same = [x for x in hopyo["줄"] if x is not line] blk = blocks[no] out[no] = hs.block(master, blk, hs.parts(blk, cands), {"줄": line, "같은줄": same}) return out def _cand(solved, no, key, sec=0): return next(c for c in solved[no]["절"][sec]["후보"] if c["키"] == key) def _where(cand): return {k: v["길"] for k, v in cand["바인딩"]["출처"].items()} def test_예시1_단산10_소운반(solved): got = solved[10] assert got["판정"] == "기초로 풂" and got["고를수"] == 0 assert got["절"][0]["고름"] == "GC001140" c = _cand(solved, 10, "GC001140") b = c["바인딩"] assert b["입력값"] == { "운반무게": 575, "회당운반량": 25, "운반거리": 6, "경사각도": 35, "운반회수": 42, } assert _where(c) == { "운반무게": "B4", "회당운반량": "B4", "운반거리": "B3", "경사각도": "B3", "운반회수": "B3", } assert b["입력필요"] == [] and b["엇갈림"] == {} assert set(b["안씀"]) == {"재료종별", "재료형상", "재료갈래", "운반로", "실작업시간"} (row,) = c["줄"] assert row["판정"] == "품 같음" assert Decimal(str(row["우리"]["품"])).quantize(Decimal("0.000001")) == Decimal("0.547619") assert round(c["금액"]["계"], 1) == 94227.7 def test_예시2_단산47_절_둘(solved): got = solved[47] one, two = got["절"] assert (one["고름"], two["고름"]) == ("GC000206", "GC000207") a = _cand(solved, 47, "GC000206", 0)["바인딩"] assert a["입력값"] == {"Type": "Type-Ⅰ"} and a["출처"]["Type"]["길"] == "B1" c = _cand(solved, 47, "GC000207", 1) b = c["바인딩"] assert b["입력값"] == {"Type": "Type-Ⅰ", "호": "Ⅰ-1", "소량감률": 50} assert _where(c) == {"Type": "B1", "호": "B5", "소량감률": "B5"} assert [x["값"] for x in b["엇갈림"]["구분"]] == ["토목", "건축"] assert b["기본"]["구분"] == "토목" assert [r["판정"] for r in c["줄"]] == ["품 같음"] * 4 assert got["판정"] == "고를 것" and got["고를수"] == 1 # 구분 하나만 사용자 def test_예시3_단산24_합판거푸집(solved, master, book): c = _cand(solved, 24, "GC000211") b = c["바인딩"] assert b["입력값"] == { "사용횟수": "4회", "유형": "보통", "지붕슬래브": "아님", "폼타이": "아님", } assert _where(c) == {"사용횟수": "B1", "유형": "B2", "지붕슬래브": "B2", "폼타이": "B2"} assert b["입력필요"] == ["높이감률", "구멍땜"] assert b["기본"]["높이감률"] is None and b["기본"]["구멍땜"] is None # 규칙에 맞는 칸 없음 assert set(b["안씀"]) == {"폼타이규격", "측압"} and b["비움"] == ["자재지역"] assert solved[24]["판정"] == "고를 것" and solved[24]["고를수"] == 2 # 사용자가 0 넣음 → 노무 줄 품 같음 · 노무비 38,291 = 엑셀 given = {**b["입력값"], "높이감률": 0, "구멍땜": 0} items = [x for x in next(b for b in book["단산"] if b["번"] == 24)["항목"] if not x.get("밖")] rows = hs.pair(hb.quantities(master, "GC000211", given), items) assert {r["판정"] for r in rows if r["엑셀"]} == {"품 같음"} fill = {n: b["기본"][n] for n in b["안씀"]} | {"자재지역": None} money = hb.mf.run(master, "GC000211", {**fill, **given}) assert round(money["노무비"]) == 38291 def test_단산46_레미콘타설(solved): got = solved[46] c = _cand(solved, 46, "GC000198") b = c["바인딩"] assert b["입력값"] == {"타설방법": "장비사용타설", "구조물": "철근구조물", "소량감률": 50} assert _where(c) == {"타설방법": "B1", "구조물": "B1", "소량감률": "B5"} assert b["입력필요"] == ["굴착기"] and b["비움"] == ["지역"] # 굴착기 규격 = B3 · 사용자 mach = next(r for r in c["줄"] if r["엑셀"] and r["엑셀"]["묶음"] == "X") assert mach["판정"] == "품 같음" and mach["표지"] == "기계 갈래 엇갈림" assert [d["이름"] for d in got["덧"]] == ["레미콘"] # 별도계상 = 덧 줄 def test_단산39_찰붙임(solved): sec = solved[39]["절"][0] assert sec["고름"] == "GF000225" and solved[39]["판정"] == "기초로 풂" c = _cand(solved, 39, "GF000225") assert c["바인딩"]["입력값"] == { "뒷길이": 35, "굵은골재최대치수": 25, "강도": 21, "슬럼프": "120", "채움콘크리트량": 0.12, "보정작업": "해당 없음", } assert _where(c)["뒷길이"] == "B1" and _where(c)["채움콘크리트량"] == "B6" assert _cand(solved, 39, "GF000224")["판정"] == "품 다름" def test_지어_넣은_값_없음(solved): for got in solved.values(): for sec in got["절"]: for c in sec["후보"]: b = c["바인딩"] assert set(b["입력값"]) == set(b["출처"]) # 출처 없는 값 0 assert {v["길"] for v in b["출처"].values()} <= PATHS @pytest.mark.parametrize( "excel, ours, want", [ (0.67, 0.6667, True), (1.18, 1.1765, True), (3.53, 3.5294, True), (0.1111111111, 0.11111, True), (0.67, 0.69, False), (0.547619, 0.5476190476, True), (None, 1, False), ], ) def test_품_같음(excel, ours, want): assert hs.same(excel, ours) is want def test_defaults_규칙(master): ma = hb.ma jige = ma.defaults(master, master.logic("GC001140")) assert jige["실작업시간"] == 0 and jige["운반무게"] == 0 # 가드 assert jige["경사각도"] == 0 and jige["운반거리"] is None # 고르기 첫째 · 못 정함 pour = ma.defaults(master, master.logic("GC000198")) assert pour["소량감률"] == 0 and pour["타설방법"] == "인력운반타설" # 0 을 품은 범위 form = ma.defaults(master, master.logic("GC000211")) assert form["높이감률"] is None and form["구멍땜"] is None assert 1 not in [v for v in jige.values() if not isinstance(v, str)] # 지어 넣는 1 없음