Files
Aislo/resources/tester/test_642_1_clause_formula.py
eomsangdonandClaude Opus 5.5 48848bb287 refactor(785-7): 옛 맞춤 층 Python 지움 · 서버 새 길로 정리 · 낡은 시험 정리
- 지움(git rm) 10 파일 3,374 줄: M02_Hopyo_Finger · Ours · Option · Fit · Combine · Verdict · Line · SkLink · SkPatch · Basis
- Match: 흐름 몫(candidates · name_cands · fix_typo · clause · b_order · _logic_specs · _line_mod · units_of · _state · view · guess · picks · chain) 지움 · 이름 도구 몫 · line_key 남김
- Words: conds · cond_score · close · cosine · jacc 지움 / Shares: sections 지움(trade · eq_key 남김)
- 옮김: Finger.kind_of → Fixed.kind_of · SkLink(sk_line · kept · left · LEFT · WHY) → Sangeun · SkPatch 기계 몫(MACH_IN · MACHINE · SOLO · _mach_logics) → Units.mach_logics
- Sangeun: 옛 /sangeun 몸(build · _sure · Maker.basis · judge · plan · make) 지움 · Kids: first · proposal · order 지움 · Build: build() · unlinked() 지움(모두 고정 Builder · build_all · _order · _origins 남김)
- Router: 옛 길 지움(GET hopyo/{키}/{번} · guess · hopyo-picks · hopyo-build · hopyo-unlinked · hopyo/{키}/sangeun) · GET hopyo/{키} = Judge.view(1차 줄 판정 줄상태 · 만든 · 블록 풀이 셈)
- Marks: 칠 = Judge.judged 만 · Forget: 맞춤 캐시 지움 줄 걷음 · Ledger: Ours.version 판 칸 걷음 · Item: Kids.first 걷음
- Row(덧): 단산 줄 = 장부 짝이 있어도 늘 「2차 필요」(장부 = 1차 후보 힌트만)

시험 정리(까닭 = 지운 길 · 모듈만 시험함):
- 지움 11 파일: test_m02_hopyo_finger · fit · golden · option · ours · basis · seam · flow · draft_calc · test_635 · test_628(지문 · 순위 · 옵션 거꾸로 · 맡은 절 · 옛 확인 흐름)
- 함수 지움: /sangeun · /hopyo-build · /hopyo-unlinked · hopyo-picks · 옛 줄 맞춤 보기를 부르던 것(572 · 718_1 · 729 · 739_1 · 777_1 · 778_5 · 780_1 · labor_national · 705_3 · m02_hopyo_match · 746_1)
- test_621 S2(지문 해시) 지움 · S1 남김 · test_m02_hopyo_build = 장부 길 시험만(PUT 으로 심음)
- 새 길로: 779_2(줄상태 = 1차 판정 · 칠 셈 전 None) · grid · stmate(hopyo-judge 줄 판) · 778_1 · 778_2(Units.mach_logics) · 642_6(Marks 원문) · 642_3 장부 짝 단산 줄 2차 지킴

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 01:15:20 +09:00

