- 809_1 이름 = 진짜 흠 + 낡은 기대 — 읽기기(812-1)가 지은 율 줄 이름 「율」이 엑셀 글 머리로 안 바뀜 → `Import_Shape._MADE` 에 율 더함 · 합계 = 차림 글줄 · 기계 = 한 줄(이름 · 규격 칸) 은 낡은 기대 고침 · 금액 15,935 · 336 · 723 · 16,994 그대로 - 812_9 별칭 = 낡은 기대 — 815-2 가 아스팔트페이버 별칭을 마스터 기계 줄 칸으로 옮겨 「넣기 전 못 찾음」 이 안 맞음 · 시험은 마스터 칸으로 봄 · 부르는 곳 없는 `M02_Machine_Find.learn`(옛 장부 쓰기) · `by_code` · `same_kind`(걷은 연결 찾기 몫) 지움 - 804_2 hourly = 낡은 기대 — 814-1 이 GC000268 목록을 117 → 209 로 늘려 겹친 116 대 고름이 GC000269 로 뒤집힘(116 대 모두 원 단위 같음) · 같은 로직 줄만 대던 시험 → 같은 갈래 식(부착 · 손료만 · 조종원) 줄 모두 댐 - 진짜 흠(브레인 갈래 가) — GC000272 「조종원 없는 기종」 목록에 기계표 조종원 1 인 9 대가 겹쳐 목록 길이 차례로 고름 → `logic_of` = 기계표 조종원 > 0 이면 조종원 호표 든 식 먼저 · 불러오기 `hourly_link` 도 같은 길 · 바뀐 4 대 = 진동롤러(핸드가이드식) 0.7ton 6,557 → 41,969 · 래머 80kg 1,959 → 23,708 · 플레이트 콤팩터 1.5ton 2,855 → 24,604 · 발전기 100kW 45,535 → 67,284(M01 문서 금액 그대로) · 시험에 9 대 굳힘 - 778_5 기계 표 = 진짜 흠 — 812-9 찾기가 호표 기계 글 「운 반 : 덤프트럭(4.5Ton)」 · 「…[hr] : 90%」 · 「(90%)」 머리 꼬리를 안 뗌 → `Hopyo_Fixed._eqs` 쌍점 조각 · 율 꼬리 뗌 · 이어짐 187 · 고를 것 1 · 못 이음 2(조합 한 줄 · 괄호 깨짐) · 모두 이음 블록 161 - 721 단추 자리 = 낡은 기대(808-1 [단가 갱신][재생성] tools 더함) - 815-1 보고 요약에 「합친 뒤」 열 — 기계 이음 95.7 % · 우리 값 원파일 1,891 · 0 원 줄 블록 147 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
163 lines
9.3 KiB
Python
163 lines
9.3 KiB
Python
# 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)
|