- 등록부 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
136 lines
5.0 KiB
Python
136 lines
5.0 KiB
Python
"""거푸집 사용횟수 검사 — 품셈 1-7-1 · 2026-09-07 ⑩.
|
||
|
||
⚠ 사용횟수는 **관측값이 아니라 법**이다 — 품셈 1-7-1 이 구조물 종류별로 정해 둔다.
|
||
⚠⚠ **횟수별 재료 환산은 여기서 하지 않는다.** 품셈 12-4 의 비율(%)은 일위대가 재료비에
|
||
걸리는 값이라, B08 이 곱해 넘기면 B09 가 또 곱해 두 번 준다.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
ROOT = Path(__file__).resolve().parents[2]
|
||
sys.path.insert(0, str(ROOT))
|
||
|
||
from B08_Quantity.B08_Quantity_Engine_Formwork import ( # noqa: E402
|
||
FORMWORK_NAMES,
|
||
NOTE_REUSE_MISSING,
|
||
FormworkTable,
|
||
annotate,
|
||
load_formwork_table,
|
||
shoring_status,
|
||
)
|
||
from B08_Quantity.B08_Quantity_Engine_UnitQuantity import build_table # noqa: E402
|
||
|
||
|
||
def 옹벽() -> dict:
|
||
return {
|
||
"structure_id": "w1",
|
||
"type_id": "retaining_wall",
|
||
"start_m": 100.0,
|
||
"end_m": 110.0,
|
||
"options": {"form": "반중력식", "height_m": 2.0, "length_m": 10.0},
|
||
}
|
||
|
||
|
||
def test_원문에_이름이_있는_구조물은_횟수가_붙음() -> None:
|
||
"""품셈 1-7-1 의 3회 줄에 「옹벽」이 그대로 있다."""
|
||
table = build_table([옹벽()], {"retaining_wall": "옹벽"})
|
||
forms = [c for s in table["structures"] for c in s["components"] if c["name"] in FORMWORK_NAMES]
|
||
assert forms
|
||
assert all(c["reuse_count"] == 3 for c in forms)
|
||
assert all("1-7-1" in c["reuse_note"] for c in forms)
|
||
assert table["formwork_reuse_missing"] == []
|
||
|
||
|
||
def test_횟수별_비율을_여기서_곱하지_않을것() -> None:
|
||
"""⚠ 이 시험이 이 파일의 핵심 — 합판 3회 46.1 % 를 여기서 곱하면 B09 와 겹쳐 두 번 준다.
|
||
B08 이 내는 것은 **접촉 면적 그대로**이고 횟수는 옆에 적기만 한다."""
|
||
table = build_table([옹벽()], {"retaining_wall": "옹벽"})
|
||
euroform = next(
|
||
c for s in table["structures"] for c in s["components"] if c["name"] == "유로폼"
|
||
)
|
||
# 3.205 ㎡/m × 10m — 46.1 % 를 곱하지 않았다(2026-09-14 원문 값 3.205 · 전 라이브러리 3.20 → 32.0)
|
||
assert abs(euroform["amount"] - 32.05) < 1e-9
|
||
# 비율표는 데이터에 있되 값에 안 걸린다.
|
||
assert load_formwork_table().reuse_ratio_pct["plywood"]["3"] == 46.1
|
||
|
||
|
||
def test_모르는_종류는_지어내지_않고_미확보() -> None:
|
||
structures = [
|
||
{
|
||
"type_id": "듣도보도못한구조물",
|
||
"name": "무엇",
|
||
"components": [{"name": "합판거푸집", "unit": "㎡", "amount": 5.0}],
|
||
}
|
||
]
|
||
notes, missing = annotate(structures)
|
||
assert missing == ["듣도보도못한구조물"]
|
||
assert structures[0]["components"][0]["reuse_count"] is None
|
||
assert structures[0]["components"][0]["reuse_note"] == NOTE_REUSE_MISSING
|
||
|
||
|
||
def test_거푸집이_없는_구조물은_건드리지_않음() -> None:
|
||
structures = [
|
||
{
|
||
"type_id": "retaining_wall",
|
||
"components": [{"name": "콘크리트", "unit": "㎥", "amount": 1.0}],
|
||
}
|
||
]
|
||
notes, missing = annotate(structures)
|
||
assert notes == [] and missing == []
|
||
assert "reuse_count" not in structures[0]["components"][0]
|
||
|
||
|
||
def test_이름은_정확히_일치로만_본다() -> None:
|
||
"""부분일치면 「거푸집씻기」(공사용수 항목)가 거푸집으로 잡힌다."""
|
||
structures = [
|
||
{
|
||
"type_id": "retaining_wall",
|
||
"components": [{"name": "거푸집씻기", "unit": "㎥", "amount": 1.0}],
|
||
}
|
||
]
|
||
annotate(structures)
|
||
assert "reuse_count" not in structures[0]["components"][0]
|
||
|
||
|
||
def test_파일이_없으면_전부_미확보() -> None:
|
||
structures = [
|
||
{
|
||
"type_id": "retaining_wall",
|
||
"components": [{"name": "유로폼", "unit": "㎡", "amount": 1.0}],
|
||
}
|
||
]
|
||
notes, missing = annotate(structures, FormworkTable())
|
||
assert missing == ["retaining_wall"]
|
||
|
||
|
||
# ── 동바리 ──────────────────────────────────────────────────────────
|
||
|
||
|
||
def test_동바리는_대상이_없으면_없다고_말할것() -> None:
|
||
"""0 으로 적으면 「대상이 없음」과 「값이 0」이 구별되지 않는다."""
|
||
status = shoring_status()
|
||
assert status["applicable"] is False
|
||
assert status["reason"]
|
||
# BOX암거는 2026-09-15 전개식이 서서 대상에서 빠짐 — 세월교만 남음(근거 없음 사유).
|
||
assert set(status["pending_types"]) == {"ford_bridge"}
|
||
|
||
|
||
def test_표에도_동바리_상태가_실림() -> None:
|
||
assert build_table([옹벽()])["shoring"]["applicable"] is False
|
||
box = {
|
||
"type_id": "box_culvert",
|
||
"options": {
|
||
"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": 5.0,
|
||
},
|
||
}
|
||
assert build_table([box])["shoring"]["applicable"] is True
|