Files
Aislo/resources/tester/test_812_3_native_layout.py
T

228 lines
9.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""812-3 지킴 — 가져온 단가산출(원파일) 줄 쌓기 차림 = 경쟁사 산출근거 꼴(요구 713).
차림 = `M02_Doc_NativeLayout`.
(a) 견본(거창 덤프 꼴) — 1 측구터파기(토사) 「기 계 … 굴삭기」 항목 + 공식 묶음
q · K · f · E · Cm · Q + 보조인부 항목 · 41 합판거푸집(4회) 근거 글 ·
재료비(재료 · 소계 · 율 · 글) · 인건비(인력 · 소계 · 율 · 글) ·
15 전석 메쌓기 별산 · 단산 참조 Q 직접 · 기계 · 인부 ·
2 가 · 나 소항목 · 봉화 STC = 계 단마다 항목
(b) 전수 — 원파일 EST · STC 산근 블록 모두: 차림 전후 셈(비목 · 계 · 줄 금액) 같음 ·
TS 왕복(문서 → 줄 → 문서) 같음 · TS `stackOf` = 파이썬 거울 `stack_of` ·
수 표 = tmp/s8123/sweep.json(일회 기록)
(c) M01 문서 1,518 — 차림 안 바뀜(쌓기 없음 · 왕복 같음 · 거울 같음)
원파일 없으면 그 시험만 skip(다른 PC).
"""
from __future__ import annotations
import json
import subprocess
import sys
from functools import lru_cache
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
HERE = Path(__file__).resolve().parent
for p in (ROOT, HERE):
if str(p) not in sys.path:
sys.path.insert(0, str(p))
import test_810_2_native_doc as t2 # noqa: E402
from test_810_1_estx import FILES as EST # noqa: E402
from test_810_1_stc import FILES as STC # noqa: E402
from M02_MasterTemplete import M02_Doc_Copy as dc # noqa: E402
from M02_MasterTemplete import M02_Doc_Import as dim # noqa: E402
from M02_MasterTemplete import M02_Doc_NativeLayout as lay # noqa: E402
COSTS = ("노무비", "재료비", "경비", "계")
def _ts(cases: list[dict]) -> list[dict]:
proc = subprocess.run(
["node", str(HERE / "helper_812_3_layout.cjs")],
input=json.dumps(dc.ml._norm(cases), ensure_ascii=False),
capture_output=True,
text=True,
encoding="utf-8",
)
assert proc.returncode == 0, proc.stderr[-3000:]
return json.loads(proc.stdout)
def _doc(kind: str, tag: str, no: int, laid: bool = True) -> tuple[dict, dict]:
sk = t2.sk_of(t2.fn.emit(t2.native(kind, tag), no))
keep = lay.apply
if not laid:
lay.apply = lambda doc, meta: [x.pop(lay.KEEP, None) for x in doc.get("금액") or []]
try:
return sk, dim.derive(sk, None, t2.whole())
finally:
lay.apply = keep
def _items(doc: dict) -> list[dict]:
return lay.stack_of(doc)
def _show(doc: dict) -> list[tuple]:
"""(제목, 가나다, [줄 글]) — 금액 = 이름 · 공식 = 「=기호…」 · 글 = 「#글」."""
money = doc["금액"]
out = []
for it in _items(doc):
lines = []
for x in it["줄"]:
if x["꼴"] == "금액":
lines.append(money[x["i"]]["이름"])
elif x["꼴"] == "공식":
lines.append("=" + " ".join(x["기호"]))
else:
lines.append("#" + x["글"])
out.append((it["제목"], bool(it.get("가나다")), lines))
return out
# ── (a) 견본 ─────────────────────────────────────────────────────────────
def test_sample_excavation_machine_and_formula() -> None:
_, doc = _doc("EST", "거창", 1)
got = _show(doc)
assert got == [
("기 계(100%) : 굴삭기(무한궤도) (0.7㎥)", False, ["=q K f E Cm Q", "굴삭기(무한궤도)"]),
("보조인부(1일당 1인)", False, ["보통인부"]),
], got
assert [x["묶음"] for x in doc["금액"]] == [got[0][0], got[1][0]]
def test_sample_formwork_text_rate_subtotal() -> None:
_, doc = _doc("EST", "거창", 41)
got = _show(doc)
assert got[0] == (
"",
False,
["#< 토목품셈 6-3-1 합판거푸집(2022개정) >", "#※ 사용횟수 4회 : 보통"],
)
assert got[1] == (
"재료비",
False,
[
"합판(내수),대전",
"외송각재,대전",
"주자재소계",
"#소모자재(박리재등) (주자재비의 9%)",
"율",
],
)
assert got[2][0] == "인건비 (24년 품셈 개정)" and got[2][2][:3] == [
"형틀목공",
"보통인부",
"인부소계",
]
assert got[2][2][3].startswith("#공구손료") and len(got) == 3
sub = next(x for x in doc["금액"] if x["이름"] == "주자재소계")
assert sub["합밖"] and "비목" not in sub # 소계 = 합 밖 · 비목 칸 빈칸 그대로(810-2)
def test_sample_stone_wall_reference_and_direct_q() -> None:
_, doc = _doc("EST", "거창", 15)
got = _show(doc)
titles = [g[0] for g in got]
assert titles[0] == "전석 구입운반 : 별산" and got[0][2] == []
assert (
titles[1].startswith("뒷채움돌 운반 및 채집")
and got[1][2][-1] == "뒷채움돌기초잡석채집운반"
)
assert got[1][2][0].split()[0] == "=Q" # 단산 참조 Q 직접 = 그 항목 공식 묶음
# 조합 기계 「굴삭기 0.6㎥+부착용집게」 = 기계마다 항목 하나 · 제목 = 그 기계(812-9 · 요구 738-3)
assert titles[3] == "굴착기(무한궤도) 0.6㎥" and got[3][2] == ["=Q_3", "굴삭기(무한궤도)"]
assert got[4] == ("부착용 집게 0.6∼0.8", False, ["부착용 집게"])
assert got[5] == ("전석쌓기 인부", False, ["석공", "보통인부"])
assert "합" not in "".join(titles) # 합계 절 = 화면 자동 합계(두 번 안 보임)
def test_sample_stc_steps_are_items() -> None:
_, doc = _doc("STC", "봉화", 14)
got = [
(t, g, [x for x in ls if not x.startswith("=P_")]) for t, g, ls in _show(doc)
] # 손 단가 = 기본 자리
assert got == [
("", False, ["#▣ 토공의 비탈규준틀[개소]2-6-1"]),
("재 료 비", False, ["각재(거푸집용,외송)", "판재", "일반못(철못)"]),
("인 건 비", False, ["건축목공", "보통인부"]),
], got
def test_sample_ganada_sub_items() -> None:
_, doc = _doc("EST", "거창", 2)
got = _show(doc)
assert got[0] == ("기 계(100%)", False, [])
# 「가. 굴삭기(무한궤도)(0.7+브레이카)」 = 조합 기계 둘 = 가나다 항목 둘(812-9 · 요구 738-3)
assert got[1][:2] == ("굴착기(무한궤도) 0.7㎥", True)
assert got[2] == ("대형 브레이커 0.7㎥", True, ["대형 브레이커"])
assert got[3] == ("치즐(0.7㎥용)", True, ["치즐(0.7㎥)"]) # 「다. 소 계」 절 = 뺌
# ── (b) 전수 ─────────────────────────────────────────────────────────────
def _money(sk: dict, doc: dict) -> tuple:
got = dim.run(doc, sk, t2.whole())
lines = sorted(str(x.get("금액")) for x in got.get("줄") or [])
return tuple(str(got.get(c)) for c in COSTS), lines
@lru_cache(maxsize=1)
def _sweep() -> dict:
stat = {"블록": 0, "금액같음": 0, "다름": [], "cases": []}
for kind, files in (("EST", EST), ("STC", STC)):
for tag, path in files.items():
if not path.exists():
continue
for no in t2.fn.numbers(t2.native(kind, tag)):
sk, doc = _doc(kind, tag, no)
_, plain = _doc(kind, tag, no, laid=False)
stat["블록"] += 1
if _money(sk, doc) == _money(sk, plain):
stat["금액같음"] += 1
else:
stat["다름"].append((kind, tag, no))
stat["cases"].append(
{"키": f"{kind}|{tag}|{no}", "문서": doc, "파이썬": _items(doc)}
)
return stat
def test_sweep_money_same_and_ts_round_trip() -> None:
stat = _sweep()
if not stat["블록"]:
pytest.skip("원파일 없음")
assert not stat["다름"] and stat["금액같음"] == stat["블록"], stat["다름"][:5]
got = _ts(stat["cases"])
bad = {f: [g["키"] for g in got if not g[f]] for f in ("같음", "거울")}
assert bad == {"같음": [], "거울": []}, {k: v[:5] for k, v in bad.items()}
by = {}
for g in got:
kind = g["키"].split("|")[0]
row = by.setdefault(
kind, dict.fromkeys(("블록", "항목", "제목", "공식", "글", "쌓기", "무제목만"), 0)
)
row["블록"] += 1
for f in ("항목", "제목", "공식", "글"):
row[f] += g[f]
row["쌓기"] += g["쌓기"]
row["무제목만"] += g["항목"] > 0 and g["제목"] == 0
out = ROOT / "tmp" / "s8123"
out.mkdir(parents=True, exist_ok=True)
(out / "sweep.json").write_text(json.dumps(by, ensure_ascii=False, indent=1), "utf-8")
assert by.get("EST", {}).get("제목", 0) > by.get("EST", {}).get("블록", 0)
# ── (c) M01 ──────────────────────────────────────────────────────────────
def test_m01_docs_layout_unchanged() -> None:
docs = dc.folder()
assert len(docs) >= 1513 # 813-2 지움 6
cases = [{"키": k, "문서": d, "파이썬": _items(d)} for k, d in docs.items()]
got = _ts(cases)
bad = {f: [g["키"] for g in got if not g[f]] for f in ("같음", "거울")}
assert bad == {"같음": [], "거울": []}, {k: v[:5] for k, v in bad.items()}
assert not any(g["쌓기"] for g in got)