Files
Aislo/resources/tester/test_288_underdrain_cross.py
T
eomsangdonandClaude Opus 5.5 172ff8170b refactor(511-B B-6 정리): 운영 부름 0 파이썬 짝 걷음 · 남는 짝 머리에 남는 까닭 · 등록부 until 걷음 · 알려진 차 까닭 · 322_3 flow 빨강 고침
걷은 함수(운영 부름 0 · 전수 확인 = 화면 · 서버 · 체인 · 추천 엔진 · B07 · 시험)
- common_util/common_util_excavation.py 통째(126 줄 · pipe_trench_width_m · wall_trench_area_m2 · wall_trench_width_m · 서버 = Node excavation.trench)
- Engine_Grade_Carry.carry_edits(Node carry_seat 겉) · Route_Save_Parts.seat_route_moved(시험 헬퍼 helper_475_1_moved 로 옮김)
- Engine_Tilt.attach_underdrains · Engine_Structures_Wall.attach_wall_structures · load_wall_structures
- Engine_Grade_Alignment.tangent_elevation(부름 0) · station_key.dedupe_station_keys · loose_chainage
줄 수 전 → 후: excavation.py 126 → 0 · Grade_Carry 178 → 157 · Save_Parts 289 → 269 · Wall 221 → 197 · Tilt 115 → 112 · station_key 141 → 122 · Alignment 657 → 654

남는 짝(머리 「남는 까닭」 한 줄) — Grade · Grade_Profile · Alignment · CurveLaw(2~4 순위 폴백 · 추천) · NodeEdits · SampleExtend · Router_Design · Ranges · Engine_Design · Areas · cross_berm(DTM 아닌 지표면 · Node 못 부를 때 · B07 · 추천) · Tilt · Wall · Culvert(B04 B07 B08) · route_follow · structure_station · station_key · route_polyline(745 줄 · 까닭 = 등록부 R1 줄)

등록부 — until 칸 걷음(G1 = py 표본기 남김 · 까닭 note) · C6 pair = attach_underdrain_specs · F8 pair null
알려진 차 — golden KNOWN 셋 그대로 나타남(R7 표본 출처 · W0 저장 때 경고 · X2 입력 선 자릿수 · 까닭 고침) · underdrain 알려진 차 까닭 · 511_b5 KNOWN_ARC 그대로
계산 자리 창 — 45 줄 · 서버만 아닌 41(바꿀 수 있음 40 + F9) · 서버만 넷(G3 D1 D2 N1 줄마다 까닭) · 옛 까닭 「짝 모으는 중 · B-4」 글 걷음
test_322_3_flow 빨강 — 헬퍼가 @config/config_frontend 흉내(511-A design_calc_server import)
시험 기준 — 걷은 짝 거울 = 걷기 전 파이썬 값 고정(excavation · station_key) · Node carry_seat · attach_underdrain_specs · wall_specs_from

검증: 고친 대상 시험 97 파일 562 통과 · 21 건너뜀 · typecheck 0 · 700 줄 넘는 새 파일 없음 — 요구 320-4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:34:08 +09:00

