diff --git a/B06_Section/B06_Section_UI_Cross_Detail_Cad.ts b/B06_Section/B06_Section_UI_Cross_Detail_Cad.ts index 3cb8b04b4..cf3fff35c 100644 --- a/B06_Section/B06_Section_UI_Cross_Detail_Cad.ts +++ b/B06_Section/B06_Section_UI_Cross_Detail_Cad.ts @@ -19,6 +19,7 @@ import { PIPE_MATCH_M } from "../A00_Common/design/design_selection"; import { fetchDetailPipePoints } from "../B04_PreProcess/B04_PreProcess_Api_Fetch"; import { readPendingPipes } from "../B05_Profile/B05_Profile_Api_Pipes_Draft"; import type { CrossNoteCad, CrossSection, CulvertSideSpec } from "./B06_Section_Api_Fetch"; +import { pipeLengthWhole } from "./B06_Section_UI_Cross_Culvert_Const"; const noteKey = (chainageM: number): string => chainageM.toFixed(2); @@ -109,6 +110,9 @@ export function legacyNoteEntities(note: string | undefined): Record typeof value === "number" && Number.isFinite(value) ? value.toFixed(1) : ""; const metres = (value: number | null | undefined): string => (one(value) ? `${one(value)}m` : ""); +/** 관 길이 = 사사오입 정수(265-3 · B08 「관매설」과 같은 값) · 없으면 빈칸. */ +const wholeMetres = (value: number | null | undefined): string => + typeof value === "number" && Number.isFinite(value) ? `${pipeLengthWhole(value)}m` : ""; const trim = (value: number): string => String(Math.round(value * 100) / 100); /** 돌쌓기(찰) · 찰붙임 → 찰 · (메) · 메붙임 → 메 · 그 밖 = 그대로. */ const bondOf = (form: string | null | undefined): string => @@ -183,7 +187,7 @@ export function culvertNoteEntities( const inside = [ "배수관", `Φ${Math.round(culvert.diameter_m * 1000)}m/m`, - `L=${metres(section.design?.pipe_length_m)}`, + `L=${wholeMetres(section.design?.pipe_length_m)}`, ]; const right = [ station, diff --git a/B07_DesignDetail/B07_DesignDetail_Engine_Cad_CulvertNote.py b/B07_DesignDetail/B07_DesignDetail_Engine_Cad_CulvertNote.py index 53dc81fa1..18c584202 100644 --- a/B07_DesignDetail/B07_DesignDetail_Engine_Cad_CulvertNote.py +++ b/B07_DesignDetail/B07_DesignDetail_Engine_Cad_CulvertNote.py @@ -40,6 +40,11 @@ def _metres(value: Any) -> str: return f"{_one(value)}m" if _one(value) else "" +def _whole_metres(value: Any) -> str: + """관 길이 = 사사오입 정수(265-3 · TS `pipeLengthWhole` · B08 「관매설」과 같은 값).""" + return f"{_js_round(value)}m" if _one(value) else "" + + def _trim(value: float) -> str: rounded = _js_round(value * 100) / 100 return str(int(rounded)) if rounded == int(rounded) else str(rounded) @@ -159,7 +164,7 @@ def culvert_note_entities( inside = [ "배수관", f"Φ{_js_round(float(culvert.get('diameter_m') or 0.0) * 1000)}m/m", - f"L={_metres(length)}", + f"L={_whole_metres(length)}", ] chute = culvert.get("chute") if isinstance(culvert.get("chute"), dict) else {} right = [ diff --git a/resources/tester/helper_260_note_cad.cjs b/resources/tester/helper_260_note_cad.cjs index 4cd84f09e..bf9ff6244 100644 --- a/resources/tester/helper_260_note_cad.cjs +++ b/resources/tester/helper_260_note_cad.cjs @@ -28,6 +28,10 @@ function loadTs(file) { return box.exports; } +// 관 길이 정수(265-3) = 실제 `pipeLengthWhole`(타입 import 만 있는 파일) +STUBS["./B06_Section_UI_Cross_Culvert_Const"] = loadTs( + path.join(ROOT, "B06_Section", "B06_Section_UI_Cross_Culvert_Const.ts"), +); const cad = loadTs(path.join(ROOT, "B06_Section", "B06_Section_UI_Cross_Detail_Cad.ts")); const wall = (role, form, height, length) => ({ diff --git a/resources/tester/test_260_note_cad.py b/resources/tester/test_260_note_cad.py index 5a85aed87..34aae9442 100644 --- a/resources/tester/test_260_note_cad.py +++ b/resources/tester/test_260_note_cad.py @@ -107,7 +107,7 @@ def test_관_유입_유출_도수로_한_벌(out): assert _labels(full) == [ "배수관", "Φ1200m/m", - "L=22.0m", + "L=22m", "NO.71+13", "관보호공(유입) 기슭막이(찰,H=2.0) L=10.0m", "관보호공(유출) 기슭막이(메,H=2.0) L=10.0m", @@ -136,7 +136,7 @@ def test_도수로_옵션이_없으면_세트_구조만(out): def test_보호공을_안_고르면_관과_측점만(out): - assert _labels(out["noSides"]) == ["배수관", "Φ1200m/m", "L=22.0m", "NO.71+13"] + assert _labels(out["noSides"]) == ["배수관", "Φ1200m/m", "L=22m", "NO.71+13"] @pytest.mark.parametrize("case", ["none", "ford", "hidden", "neighbour"]) @@ -425,7 +425,7 @@ def test_칸_없음_만_미리_넣음_지운_칸_옛_글_설계없음_이웃은_ assert labels[2:] == [ "배수관", "Φ1200m/m", - "L=22.0m", + "L=22m", "NO.71+13", "관보호공(유입) 기슭막이(찰,H=2.0) L=10.0m", "관보호공(유출) 기슭막이(메,H=2.0) L=10.0m", diff --git a/resources/tester/test_265_note_cad.py b/resources/tester/test_265_note_cad.py new file mode 100644 index 000000000..c92db042e --- /dev/null +++ b/resources/tester/test_265_note_cad.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +"""PLAN 265 — B06 [상세] 표 위 CAD 칸 · 접기 · 차례 · 구조물 글 · 관 길이 정수. + +- 265-3 원형 마크 L = 사사오입 정수(TS `pipeLengthWhole` = 파이썬 짝 같은 값). + +./venv/Scripts/python.exe -m pytest resources/tester/test_265_note_cad.py -q +""" + +import json +import subprocess +import sys +from pathlib import Path + +import pytest + +PROJECT_ROOT = Path(__file__).resolve().parents[2] +if str(PROJECT_ROOT) not in sys.path: + sys.path.insert(0, str(PROJECT_ROOT)) + +from B06_Section.B06_Section_Engine_Culvert import _culvert_set # noqa: E402 +from B07_DesignDetail.B07_DesignDetail_Engine_Cad_CulvertNote import ( # noqa: E402 + culvert_note_entities, +) + +HERE = Path(__file__).resolve().parent +_PIPE = {"pipe_diameter_mm": 800} + + +def _mirror(cases: list[dict]) -> list[dict]: + proc = subprocess.run( # noqa: S603 — 고정 실행 파일 + ["node", str(HERE / "helper_260_note_cad.cjs"), "mirror"], + input=json.dumps(cases, ensure_ascii=False), + capture_output=True, + text=True, + encoding="utf-8", + timeout=60, + ) + assert proc.returncode == 0, proc.stderr + return json.loads(proc.stdout) + + +def _labels(entities: list[dict]) -> list[str]: + return [e["shapeData"]["label"] for e in entities if e["type"] == "Text"] + + +# ── 265-3 관 길이 정수 ───────────────────────────────────────────────── + + +@pytest.mark.parametrize( + ("length", "label"), + [(8.5, "L=9m"), (8.4, "L=8m"), (8.45, "L=8m"), (7.0, "L=7m"), (None, "L=")], +) +def test_원형_마크_L_은_사사오입_정수_TS_파이썬_같음(length, label): + design = {} if length is None else {"pipe_length_m": length} + culvert = {**_culvert_set(_PIPE), "chainage_m": 40.0} + case = { + "section": {"chainage_m": 40.0, "culvert": culvert, "design": design}, + "station": "NO.2", + "options": _PIPE, + "chainage": 40.0, + "interval": 20.0, + } + [ts] = _mirror([case]) + py = culvert_note_entities(40.0, culvert, design, "NO.2", _PIPE) + assert _labels(ts["entities"])[2] == label + assert _labels(py)[2] == label