Files
Aislo/resources/tester/test_846_2_work_view.py
T
eomsangdonandClaude Opus 5.5 c8cd98dec1 feat(846-2): 기계 줄 시간당 작업량 화면 — 산출근거 「시간당 작업량」 줄 먼저 · 비목 ÷ 시간당 작업량 · 제어 구역 일당 / 시간당 식 고르기 · 변수 칸 · 기계 보기 작업량 식
- 산출근거: 일당 「시간당 작업량 = 시공량 560 ㎥/일 ÷ (1 대 × 8 시간) = 70 ㎥/시간」 · 시간당 식 = q · K · f · E · cm 값 줄 + 한 층 분수 Q 식 · 비목 줄 「59,020 원/시간 ÷ 70 ㎥/시간 = 843.1 원」(금액 그대로)
- 제어 구역: 「시간당 작업량」 묶음 맨 위 — 방식 고르기(식 없는 기계 = 잠김 + 까닭) · 시간당 = 작업량 틀 변수 칸(고침 · 원래 값) · 덮음 = 기계덮음.<기계>.작업량
- 기계 보기: 왼쪽 작업량 카드(기계 정본 변수) · 오른쪽 풀이 · 서버가 작업량 틀 연결을 실음(M02_MyMachine_Sheet._work)
- 시험: test_846_2_work_view 새로 · test_845_2 GC000088 비목 줄 기대값을 새 꼴로 고침(846-2 표시 변경) · 헬퍼 가짜 DOM 이 진짜 DOM 처럼 요소를 옮김(같은 요소 두 줄 재사용 흠을 잡게)

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

