Files
Aislo/resources/tester/test_794_2a2_m01_add.py
T
eomsangdonandClaude Opus 5.5 254d275d21 feat(794-2a2): M01 보강 — 자재 운반 GF 둘 · 기계 채집 자체 GX · 덤프운반 GF000284 적재기계 · 덤프 고르기
- GF000335 시멘트운반 · GF000336 철근운반(산림 10-1 · 10-2) — 운임 = 자재 구역화물(톤급 · 거리 줄) ÷ 1.1 ÷ 적재 톤 · 하차 품 = 입력(기본값 없음 · 옛 판 값은 설명 참고만)
- GX000004 깬잡석 · 야면석 · 고임돌 · 파쇄석 채집(기계) — 굴착기 1회 N개 · 건설 공통 8-2-3(GC001147) · 상차 · 소운반 덤프 8-2-8(GC001155) · 실무 봉화#3 Q 12.38 · 9.44 재현
- GF000284 입력 끝에 적재기계(굴착기 0.2~1.0 · 로더 타이어 1.34 · 1.72) · 덤프(15 · 8 · 24ton) — 첫째 = 원문 · 기존 셈 그대로(54 조합 전후 같음)
- 빌더 build_로직_M01보강_794_2a2.py = M01 [저장] 길 · 지킴 시험 test_794_2a2_m01_add.py

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 19:56:16 +09:00

