# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀 """809-1 엑셀 불러오기 일위대가 = 경쟁사 로직 인정 · 요소만 연결(요구 702 · 703) — 임시 storage · M01 그대로. ① 기초 길 0 — 판정 · 근거 기초 제안 · [우리 기초로 바꾸기] · [옮기기] 서버 · 화면 모두 없음 ② 줄 표지 = 로직(단가산출 · 일위대가) / 요소(연결됨 · 후보 · 못 이음) — 실무 여덟 1,350 줄 · 갈래 수 그대로 ③ 로직 줄 = 가져온 식 SK(경쟁사 로직 그대로 · 엑셀 단가 = 엑셀) ④ [수정] 모달 = 일위대가 컨테이너 부품(`Labor_Table` · 읽기 · 덧칸 표지 · 엑셀 금액) ⑤ 금액 같음 — 보기 문서(엑셀 숫자 그대로 고정 줄) 1 단위 값 = 엑셀 호표 단가(실무 북부청 호표 모두) 실무 엑셀 없으면 그 시험만 skip. """ from __future__ import annotations import importlib.util from collections import Counter from pathlib import Path import helper_m02_hopyo_golden as golden import pytest from M02_MasterTemplete import M02_Hopyo_Build as hb from M02_MasterTemplete import M02_Hopyo_Excel as hx from M02_MasterTemplete import M02_Hopyo_Fixed as hfix from M02_MasterTemplete import M02_Hopyo_Store as hs from M02_MasterTemplete import M02_Hopyo_View as hv from M02_MasterTemplete import M02_Labor_Ref as refs from M02_MasterTemplete import M02_MasterTemplete_Store as store from M02_MasterTemplete import M02_Sangeun_FromHopyo as fh from M02_MasterTemplete import M02_Sangeun_Imported as imp from resources.tester.test_m02_labor_ref import world # noqa: F401 — 같은 임시 층 ROOT = Path(__file__).resolve().parents[2] M02 = ROOT / "M02_MasterTemplete" MARKS = {"단가산출", "일위대가", "연결됨", "후보", "못 이음"} def _ts(name: str) -> str: return (M02 / f"M02_MasterTemplete_{name}.ts").read_text(encoding="utf-8") # ── ① 기초 길 0 ────────────────────────────────────────────────────────── def test_basis_paths_gone() -> None: for mod in ("Hopyo_Judge", "Hopyo_Solve", "Hopyo_Bind", "San_Basis", "San_Basis_Store"): assert importlib.util.find_spec(f"M02_MasterTemplete.M02_{mod}") is None, mod for name in ("Hopyo_Check", "Hopyo_CheckRows", "Hopyo_Rows", "Hopyo_Block", "Hopyo_JudgeApi"): assert not (M02 / f"M02_MasterTemplete_{name}.ts").exists(), name assert not (M02 / "logic/M02_MasterTemplete_Hopyo_Picks.ts").exists() assert not hasattr(imp, "to_base") router = (M02 / "M02_Hopyo_Router.py").read_text(encoding="utf-8") for path in ('"/layers/{layer}/hopyo-judge', '/check"', '/move"', '/price"', "hopyo-basis/"): assert f"@router.get({path}" not in router and f"@router.post({path}" not in router assert "/hopyo/{key}/move" not in router and "/hopyo/{key}/check" not in router assert '"to-base"' not in (M02 / "M02_Sangeun_Router.py").read_text(encoding="utf-8") from M02_MasterTemplete import M02_Hopyo_Make as hmake assert not hasattr(hmake, "Move") and not hasattr(hmake.Lines, "picks") panel, model = _ts("Hopyo_ImportedPanel"), (M02 / "logic/M02_Hopyo_Imported_Model.ts").read_text("utf-8") # fmt: skip for gone in ("ToBase", "PickBase", "우리 기초로 바꾸기", "BasisCand"): assert gone not in panel + model + _ts("Hopyo_Imported"), gone # ── ② 줄 표지 ─────────────────────────────────────────────────────────── def test_marks_on_eight_files() -> None: linker, total, n = hfix.Linker(True), Counter(), 0 for tag in golden.FILES: book = golden.read(tag) if golden.FILES[tag].exists() else None if book is None: pytest.skip(f"실무 엑셀 없음 {tag}") for h in book["호표"]: got = hv.of(book, int(h["번"]), linker) assert len(got["줄"]) == len(h.get("줄") or []) == len(got["문서"]["담은로직"]) for m in got["줄"]: assert m["표지"] in MARKS and m["갈래"] == ("로직" if m["표지"] in ("단가산출", "일위대가") else "요소") # fmt: skip if m["표지"] == "연결됨": assert m["이음"] # 노임 · 기계 · 자재 · 비율 중 하나 if m["표지"] == "후보": assert m["후보"] and all(c["키"] for c in m["후보"]) total[m["표지"]] += 1 n += 1 assert n == 1350 # 호표 줄 수 전후 같음 assert (total["단가산출"], total["일위대가"]) == ( 735, 90, ) # 로직 줄 = 옛 갈래 단가산출 · 일위대가 그대로 assert total["연결됨"] + total["후보"] + total["못 이음"] == 525 def _hp(world, tag: str = "북부청임도") -> tuple[Path, str]: src = golden.FILES.get(tag) if src is None or not src.exists(): pytest.skip(f"실무 엑셀 없음 {tag}") folder, data = world["personal"], src.read_bytes() with store.at(folder): parsed = hx.read_book(src.name, data) row = hs.save(folder, src.name, parsed, {"이름": "sub1임시"}, None, hs.fingerprint(data)) fh.keep(folder, row["키"], src.name, data) return folder, row["키"] # ── ③ 로직 줄 = 가져온 식 SK ───────────────────────────────────────────── def test_logic_line_is_imported_sk(world) -> None: folder, hp = _hp(world) with store.at(folder): got = hv.view(folder, hp, 3) # 제3호표 — 단산 셋 logic = [m for m in got["줄"] if m["표지"] == "단가산출"] assert len(logic) == 3 and all(m["번"] for m in logic) made = fh.make(folder, hp, logic[0]["번"]) doc = store.read("sangeun", made["이름"])["문서"] assert imp.is_imported(doc) and doc[imp.KEY]["단가기준"] == "엑셀" # 경쟁사 로직 그대로 assert doc.get("바탕") is None and "근거후보" not in doc[imp.KEY] assert not list((folder / "precombo").glob("*.json")) # 보기는 일위대가를 안 씀 # ── ④ 모달 = Labor_Table ──────────────────────────────────────────────── def test_modal_is_labor_table() -> None: view, sheet, table = _ts("Hopyo_View"), _ts("Hopyo_Sheet"), _ts("Labor_Table") assert "mountLaborTable(host, r.문서" in view and "readOnly: true" in view assert 'mode: "precombo"' in view and "openSkEdit(" in view and "mineButton(" in view assert "/hopyo-view/" in view and "x_mark" in view and "x_excel" in view assert "openHopyoView({" in sheet and "openHopyoCheck" not in sheet assert "opt.view?.onRow?.(n)" in table and "viewRows(g)" in table # 덧칸 · 읽기만 for name in ("Hopyo_View", "Hopyo_Sheet", "Hopyo_SheetSk", "Labor_Table"): assert len(_ts(name).splitlines()) <= 700, name # ── ⑤ 금액 같음 ───────────────────────────────────────────────────────── def test_view_money_equals_excel(world) -> None: folder, hp = _hp(world) with store.at(folder): book = hs.read(folder, hp) linker, bad = hfix.Linker(True), [] for h in book["호표"]: doc = hv.of(book, int(h["번"]), linker)["문서"] key = refs.create("precombo", {**doc, "구분": "실무자료"})["이름"] got = hb.compare({"층": refs.here()[0], "주인": refs.here()[1], "키": key}, h) if not got["같음"]: bad.append((h["번"], got["엑셀"], got["우리"])) assert not bad, bad[:5] # ── ⑥ 가져온 식 표시(요구 704) — 지은 이름 → 엑셀 글 머리 · EST 기계 세 줄 = 한 줄 · 금액 그대로 ───── def test_imported_doc_names_and_machine_line(world) -> None: from M02_MasterTemplete import M02_Doc_Import as dim from M02_MasterTemplete import M02_Doc_Import_Shape as shp folder, hp = _hp(world) out = fh.emit( folder, hp, 8 ) # 배수관부설(파형강관)(D=800mm) — 크레인 세 줄 · 줄_2 · 노무비_2 · 계_2 unit = out["로직"]["결과단위"].split("/", 1)[1] sk = {"키": "SK-000000", "이름": "x", "단위": unit, "바탕": None, "입력값": {}} sk[imp.KEY] = {"로직": out["로직"], "단가기준": "엑셀", "원본": out["원본"]} new = dim.derive(sk) names = [x["이름"] for x in new["금액"]] assert "공구손료 및 잡재료비(노무비의 2%)" in names # 읽기기 지은 「율」 → 엑셀 글 머리 assert not any(shp._MADE.match(n) for n in names) # 줄_2 · 율 · 노무비_2 · 계_2 없음 # 812-1 읽기기 — 합계 = 차림 글줄(금액 줄 아님) · 기계 = 한 줄(이름 · 규격 칸 따로) assert "노무비 합계" not in names and "계" not in names crane = [x for x in new["금액"] if x.get("비목") == "묶음"] assert [(x["이름"], x.get("규격")) for x in crane] == [("트럭탑재형크레인", "5Ton")] got = dim.run(new, sk) assert (got["노무비"], got["재료비"], got["경비"], got["계"]) == (15935, 336, 723, 16994)