- 데이터 = 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
176 lines
8.9 KiB
Python
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
|