- 새 M02_San_MatPick: 종 열쇠(이름 · 규격 · 단위 · 단서) · 고름 자리 = 개인 층 실무자료/재료고름/<파일 지문>.json([끝내기] 뒤 남음 · 같은 파일 다시 올리면 그대로) · 판(블록마다 재료찾기 판정 → 종 · 첫 판 한 번 셈 → 산근 저장본 칸) · 만들 때 얹기 · 이미 만든 SK 얹기(가져온 그대로인 줄만 · 바꿈 · 따로 고른 줄 그대로 · 풀면 가져온 그대로)
- 만들기: from-hopyo 가 그 파일 고름째 만듦(저장 안 한 판 고름 = 만들 때 저장) · 원본 지문 · 고른 줄 = 이음 + 재료찾기.고름 · 문서 셈 우리 값(엑셀 값 기억 없음) · 별도계상 = 품목만(줄 이음 · 단가 0 그대로)
- 문서 머리 가져온.재료(판정 · 후보 · 단서 · 처음 연결) · 가져온.지문
- 길: GET/POST hopyo-materials/{HP}(판 · [저장] · 만든 것 적용은 물음 예일 때만) · GET/POST material-picks/{지문}(SK 상세 물음)
- 화면: 단가산출 목록 위 「고를 재료」 판(후보 고름 · 찾기 · 고름 풀기 · n 블록에 적용 · 이어진 재료 접힘 · [저장]) · 저장 안 한 고름 = 떠날 때 물음 · 줄 쌓기 재료 줄 판정 배지 · 풍선말(판정 · 단서 · 후보) · 이름 단추 · [이음 고르기] 뒤 「같은 파일 n 줄」 물음
- 별도계상 설명 = 「별도계상 · 품목: 이름 규격」(엔진 이은 품목은 자재표 이름) — test_827_2 기대 글 고침(품목 이음 → 품목 이름)
- 시험 test_827_3 셋(파일 고름 블록 둘 · 다른 파일 0 · 별칭 · 마스터 그대로 · 풀기 · 다시 올림 유지 · 물음 예일 때만 · 따로 바꾼 줄 보존 · 별도계상 품목 + 단가 0)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
187 lines
9.5 KiB
Python
187 lines
9.5 KiB
Python
# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀
|
|
"""827-3 지킴 — 불러오기 재료 고름(요구 811-2 · 812-1 · 임시 storage).
|
|
|
|
① 파일 고름 → 그 파일 같은 종 줄 전부 이음(블록 둘) · 고른 줄 = 우리 값(엑셀 값 기억 없음) · 문서 머리 `가져온.재료`
|
|
② 다른 파일 영향 0 · 별칭 · 마스터 안 바뀜 · 고름 풀기 · [끝내기] 뒤 같은 파일 다시 올림 = 고름 그대로
|
|
③ 이미 만든 SK = 물음 예(`apply_made`)일 때만 · 사람이 따로 바꾼 줄 그대로 · 풀면 가져온 그대로로
|
|
④ 별도계상 줄 = 품목 「별도계상 · 품목: 이름 규격」 + 단가 0 그대로(줄 이음 없음)
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import copy as cp
|
|
import json
|
|
from functools import lru_cache
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from M02_MasterTemplete import M02_Doc_Import as dim
|
|
from M02_MasterTemplete import M02_Doc_Sk_Path as skpath
|
|
from M02_MasterTemplete import M02_Elem_Alias as ea
|
|
from M02_MasterTemplete import M02_Hopyo_Forget as forget
|
|
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
|
from M02_MasterTemplete import M02_San_MatPick as mp
|
|
from M02_MasterTemplete import M02_Sangeun_FromHopyo as fh
|
|
from M02_MasterTemplete import M02_Sangeun_Imported as imp
|
|
from resources.tester import test_810_1_stc as stc
|
|
from resources.tester.test_m02_labor_ref import world # noqa: F401 — 같은 임시 층
|
|
|
|
WIRE = "결속선(철선)|#20 , 0.9M/M|KG|" # 봉화 산근 98 · 99 — 후보 여럿
|
|
PIPE_BLOCK = 71 # 동부청영월 파형강관(별도계상) Φ800 머리
|
|
|
|
|
|
@lru_cache(maxsize=2)
|
|
def _raw(tag: str) -> bytes:
|
|
p = stc.FILES[tag]
|
|
if not p.exists():
|
|
pytest.skip(f"STC 없음 {tag}")
|
|
return p.read_bytes()
|
|
|
|
|
|
def _upload(folder: Path, tag: str) -> str:
|
|
return fh.upload_native(folder, stc.FILES[tag].name, _raw(tag), {"이름": "sub3임시"})["키"]
|
|
|
|
|
|
def _lines(doc: dict, kind: str) -> list[dict]:
|
|
return [x for x in doc[imp.KEY]["로직"]["호표"] if mp.kind_of(x.get("자원") or {}) == kind]
|
|
|
|
|
|
def _var(conv: dict, sym: str) -> dict:
|
|
return next(v for v in conv["변수"] if dim.dc.md.var_id(v) == sym)
|
|
|
|
|
|
def _entry(conv: dict, kind: str) -> dict:
|
|
return next(e for e in conv["가져온"]["재료"] if e["종"] == kind)
|
|
|
|
|
|
def test_pick_whole_file(world) -> None:
|
|
me = world["personal"]
|
|
me.mkdir(parents=True, exist_ok=True)
|
|
alias = json.dumps(ea.book()["별칭"], ensure_ascii=False, sort_keys=True, default=str)
|
|
with store.at(me):
|
|
hp = _upload(me, "봉화")
|
|
fp, name = mp.fingerprint(me, hp)
|
|
groups = {g["종"]: g for g in mp.census(me, hp)}
|
|
wire = groups[WIRE]
|
|
assert wire["판정"] == "후보 여럿" and wire["블록"] == [98, 99] and len(wire["후보"]) > 1
|
|
assert all(g["판정"] == "이어짐" or g["후보"] is not None for g in groups.values())
|
|
pick = wire["후보"][1]
|
|
assert mp.write(me, fp, name, {WIRE: pick})[WIRE]["키"] == pick["키"]
|
|
assert mp.tally(me, fp, WIRE) == {"블록": 2, "줄": 2}
|
|
with mp.using(mp.read(me, fp)["고름"]):
|
|
made = [fh.make(me, hp, no)["이름"] for no in (98, 99)]
|
|
for key in made:
|
|
doc = store.read("sangeun", key)["문서"]
|
|
assert doc[imp.KEY]["원본"]["지문"] == fp
|
|
lines = _lines(doc, WIRE)
|
|
assert lines and all(x["이음"] == pick["키"] for x in lines)
|
|
assert all(x["자원"]["재료찾기"]["고름"]["키"] == pick["키"] for x in lines)
|
|
assert all(
|
|
x["자원"]["재료찾기"]["판정"] == "후보 여럿" for x in lines
|
|
) # 엔진 판정 그대로
|
|
conv = dim.derive(doc, None, imp._whole())
|
|
e = _entry(conv, WIRE)
|
|
assert e["고름"]["키"] == pick["키"] and e["처음"]["갈래"] == "숫자"
|
|
assert _var(conv, e["기호"])["연결"] == {"갈래": "요소", "키": pick["키"]} # 우리 값
|
|
assert sorted(mp.made(me, fp)) == sorted(made)
|
|
assert mp.uses(fp, WIRE, (made[0],)) == [made[1]]
|
|
# 다른 파일 = 영향 0
|
|
other = _upload(me, "울진대흥")
|
|
fp2, _ = mp.fingerprint(me, other)
|
|
assert fp2 != fp and mp.read(me, fp2)["고름"] == {}
|
|
# 고름 풀기
|
|
assert mp.write(me, fp, name, {WIRE: None}) == {}
|
|
assert mp.read(me, fp)["고름"] == {}
|
|
# [끝내기] 뒤 같은 파일 다시 올림 = 고름 그대로
|
|
mp.write(me, fp, name, {WIRE: pick})
|
|
forget.finish(me, hp)
|
|
again = _upload(me, "봉화")
|
|
assert again != hp and mp.fingerprint(me, again)[0] == fp
|
|
assert mp.read(me, fp)["고름"][WIRE]["키"] == pick["키"]
|
|
# 별칭 · 마스터 안 바뀜 · 고름 자리 = 개인 층 실무자료/재료고름 한 파일씩
|
|
assert json.dumps(ea.book()["별칭"], ensure_ascii=False, sort_keys=True, default=str) == alias
|
|
assert sorted(p.name for p in (me / "실무자료" / mp.DIRNAME).iterdir()) == [f"{fp}.json"]
|
|
assert not list(world["system"].rglob(f"{fp}*"))
|
|
|
|
|
|
def test_made_only_on_yes_and_keeps_user_lines(world) -> None:
|
|
me = world["personal"]
|
|
me.mkdir(parents=True, exist_ok=True)
|
|
with store.at(me):
|
|
hp = _upload(me, "봉화")
|
|
fp, name = mp.fingerprint(me, hp)
|
|
sk98, sk99 = (fh.make(me, hp, no)["이름"] for no in (98, 99)) # 고름 전에 만듦
|
|
conv = skpath.current(store.read("sangeun", sk98)["문서"])["문서"]
|
|
e = _entry(conv, WIRE)
|
|
before = _var(conv, e["기호"])["연결"]
|
|
assert before["갈래"] == "숫자" and "고름" not in e
|
|
pick = e["후보"][0]
|
|
mp.write(me, fp, name, {WIRE: pick}) # 판 [저장](물음 아니오) = SK 그대로
|
|
assert _var(skpath.current(store.read("sangeun", sk98)["문서"])["문서"], e["기호"])["연결"] == before # fmt: skip
|
|
# 99 = 사람이 그 줄을 따로 고름(줄 쌓기 이름 단추)
|
|
got = store.read("sangeun", sk99)
|
|
doc = cp.deepcopy(skpath.current(got["문서"])["문서"])
|
|
other = e["후보"][2]["키"]
|
|
_var(doc, _entry(doc, WIRE)["기호"])["연결"] = {"갈래": "요소", "키": other}
|
|
skpath.save(sk99, {"문서": doc, "판": got["판"]})
|
|
money = skpath.read(sk98)["셈"]
|
|
out = mp.apply_made(fp, mp.read(me, fp)["고름"], {WIRE}) # 물음 예
|
|
assert out == {"고침": [sk98], "그대로": [sk99]}
|
|
now = skpath.read(sk98)
|
|
e98 = _entry(now["문서"], WIRE)
|
|
assert _var(now["문서"], e98["기호"])["연결"] == {"갈래": "요소", "키": pick["키"]}
|
|
assert e98["고름"]["키"] == pick["키"] and now["셈"] != money # 우리 값으로 다시 셈
|
|
assert (
|
|
_var(skpath.read(sk99)["문서"], e["기호"])["연결"]["키"] == other
|
|
) # 따로 고친 줄 그대로
|
|
# 풂 = 가져온 그대로(엑셀 값)
|
|
mp.write(me, fp, name, {WIRE: None})
|
|
assert mp.apply_made(fp, {}, {WIRE})["고침"] == [sk98]
|
|
back = skpath.read(sk98)
|
|
assert _num(_var(back["문서"], e98["기호"])["연결"]) == _num(before)
|
|
assert "고름" not in _entry(back["문서"], WIRE) and back["셈"] == money
|
|
|
|
|
|
def _num(link: dict) -> tuple:
|
|
return link["갈래"], float(link["값"])
|
|
|
|
|
|
def test_apart_item_zero(world) -> None:
|
|
me = world["personal"]
|
|
me.mkdir(parents=True, exist_ok=True)
|
|
with store.at(me):
|
|
hp = _upload(me, "동부청영월")
|
|
out = fh.emit(me, hp, PIPE_BLOCK)
|
|
line = next(x for x in out["로직"]["호표"] if "파형강관" in str((x.get("자원") or {}).get("이름"))) # fmt: skip
|
|
kind = mp.kind_of(line["자원"])
|
|
found = line["자원"]["재료찾기"]
|
|
assert found["판정"] == "규격 없음 — 고르기" and line["이음"] is None
|
|
gov = next(c for c in found["후보"] if "관급" in c["이름"])
|
|
fp, name = mp.fingerprint(me, hp)
|
|
plain = fh.make(me, hp, PIPE_BLOCK)
|
|
mp.write(me, fp, name, {kind: gov})
|
|
with mp.using(mp.read(me, fp)["고름"]):
|
|
got = fh.make(me, hp, PIPE_BLOCK)
|
|
assert got["이름"] != plain["이름"] and got["검산"] == plain["검산"] # 금액 그대로(단가 0)
|
|
doc = store.read("sangeun", got["이름"])["문서"]
|
|
mine = _lines(doc, kind)[0]
|
|
assert mine["이음"] is None and mine["자원"]["재료찾기"]["고름"]["키"] == gov["키"]
|
|
conv = dim.derive(doc, None, imp._whole())
|
|
e = _entry(conv, kind)
|
|
var = _var(conv, e["기호"])
|
|
assert e["별도계상"] and var["연결"]["갈래"] == "숫자"
|
|
assert f"별도계상 · 품목: {gov['이름']} {gov['규격']} · 셈은 프로젝트에서" in var["설명"]
|
|
assert mp.item_key(mine["자원"]) == gov["키"]
|
|
assert skpath.read(got["이름"])["셈"] == skpath.read(plain["이름"])["셈"]
|
|
# 이미 만든(고름 전) SK 에 물음 예 = 품목 설명만 · 단가 0 그대로
|
|
money = skpath.read(plain["이름"])["셈"]
|
|
assert plain["이름"] in mp.apply_made(fp, {kind: gov}, {kind})["고침"]
|
|
now = skpath.read(plain["이름"])
|
|
assert _entry(now["문서"], kind)["고름"]["키"] == gov["키"] and now["셈"] == money
|
|
assert "품목: 파형강관(관급)" in _var(now["문서"], e["기호"])["설명"]
|
|
# 이음 없는 줄 목록(가져온 식 머리 [이음 고르기]) = 종 열쇠(같은 파일 물음)
|
|
assert any(
|
|
u.get("종") == kind for u in imp.unlinked(store.read("sangeun", plain["이름"])["문서"])
|
|
)
|