"""PLAN 821-2 기계 계수 계산 짝 — 지킴 시험. ① 계수 전수 대조: M01 기계표에서 품셈 입력 다섯 · 계수 넷이 다 있는 줄 = 파이썬 계수 셈과 같음 (다른 줄 = 원문 표 값 그대로인 점보드릴 2 줄만 · 원문 777 · 셈 777.8). ② 파이썬 · TS 거울: 같은 입력 → 같은 계수 · 같은 시간당 미리보기. ③ 미리보기 = 서버 기계경비식(`M02_Machine_Hourly.hourly`) 견본 스무 대 · 비목 셋 같음. """ from __future__ import annotations import json import subprocess import sys from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT)) sys.path.insert(0, str(ROOT / "resources" / "master_data" / "scripts")) import master_machine_coef as mc # noqa: E402 import master_machine_files as mmf # noqa: E402 DATA = ROOT / "resources" / "master_data" TS = ROOT / "common_util" / "common_util_machine_coef.ts" # 원문 표 값 ≠ 식(정비비계수 0.7 ÷ 9,000 × 10^7 = 777.8 → 원문 777 · 계 2,515) — 식을 비틀지 않음 ORIGINAL_ODD = {"EQ000347", "EQ000348"} SAMPLES = ( "EQ000001 EQ000006 EQ000022 EQ000026 EQ000040 EQ000051 EQ000073 EQ000076 EQ000126 EQ000151 " "EQ000176 EQ000201 EQ000226 EQ000229 EQ000246 EQ000326 EQ000401 EQ000434 EQ000455 EQ000544" ).split() def _load(name: str) -> list[dict]: return json.loads((DATA / name).read_text(encoding="utf-8"))["줄"] MACHINES = mmf.table(DATA, exact=False)[0]["줄"] # 기계 = 폴더(826-2) FUEL = {r["원문번호"]: r["값"] for r in _load("재료_유가전력.json")} WAGE = {r["원문번호"]: r["값"] for r in _load("인력.json")} def _full(row: dict) -> bool: v = row["값"] return all(isinstance(v.get(k), (int, float)) for k in (*mc.INPUTS, *mc.COEFS)) def _prices(v: dict) -> tuple[float, float]: """전국평균 유가 · 노임(직종 비면 기계경비식 조종원직종 첫 고르기 1048).""" return FUEL.get(f"{v.get('주연료종류')}:전국평균", 0), WAGE.get( str(v.get("조종원직종") or "1048"), 0 ) def test_coef_matches_master_table(): rows = [r for r in MACHINES if _full(r)] assert len(rows) >= 590 odd = {r["키"] for r in rows if mc.coef_of(r["값"]) != {k: r["값"][k] for k in mc.COEFS}} assert odd == ORIGINAL_ODD def test_coef_excavator_example(): got = mc.coef(10000, 1250, 0.9, 0.7, 0.1) assert got == {"상각비계수": 900, "정비비계수": 700, "관리비계수": 485, "손료계수": 2085} assert mc.coef(0, 1250, 0.9, 0.7, 0.1) is None assert mc.coef_of({"내용시간": 10000}) is None @pytest.mark.skipif( not (ROOT / "config" / "node_modules" / "typescript").is_dir(), reason="프론트엔드 의존성 미설치", ) def test_ts_mirror(tmp_path): coef_cases = [[r["값"][k] for k in mc.INPUTS] for r in MACHINES if _full(r)] coef_cases += [[10000, 1250, 0.9, 0.7, 0.1], [0, 1, 1, 1, 1], [3600, 540, 0.9, 1.15, 0.1]] hour_cases = [] for r in MACHINES: fuel, wage = _prices(r["값"]) hour_cases.append([r["값"], fuel, wage, 114.4, 0.125]) hour_cases.append( [ {"가격": 2680, "손료계수": 5313, "주연료": 1.0, "잡재료율": 10, "전력": 3.73}, 2000, 0, 114.4, 0.125, ] ) src = tmp_path / "in.json" src.write_text( json.dumps({"coef": coef_cases, "hour": hour_cases}, ensure_ascii=False), encoding="utf-8" ) script = f""" const fs = require("fs"); const ts = require({json.dumps(str(ROOT / "config" / "node_modules" / "typescript"))}); const opt = {{ module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }}; const src = fs.readFileSync({json.dumps(str(TS))}, "utf8"); const js = ts.transpileModule(src, {{ compilerOptions: opt }}).outputText; const m = {{ exports: {{}} }}; new Function("exports", "module", js)(m.exports, m); const inp = JSON.parse(fs.readFileSync({json.dumps(str(src))}, "utf8")); process.stdout.write(JSON.stringify({{ coef: inp.coef.map((c) => m.exports.machineCoef(...c)), hour: inp.hour.map((h) => m.exports.machineHourly(...h)), }})); """ out = subprocess.run( ["node", "-e", script], capture_output=True, text=True, encoding="utf-8", check=True ) got = json.loads(out.stdout) want_coef = [mc.coef(*c) for c in coef_cases] want_hour = [mc.preview(*h) for h in hour_cases] assert got["coef"] == want_coef assert len(got["hour"]) == len(want_hour) for a, b in zip(got["hour"], want_hour): assert a == pytest.approx(b, abs=1e-9) def test_preview_equals_server_hourly(): from M02_MasterTemplete import M02_Machine_Hourly as mh rows = {r["키"]: r for r in MACHINES} for key in SAMPLES: h = mh.hourly(key) assert h["ok"], (key, h) v = rows[key]["값"] p = mc.preview(v, *_prices(v)) assert (p["노무비"], p["재료비"], p["경비"], p["계"]) == ( h["노무비"], h["재료비"], h["경비"], h["계"], ), (key, h["로직"])