- B08_Quantity_Haul_Cycle.py · .ts 짝 — 건설품셈 8-2-8 덤프트럭 · 거창 25 단가산출 1308~1347행 재현(t1 5.33 · t2 101.23 · Cm 109.78) - 로직 입력 이름 채움 Cm · 사이클시간 · t2 · 운반거리 · 덮은 칸(own) 그대로 - B09 셈(hand_sources) 때 거리이정표 거리로 다시 채움 · 덮개 ref 에 cycle · own 남김 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JywHxQuWPk6UYXTebyrBoU
115 lines
5.2 KiB
Python
115 lines
5.2 KiB
Python
"""253-2 운반 사이클시간 짝 — 거창 25 설계서 단가산출 1308~1347행 재현 · Py ↔ TS 거울 ·
|
|
로직 입력 이름 채움 · B09 셈(hand_sources) 때 거리이정표 거리로 다시 채움."""
|
|
|
|
import json
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from B08_Quantity.B08_Quantity_Haul_Cycle import cycle_time, fill_given # noqa: E402
|
|
from B09_Estimation.B09_Estimation_Sangeun import hand_sources # noqa: E402
|
|
from B09_Estimation.B09_Estimation_Store import ref_rows # noqa: E402
|
|
|
|
XLSX = next(
|
|
(ROOT / "resources/knowledge/original/실무문서").glob(
|
|
"01.산불거창*/01.내역서/00.25년_산불_거창*내역+수량.xlsx"
|
|
),
|
|
None,
|
|
)
|
|
#: 칸 → 입력 이름(단가산출 시트 · 32.혼합골재 운반(덤프15톤))
|
|
CELLS = {"S1318": "L1", "AR1318": "L2", "R1320": "V1", "R1321": "V2", "AQ1320": "V3",
|
|
"AQ1321": "V4", "K1323": "T", "AB1323": "rt", "Q1325": "f", "L1327": "qs",
|
|
"Z1327": "k", "AL1327": "Es", "BA1327": "Cms", "L1337": "t3", "X1337": "t4",
|
|
"AK1337": "t5", "AW1337": "t6"} # fmt: skip
|
|
|
|
|
|
@pytest.mark.skipif(XLSX is None, reason="거창 설계서 없음")
|
|
def test_거창_혼합골재_운반_재현():
|
|
import openpyxl
|
|
|
|
ws = openpyxl.load_workbook(XLSX, read_only=False)["단가산출"]
|
|
inputs = {}
|
|
for cell, name in CELLS.items():
|
|
text = str(ws[cell].value) # =TEXT(25.1,"#,##0.#######")
|
|
inputs[name] = float(re.match(r"=TEXT\(([\d.]+),", text).group(1))
|
|
# 식 줄이 같은 꼴인지(t2 · Cm)
|
|
assert (
|
|
"(S1318)/(R1320)+(S1318)/(R1321)+(AR1318)/(AQ1320)+(AR1318)/(AQ1321)" in ws["BX1334"].value
|
|
)
|
|
assert "BQ1331+BX1334+L1337+X1337+AK1337+AW1337" in ws["CE1339"].value
|
|
assert (inputs["L1"], inputs["L2"]) == (25.1, 0.8)
|
|
got = cycle_time(inputs)
|
|
assert (got["q"], got["n"], got["t1"]) == (8.82, 5.13, 5.33)
|
|
assert (got["t2"], got["cm"]) == (101.23, 109.78)
|
|
assert cycle_time({"L1": 25.1, "L2": 0.8}) == got # 기본값 = 거창 값
|
|
|
|
|
|
CASES = [
|
|
{"L1": 25.1, "L2": 0.8},
|
|
{"L1": 3.333, "L2": 1.257, "V1": 20, "V2": 25, "V3": 7, "V4": 8, "t5": 3.77},
|
|
{"L1": 0, "L2": 0, "t1": 2.5, "V3": 0},
|
|
{"L1": 12.5, "L2": 4.05, "T": 24, "rt": 1.8, "f": 0.8, "qs": 1.2, "k": 0.9, "Es": 0.75},
|
|
]
|
|
|
|
|
|
def test_거울_ts():
|
|
calc = ROOT / "B08_Quantity" / "B08_Quantity_Haul_Cycle.ts"
|
|
body = (
|
|
f"\nconst cases = {json.dumps(CASES)};"
|
|
"\nconsole.log(JSON.stringify(cases.map((c) => cycleTime(c))));"
|
|
"\nconst ref = {given: {qt: 1, Cm: 9}, cycle: {L1: 99, t1: 7}, own: ['L1']};"
|
|
"\nconst got = fillGiven(ref, ['Cm', 't2', '운반거리'], {L1: 25.1, L2: 0.8});"
|
|
"\nconsole.log(JSON.stringify(got));"
|
|
)
|
|
result = subprocess.run(
|
|
["node", "--experimental-strip-types", "--no-warnings",
|
|
"--input-type=module-typescript", "-e", calc.read_text(encoding="utf-8") + body],
|
|
capture_output=True, text=True, encoding="utf-8", timeout=60,
|
|
) # fmt: skip
|
|
assert result.returncode == 0, result.stderr
|
|
ts, ts_fill = (json.loads(line) for line in result.stdout.strip().splitlines())
|
|
assert ts == [cycle_time(c) for c in CASES]
|
|
ref = {"given": {"qt": 1, "Cm": 9}, "cycle": {"L1": 99, "t1": 7}, "own": ["L1"]}
|
|
assert ts_fill == fill_given(ref, ["Cm", "t2", "운반거리"], {"L1": 25.1, "L2": 0.8})
|
|
|
|
|
|
def test_입력_이름_채움():
|
|
ref = {"given": {"qt": 15}, "cycle": {}}
|
|
got = fill_given(ref, ["qt", "Cm", "지역"], {"L1": 25.1, "L2": 0.8})
|
|
assert got == {"qt": 15, "Cm": 109.78}
|
|
assert fill_given(ref, ["운반거리"], {"L1": 25.1, "L2": 0.8}) == {"qt": 15, "운반거리": 25.9}
|
|
assert fill_given(ref, ["사이클시간", "t2"], {"L1": 25.1, "L2": 0.8}) == {
|
|
"qt": 15, "사이클시간": 109.78, "t2": 101.23} # fmt: skip
|
|
# 덮은 칸 = 그 값 · 덮은 L1 = 거리 대신 사이클 값
|
|
own = {"given": {"Cm": 50}, "own": ["Cm"]}
|
|
assert fill_given(own, ["Cm"], {"L1": 25.1, "L2": 0.8}) == {"Cm": 50}
|
|
mine = {"given": {}, "cycle": {"L1": 10}, "own": ["L1"]}
|
|
assert fill_given(mine, ["운반거리"], {"L1": 25.1, "L2": 0.8}) == {"운반거리": 10.8}
|
|
|
|
|
|
def test_서버_셈_거리로_다시_채움():
|
|
ref = {"tab": "logic", "code": "GF000134", "name": "골재운반", "given": {"Cm": 1}, "cycle": {}}
|
|
user = {"haul_refs": {"haul:r-u1": ref}, "haul_dist": {"haul:r-u1": {"L1": 25.1, "L2": 0.8}}}
|
|
((_key, _use, src),) = hand_sources(user)
|
|
assert src["given"]["Cm"] == 109.78
|
|
user["haul_dist"]["haul:r-u1"] = {"L1": 10, "L2": 0} # 거리 바뀌면 따라감
|
|
((_key, _use, src),) = hand_sources(user)
|
|
assert src["given"]["Cm"] == 45.69 and "운반거리" not in src["given"] # 그 로직 입력만
|
|
|
|
|
|
def test_덮개_ref_사이클_남김():
|
|
from B09_Estimation.B09_Estimation_Store import _clean_ref
|
|
|
|
ref = {"tab": "logic", "code": "GF000134", "given": {"Cm": 3}, "cycle": {"V1": 20, "x": "a"},
|
|
"own": ["Cm", 3]} # fmt: skip
|
|
got = _clean_ref(ref)
|
|
assert got["cycle"] == {"V1": 20} and got["own"] == ["Cm"]
|
|
rows = ref_rows({"bill": {"rows": [{"key": "haul:r-u1", "ref": got}]}})
|
|
assert rows[0]["ref"]["cycle"] == {"V1": 20}
|