Files
Aislo/resources/tester/test_827_3_material_pick.py
T
eomsangdonandClaude Opus 5.5 520bb33909 feat(827-3): 불러오기 재료 걸음 3 — 고를 재료 판 · 그 파일 안 고름(별칭 아님) · SK 상세 같은 파일 물음
- 새 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>
2026-11-13 12:51:04 +09:00

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["이름"])["문서"])
)