196 lines
8.1 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.
"""288 맹암거 횡단 형상(요구 183-3 · 9).
못박는 것
- 288-1 맹암거 기준 측점 = 측구 없음 · 노면 = 표준 경사로 지대 낮은 쪽 한 경사
(물넘이 286 · 287 길 — 서버 `tilt_ranges` · `tilt_args` · TS 짝 `tilted` · 암거 경사는
노면에 안 감 371-3) · 제원 얹기 서버 `attach_underdrain_specs` ↔ TS `attachUnderdrainSpecs`
같은 값 · 형상 = 노면을 암거 높이만큼 아래로 옵셋 → 성토 쪽은 성토 사면과 만남 ·
절토 쪽은 절토 사면을 늘여 만남 · 그 안 돌 무늬 · 높이 비면 안 그림.
- 371-3 암거 경사 = 형상 하단 경계 — 높은 쪽 노면 끝 − 높이에서 암거 경사로 낮은 쪽으로 내림.
- 288-2 맹암거 형상 누름 = 그 구조물 id 고름(구조물 목록 창구 · 알약 · 폼 · 측점선 따라옴) · 물넘이와 같은
판정면(보라 손잡이 아래 · 끌기 = 고름 아님 · 287-5) · 노면공 포장 범위 고름과 따로.
진짜 TS 는 `helper_288_underdrain.cjs` 가 Node 로 돌림. 화면은 사용자 확인.
"""
from __future__ import annotations
import json
import subprocess
import sys
from pathlib import Path
import pytest
HERE = Path(__file__).resolve().parent
ROOT = HERE.parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
import B06_Section.B06_Section_Engine_Tilt as tilt # noqa: E402
from B05_Profile.B05_Profile_Structures_Schema import StructureInstance # noqa: E402
from B06_Section.B06_Section_Engine_Design import compute_cross_design # noqa: E402
STRUCTURES = [
{
"structure_id": "ud-1",
"type_id": "cross_underdrain",
"placement": "point",
"chainage_m": 40.0,
"options": {"culvert_height_m": 0.8, "culvert_slope_pct": 4},
},
{ # 경사 · 높이 빈 맹암거 — 표준 횡단경사 · 형상 없음
"structure_id": "ud-2",
"type_id": "cross_underdrain",
"placement": "point",
"chainage_m": 80.0,
"options": {},
},
{
"structure_id": "x-1",
"type_id": "cross_drain_exposed",
"placement": "point",
"chainage_m": 60.0,
},
]
SECTIONS = [{"chainage_m": c} for c in (20.0, 40.0, 60.0, 80.0)]
pytestmark = pytest.mark.skipif(
not (ROOT / "config" / "node_modules" / "typescript").is_dir(),
reason="프론트엔드 의존성 미설치",
)
@pytest.fixture()
def project(monkeypatch, tmp_path) -> Path:
items = [StructureInstance(**s) for s in STRUCTURES]
monkeypatch.setattr(tilt, "load_structures", lambda _root: (1, items))
monkeypatch.setattr(tilt, "load_culvert_sets", lambda _root: {})
return tmp_path
@pytest.fixture(scope="module")
def ts(tmp_path_factory) -> dict:
source = tmp_path_factory.mktemp("s288") / "in.json"
source.write_text(
json.dumps({"structures": STRUCTURES, "sections": SECTIONS}), encoding="utf-8"
)
proc = subprocess.run(
["node", str(HERE / "helper_288_underdrain.cjs"), str(source)],
capture_output=True,
text=True,
encoding="utf-8",
timeout=60,
)
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
def test_맹암거_자리_기울기_서버(project):
ranges = tilt.tilt_ranges(project)
# 371-3 — 암거 경사(4 %)는 노면에 안 감 · 노면 = 표준 경사
assert ranges == [(40.0, 40.0, None), (80.0, 80.0, None)]
assert tilt.tilt_args(40.0, ranges) == {"tilted": True, "tilt_pct": None}
assert tilt.tilt_args(80.01, ranges) == {"tilted": True, "tilt_pct": None}
assert tilt.tilt_args(60.0, ranges) == {"tilted": False, "tilt_pct": None}
def test_제원_얹기_서버_TS_같은_값(project, ts):
sections = [dict(s) for s in SECTIONS]
attached = tilt.attach_underdrain_specs(sections, tilt.load_underdrains(project))
assert attached == 2 == ts["attached"]
assert [s.get("underdrain") for s in sections] == ts["sections"]
assert ts["sections"][1] == {
"structure_id": "ud-1",
"chainage_m": 40.0,
"height_m": 0.8,
"slope_pct": 4.0,
}
def test_맹암거_측점_설계_측구_없음_낮은_쪽_기울기():
samples = [
{"offset_m": o / 2, "elevation_m": round(100.0 + 0.35 * o / 2, 4), "valid": True}
for o in range(-24, 25)
]
kwargs = {"ground_type": "soil", "section_mode": "left_cut", "ditch_side": "left"}
got = compute_cross_design(samples, 100.4, tilted=True, tilt_pct=None, **kwargs)
standard = compute_cross_design(samples, 100.4, **kwargs)["cross_slope_pct"]
assert got["ditch_enabled"] is False and got["cross_slope_pct"] == standard
left, right = (got["road_edges"][k]["elevation_m"] for k in ("left", "right"))
assert left > right # 지반이 좌로 오름 = 우가 낮음
def test_형상_옵셋선_성토_절토와_만남(ts):
outline = [(p["offset"], p["elevation"]) for p in ts["outline"]]
expected = [
(3, 100.15), # 좌 노면 끝
(-3, 99.85), # 우 노면 끝
(-4.632653, 98.768367), # 우 성토 사면 ∩ 옵셋선
(1.846154, 99.092308), # 좌 절토 사면을 늘인 선 ∩ 옵셋선
]
assert len(outline) == len(expected)
for got, want in zip(outline, expected):
assert got == pytest.approx(want, abs=1e-5)
assert ts["noHeight"] is None
assert ts["drew"] is True and ts["stones"] > 0
def test_하단_경계는_암거_경사(ts):
"""371-3 — 노면 5 % · 암거 3 % · 높이 1 m: 하단 = 좌(높은 쪽) 끝 99.15 에서 3 % 로 우로 내림."""
outline = [(p["offset"], p["elevation"]) for p in ts["sloped"]]
expected = [
(3, 100.15),
(-3, 99.85),
(-4.391304, 98.928261), # 우 성토 사면 ∩ 하단(99.15 − 0.03 × 7.391)
(1.872180, 99.116165), # 좌 절토 사면을 늘인 선 ∩ 하단
]
assert len(outline) == len(expected)
for got, want in zip(outline, expected):
assert got == pytest.approx(want, abs=1e-5)
def test_양측절토_늘인_노면_원지반_교점에서_끊음(ts):
"""356-1 — 낮은 쪽 노면 끝도 절토(tilt_reach) = reach 에서 수직으로 끊음 · 너머 원지반 따라가지 않음."""
outline = [(p["offset"], p["elevation"]) for p in ts["bothCut"]]
expected = [
(3, 100.15),
(-3, 99.85),
(-4, 99.8),
(-4.5, 99.775), # 늘인 노면 ∩ 원지반(tilt_reach)
(-4.5, 98.775), # reach 아래 옵셋선
(1.846154, 99.092308),
]
assert len(outline) == len(expected)
for got, want in zip(outline, expected):
assert got == pytest.approx(want, abs=1e-5)
def test_횡단도_B07_이_맹암거를_그림():
view = (ROOT / "B06_Section" / "B06_Section_UI_Cross_View.ts").read_text(encoding="utf-8")
assert (
"appendUnderdrainOverlay(plotLayer, section.underdrain, section.design, x, toDisplayY);"
in view
)
b07 = (ROOT / "B07_DesignDetail" / "B07_DesignDetail_Cross_Overlay.ts").read_text(
encoding="utf-8"
)
assert "appendUnderdrainOverlay(svg, section.underdrain, design, x, y);" in b07
refresh = (ROOT / "B06_Section" / "B06_Section_Cross_Refresh.ts").read_text(
encoding="utf-8"
) # 셈 몫 = logic/B06_Section_Cross_Refresh_Calc.ts(511-B B-4 나눔)
refresh += (ROOT / "B06_Section" / "logic" / "B06_Section_Cross_Refresh_Calc.ts").read_text(
encoding="utf-8"
)
assert "tilted: Boolean(section.ford_pavement || section.underdrain)," in refresh
def test_맹암거_형상_누름은_그_구조물_고름(ts):
assert ts["bodyId"] == "ud-1" # 둘레 = 형상 고름 표식
assert ts["routed"] == [["pipe"], [ts["eventName"], "ud-1", True]]
view = (ROOT / "B06_Section" / "B06_Section_UI_Section_View.ts").read_text(encoding="utf-8")
assert "grid.addEventListener(SHAPE_PICK_EVENT, (event) => {" in view
assert "if (id) pavementSelectListener?.(id);" in view # = structuresPanel.selectStructure(id)
cross = (ROOT / "B06_Section" / "B06_Section_UI_Cross_View.ts").read_text(encoding="utf-8")
assert 'revetOffset && shapePickRoute(svg, (at) => toggleRevet("pipe", at)),' in cross