걷은 함수(운영 부름 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>
144 lines
5.2 KiB
Python
144 lines
5.2 KiB
Python
"""터파기 단면 — TS 한 벌(`common_util_excavation.ts`)이 법정 표 · 실무 정본 값을 냄 (2026-09-09).
|
|
|
|
화면(B06 횡단도)이 그리고 서버(B08 수량 · Node `excavation.trench`)가 세는 값.
|
|
파이썬 짝(`common_util_excavation.py`)은 운영 부름 0 이라 걷음(511-B B-6) — 기대값 = 걷기 전
|
|
파이썬 값 그대로.
|
|
|
|
⚠ 근거의 급이 다른 두 갈래를 함께 잠근다 —
|
|
관은 **법정 표**(KCS 44 40 10 그림 3.2-1), 벽은 **실무 정본 식**(구조도 기슭막이 xls).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
|
# 걷기 전 파이썬 짝 값(511-B B-6)
|
|
PIPE_TABLE = {
|
|
300: 700, 350: 800, 400: 900, 450: 1000, 500: 1100, 600: 1200, 700: 1400, 800: 1600,
|
|
900: 1700, 1000: 1800, 1100: 2000, 1200: 2100, 1300: 2200, 1400: 2400, 1500: 2500,
|
|
} # fmt: skip
|
|
WALL_FOUNDATION_DEPTH_M = 0.5
|
|
WALL_FOUNDATION_WIDTH_M = 0.9
|
|
WALL_TRENCH_CLEARANCE_M = 0.2
|
|
|
|
TSC = PROJECT_ROOT / "config" / "node_modules" / "typescript" / "bin" / "tsc"
|
|
|
|
_RUNNER = """
|
|
import { writeFileSync } from "node:fs";
|
|
import {
|
|
PIPE_TRENCH_WIDTH_MM,
|
|
WALL_FOUNDATION_DEPTH_M,
|
|
WALL_FOUNDATION_WIDTH_M,
|
|
WALL_TRENCH_CLEARANCE_M,
|
|
pipeTrenchWidthM,
|
|
wallTrenchAreaM2,
|
|
wallTrenchWidthM,
|
|
} from "./common_util_excavation.js";
|
|
|
|
const out = {
|
|
table: PIPE_TRENCH_WIDTH_MM,
|
|
pipe: [300, 800, 1000, 1200, 1500, 250, 1600].map((d) => pipeTrenchWidthM(d)),
|
|
wallWithFoundation: [1.0, 2.0, 3.0].map((h) => wallTrenchAreaM2(h, 0.7, true)),
|
|
wallBlindingOnly: [1.0, 2.0, 3.0].map((h) => wallTrenchAreaM2(h, 0.7, false)),
|
|
wallWidth: [0.7, 1.05, 0].map((t) => wallTrenchWidthM(t)),
|
|
nulls: [pipeTrenchWidthM(null), wallTrenchAreaM2(null, 0.7, true), wallTrenchAreaM2(1, null, true)],
|
|
practice: [1.0, 2.0, 3.0].map((h) => wallTrenchAreaM2(h, 0.83, true)),
|
|
consts: [WALL_FOUNDATION_DEPTH_M, WALL_FOUNDATION_WIDTH_M, WALL_TRENCH_CLEARANCE_M],
|
|
};
|
|
writeFileSync(process.argv[2], JSON.stringify(out));
|
|
"""
|
|
|
|
|
|
def _ts_values(tmp_path: Path) -> dict:
|
|
out = tmp_path / "js"
|
|
subprocess.run( # noqa: S603 — 고정 실행 파일
|
|
[
|
|
"node",
|
|
str(TSC),
|
|
str(PROJECT_ROOT / "common_util" / "common_util_excavation.ts"),
|
|
"--outDir",
|
|
str(out),
|
|
"--module",
|
|
"esnext",
|
|
"--target",
|
|
"es2022",
|
|
"--moduleResolution",
|
|
"bundler",
|
|
"--ignoreConfig",
|
|
],
|
|
cwd=str(PROJECT_ROOT),
|
|
check=True,
|
|
capture_output=True,
|
|
)
|
|
for emitted in out.glob("*.js"):
|
|
text = emitted.read_text(encoding="utf-8")
|
|
emitted.write_text(
|
|
re.sub(r'(from "\./[^"]+?)(")', lambda m: m.group(1) + ".js" + m.group(2), text),
|
|
encoding="utf-8",
|
|
)
|
|
(out / "runner.mjs").write_text(_RUNNER, encoding="utf-8")
|
|
result = tmp_path / "out.json"
|
|
subprocess.run( # noqa: S603
|
|
["node", str(out / "runner.mjs"), str(result)],
|
|
cwd=str(PROJECT_ROOT),
|
|
check=True,
|
|
capture_output=True,
|
|
)
|
|
return json.loads(result.read_text(encoding="utf-8"))
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def ts(tmp_path_factory) -> dict:
|
|
if not TSC.is_file():
|
|
pytest.skip("프론트엔드 의존성 미설치")
|
|
return _ts_values(tmp_path_factory.mktemp("excavation"))
|
|
|
|
|
|
def test_걷기_전_파이썬_값과_같다(ts: dict) -> None:
|
|
assert {int(k): v for k, v in ts["table"].items()} == PIPE_TABLE
|
|
assert ts["pipe"] == [0.7, 1.6, 1.8, 2.1, 2.5, None, None]
|
|
assert ts["wallWithFoundation"] == pytest.approx([1.35, 2.25, 3.15], abs=1e-12)
|
|
assert ts["wallBlindingOnly"] == pytest.approx([0.97, 1.87, 2.77], abs=1e-12)
|
|
assert ts["wallWidth"][:2] == pytest.approx([0.9, 1.25], abs=1e-12)
|
|
assert ts["wallWidth"][2] is None
|
|
assert ts["nulls"] == [None, None, None]
|
|
assert ts["consts"] == [
|
|
WALL_FOUNDATION_DEPTH_M,
|
|
WALL_FOUNDATION_WIDTH_M,
|
|
WALL_TRENCH_CLEARANCE_M,
|
|
]
|
|
|
|
|
|
def test_관은_표를_그대로_쓴다(ts: dict) -> None:
|
|
"""⚠ 관경 + 2b 로 계산하지 않는다 — 표가 관 두께·작업여유를 담고 있다(800 → 1.6)."""
|
|
width_800 = ts["pipe"][1]
|
|
assert width_800 == 1.6 and width_800 != 0.8 + 2 * 0.3
|
|
assert all(width is not None for width in ts["pipe"][1:5]) # 800 · 1000 · 1200 · 1500
|
|
|
|
|
|
def test_표에_없는_관경은_지어내지_않는다(ts: dict) -> None:
|
|
assert ts["pipe"][5:] == [None, None] # 250 · 1600
|
|
|
|
|
|
def test_실무_정본_수치와_맞는다(ts: dict) -> None:
|
|
"""xls 7탭: H=1.0 → 1.03+0.45 = 1.48 · H=2.0 → 2.51 · H=3.0 → 3.54 (평균두께 0.83)."""
|
|
for area, expected in zip(ts["practice"], (1.48, 2.51, 3.54), strict=True):
|
|
assert area == pytest.approx(expected, abs=0.01)
|
|
|
|
|
|
def test_기초_유무가_기초분을_가른다(ts: dict) -> None:
|
|
with_base, without = ts["wallWithFoundation"][1], ts["wallBlindingOnly"][1] # H 2.0 · 0.7
|
|
assert with_base - without == pytest.approx(
|
|
WALL_FOUNDATION_DEPTH_M * WALL_FOUNDATION_WIDTH_M - 0.1 * 0.7
|
|
)
|
|
|
|
|
|
def test_여유폭은_평균두께에_더한다(ts: dict) -> None:
|
|
assert ts["wallWidth"][0] == pytest.approx(0.7 + WALL_TRENCH_CLEARANCE_M)
|