Files
Aislo/resources/tester/test_250_1_b08_milestone_roads_width.py
T
eomsangdonandClaude Opus 5.5 7fdbcc4794 feat(B08): 거리이정표 여러 기준점 — bases[] · 경로 to · 옛 base 옮김 · 사슬 셈(254-1)
- 데이터 = bases[](id · to = 시점 또는 다른 기준점 · linked) · 경로 via_base 대신 to
- 옛 꼴(base · via_base) = 읽을 때 · 받을 때 새 꼴로 옮김(migrate 짝 Py · TS · 저장 = 새 꼴)
- 행 합 = 거래처 구간 + 기준점 사슬 구간들(방문 집합으로 돌기 끊음 · 안 이은 기준점에서 멈춤)
- 도로 구분 = 시점에 닿는 기준점 구간 1차 · 그 밖 기준점 구간 · 거래처 → 기준점 2차
- 화면 = 기준점 여럿 등록 · 삭제(이은 상자 안 이음) · 연결 놓기 = 기준점 id · 길찾기 · 표지 사슬
- 시험 = test_254_1 새로 · 244 · 250 기대값 새 꼴로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STqEqztUthVFBmEP18wBSz
2026-10-11 13:58:00 +09:00

176 lines
8.9 KiB
Python

"""PLAN 250(사용자 149 · 150 · 152) — 거리이정표 도로 구분 = 선 자리 · 모달 정리 · 폭 · 지도 높이."""
import json
import re
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
from B08_Quantity.B08_Quantity_Milestone_Calc import ( # noqa: E402
compute_milestone,
migrate_milestone,
road_of,
)
IMPORTS = re.compile(r"^import[^;]*;\n", re.MULTILINE)
def _read(rel: str) -> str:
return (ROOT / rel).read_text(encoding="utf-8")
def _ts(expr: str):
src = IMPORTS.sub("", _read("B08_Quantity/B08_Quantity_Milestone_Calc.ts"))
out = subprocess.run(
["node", "--experimental-strip-types", "--no-warnings", "--input-type=module-typescript",
"-e", src + f"\nconsole.log(JSON.stringify({expr}));"],
capture_output=True, text=True, encoding="utf-8", timeout=60,
) # fmt: skip
assert out.returncode == 0, out.stderr
return json.loads(out.stdout)
def test_도로_구분_선_자리_짝():
cases = [
(r, k)
for r in (None, "2차포장", "1차포장", "포장", "비포장")
for k in ("via", "base", "direct", "mid")
]
py = [road_of(r, k) for r, k in cases]
assert py == _ts(f"{json.dumps(cases)}.map(([r, k]) => roadOf(r, k))")
assert road_of(None, "via") == "2차포장" and road_of("포장", "base") == "1차포장"
assert road_of("2차포장", "direct") == "포장" and road_of("비포장", "via") == "비포장"
def test_기준점_생기면_2차_1차_없으면_포장():
leg = lambda road, km: {"road": road, "km": km} # noqa: E731
route = {"id": "r", "name": "거창군청", "address": "", "via_base": True, "dir": None,
"legs": [leg(None, 23.8)], "rows": [{"item": "장비", "dest": "mid", "note": ""}]} # fmt: skip
data = {"site": {"mid_station_m": 800, "mid_road": "비포장"},
"base": {"enabled": True, "name": "병곡리", "address": "", "dir": None,
"legs": [leg(None, 0.6)]},
"routes": [route]} # fmt: skip
(a,) = compute_milestone(data, None)
assert (a["s2"], a["s1"], a["pv"], a["un"], a["total"]) == (23.8, 0.6, 0, 0.8, 25.2)
data["base"]["enabled"] = False
(b,) = compute_milestone(data, None)
assert (b["s2"], b["s1"], b["pv"], b["un"], b["total"]) == (0, 0, 23.8, 0.8, 24.6)
def test_모달_상자_하나_항목_하나_수정_글():
modal = _read("B08_Quantity/B08_Quantity_UI_Milestone_Modal.ts")
leg = _read("B08_Quantity/B08_Quantity_UI_Milestone_LegModal.ts")
assert '"+ 줄"' not in modal and "same.rows" not in modal
assert 'label: "고침"' not in modal + leg and modal.count('"수정"') >= 2
assert 'item.root.style.visibility = dest.root.style.visibility = k === "base"' in modal
def test_폭_전체_지도_높이_한_값():
css = _read("B08_Quantity/B08_Quantity_UI_Milestone.css")
block = css[css.index(".b08-milestone {") :]
assert "width: 100%;" in block[: block.index("}")]
assert ".b08-milestone > .ui-data-table .ui-data-table__table {\n width: 100%;" in css
assert css.count("height: 480px;") == 1 and ".b08-milestone__map {" in css
def test_153_지도_보기_유지_상자_색_끌어_옮김():
kmap = _read("B08_Quantity/B08_Quantity_UI_Milestone_KakaoMap.ts")
assert (
"let fitted = false;" in kmap and "map.panTo(" in kmap
) # 처음 한 번만 맞춤 · 그 뒤 줌 그대로
css = _read("B08_Quantity/B08_Quantity_UI_Milestone.css")
for kind, var in (
("is-site", "--color-accent"),
("is-base", "--color-warning"),
("is-route", "--color-success"),
):
block = css[css.index(f".b08-milestone__box.{kind} rect {{") :]
assert var in block[: block.index("}")], kind
ui = _read("B08_Quantity/B08_Quantity_UI_Milestone_Diagram.ts")
assert 'if (box.kind === "route" || box.kind === "base") dragBox(g, box, shift);' in ui
assert (
'label: "방위 배치"' in ui and "fresh.routes.forEach((r) => (r.pos = null));" in ui
) # 159 이름
def test_153_끌어_놓은_자리_레이아웃():
src = IMPORTS.sub("", _read("B08_Quantity/B08_Quantity_Milestone_Layout.ts"))
data = {"site": {"mid_station_m": None, "mid_road": "비포장"},
"base": {"enabled": True, "name": "기준", "address": "", "dir": "W", "legs": [], "pos": [500, -40]},
"routes": [{"id": "r", "name": "a", "address": "", "via_base": False, "dir": "N", "legs": [],
"rows": [], "pos": [-700, 300]}]} # fmt: skip
data = migrate_milestone(data) # 254 옛 꼴 → bases(옛 기준점 = b1)
expr = f"layoutMilestone({json.dumps(data)}, {{}}, 0.5).boxes.filter((b) => b.id === 'b1' || b.id === 'r').map((b) => [b.id, b.x, b.y])"
out = subprocess.run(
["node", "--experimental-strip-types", "--no-warnings", "--input-type=module-typescript",
"-e", src + f"\nconsole.log(JSON.stringify({expr}));"],
capture_output=True, text=True, encoding="utf-8", timeout=60,
) # fmt: skip
assert out.returncode == 0, out.stderr
assert sorted(json.loads(out.stdout)) == [["b1", 500, -40], ["r", -700, 300]]
def test_154_연결_점_끌어_잇기():
ui = _read("B08_Quantity/B08_Quantity_UI_Milestone_Diagram.ts")
assert 'if (box.kind === "route") g.append(connectDot(box, shift));' in ui
assert "route.to = t;" in ui # 254 놓은 기준점 id · 공사지 = "site"
assert "route.legs = [{ road: null, km: null }];" in ui # 옛 구간 km 비움
assert "void findSegmentKm(projectId, route.id).then(toastKm);" in ui # 새 길찾기 한 번
assert "if (route.linked !== false && route.to === t) return;" in ui # 같은 연결 = 그대로
modal = _read("B08_Quantity/B08_Quantity_UI_Milestone_Modal.ts")
assert (
'createCheckbox("기준점 거침")' not in modal and "via_base: via.input.checked" not in modal
)
css = _read("B08_Quantity/B08_Quantity_UI_Milestone.css")
assert ".b08-milestone__connect {" in css and ".b08-milestone__box.is-drop-target rect {" in css
def test_155_요소_추가_안_이은_거래처_수기_입력():
leg = lambda road, km, auto=None: {"road": road, "km": km, "auto_km": auto} # noqa: E731
route = {"id": "r", "name": "a", "address": "", "via_base": True, "dir": None, "linked": False,
"legs": [leg(None, 5.0)], "rows": [{"item": "장비", "dest": "mid", "note": ""}]} # fmt: skip
data = {"site": {"mid_station_m": 700, "mid_road": "비포장"},
"base": {"enabled": True, "name": "b", "address": "", "dir": None, "legs": [leg(None, 0.6)]},
"routes": [route]} # fmt: skip
(a,) = compute_milestone(data, None) # 안 이음 = 구간 km 없음 · 중점 구간만
assert (a["s2"], a["s1"], a["un"], a["total"]) == (0, 0, 0.7, 0.7)
assert _ts(f"computeMilestone({json.dumps(data)}, null)") == [a]
route["linked"] = True
(b,) = compute_milestone(data, None)
assert (b["s2"], b["s1"], b["total"]) == (5.0, 0.6, 6.3)
ui = _read("B08_Quantity/B08_Quantity_UI_Milestone_Diagram.ts")
assert "route.linked = true;" in ui and 'label: "요소 추가"' in ui and "plus(box" not in ui
modal = _read("B08_Quantity/B08_Quantity_UI_Milestone_Modal.ts")
assert "linked: false," in modal and "export type PlaceTarget = { add: true } |" in modal
leg_ui = _read("B08_Quantity/B08_Quantity_UI_Milestone_LegModal.ts")
assert (
'createCheckbox("수기 입력' in leg_ui and "target.manual = manual.input.checked;" in leg_ui
)
state = _read("B08_Quantity/B08_Quantity_UI_Milestone_State.ts")
assert "if (owner?.manual) return" in state # 수기 입력 = 카카오 길찾기 안 함
layout = _read("B08_Quantity/B08_Quantity_Milestone_Layout.ts")
assert "if (!owner || owner.linked === false) return null;" in layout # 254 거래처 · 기준점
def test_157_도구_왼쪽_아래_경로_탭_지도_그림_변경_초기화():
css = _read("B08_Quantity/B08_Quantity_UI_Milestone.css")
tools = css[css.index(".b08-milestone__tools {") :]
assert "bottom: 8px;" in tools[: tools.index("}")] and "left: 8px;" in tools[: tools.index("}")]
ui = _read("B08_Quantity/B08_Quantity_UI_Milestone_Diagram.ts")
assert (
'label: "요소 추가",\n size: "sm"' not in ui
and 'label: "방위 배치",\n size: "sm"' not in ui
)
route = _read("B08_Quantity/B08_Quantity_UI_Milestone_Route.ts")
assert 'label: "그림 변경"' in route and 'label: "초기화"' in route
assert "그림 지우기" not in route and "자동 그림으로 되돌리기" not in route.split("*/", 1)[1]
assert (
"map.root.hidden = user;" in route and "editMap(projectId" in route
) # 그림 없으면 길찾기 지도
assert ".b08-milestone__route-head {" in css and "justify-content: space-between;" in css
state = _read("B08_Quantity/B08_Quantity_UI_Milestone_State.ts")
assert "export const hasUserImage" in state and "canRevertImage" not in state