202 lines
8.1 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.
# -*- coding: utf-8 -*-
"""PLAN 846-2(요구 879 · 881 · 882 · 883) — 기계 줄 시간당 작업량 그리기 · 제어 구역 · 기계 보기.
① 일당(GC000088) — 「시간당 작업량 = 시공량 560 ㎥/일 ÷ (1 대 × 8 시간) = 70 ㎥/시간」 줄 먼저 ·
비목 줄 「59,020 원/시간 ÷ 70 ㎥/시간 = 843.1 원」 · 화면 셈 = 서버 셈(금액 그대로)
② 시간당 식 덮음 — 작업량 틀 풀이(q · K · f · E · cm 값 줄 + 한 층 분수 Q 식) · 비목 ÷ Q ·
변수 E 고침 → Q 그만큼 · 화면 셈 = 서버 셈
③ 왼쪽 제어 구역 — 방식 고르기 · 시간당 = 그 식 변수 칸 · 식 없는 기계 = 잠김 글
④ 전수 — 기계 작업량 식이 있는 문서를 모두 시간당 식으로 바꿔 그려도 안 멈춤 ·
겹분수 · 단위 규칙 어긋남 0 · 화면 셈 = 서버 셈
⑤ 기계 보기 서버 — 작업량 틀 문서 · 표 연결을 실음
"""
from __future__ import annotations
import copy
import importlib
import json
import os
import subprocess
from decimal import Decimal
import pytest
from M01_MasterData import M01_MasterData_Store as m01 # noqa: F401 (scripts 길)
from M02_MasterTemplete import M02_Doc_Copy as dc
mr = importlib.import_module("master_doc_record")
HERE = os.path.dirname(os.path.abspath(__file__))
WORK = "작업량"
def _js(payload: dict):
proc = subprocess.run(
["node", os.path.join(HERE, "helper_845_2_unit_show.cjs")],
input=json.dumps(dc.ml._norm(payload), ensure_ascii=False),
capture_output=True,
text=True,
encoding="utf-8",
)
assert proc.returncode == 0, proc.stderr[-3000:]
return json.loads(proc.stdout)
@pytest.fixture(scope="module")
def base():
docs = dc.folder()
return docs, dc.ml.Links(dc.skc.whole_(), docs)
def _case(base, key: str, doc: dict):
rec = mr.Rec(base[1])
rec.prefetch(doc)
ans = dc.mc.calc(doc, rec)
return {"키": key, "문서": doc, "연결": rec.out(), "기계": False}, ans
def _hourly(doc: dict, machine: str, key: str, vals: dict | None = None) -> dict:
out = copy.deepcopy(doc)
w = {"방식": "시간당", "식": key, **({"변수": vals} if vals else {})}
out["기계덮음"] = {machine: {WORK: w}}
return out
def _num(v):
return None if v is None else float(Decimal(str(v)))
@pytest.fixture(scope="module")
def gc88(base):
d = base[0]["GC000088"]
cases, py = [], {}
for key, doc in [
("일당", d),
("시간당", _hourly(d, "0201-0100", "GC001147")),
("E", _hourly(d, "0201-0100", "GC001147", {"E입력": 0.8})),
]:
c, a = _case(base, key, doc)
cases.append(c)
py[key] = a
got = _js({"할일": "전수", "문서들": cases, "보기": [c["키"] for c in cases]})
return {g["키"]: g for g in got}, py
def test_일당_시간당_작업량_줄_먼저_비목_나눔(gc88):
g, py = gc88
rows = [r["t"] for r in g["일당"]["오른쪽"]]
work = (
r"품셈 일당⟦\text{시간당 작업량}⟧ = ⟦\text{시공량}\ 560\ \text{㎥/일} \div "
r"\left(1\ \text{대} \times 8\ \text{시간}\right)⟧ = ⟦70\ \text{㎥/시간}⟧"
)
assert work in rows
labor = "노무비 : 59,020 원/시간 ⟦{}\\div 70\\ \\text{㎥/시간}⟧ = 843.1 원"
assert labor in rows
assert rows.index(work) < rows.index(labor) # 작업량 줄 먼저
assert g["일당"]["bad"] == []
assert _num(py["일당"]["계"]) == g["일당"]["셈"]["계"] == 1893 # 금액 그대로 · 화면 = 서버
def test_시간당_식_풀이_비목_나눔_금액(gc88):
g, py = gc88
rows = [r["t"] for r in g["시간당"]["오른쪽"]]
text = "\n".join(rows)
assert (
"⟦q = 1\\ \\text{㎥}⟧⟦K = 0.9⟧⟦f = 0.7⟧⟦E = 0.75⟧⟦\\mathit{cm} = 21\\ \\text{sec}⟧" in text
)
assert (
r"⟦Q⟧ = ⟦\dfrac{3{,}600 \times q \times K \times f \times E}{\mathit{cm}}⟧ = "
r"⟦\dfrac{3{,}600 \times 1 \times 0.9 \times 0.7 \times 0.75}{21}⟧ = ⟦81\ \text{㎥/시간}⟧"
) in text
assert "노무비 : 59,020 원/시간 ⟦{}\\div 81\\ \\text{㎥/시간}⟧ = 728.6 원" in rows
assert any(t.startswith("시간당 식") for t in rows) # 머리 작은 글 = 방식
assert g["시간당"]["bad"] == []
assert _num(py["시간당"]["계"]) == g["시간당"]["셈"]["계"] != g["일당"]["셈"]["계"]
def test_시간당_변수_E_고침_그만큼(gc88):
g, py = gc88
text = "\n".join(r["t"] for r in g["E"]["오른쪽"])
assert "⟦E = 0.8⟧" in text and r"= ⟦86.4\ \text{㎥/시간}⟧" in text # 81 × 0.8 / 0.75
assert "노무비 : 59,020 원/시간 ⟦{}\\div 86.4\\ \\text{㎥/시간}⟧ = 683.1 원" in text
assert _num(py["E"]["계"]) == g["E"]["셈"]["계"]
w = next(r["작업량"] for r in g["E"]["셈"]["줄"] if r["작업량"])
assert w["꼴"] == "시간당" and w["값"] == 86.4 and w["변수"]["E입력"] == 0.8
def test_왼쪽_제어_구역_방식_변수_칸(gc88):
g, _ = gc88
day = [r["t"] for r in g["일당"]["왼쪽"]]
hour = [r["t"] for r in g["E"]["왼쪽"]]
assert "품셈 일당시간당 식" in [t.strip() for t in day] # 고르기 두 칸
assert not any(t.strip().startswith("버킷용량") for t in day) # 일당 = 변수 칸 없음
assert "버킷용량 q=[1]㎥" in [t.strip() for t in hour]
assert "체적환산계수 f=[0.7]" in [t.strip() for t in hour]
e = next(t.strip() for t in hour if t.strip().startswith("E입력"))
assert e.startswith("E입력=[0.8]고침") # 고친 칸 표지
@pytest.fixture(scope="module")
def every(base):
"""작업량 자리가 있는 문서 전수 — 일당 그대로 + 기계 식이 있으면 시간당 식으로."""
docs = base[0]
plain, hourly, kinds, nofit = [], [], {}, []
for key, doc in sorted(docs.items()):
c, a = _case(base, key, doc)
works = [r[WORK] for r in a.get("줄") or [] if isinstance(r, dict) and r.get(WORK)]
if not works:
continue
plain.append(c)
for w in works:
kinds.setdefault(w["꼴"], set()).add(key)
cover = {
w["기계"]: {WORK: {"방식": "시간당", "식": w["식들"][0]}} for w in works if w["식들"]
}
if not cover:
nofit.append(key)
continue
h = copy.deepcopy(doc)
h["기계덮음"] = cover
hc, ha = _case(base, f"{key}~시간당", h)
hc["py계"] = _num(ha.get("계"))
hourly.append(hc)
return plain, hourly, kinds, nofit
def test_전수_일당_시간당_차림_어긋남_0(every):
plain, hourly, kinds, nofit = every
assert len(kinds.get("일당", ())) >= 200 and len(kinds.get("식", ())) >= 40
want = {c["키"]: c.get("py계") for c in hourly}
got = _js({"할일": "전수", "문서들": plain + hourly, "보기": list(want)})
assert len(got) == len(plain) + len(hourly)
bad = {g["키"]: g["bad"][:2] for g in got if g["bad"]}
assert bad == {}
assert all(g["같음"] for g in got)
off = {
g["키"]: (want[g["키"]], g["셈"]["계"])
for g in got
if g["키"] in want and want[g["키"]] != g["셈"]["계"]
}
assert off == {} # 시간당 식 화면 셈 = 서버 셈
def test_식_없는_기계_잠김_글(base, every):
_, _, _, nofit = every
assert nofit
c, _ = _case(base, nofit[0], base[0][nofit[0]])
g = _js({"할일": "전수", "문서들": [c], "보기": [c["키"]]})[0]
assert any("시간당 작업량 식 없는 기계" in r["t"] for r in g["왼쪽"])
def test_기계_보기_서버_작업량_연결():
from M02_MasterTemplete import M02_MyMachine_Sheet as sheet
folder = os.path.join(HERE, "..", "master_data", "기계")
rows = (json.load(open(os.path.join(folder, f), encoding="utf-8")) for f in os.listdir(folder))
row = next(r for r in rows if r.get("원문번호") == "0201-0100")
got = sheet.sheet(row)
keys = [s["식"] for s in row["값"][WORK]]
assert "GC001147" in keys
assert all(k in got["연결"]["문서"] for k in keys)
assert got["연결"]["표"] # 작업량 틀이 읽는 표(버킷계수 · 작업효율 · 싸이클)