- 검사 넷: 틀 · 본문 대조(표 허구 · 절 결손) · 로직 검사(요소·표·칸 · 찾기 조건 · 로직 입력 · 돌고 도는 참조) · 시험 계산(호표 줄 · 비목 합) - 식 읽기·계산은 master_formula.py(틀 7장 범위 · eval 없음 · Decimal) - 틀: 번호로 적은 값은 원문 이름을 비고에 · 분수·식 값 · 짝 칸 · 8장 검사 - 기계 아홉 줄 비고에 조종원직종 원문 이름(건설기계운전사) - 시험 test_masterdata_check.py: 견본 셋 손 계산 그대로 · 견본 파일 검사 통과 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JgUhN55Z3BCYhUJTjwTNfj
121 lines
3.9 KiB
Python
121 lines
3.9 KiB
Python
"""마스터 데이터 검사 도구 — 견본 셋 손 계산 · 견본 파일 검사 통과 (`resources/master_data/scripts/check_master.py`)."""
|
|
|
|
import sys
|
|
from decimal import ROUND_HALF_UP, Decimal
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "master_data/scripts"))
|
|
|
|
import check_master as cm # noqa: E402
|
|
import master_formula as mf # noqa: E402
|
|
|
|
SAMPLES = [
|
|
"계수_건설품셈_공통8장.json",
|
|
"계수_산림품셈_9장.json",
|
|
"기계_건설품셈.json",
|
|
"로직_건설품셈_공통8장.json",
|
|
"로직_산림품셈_13장.json",
|
|
"로직_산림품셈_9장.json",
|
|
"소요량_산림품셈_13장.json",
|
|
]
|
|
EXCAVATOR = "건설품셈:8 굴착기(무한궤도) 시간당 경비"
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def whole():
|
|
return cm.master()
|
|
|
|
|
|
def cents(x) -> str:
|
|
return str(Decimal(x).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP))
|
|
|
|
|
|
def test_메쌓기(whole):
|
|
got = mf.run(
|
|
whole,
|
|
"산림품셈:13-4-1 메쌓기",
|
|
{"뒷길이": 35, "돌": "견치돌", "쌓기": "골쌓기", "높이": 2, "초과증가율": 80},
|
|
)
|
|
assert cents(got["계"]) == "203281.20"
|
|
|
|
|
|
def test_메쌓기_높이_증가율(whole):
|
|
got = mf.run(
|
|
whole,
|
|
"산림품셈:13-4-1 메쌓기",
|
|
{"뒷길이": 35, "돌": "견치돌", "쌓기": "골쌓기", "높이": 5, "초과증가율": 80},
|
|
)
|
|
assert [line["수량"] for line in got["줄"]] == [Decimal("0.70"), Decimal("0.56")]
|
|
|
|
|
|
def test_굴착기(whole):
|
|
got = mf.run(
|
|
whole, EXCAVATOR, {"기계": "0201-0070", "지역": "전국평균", "보정작업": "해당 없음"}
|
|
)
|
|
assert [cents(got[k]) for k in ("노무비", "재료비", "경비", "계")] == [
|
|
"35412.13",
|
|
"26130.11",
|
|
"24554.00",
|
|
"86096.24",
|
|
]
|
|
|
|
|
|
def test_굴착기_암석_보정(whole):
|
|
got = mf.run(
|
|
whole,
|
|
EXCAVATOR,
|
|
{"기계": "0201-0070", "지역": "전국평균", "보정작업": "암석작업(연암・보통암・경암)"},
|
|
)
|
|
assert got["중간"]["손료"] == 28322
|
|
assert cents(got["계"]) == "89864.24"
|
|
|
|
|
|
def test_노체포설(whole):
|
|
assert mf.run(whole, "산림품셈:9-16-1 노체포설 작업량", {})["결과"] == Decimal("24.255")
|
|
got = mf.run(whole, "산림품셈:9-16-1 노체포설", {"지역": "전국평균", "보정작업": "해당 없음"})
|
|
assert [cents(got[k]) for k in ("노무비", "재료비", "경비", "계")] == [
|
|
"1459.99",
|
|
"1077.31",
|
|
"1012.33",
|
|
"3549.63",
|
|
]
|
|
|
|
|
|
def test_범위_입력은_막음(whole):
|
|
with pytest.raises(mf.FormulaError):
|
|
mf.run(
|
|
whole,
|
|
"산림품셈:13-4-1 메쌓기",
|
|
{"뒷길이": 35, "돌": "견치돌", "쌓기": "골쌓기", "높이": 8, "초과증가율": 120},
|
|
)
|
|
|
|
|
|
def test_견본_파일_검사_통과(whole):
|
|
files = cm.load(SAMPLES)
|
|
assert len(files) == len(SAMPLES)
|
|
assert [x for name, data in files.items() for x in cm.check_form(name, data)] == []
|
|
assert cm.check_body(files) == []
|
|
assert cm.check_logics(files, whole) == []
|
|
|
|
|
|
def test_로직_검사가_틀린_열쇠를_잡음(whole):
|
|
row = {
|
|
"열쇠": "시험",
|
|
"입력": [{"이름": "지역", "고르기": ["전국평균", "없는곳"]}],
|
|
"중간": [{"이름": "x", "식": "찾기(계수:산림품셈:9-16-1 노체포설, 없는조건='K').적용"}],
|
|
"호표": [
|
|
{"종류": "재료", "요소": "재료:오피넷유가:경유:{지역}", "수량": "y", "비목": "재료비"}
|
|
],
|
|
"덧줄": [],
|
|
}
|
|
found, _ = mf.check_logic(whole, "산림품셈", row)
|
|
assert any("없는조건" in x for x in found)
|
|
assert any("경유:없는곳" in x for x in found)
|
|
assert any("「y」" in x for x in found)
|
|
|
|
|
|
def test_돌고_도는_참조():
|
|
assert mf.find_loops({"가": {"나"}, "나": {"가"}}) == [["가", "나", "가"]]
|