Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
77 lines
3.2 KiB
Python
77 lines
3.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""메쌓기 높이 사유(B2) · 성토/절토 칸(B3) — 2026-09-14 브레인 판정 ㉰·㉲.
|
|
|
|
㉰ 두 기준 공존 — 임도기술교본 7-3:27 「메쌓기 2.0m 이하」 ↔ 품셈 13-4-4 [주]⑪ 원문 L7192
|
|
「3m 이상이면 전부 또는 하부를 찰쌓기」. 막지 않고 사유(B08 줄) · 칸 경고(B05, `warn_above`).
|
|
㉲ `face_role` — 비우면 단면유형 + 설치 측 자동 판정(계산이지 기본값이 아님) · 고르면 그 값이 이김.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
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_UnitQuantity import expand, stone_masonry # noqa: E402
|
|
from common_util.common_util_structure_face_role import structure_face_role_of # noqa: E402
|
|
|
|
REGISTRY = json.loads(
|
|
(ROOT / "B05_Profile" / "B05_Profile_Structure_Types.json").read_text(encoding="utf-8")
|
|
)
|
|
FIELDS = (ROOT / "B05_Profile" / "B05_Profile_UI_Structures_Fields.ts").read_text(encoding="utf-8")
|
|
|
|
|
|
def _options(type_id: str) -> dict[str, dict]:
|
|
item = next(t for t in REGISTRY["types"] if t["type_id"] == type_id)
|
|
return {o["key"]: o for o in item["options"]}
|
|
|
|
|
|
def _height_notes(height: float, wet: bool) -> list[str]:
|
|
_rows, notes = stone_masonry(height, 10.0, {"back_len_cm": 45}, wet)
|
|
return [n for n in notes if "메쌓기 높이" in n]
|
|
|
|
|
|
def test_메쌓기_높이_두_기준_사유() -> None:
|
|
assert _height_notes(2.0, False) == []
|
|
over_guide = _height_notes(2.5, False)
|
|
assert len(over_guide) == 1 and "7-3:27" in over_guide[0] and "L7192" in over_guide[0]
|
|
over_limit = _height_notes(3.0, False)
|
|
assert len(over_limit) == 1 and "3m 이상이면" in over_limit[0]
|
|
assert _height_notes(3.5, True) == [] # 찰쌓기는 이 사유 대상 아님
|
|
|
|
|
|
def test_등록부_메쌓기_높이_칸은_경고만() -> None:
|
|
height = _options("masonry_dry")["height_m"]
|
|
assert height["warn_above"] == 2.0 and "L7192" in height["warn_message"]
|
|
assert "is-outside-standard" in FIELDS and "warn_above" in FIELDS
|
|
|
|
|
|
def test_성토절토_칸이_이긴다() -> None:
|
|
assert structure_face_role_of("both_fill", {"face_role": "절토"}) == (
|
|
"절토",
|
|
"사용자 지정 절토면(성토/절토 칸)",
|
|
)
|
|
# 비우면 지금 판정 그대로
|
|
assert structure_face_role_of("both_fill", {})[0] == "성토"
|
|
for type_id in ("masonry_wet", "masonry_dry", "revetment"):
|
|
role = _options(type_id)["face_role"]
|
|
assert role["choices"] == ["성토", "절토"] and role["default"] is None, type_id
|
|
|
|
|
|
def test_전개가_칸_값으로_기울기를_고른다() -> None:
|
|
structure = {
|
|
"structure_id": "w",
|
|
"type_id": "masonry_wet",
|
|
"start_m": 0.0,
|
|
"end_m": 10.0,
|
|
"options": {"height_m": 2.5, "length_m": 10.0, "back_len_cm": 45, "face_role": "절토"},
|
|
}
|
|
result = expand(structure, None, None, "both_fill")
|
|
wall = next(c for c in result.components if c.name == "돌쌓기")
|
|
# 찰쌓기 · 절토 · 직고 ≤3m → 1:0.25 (성토였으면 1:0.30)
|
|
assert "절토" in wall.basis and "1:0.25" in wall.basis
|