Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
116 lines
4.2 KiB
Python
116 lines
4.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""다단 추가 기슭막이도 수량이 섬 — ④ (2026-09-14 브레인 판정 「수량이 통째로 빠지는 것」).
|
|
|
|
앞서 B06 횡단도는 다단 벽(`design.extra_wall_counts` · `revet_adjust.extra*` · `extra_spans`)을
|
|
그리고 면적에도 넣었는데, B08 전개는 관 유입·유출 기준벽만 셈 → 다단 벽 돌쌓기·터파기가 한 줄도 안 섬.
|
|
|
|
길 — 실제로 **선** 단 수·높이는 지형이 정하므로(요청보다 적게 설 수 있음) 서버 Node 재계산이
|
|
관 연장처럼 「선 다단 벽 목록」(`design.extra_walls`)을 소유 측점에 남기고, B08 이 그 목록으로 줄을 세움.
|
|
값이 없는 다단(높이를 안 적어 기본 1.5m 로 지반에 맞춰 그린 단)은 **미확정·금액 밖** 규칙 그대로.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
if str(ROOT) not in sys.path:
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from B08_Quantity.B08_Quantity_Engine_Pipe import ( # noqa: E402
|
|
extra_walls_from_designs,
|
|
facility_structures,
|
|
)
|
|
from B08_Quantity.B08_Quantity_Engine_UnitQuantity import build_table # noqa: E402
|
|
|
|
LAYOUTS = (ROOT / "B06_Section" / "B06_Section_Structure_Layouts.ts").read_text(encoding="utf-8")
|
|
NODE = (ROOT / "B06_Section" / "B06_Section_Server_Calc_Node.ts").read_text(encoding="utf-8")
|
|
PREBUILD = (ROOT / "B06_Section" / "B06_Section_Server_Calc_Prebuild.py").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
|
|
WALLS = [
|
|
{
|
|
"key": "extra0",
|
|
"side": "outlet",
|
|
"form": "돌쌓기(메)",
|
|
"height_m": 1.8,
|
|
"height_set": True,
|
|
"form_set": True,
|
|
"before_m": 4.0,
|
|
"after_m": 4.0,
|
|
},
|
|
{
|
|
"key": "extra1",
|
|
"side": "outlet",
|
|
"form": "돌쌓기(메)",
|
|
"height_m": 1.3,
|
|
"height_set": False,
|
|
"form_set": False,
|
|
"before_m": 5.0,
|
|
"after_m": 5.0,
|
|
},
|
|
]
|
|
POINT = {
|
|
"chainage_m": 85.05,
|
|
"options": {
|
|
"pipe_diameter_mm": 1000,
|
|
"outlet_revet_form": "돌쌓기(메)",
|
|
"outlet_revet_height_m": 2.0,
|
|
"outlet_revet_length_m": 10,
|
|
"inlet_revet_form": "돌쌓기(찰)",
|
|
"inlet_revet_height_m": 2.0,
|
|
"inlet_revet_length_m": 10,
|
|
"revet_foundation": "기초유",
|
|
},
|
|
}
|
|
|
|
|
|
def _tiers(rows: list[dict]) -> list[dict]:
|
|
return [row for row in rows if "다단" in str(row.get("attachment_label") or "")]
|
|
|
|
|
|
def test_설계에서_다단_목록을_측점별로_읽는다() -> None:
|
|
designs = [
|
|
{"chainage_m": 85.0, "design": {"extra_walls": WALLS}},
|
|
{"chainage_m": 140.0, "design": {"cut_area_m2": 1.0}},
|
|
]
|
|
assert extra_walls_from_designs(designs) == {85.0: WALLS}
|
|
|
|
|
|
def test_선_다단_벽마다_기슭막이_줄이_선다() -> None:
|
|
rows = facility_structures([POINT], {85.0: WALLS})
|
|
tiers = _tiers(rows)
|
|
assert [row["attachment_label"] for row in tiers] == [
|
|
"유출측 다단 기슭막이 1단",
|
|
"유출측 다단 기슭막이 2단",
|
|
]
|
|
first, second = tiers
|
|
assert first["type_id"] == "revetment"
|
|
assert first["options"]["height_m"] == 1.8 and first["options"]["form"] == "돌쌓기(메)"
|
|
assert (first["start_m"], first["end_m"]) == (85.05 - 4.0, 85.05 + 4.0)
|
|
assert first["options"]["foundation"] == "기초유" # 기준벽과 같은 기초
|
|
assert first["unconfirmed"] == ""
|
|
# 높이를 안 적은 단 — 줄은 서되 미확정
|
|
assert "다단 높이를 안 적음" in second["unconfirmed"]
|
|
|
|
|
|
def test_다단_줄이_원단위와_금액_합에_든다() -> None:
|
|
table = build_table(facility_structures([POINT], {85.0: WALLS}), {"revetment": "기슭막이"}, {})
|
|
tiers = [s for s in table["structures"] if "다단" in s["name"]]
|
|
assert len(tiers) == 2
|
|
assert tiers[0]["components"] and not tiers[0]["unconfirmed"]
|
|
assert tiers[1]["unconfirmed"]
|
|
|
|
|
|
def test_목록이_없으면_종전과_같다() -> None:
|
|
assert not _tiers(facility_structures([POINT]))
|
|
assert not _tiers(facility_structures([POINT], {}))
|
|
|
|
|
|
def test_서버_재계산이_선_다단_목록을_남긴다() -> None:
|
|
assert "export function extraWallRows" in LAYOUTS
|
|
assert "extraWallRows(sections)" in NODE and "extra_walls" in NODE
|
|
assert '"extra_walls"' in PREBUILD
|