Files
Aislo/resources/tester/test_b08_topsoil_haul.py
T
eomsangdonandClaude Opus 5 9f72ad19b8 feat(b08): 표토제거 대상 고르개 — 노면 + 절토 사면(기본 · 법 문언) / 노면만
- 산출 조건 칸 topsoil_target · 기본 None = 별표2 Ⅰ.2.차.(6) 「노면·절토대상지」
- 노면만: 실무 영월 한 건(1m당 3.34㎡) 관측 — 사유에 법 문언이 아님을 함께 적음
- 칸 밑에 근거 두 줄 · 운반 부피도 고른 대상 따라감

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
2026-09-14 00:14:46 +09:00

164 lines
7.2 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.
"""표토 운반·적치 — 법이 요구하는데 제거만 세고 있던 줄 (2026-09-09 · 2026-09-13 판정 「실무대로」).
⚠⚠ 시행규칙 별표2 Ⅰ.2.차.(6)·Ⅰ.3.카.(6):
「노면·절토대상지에 있는 입목…과 그 뿌리, **표토는 전량 제거한 후** 강우 시 유실되거나
경관에 저해되지 않도록 **최고 홍수위보다 높은 장소로 운반하고 쌓아두어야 한다**」
⇒ 제거(9-15)만 세면 **운반이 빠진다.**
⚠ 겨누는 것
① 줄이 **선다** — 값이 없어도 목록에 남는다
② 부피는 **제거 면적(㎡) × 두께** — 제거는 ㎡ 로 서고 두께는 운반 부피에만 쓴다
③ 거리는 **설계 입력** · 제거 품(9-15-2)이 **20m 압토**를 품어 **넘는 몫만** 운반으로 셈
④ 제거가 안 서거나 두께가 비면 운반도 안 선다 — 0 으로 때우지 않는다
⑤ 법 문구가 **사유에 적힌다** — 왜 세는지 화면에서 보여야 한다
"""
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_Preparation import ( # noqa: E402
STATUS_COUNTED_ELSEWHERE,
STATUS_PENDING,
STATUS_READY,
build_table,
preparation_rows,
)
사면 = {"face_dressing_fill": 600.0, "face_dressing_cut": 400.0}
노면 = 600.0 # 노면 600 + 절토 사면 400 = 제거 1,000㎡
def 줄(thickness: float | None, distance: float | None, road: float | None = 노면) -> dict:
rows = preparation_rows(사면, [], thickness, distance, None, road)
return {row["item"]: row for row in rows}
def test_줄이_선다() -> None:
assert "표토 운반·적치" in 줄(None, None)
def test_부피는_제거_면적_곱하기_두께_거리는_20m_넘는_몫() -> None:
rows = 줄(0.2, 300.0)
assert rows["표토제거"]["amount"] == 1000.0
운반 = rows["표토 운반·적치"]
assert 운반["amount"] == 1000.0 * 0.2 and 운반["status"] == STATUS_READY
assert 운반["work_item_code"] == "FP-10-12"
assert 운반["haul_distance_m"] == 280.0 # 300 − 압토 20
assert "20m 를 넘는 몫" in 운반["reason"]
def test_20m_이내면_제거_품에_들어_따로_안_셈() -> None:
운반 = 줄(0.2, 15.0)["표토 운반·적치"]
assert 운반["amount"] is None and 운반["status"] == STATUS_COUNTED_ELSEWHERE
assert 운반["reference_amount"] == 200.0
def test_거리를_안_넣으면_막고_사유() -> None:
"""⚠ 「최고 홍수위보다 높은 장소」는 현장이 정한다 — 품셈이 거리를 주지 않는다."""
row = 줄(0.2, None)["표토 운반·적치"]
assert row["amount"] is None and row["status"] == STATUS_PENDING
assert "운반거리가 아직 입력되지 않았습니다" in row["reason"]
assert row["reference_amount"] == 200.0 # 값을 버리지 않는다
def test_두께가_비거나_제거가_안_서면_운반도_안_선다() -> None:
no_thickness = 줄(None, 300.0)["표토 운반·적치"]
assert no_thickness["amount"] is None and "T=0.2m" in no_thickness["reason"]
no_removal = 줄(0.2, 300.0, road=None)["표토 운반·적치"]
assert no_removal["amount"] is None and "제거 면적이 아직 안 서서" in no_removal["reason"]
def test_법_문구가_사유에_적힌다() -> None:
for thickness, distance in ((None, None), (0.2, None), (0.2, 300.0), (0.2, 10.0)):
reason = 줄(thickness, distance)["표토 운반·적치"]["reason"]
assert "별표2" in reason and "운반하고 쌓아두어야" in reason
# ── 뿌리 적재·운반 — 품셈이 네 단계로 둔 자리 (2026-09-09) ──────────────
def test_뿌리_뒤_단계_두_줄이_선다() -> None:
rows = 줄(0.2, 300.0)
assert "뿌리 적재" in rows and "뿌리 운반" in rows
def test_적재는_9_20_2_운반은_덤프() -> None:
"""⭐ 2026-09-09 확정 5차 4번 — 「뿌리 운반은 **덤프**」. 물량은 부피가 없어 안 선다."""
rows = 줄(0.2, 300.0)
assert rows["뿌리 적재"]["work_item_code"] == "FP-09-20-02"
assert rows["뿌리 운반"]["work_item_code"] == "FP-10-12"
assert rows["뿌리 운반"]["amount"] is None
assert "부피" in rows["뿌리 운반"]["reason"]
def test_적재는_면적으로_서고_운반은_못_선다() -> None:
사면_ = {"tree_removal_fill": 600.0, "tree_removal_cut": 400.0}
rows = {row["item"]: row for row in preparation_rows(사면_, [], 0.2, 300.0)}
assert rows["뿌리 적재"]["amount"] == 1000.0
assert rows["제근·뿌리다듬기"]["amount"] == 1000.0 # 같은 밑수로 함께 섬
assert rows["뿌리 운반"]["amount"] is None
assert rows["뿌리 운반"]["reference_amount"] == 1000.0
def test_운반이_갈래를_실어_보낸다() -> None:
"""⚠ `FP-10-12` 는 **부모**다 — 갈래(토사)를 안 보내면 B09 가 「후보 3건」에서 멈춘다."""
from B08_Quantity.B08_Quantity_Engine_Handoff import build_handoff
table = build_table(
slope_totals={"face_dressing_cut": 9000.0, "face_dressing_fill": 6726.9},
topsoil_thickness_m=0.2,
topsoil_haul_distance_m=500.0,
road_surface_area_m2=12000.0,
)
handed = {r["name"]: r for r in build_handoff(preparation_table=table)["work_items"]}
운반 = handed["표토 운반·적치"]
assert 운반["in_bill"] is True
assert (운반["variant_axis"], 운반["variant_value"]) == ("ground_class", "토사")
제거 = handed["표토제거"]
assert (제거["unit"], 제거["quantity"], 제거["in_bill"]) == ("㎡", 21000.0, True)
def test_노면_면적은_노체_폭의_평균단면적() -> None:
from types import SimpleNamespace as S
from B08_Quantity.B08_Quantity_Engine_SlopeLength import _roadbed_width, road_surface_area
assert (
_roadbed_width({"road_edges": {"left": {"offset_m": 2.5}, "right": {"offset_m": -2.5}}})
== 5.0
)
assert (
road_surface_area(
[S(chainage_m=20.0, roadbed_width_m=6.0), S(chainage_m=0.0, roadbed_width_m=4.0)]
)
== 100.0
)
assert (
road_surface_area(
[S(chainage_m=0.0, roadbed_width_m=4.0), S(chainage_m=20.0, roadbed_width_m=None)]
)
is None
)
assert road_surface_area([]) is None
def test_노면만을_고르면_절토_사면을_안_더한다() -> None:
"""⭐ 2026-09-14 판정 — 기본은 법 문언(노면 + 절토) · 노면만은 영월 한 건이라 설계자가 고름."""
rows = {r["item"]: r for r in preparation_rows(사면, [], 0.2, 300.0, None, 노면, True)}
assert rows["표토제거"]["amount"] == 600.0 and "노면만" in rows["표토제거"]["reason"]
assert "법 문언" in rows["표토제거"]["reason"]
assert rows["표토 운반·적치"]["amount"] == 600.0 * 0.2 # 운반도 고른 대상 따라감
def test_대상_설정은_모르는_값이면_기본() -> None:
from common_util.common_util_project_settings import topsoil_target
assert topsoil_target({}) == "road_and_cut"
assert topsoil_target({"topsoil_target": "road_only"}) == "road_only"
assert topsoil_target({"topsoil_target": "cut_only"}) == "road_and_cut"