- 표지 = 기계 줄 맨 위 칸 `비활성: true`(없음 = 켜짐 · 값 묶음 밖) · 틀 검사 = 기계 줄에만 true(check_master OFF) - M01 기계 목록 = 끝 열 「사용」 스위치(초안 → 페이지 [저장] · 자동저장 없음) · 끈 줄 흐림 · 머리 「비활성 같이 보기」(끄면 서버 rows state 「켜짐」) - 뺌 = 요소 찾기 창(elements · 찾아 넣기 · 단가산출 기계 창) · logic-lib rows · M01 시험 계산 기계 고르기 표 · 내 기계 「비슷한 기계에서 시작」 · 불러오기 기계 잇기(M02_Machine_Find 표) · 기계경비식 「기계」 옵션(새 GET /api/m02/df/machine-off · 고른 값은 남기고 「· 비활성 기계」 꼬리) - 셈 길은 그대로 — 끈 기계를 쓰던 단가산출 · 사본 값 같음 - 자료 = 해상장비 79 대(펌프 준설선 13 · 그래브 준설선 10 · 하천골재채취선 7 · 기중기선 5 · 예선 9 · 양묘선 11 · 토운선 6 · 이우선 2 · 대선 16) 끔 - 시험 = test_842_2_machine_off 새로 · test_804_10 옵션 글 단언을 새 글로 고침(끈 기계 거름이 들어간 줄) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
119 lines
5.4 KiB
Python
119 lines
5.4 KiB
Python
"""842-2 기계 비활성 — 지킴 시험(쓰기는 임시 사본만).
|
|
|
|
① 틀 검사 — 기계 줄 맨 위 칸 `비활성` = true 만 · 다른 그룹 줄 · false 는 오류
|
|
② 저장 왕복 — M01 저장 길이 표지를 받고(그 대 파일 하나) 지우면 칸이 사라짐 · 셈 값 그대로
|
|
③ 끈 기계는 찾기 · 고르기 · 잇기에서 안 나옴 — 요소 찾기 창 · 비슷한 기계 · 불러오기 기계 잇기 ·
|
|
목록 거름 「켜짐」 · 끈 원문번호 목록 · 셈(시간당)은 그대로
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import copy
|
|
import shutil
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from M01_MasterData import M01_MasterData_Store as store
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
sys.path.insert(0, str(ROOT / "resources" / "master_data" / "scripts"))
|
|
import master_machine_files as mmf # noqa: E402
|
|
|
|
from M01_MasterData import M01_MasterData_Machine as m01m # noqa: E402
|
|
from M02_MasterTemplete import M02_Machine_Find as mfind # noqa: E402
|
|
from M02_MasterTemplete import M02_Machine_Hourly as mh # noqa: E402
|
|
from M02_MasterTemplete.DataFlow import M02_DF_Router as dfr # noqa: E402
|
|
|
|
REAL = store.FOLDER
|
|
cm, mk, M = store.cm, store.mk, mmf.NAME
|
|
|
|
|
|
@pytest.fixture
|
|
def master(tmp_path: Path, monkeypatch) -> Path:
|
|
for path in REAL.glob("*.json"):
|
|
if not path.name.startswith("_") or path.name == mk.BOOK.name:
|
|
shutil.copy(path, tmp_path / path.name)
|
|
shutil.copytree(REAL / mmf.DIR, tmp_path / mmf.DIR)
|
|
monkeypatch.setattr(store, "FOLDER", tmp_path)
|
|
return tmp_path
|
|
|
|
|
|
def _rows() -> list[dict]:
|
|
return store.read(M)[0]["줄"]
|
|
|
|
|
|
def _part(*changes: dict) -> dict:
|
|
return {"file": M, "version": store.read(M)[1], "changes": list(changes)}
|
|
|
|
|
|
# ① 틀 검사 ──────────────────────────────────────────────────────────
|
|
def test_form_check() -> None:
|
|
data = copy.deepcopy(store.read(M)[0])
|
|
data["줄"] = data["줄"][:3]
|
|
assert cm.check_form(M, data) == []
|
|
data["줄"][0][cm.OFF] = True
|
|
assert cm.check_form(M, data) == []
|
|
data["줄"][0][cm.OFF] = False
|
|
assert any(cm.OFF in e for e in cm.check_form(M, data))
|
|
labor = copy.deepcopy(store.read("인력.json")[0])
|
|
labor["줄"] = labor["줄"][:1]
|
|
labor["줄"][0][cm.OFF] = True
|
|
assert any(cm.OFF in e for e in cm.check_form("인력.json", labor))
|
|
|
|
|
|
# ② 저장 왕복 ───────────────────────────────────────────────────────
|
|
def test_save_round_trip(master: Path) -> None:
|
|
key = next(r["키"] for r in _rows() if not r.get(cm.OFF) and r["이름"].startswith("불도저"))
|
|
row = copy.deepcopy(next(r for r in _rows() if r["키"] == key))
|
|
before = mh.hourly(key)
|
|
snap = {p.name: p.read_bytes() for p in (master / mmf.DIR).glob("*.json")}
|
|
store.save([_part({"op": "edit", "key": key, "row": {**row, cm.OFF: True}})])
|
|
after = {p.name: p.read_bytes() for p in (master / mmf.DIR).glob("*.json")}
|
|
assert {n for n in after if after[n] != snap[n]} == {f"{key}.json"}
|
|
got = next(r for r in _rows() if r["키"] == key)
|
|
assert got[cm.OFF] is True and got["값"] == row["값"]
|
|
assert mh.hourly(key) == before # 셈은 그대로(쓰던 단가산출 · 사본이 깨지지 않음)
|
|
assert key not in {x["ref"] for x in store.elements("기계", "불도저", 200)["items"]}
|
|
store.save([_part({"op": "edit", "key": key, "row": row})])
|
|
assert (master / mmf.DIR / f"{key}.json").read_bytes() == snap[f"{key}.json"]
|
|
|
|
|
|
# ③ 찾기 · 고르기 · 잇기에서 뺌 ──────────────────────────────────────
|
|
def _off() -> list[dict]:
|
|
got = [r for r in _rows() if r.get(cm.OFF)]
|
|
if not got:
|
|
pytest.skip("끈 기계 없음")
|
|
return got
|
|
|
|
|
|
def test_hidden_everywhere() -> None:
|
|
off = _off()
|
|
keys = {r["키"] for r in off}
|
|
for r in off[:: max(1, len(off) // 6)]:
|
|
name = str(r["상세구분"] or r["이름"]).replace(" ", "")
|
|
assert not keys & {x["ref"] for x in store.elements("기계", name, 200)["items"]}
|
|
assert not keys & {x["키"] for x in m01m.find(name, 200)}
|
|
with mfind.m01_only():
|
|
got = mfind.find(r["이름"], r.get("규격") or "")
|
|
assert got["키"] not in keys and not keys & {x["키"] for x in got["후보"]}
|
|
listed = store.rows(M, 1, 500, "", state="켜짐")
|
|
assert not keys & {r["키"] for r in listed["rows"]}
|
|
assert listed["total"] == len(_rows()) - len(keys)
|
|
assert store.rows(M, 1, 1, "")["total"] == len(_rows()) # 같이 보기 = 전부
|
|
codes = {str(r["원문번호"]) for r in off} - {
|
|
str(r["원문번호"]) for r in _rows() if not r.get(cm.OFF)
|
|
}
|
|
assert set(dfr.get_machine_off()) == codes
|
|
assert all(c in dfr.get_machine_names() for c in codes) # 고른 값 글은 그대로
|
|
|
|
|
|
def test_sea_machines_off() -> None:
|
|
"""842-2 ㉣ 해상 기계([90]해상장비 = 선원류 칸 있는 대)는 끔 — 사용자가 다시 켤 수 있으므로 켠 대는 알림만."""
|
|
sea = [r for r in _rows() if "해상" in str(r.get("구분"))]
|
|
assert len(sea) == 79
|
|
on = [r["키"] for r in sea if not r.get(cm.OFF)]
|
|
if on:
|
|
pytest.skip(f"다시 켠 해상 기계 {len(on)} 대 — 사용자 판단")
|