227 lines
9.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""794-2a2 M01 보강 지킴 — 자재 운반 GF 둘(산림 10-1 · 10-2) · 기계 채집 자체 GX · 덤프 조합 GF000284 넓힘.
① 새 줄 셋 엔진 셈 — 운임 = 자재 구역화물(톤급 · 거리 줄 하나) ÷ 1.1 ÷ 적재 톤 · 하차 품 = 입력(기본값 없음) ·
채집 = 실무 봉화#3 식(q = 4/16 · Q 12.38 · 소운반 Q 9.44) · 견본 자동값으로 멈추지 않음.
② GF000284 — 고르기 첫째(굴착기 0.7㎥ · 15ton) = 넓히기 전 식(원문 수 0.7 · 15 · 20 · 0.85 · 22초) 그대로 ·
다른 고름 = 건설 공통 8-2-3 싸이클 · 8-2-5 로더 cm(거창#27 1.8 × 8 + 6 + 14 = 34.4) · 덤프 T.
③ 근거 제안(793-3a) — 794-2a 「없음」 블록 견본이 새 줄을 기초로 잡음(철근 · 깬잡석 · 파쇄석).
마스터 = 지금 M01(읽기만) · 엑셀 = `helper_m02_hopyo_golden.FILES`(없으면 ③ skip).
"""
from __future__ import annotations
import importlib
import itertools
from decimal import Decimal as D
from functools import lru_cache
import helper_m02_hopyo_golden as golden
import pytest
from M01_MasterData import M01_MasterData_Store as m01 # scripts 길을 둠
ma = importlib.import_module("master_auto")
mcut = importlib.import_module("master_cut")
mf = importlib.import_module("master_formula")
CEMENT, REBAR, GATHER, DUMP = "GF000335", "GF000336", "GX000004", "GF000284"
@lru_cache(maxsize=1)
def whole():
return m01.loaded()[1]
def run(key: str, given: dict) -> dict:
w = whole()
auto = {k: v for k, v in ma.values(w, w.logic(key)).items() if v is not None}
return mcut.engine(w, key, mf.run(w, key, {**auto, **given}))
def near(a: D, b: D) -> bool:
"""줄 단가 = 하위 로직 계(끝수 셈이 원 미만을 끊음)."""
return abs(a - b) < 1
def line(got: dict, name: str) -> dict:
return next(x for x in got["줄"] if x["이름"] == name)
# ── ① 새 줄 ────────────────────────────────────────────────────────────
def test_새_줄_셋_자리():
w = whole()
for key, no, unit in (
(CEMENT, "10-1", "원/포"),
(REBAR, "10-2", "원/ton"),
(GATHER, "13-2-4", "원/㎡"),
):
row = w.logic(key)
assert row["원문번호"] == no and row["결과단위"] == unit and row["소유"] == "공용"
got = run(key, {}) # 견본 자동값 — 멈추지 않음
assert got["계"] >= 0
def test_철근운반_실무_북부청임도36():
got = run(REBAR, {"톤급": "10.5톤", "거리": "20km", "하차품": D(2) * 11 / 450})
fr = line(got, "구역화물 운임")
assert fr["품목"]["규격"] == "10.5톤, 20km" and fr["비목"].keys() == {"경비"}
assert fr["수량"] == 1 / (D("1.1") * D("10.5"))
assert fr["금액"] == (fr["단가"] / D("1.1") / D("10.5")).quantize(
D("0.1"), "ROUND_DOWN"
) # 줄 0.1 원 버림
labor = line(got, "보통인부(하차)")
assert labor["수량"] == D(2) * 11 / 450 and labor["비목"].keys() == {"노무비"}
# 엑셀 「구역화물(10.5톤,20km) 111,240 ÷ 1.1 ÷ 10.5 = 9,631.1」 — 같은 단가면 같은 금액
if fr["단가"] == 111240:
assert round(fr["금액"], 1) == D("9631.1")
def test_시멘트운반_포당():
got = run(CEMENT, {"톤급": "5.0톤", "거리": "20km", "하차품": D("0.31")})
fr = line(got, "구역화물 운임")
assert fr["품목"]["규격"] == "5.0톤, 20km"
assert fr["수량"] == 1 / (D("1.1") * D("5.0") * 25) # 40kg 포대 · 25 포/ton(원문 주)
assert line(got, "보통인부(하차)")["수량"] == D("0.31") / 25
def test_구역화물_톤급_거리_줄_하나씩():
w = whole()
spec = {s["이름"]: s["고르기"] for s in w.logic(REBAR)["입력"] if "고르기" in s}
assert spec["톤급"][0] == "10.5톤" and len(spec["톤급"]) == 20 and len(spec["거리"]) == 24
for ton, km in itertools.product(spec["톤급"], spec["거리"]):
got = run(REBAR, {"톤급": ton, "거리": km, "하차품": D(0)})
fr = line(got, "구역화물 운임")
assert fr["품목"]["규격"] == f"{ton}, {km}", (ton, km)
assert fr["수량"] == 1 / (D("1.1") * D(ton.removesuffix("톤")))
def test_하차품_기본값_없음():
w = whole()
for key in (CEMENT, REBAR):
assert ma.defaults(w, w.logic(key))["하차품"] is None
assert line(run(key, {}), "보통인부(하차)")["금액"] == 0
def test_기계채집_실무_봉화3():
w = whole()
got = run(GATHER, w.logic(GATHER)["시험입력"])
mid = got["중간"]
assert mid["q"] == D("0.25") and mid["K"] == D("0.55") and mid["E"] == D("0.45")
assert mid["cm"] == 18 and mid["Q"] == D("12.375") # 엑셀 Q = 12.38
assert round(mid["Q2"], 2) == D("9.44") # 엑셀 소운반 Q = 9.44(덤프 2.5톤 · L 0.2)
names = [x["이름"] for x in got["줄"]]
assert names == ["굴착기(채집)", "굴착기(상차)", "덤프트럭(소운반)"]
assert got["줄"][0]["수량"] == got["줄"][1]["수량"] == 1 / mid["Q"]
off = run(GATHER, {**w.logic(GATHER)["시험입력"], "상차": "안 함", "운반거리": D(0)})
assert [x["수량"] for x in off["줄"][1:]] == [0, 0]
# ── ② GF000284 넓힘 ───────────────────────────────────────────────────
OLD = { # 넓히기 전 식 — 원문 수
"Q1": "반올림(3600 * 0.7 * K * (1 / L) * E0 / 찾기(QF000336, 구분='㎝(초)').값, 3)",
"q": "15 / 감마 * L",
"n": "q / (0.7 * K)",
"t1": "20 * n / (60 * 0.85)",
}
def _old(mid: dict, name: str) -> D:
return mf.evaluate(mf.parse(OLD[name]), dict(mid), whole(), 0)
@pytest.mark.parametrize("재료", ["토사", "암절취", "발파암"])
def test_덤프운반_기본_고름_옛_식_그대로(재료):
w = whole()
row = w.logic(DUMP)
assert [s["이름"] for s in row["입력"][:4]] == ["재료", "운반거리", "지역", "보정작업"]
assert (
ma.defaults(w, row)["적재기계"] == "0201-0070"
and ma.defaults(w, row)["덤프"] == "0602-0150"
)
for dist, rock in itertools.product(
(D("0.3"), D("23.877")), ("해당 없음", "암석작업(연암・보통암・경암)")
):
got = run(DUMP, {"재료": 재료, "운반거리": dist, "지역": "전국평균", "보정작업": rock})
mid = got["중간"]
assert (
mid["Q1"] == _old(mid, "Q1")
and mid["q"] == _old(mid, "q")
and mid["n"] == _old(mid, "n")
)
assert mid["Cms"] * mid["n"] / (60 * mid["Es"]) == _old(mid, "t1")
dig, truck = got["줄"]
assert [dig["이름"], truck["이름"]] == [
"굴착기(무한궤도) 0.7㎥(적재)",
"덤프트럭 15ton(운반)",
]
base = {"지역": "전국평균", "보정작업": rock}
assert near(dig["단가"], mf.run(w, "GC000267", {"기계": "0201-0070", **base})["계"])
assert near(truck["단가"], mf.run(w, "GC000269", {"기계": "0602-0150", **base})["계"])
def test_덤프운반_넓힌_고름():
g = {"재료": "토사", "운반거리": D(1), "지역": "전국평균", "보정작업": "해당 없음"}
small = run(DUMP, {**g, "적재기계": "0201-0020"})["중간"]
assert (small["q0"], small["Cm"], small["Cms"]) == (
D("0.2"),
20,
18,
) # 8-2-3 0.12∼0.4 · 180° · 135°
big = run(DUMP, {**g, "적재기계": "0201-0100"})["중간"]
assert (big["q0"], big["Cm"], big["Cms"]) == (D("1.0"), 23, 21)
loader = run(DUMP, {**g, "적재기계": "0302-0134"})
mid = loader["중간"]
assert (
mid["q0"] == D("1.34") and mid["Cms"] == D("34.4") == mid["Cm"]
) # 거창#27 1.8 × 8 + 6 + 14
assert mid["K"] == 1 and mid["Es"] == mid["E0"] == D("0.6") # 거창#27 K 1 · Es 0.6
rock = run(DUMP, {**g, "보정작업": "암석작업(연암・보통암・경암)", "적재기계": "0302-0134"})
plain = {"기계": "0302-0134", "지역": "전국평균", "보정작업": "해당 없음"}
assert near(
rock["줄"][0]["단가"], mf.run(whole(), "GC000267", plain)["계"]
) # 타이어 로더 = 손료 보정 없음
for dump, ton in (("0602-0080", 8), ("0602-0240", 24)):
mid = run(DUMP, {**g, "덤프": dump})["중간"]
assert mid["덤프T"] == ton and mid["q"] == ton / mid["감마"] * mid["L"]
# ── ③ 근거 제안 ───────────────────────────────────────────────────────
SAMPLES = { # 블록: (기초, 단)
"북부청임도#36": (REBAR, {"확정", "근접"}), # 철근운반 [산림사업표준품셈 10-2]
"봉화#117": (
CEMENT,
{"확정", "근접", "사람"},
), # 시멘트운반 — 제목 단독(톤급 「10.5ton」 못 읽음 = 794-2c)
"봉화#3": (GATHER, {"확정", "근접"}), # 깬잡석채집
"울진소광#1448": (GATHER, {"확정", "근접"}), # 파쇄석 채집
}
@lru_cache(maxsize=8)
def _book(tag: str):
from M02_MasterTemplete import M02_San_Basis_Store as store
from M02_MasterTemplete import M02_San_Book as san_book
path = golden.FILES[tag]
if not path.exists():
return None
book = golden.read(tag)
if book is None:
return None
sheets = san_book.sheets(path.name, path.read_bytes()) if book["꼴"] == "STmate" else {}
return {**book, "숨은식": store.hidden(book, sheets)}
@pytest.mark.parametrize("block", sorted(SAMPLES))
def test_근거제안_없음에서_새_기초(block):
from M02_MasterTemplete import M02_San_Basis_Store as store
tag, no = block.split("#")
book = _book(tag)
if book is None:
pytest.skip("실무 엑셀 없음")
got = store.one(book, int(no))
key, tiers = SAMPLES[block]
assert got["단"] in tiers, got["단"]
assert key in [c["기초"] for c in got["근거후보"]]