162 lines
5.9 KiB
Python
Raw Permalink 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.
"""785-1(642-1) 지킴 — 출처 풀이 · 조항 색인(`M02_Hopyo_Clause`) · 블록 글 → 절 나무 · 상수 ·
대입식 · 절 몫(`M02_Hopyo_Formula`) · 북부청 견본 엑셀(설계 642-A 5-3 첫 줄 기대).
읽기만 · 서버 없음.
"""
from __future__ import annotations
import sys
import warnings
from functools import lru_cache
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from M02_MasterTemplete import M02_Hopyo_Clause as clause # noqa: E402
from M02_MasterTemplete import M02_Hopyo_Excel as hx # noqa: E402
from M02_MasterTemplete import M02_Hopyo_Formula as fm # noqa: E402
warnings.filterwarnings("ignore", category=UserWarning, module="openpyxl")
XLS = (
ROOT
/ "resources/knowledge/original/실무문서/2026_실무일위대가 모음"
/ "2026년_북부지방산림청_임도사업_설계내역서(공통단가).xls"
)
@lru_cache(maxsize=1)
def blocks() -> dict[int, dict]:
if not XLS.exists():
pytest.skip("견본 엑셀 없음")
book = hx.read_book(XLS.name, XLS.read_bytes())
return {b["번"]: b for b in book["단산"]}
@lru_cache(maxsize=None)
def got(no: int) -> dict:
return clause.basis(blocks()[no])
def keys(no: int) -> set[str]:
return {o["키"] for o in got(no)["후보"]}
# ── 조항 색인 → 후보 ──────────────────────────────────────────────────
@pytest.mark.parametrize(
"no, want",
[
(10, {"GC001140", "GC001141", "GC001142"}),
(46, {"GC000198"}),
(24, {"GC000211"}),
(47, {"GC000206", "GC000207"}),
(39, {"GF000224", "GF000225"}),
],
)
def test_후보(no, want):
assert keys(no) == want
assert got(no)["표지"] is None
def test_윗마디_절마다():
"""47 「6-2」 → 절 「1. 현장가공」 GC000206 · 「2. 조립」 GC000207 · 공장가공은 뺌."""
by = {o["키"]: (o["맞음"], o["절"]) for o in got(47)["후보"]}
assert by == {"GC000206": ("윗마디", "1"), "GC000207": ("윗마디", "2")}
assert {o["키"] for o in got(47)["뺌"]} >= {"GC000208"}
@pytest.mark.parametrize("no, mark", [(33, "준용"), (6, "옛 번호"), (3, "번호 없음")])
def test_표지(no, mark):
assert got(no)["표지"] == mark
def test_준용_결과단위_거름_절_번호():
"""33 「산림 12-12 준용」 → GF000177(원/식) 뺌 · 절 4 「표준품셈3-4-8」 → GC000110."""
r = got(33)
assert {o["키"]: o["까닭"][:7] for o in r["뺌"]} == {"GF000177": "결과단위 다름"}
assert [(o["키"], o["절"]) for o in r["후보"]] == [("GC000110", "4")]
def test_색인():
assert set(clause.index()[("건설품셈 공통", "1-2-7")]) == {"GC001140", "GC001141", "GC001142"}
assert clause.nature("원/10㎡") == "면적" and clause.nature("원/자재 단위") is None
# ── 절 나무 ───────────────────────────────────────────────────────────
def _works(no: int) -> list[str]:
nodes = blocks()[no]["절"]
ids = {id(w) for w in fm.works(nodes)}
return [p for p, one in clause._paths(nodes) if id(one) in ids]
def test_절_나무():
assert _works(47) == ["1", "2"]
assert _works(6) == ["1.가", "1.나", "1.다", "3"]
assert [n["번"] for n in blocks()[6]["절"]] == ["1", "3", "4"] # 「2.」 빠짐 그대로
assert _works(24) == [] # 재료비 · 인건비 · 합계뿐 = 블록 통째
b = blocks()[47]
assert len(b["글행"]) == len(b["글"])
one = fm.find(b["절"], "1")
assert one["소계"] == {"노무비": 217540.1, "재료비": 19578.6, "경비": None, "계": 237118.7}
lo, hi = b["행"]
assert all(lo < r <= hi for r in one["행"])
# ── 상수 · 대입식 · 몫 ─────────────────────────────────────────────────
def _consts(no: int, path: str) -> dict[str, dict]:
return {c["기호"]: c for c in fm.find(blocks()[no]["절"], path)["상수"]}
def test_상수():
c = _consts(10, "1")
assert (c["L"]["값"], c["L"]["단위"]) == (6, "m")
assert (c["경사도"]["값"], c["경사도"]["단위"]) == (35, "°")
assert (c["n"]["값"], c["n"]["이름"]) == (42, "운반회수")
c = _consts(6, "1.나")
assert {k: c[k]["값"] for k in ("q", "K", "f", "E", "Cm")} == {
"q": 0.6,
"K": 0.55,
"f": 1,
"E": 0.35,
"Cm": 18,
}
assert (c["q"]["단위"], c["Cm"]["단위"], c["Cm"]["덧"]) == ("㎥", "sec", "90˚")
assert isinstance(c["q"]["행"], int)
def test_대입식():
p = next(f for f in fm.find(blocks()[10]["절"], "1")["식"] if f["기호"] == "P")
assert p["꼴"] == "곱" and p["값"] == 0.547619
assert [(f["수"], f["단위"]) for f in p["분자"]] == [
(1, "㎡"),
(575, "㎏/㎡"),
(1, "회"),
(1, "일"),
]
assert p["분모"] == [{"수": 25, "단위": "㎏"}, {"기호": "n", "단위": ""}]
assert p["대입"] == {"n": 42}
def test_절_몫():
nodes = blocks()[6]["절"]
share = fm.find(nodes, "1.나")["몫"]
assert share["글"] == "× 575 / 1750 × 90%"
assert share["값"] == pytest.approx(575 / 1750 * 0.9, abs=1e-6)
assert fm.find(nodes, "1.가")["몫"]["값"] == 0.1
assert fm.find(nodes, "3")["몫"]["값"] == pytest.approx(575 / 1750, abs=1e-6)
assert fm.find(blocks()[47]["절"], "1")["몫"] is None
def test_인자_괄호():
got_ = fm.factors("Cm × n / (60 × Es)")
assert [(w, f.get("기호") or f.get("수")) for w, f in got_] == [
("곱", "Cm"),
("곱", "n"),
("나눔", 60),
("나눔", "Es"),
]
assert fm.factors("t1 + t2") is None and fm.factors("1 2") is None