feat(b07): 안 연 측점 횡단배수 미리 넣음 — 파이썬 짝 (260-4)
- 새 B07_DesignDetail_Engine_Cad_CulvertNote = TS culvertNoteEntities · stationNoLabel 짝(두 파일 머리 표시) - 횡단 장 만들 때 CAD 칸 없음 · 옛 글 없음 · 배수관 주인 측점 = 정본 pipe_points 로 미리 넣음 칸을 얹은 사본 - 지운 칸(빈 엔티티) · 옛 글 · 설계 없음 · 이웃 측점 = 그대로 - 시험 test_260_note_cad 거울 9쌍(같은 입력 → 같은 엔티티) · 정본 읽기 · B07 그림 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TNMXVCZXMpHpVvqmFkeChL
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/* PLAN 260 단위검증 헬퍼 — [상세] 표 위 CAD 칸 · 옛 글 옮김 · 횡단배수 미리 넣음.
|
||||
* `B06_Section_UI_Cross_Detail_Cad.ts` 를 그 자리에서 트랜스파일해 Node 로 돌린다(CAD · 세션 · 서버 = 흉내).
|
||||
* 결과는 표준출력에 JSON 한 벌. 확장자 `.cjs` 까닭은 `helper_108_drag_box_hit.cjs` 머리와 같다.
|
||||
* 결과는 표준출력에 JSON 한 벌 · 인자 `mirror` = 표준입력 입력으로 거울 결과(260-4). 확장자 `.cjs` 까닭은 `helper_108_drag_box_hit.cjs` 머리와 같다.
|
||||
*/
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
@@ -108,4 +108,13 @@ const out = {
|
||||
cad.stationNoLabel(1439.97, 20),
|
||||
],
|
||||
};
|
||||
process.stdout.write(JSON.stringify(out));
|
||||
// 거울(260-4) — 시험이 표준입력으로 준 같은 입력 [{section, station, options, chainage, interval}]
|
||||
// → TS 엔티티 · 측점 이름(파이썬 짝 `_Engine_Cad_CulvertNote` 와 대조)
|
||||
if (process.argv[2] === "mirror") {
|
||||
const cases = JSON.parse(fs.readFileSync(0, "utf8"));
|
||||
const mirrored = cases.map((item) => ({
|
||||
entities: cad.culvertNoteEntities(item.section, item.station, item.options),
|
||||
station: cad.stationNoLabel(item.chainage, item.interval),
|
||||
}));
|
||||
process.stdout.write(JSON.stringify(mirrored));
|
||||
} else process.stdout.write(JSON.stringify(out));
|
||||
|
||||
@@ -294,3 +294,160 @@ def test_CAD_칸이_없으면_옛_글_줄_그대로():
|
||||
noted = _draw({"structure_note": "L형 측구\n집수정 보강"})
|
||||
labels = [e["shapeData"]["label"] for e in noted["entities"] if e["type"] == "Text"]
|
||||
assert labels == ["L형 측구", "집수정 보강"]
|
||||
|
||||
|
||||
# ── 260-4 안 연 측점 = B07 이 파이썬 짝으로 넣음 ─────────────────────────
|
||||
|
||||
from B06_Section.B06_Section_Engine_Culvert import _culvert_set, pipe_points_file # noqa: E402
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_CulvertNote import ( # noqa: E402
|
||||
culvert_note_entities,
|
||||
load_pipe_notes,
|
||||
station_no_label,
|
||||
with_culvert_note,
|
||||
)
|
||||
|
||||
_OPTIONS = {
|
||||
"pipe_diameter_mm": 1200,
|
||||
"inlet_type": "기슭막이",
|
||||
"inlet_revet_form": "돌쌓기(찰)",
|
||||
"inlet_revet_height_m": 2,
|
||||
"inlet_revet_length_m": 10,
|
||||
"outlet_type": "기슭막이",
|
||||
"outlet_revet_form": "돌쌓기(메)",
|
||||
"outlet_revet_height_m": 2,
|
||||
"outlet_revet_length_m": 10,
|
||||
"chute_add_1_structure": "돌붙임",
|
||||
"chute_add_1_bond": "찰붙임",
|
||||
"chute_add_1_area": 10,
|
||||
}
|
||||
_NO_CHUTE = {k: v for k, v in _OPTIONS.items() if not k.startswith("chute")}
|
||||
|
||||
|
||||
def _case(options: dict, pipe_at: float, chainage: float, design: dict | None, **spec) -> dict:
|
||||
culvert = {**_culvert_set(options), "chainage_m": pipe_at, **spec}
|
||||
return {
|
||||
"section": {"chainage_m": chainage, "culvert": culvert, "design": design},
|
||||
"station": station_no_label(chainage, 20.0),
|
||||
"options": options,
|
||||
"chainage": chainage,
|
||||
"interval": 20.0,
|
||||
}
|
||||
|
||||
|
||||
_MIRROR_CASES = [
|
||||
_case(_OPTIONS, 1433.0, 1433.0, {"pipe_length_m": 22}),
|
||||
_case(_NO_CHUTE, 1433.0, 1433.0, {}),
|
||||
_case(
|
||||
{
|
||||
**_NO_CHUTE,
|
||||
"inlet_type": "집수정",
|
||||
"inlet_structure": "L",
|
||||
"inlet_basin_height_m": 1.7,
|
||||
"inlet_basin_length_m": 2,
|
||||
},
|
||||
1433.0,
|
||||
1433.0,
|
||||
{"pipe_length_m": 9.5},
|
||||
),
|
||||
_case({**_NO_CHUTE, "chute_add_2_structure": "도수로", "chute_add_2_area": ""}, 80, 80, None),
|
||||
_case({**_OPTIONS, "pipe_diameter_mm": 800, "chute_add_1_area": 12.345}, 12.5, 12.5, {}),
|
||||
_case({**_OPTIONS, "inlet_revet_form": "콘크리트"}, 440.241, 440.0, {"pipe_length_m": 7}),
|
||||
_case(_OPTIONS, 1433.0, 1440.0, {"pipe_length_m": 22}),
|
||||
_case(_OPTIONS, 1433.0, 1433.0, {}, body="ford"),
|
||||
_case(_OPTIONS, 1433.0, 1433.0, {}, hidden_pipe=True),
|
||||
]
|
||||
|
||||
|
||||
def _without_id(entities: list[dict]) -> list[dict]:
|
||||
return [{k: v for k, v in entity.items() if k != "id"} for entity in entities]
|
||||
|
||||
|
||||
def test_거울_TS_와_파이썬_짝은_같은_입력에_같은_엔티티():
|
||||
proc = subprocess.run( # noqa: S603 — 고정 실행 파일
|
||||
["node", str(HERE / "helper_260_note_cad.cjs"), "mirror"],
|
||||
input=json.dumps(_MIRROR_CASES, ensure_ascii=False),
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
timeout=60,
|
||||
)
|
||||
assert proc.returncode == 0, proc.stderr
|
||||
mirrored = json.loads(proc.stdout)
|
||||
assert len(mirrored) == len(_MIRROR_CASES)
|
||||
for case, ts in zip(_MIRROR_CASES, mirrored, strict=True):
|
||||
section = case["section"]
|
||||
py = culvert_note_entities(
|
||||
section["chainage_m"],
|
||||
section["culvert"],
|
||||
section["design"],
|
||||
case["station"],
|
||||
case["options"],
|
||||
)
|
||||
assert _without_id(py) == _without_id(ts["entities"]), case["station"]
|
||||
assert station_no_label(case["chainage"], case["interval"]) == ts["station"]
|
||||
# 대조가 빈 목록끼리만이 아님 — 주인 측점 여섯은 그림이 있음 · 이웃 · 세월교 · 숨은 관 = 빈값
|
||||
assert [bool(item["entities"]) for item in mirrored] == [True] * 6 + [False] * 3
|
||||
labels = [e["shapeData"].get("label") for e in mirrored[4]["entities"]]
|
||||
assert "Φ800m/m" in labels and "관유출부보강(돌붙임,찰) 12.35m2" in labels
|
||||
|
||||
|
||||
def _write_pipes(root: Path, points: list[dict]) -> None:
|
||||
path = pipe_points_file(root)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps({"points": points}, ensure_ascii=False), encoding="utf-8")
|
||||
|
||||
|
||||
def test_정본_배수관만_읽음(tmp_path):
|
||||
_write_pipes(
|
||||
tmp_path,
|
||||
[
|
||||
{"chainage_m": 1433.0, "lonlat": [0, 0], "facility": "pipe", "options": _OPTIONS},
|
||||
{"chainage_m": 80.0, "lonlat": [0, 0], "facility": "pipe", "options": {}},
|
||||
{"chainage_m": 90.0, "lonlat": [0, 0], "facility": "ford_bridge", "options": {}},
|
||||
],
|
||||
)
|
||||
pipes = load_pipe_notes(tmp_path)
|
||||
assert [pipe[0] for pipe in pipes] == [1433.0] # 관경 안 고름 · 세월교 = 빼
|
||||
assert pipes[0][1]["chainage_m"] == 1433.0 and pipes[0][2] == _OPTIONS
|
||||
assert load_pipe_notes(tmp_path / "없음") == []
|
||||
|
||||
|
||||
def test_칸_없음_만_미리_넣음_지운_칸_옛_글_설계없음_이웃은_그대로(tmp_path):
|
||||
_write_pipes(
|
||||
tmp_path,
|
||||
[{"chainage_m": 1433.0, "lonlat": [0, 0], "facility": "pipe", "options": _OPTIONS}],
|
||||
)
|
||||
pipes = load_pipe_notes(tmp_path)
|
||||
design = {"pipe_length_m": 22, "paved": False}
|
||||
seeded = with_culvert_note(design, 1433.0, pipes, "NO.71+13")
|
||||
assert seeded is not design and "structure_note_cad" not in design # 저장 설계는 안 바뀜
|
||||
labels = [e["shapeData"].get("label") for e in seeded["structure_note_cad"]["entities"]]
|
||||
assert labels[2:] == [
|
||||
"배수관",
|
||||
"Φ1200m/m",
|
||||
"L=22.0m",
|
||||
"NO.71+13",
|
||||
"관보호공(유입) 기슭막이(찰,H=2.0) L=10.0m",
|
||||
"관보호공(유출) 기슭막이(메,H=2.0) L=10.0m",
|
||||
"관유출부보강(돌붙임,찰) 10m2",
|
||||
]
|
||||
cleared = {**design, "structure_note_cad": {"entities": []}}
|
||||
assert with_culvert_note(cleared, 1433.0, pipes, "x") is cleared
|
||||
old = {**design, "structure_note": "옛 글"}
|
||||
assert with_culvert_note(old, 1433.0, pipes, "x") is old
|
||||
assert with_culvert_note(None, 1433.0, pipes, "x") is None
|
||||
assert with_culvert_note(design, 1440.0, pipes, "x") is design
|
||||
|
||||
|
||||
def test_B07_은_안_연_측점에_미리_넣음을_표_위에_그림(tmp_path):
|
||||
_write_pipes(
|
||||
tmp_path,
|
||||
[{"chainage_m": 20.2, "lonlat": [0, 0], "facility": "pipe", "options": _OPTIONS}],
|
||||
)
|
||||
design = with_culvert_note({"pipe_length_m": 22}, 20.0, load_pipe_notes(tmp_path), "NO.1")
|
||||
drawn = _draw(design)
|
||||
labels = [e["shapeData"]["label"] for e in drawn["entities"] if e["type"] == "Text"]
|
||||
assert "NO.1" in labels and "관유출부보강(돌붙임,찰) 10m2" in labels
|
||||
plain_top = _table(_draw({"pipe_length_m": 22}))["shapeData"]["origin"]["y"]
|
||||
drop = plain_top - _table(drawn)["shapeData"]["origin"]["y"]
|
||||
assert drop == pytest.approx(cross_note_block_height(design)) and drop > 15
|
||||
|
||||
Reference in New Issue
Block a user