Files
Aislo/resources/tester/test_b08_box_culvert_unit.py
T
eomsangdonandClaude Opus 5 a314ac12fd feat(b05·b06·b08): BOX암거 수량식 — 등록부 두께·연장 칸 + m당 × 연장 전개식 · 세월교·날개벽은 근거 없음 사유로 닫음(12장 D · 브레인 판정 ①~⑥)
- 등록부 box_culvert: 연장(기본값 없음) · 측벽 0.3 · 상·저판 0.25 · 헌치 0.2 · 버림 0.1 — 제안값 = 산림과임업기술(임도) 5장 수량산출 예제 · KDS 44 90 00 300㎜ 병기(판 0.25 는 못 미침을 나란히)
- B06 BOX 세트(파이썬·TS 짝)가 두께 칸을 읽음 — 종전 「세월교 값 승계」(벽 0.2·판 0.3) 걷음 · 그림 따라감
- B08 전개식 `_UnitQuantity_Box`: 콘크리트 · 버림 · 유로폼(외벽·내벽·상판 밑·귀면) · 버림 옆 합판 · 동바리 · 비계(시종점 두 면은 개소당 한 번) · 철근은 울진 2×2 관측(D13 179·D16 157kg/m)만, 나머지 크기는 「크기별 식 없음」
- 울진 기번3 「암거수량집계표(2×2)」 m당과 맞음: 레미콘 2.84 · 버림 0.28 · 유로폼 11.131 · 합판 0.2 · 동바리 3.92 · 비계 벽체 5.2
- 치수 칸을 안 적은 BOX 는 제안값으로 서되 미확정(금액 밖) · 연장이 비면 안 섬
- 묶음 조각 12-01-01·12-38·12-04(6회)·12-03·12-20·12-19·12-25 · 동바리 대상 표시 · 세월교 「수량 근거 없음」

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
2026-09-15 02:36:25 +09:00

103 lines
4.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.
"""BOX암거 전개식 — m당 × 연장 (2026-09-15 브레인 12장 D 판정 ①~⑤).
근거 둘(구조 같음)
산림과임업기술(임도) 5장 「구조물도에 의한 수량산출(예)」 — 구체 = (외곽 − 유수구 + 헌치) ·
기초 폭×두께 · 거푸집 외벽·유수구·받침판·귀면 · 동바리 유수구 단면
실무 울진 기번3 구조도 「암거수량집계표(2×2)」 m당 — 레미콘 2.84 · 버림 0.28 · 유로폼 11.132 ·
합판 0.2 · 철근 0.336t(H13 0.179 · H16 0.157) · 동바리 3.92 · 비계 벽체 5.2 + 시종점 13.52
⚠ 철근은 크기별 식이 없어 **2×2 관측값만** · 날개벽·토공은 사유.
"""
from __future__ import annotations
import pytest
from B08_Quantity.B08_Quantity_Engine_Handoff import build_handoff
from B08_Quantity.B08_Quantity_Engine_Pipe import facility_structures
from B08_Quantity.B08_Quantity_Engine_UnitQuantity import build_table, expand
ULJIN_2X2 = {
"body_width_m": 2.0,
"body_height_m": 2.0,
"wall_thickness_m": 0.3,
"slab_thickness_m": 0.3,
"haunch_m": 0.2,
"blinding_thickness_m": 0.1,
"length_m": 10.0,
}
def _amounts(options: dict) -> tuple[dict, list[str], object]:
result = expand({"type_id": "box_culvert", "options": options}, {"box_culvert": "BOX암거"})
return (
{(c.name, c.spec): c.amount for c in result.components},
result.notes,
result,
)
def test_울진_2x2_m당_값을_연장에_곱한다() -> None:
got, notes, result = _amounts(ULJIN_2X2)
assert got[("콘크리트", "")] == pytest.approx(2.84 * 10)
assert got[("버림콘크리트", "")] == pytest.approx(0.28 * 10)
assert got[("유로폼", "")] == pytest.approx(
11.1314 * 10, abs=0.01
) # 원문 11.132(귀면 0.566 반올림)
assert got[("합판거푸집", "")] == pytest.approx(0.2 * 10)
assert got[("이형철근", "D13")] == pytest.approx(179 * 10)
assert got[("이형철근", "D16")] == pytest.approx(157 * 10)
assert got[("동바리", "")] == pytest.approx(3.92 * 10)
# 비계 — 벽체 m당 5.2 × 연장 + 시종점 두 면 13.52 는 한 번(원문은 m당에 넣었음 · 사유).
assert got[("비계", "")] == pytest.approx(5.2 * 10 + 13.52)
assert (result.billing_unit, result.billing_quantity) == ("m", 10.0)
text = " ".join(notes)
assert "관측" in text and "날개벽" in text
def test_예제_두께_기본값으로_구체가_줄어든다() -> None:
got, _notes, _r = _amounts({**ULJIN_2X2, "slab_thickness_m": 0.25})
# (2.6 × 2.5 2 × 2 + 0.2² ÷ 2 × 4) = 2.58㎥/m
assert got[("콘크리트", "")] == pytest.approx(2.58 * 10)
def test_2x2_가_아니면_철근은_사유() -> None:
got, notes, _r = _amounts({**ULJIN_2X2, "body_width_m": 3.0})
assert not [key for key in got if key[0] == "이형철근"]
assert any("크기별 식 없음" in note and "2×2" in note for note in notes)
assert got[("콘크리트", "")] > 0 # 나머지는 섬
def test_연장이_없으면_안_선다() -> None:
got, notes, _r = _amounts({k: v for k, v in ULJIN_2X2.items() if k != "length_m"})
assert got == {}
assert any("연장" in note for note in notes)
def test_치수를_안_적으면_기본값으로_서되_미확정() -> None:
rows = facility_structures(
[{"chainage_m": 50.0, "facility": "box_culvert", "options": {"length_m": 8.0}}]
)
box = next(row for row in rows if row["type_id"] == "box_culvert")
assert box["unconfirmed"] and "측벽 두께" in box["unconfirmed"]
assert box["options"]["wall_thickness_m"] == 0.3 and box["options"]["slab_thickness_m"] == 0.25
stored = facility_structures(
[{"chainage_m": 50.0, "facility": "box_culvert", "options": ULJIN_2X2}]
)
assert not next(r for r in stored if r["type_id"] == "box_culvert").get("unconfirmed")
def test_인계_묶음이_조각을_세운다() -> None:
table = build_table(
facility_structures(
[{"chainage_m": 50.0, "facility": "box_culvert", "options": ULJIN_2X2}]
),
{"box_culvert": "BOX암거"},
)
row = next(
r for r in build_handoff(unit_quantity_table=table)["work_items"] if "BOX" in r["name"]
)
parts = {part["code"].split("#")[0]: part for part in row["composite_parts"] or []}
assert {"FP-12-01-01", "FP-12-04", "FP-12-38", "FP-12-03", "FP-12-20", "FP-12-19"} <= set(parts)
assert parts["FP-12-03"]["quantity"] == pytest.approx((179 + 157) * 10 / 1000)
assert parts["FP-12-20"]["quantity"] == pytest.approx(3.92 * 10)