fix(482-2 이관 막이): 옛 비정규 측점 이관이 「기타 구조물」을 만들지 않게 — 이름 빈 측점 건너뜀 · 낡은 종단이면 건너뜀 · 저장 잠금 안 · 이미 생긴 것은 알림만 · 측점 맞춤 실패 알림
- Migration: 이름 빈 측점 = 추가 측점 정본 소관 - Structures_Router: 이관 = 프로젝트 저장 잠금 안 · 종단 정본이 지금 심는 자리를 다 못 세웠으면 skipped · 응답 legacy_etc(옛 이관 「기타 구조물」) - B05 화면: legacy_etc 가 있으면 경고 알림(지우지 않음) - Router_Stations: 맞출 측점이 있는데 샘플링 조건 없음 = StationsUnsynced · 추가 측점 PUT 응답 stations_synced - 새 시험 test_280_3_migrate_guard(임시 폴더) · 옛 코드에서 다섯 실패 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -49,7 +49,7 @@ from common_util.common_util_route_follow import (
|
||||
route_outside,
|
||||
)
|
||||
from common_util.common_util_storage import resolve_stored_project_path
|
||||
from common_util.common_util_structure_station import plan_stations
|
||||
from common_util.common_util_structure_station import SAME_STATION_M, plan_stations
|
||||
from config.config_db import get_db_pool
|
||||
from M02_MasterTemplete.StructOptions.M02_SO_Options import TypesWithVisible, with_visible
|
||||
|
||||
@@ -266,9 +266,10 @@ class StructureMigrateRequest(BaseModel):
|
||||
|
||||
async def _saved_irregular_stations(
|
||||
project_id: UUID, root: str, route_id: int | None
|
||||
) -> list[dict[str, Any]]:
|
||||
"""저장 종단 정본의 비정규 측점 `{chainage_m, structure}` — 이관 원천. 화면 상세는 초안
|
||||
(옮긴 관 자리)이 섞일 수 있어 안 받음(452 · [저장] 없이 초안을 정본에 쓰지 않음)."""
|
||||
) -> tuple[list[dict[str, Any]], list[float]]:
|
||||
"""저장 종단 정본의 비정규 측점 `{chainage_m, structure}` — 이관 원천 · 서 있는 구조물 측점
|
||||
누가거리(비정규 · 이름 단 격자 · 낡음 판정 280-3). 화면 상세는 초안(옮긴 관 자리)이 섞일 수
|
||||
있어 안 받음(452 · [저장] 없이 초안을 정본에 쓰지 않음)."""
|
||||
from B06_Section.B06_Section_Repository import get_longitudinal_section
|
||||
from B06_Section.B06_Section_Router_Design import resolve_longitudinal_path
|
||||
|
||||
@@ -283,21 +284,37 @@ async def _saved_irregular_stations(
|
||||
else None
|
||||
)
|
||||
if not row or not row.get("longitudinal_file_path"):
|
||||
return []
|
||||
return [], []
|
||||
try:
|
||||
path = resolve_longitudinal_path(Path(root), str(row["longitudinal_file_path"]))
|
||||
stations = json.loads(path.read_text(encoding="utf-8")).get("stations") or []
|
||||
except (OSError, ValueError, AttributeError):
|
||||
return []
|
||||
return [
|
||||
return [], []
|
||||
stations = [station for station in stations if station.get("chainage_m") is not None]
|
||||
legacy = [
|
||||
{"chainage_m": station["chainage_m"], "structure": station.get("structure") or ""}
|
||||
for station in stations
|
||||
if station.get("kind") == "irregular" and station.get("chainage_m") is not None
|
||||
if station.get("kind") == "irregular"
|
||||
]
|
||||
standing = [
|
||||
float(station["chainage_m"])
|
||||
for station in stations
|
||||
if station.get("kind") == "irregular" or station.get("structure")
|
||||
]
|
||||
return legacy, standing
|
||||
|
||||
|
||||
@router.post("/{project_id}/route/structures/migrate", response_model=None)
|
||||
async def migrate_structures(project_id: UUID, payload: StructureMigrateRequest) -> JSONResponse:
|
||||
"""구 비정규 측점 이관 — [저장]과 같은 프로젝트 잠금 안(280-3 · 다른 탭 저장 도중 낡은 종단으로
|
||||
옮기지 않음 · 몸통 `_migrate_structures`)."""
|
||||
from common_util import common_util_save_guard as guard
|
||||
|
||||
async with guard.project_lock(str(project_id)):
|
||||
return await _migrate_structures(project_id, payload)
|
||||
|
||||
|
||||
async def _migrate_structures(project_id: UUID, payload: StructureMigrateRequest) -> JSONResponse:
|
||||
"""구 비정규 측점(자유 텍스트)을 구조물 정본으로 옮긴다. 멱등 — 이미 정본에 있는
|
||||
(타입, 위치)는 건너뛰고, 배관은 관 지점 정본 소관이라 옮기지 않는다
|
||||
(2026-08-17 컨테이너 병합 3단계, 매핑은 `B05_Profile_Structures_Migration` 정의)."""
|
||||
@@ -320,15 +337,38 @@ async def migrate_structures(project_id: UUID, payload: StructureMigrateRequest)
|
||||
plus_chainages(root),
|
||||
)
|
||||
]
|
||||
legacy = await _saved_irregular_stations(project_id, root, payload.route_id)
|
||||
plan = migrate_irregular_stations(legacy, planted)
|
||||
legacy, standing = await _saved_irregular_stations(project_id, root, payload.route_id)
|
||||
|
||||
# 표식 = "타입@위치". 원천(종단 정본의 비정규 측점)은 원복용으로 그대로 두고,
|
||||
# 옮긴 이력만 남긴다 — 그래야 사용자가 지운 구조물이 재진입 때 되살아나지 않는다
|
||||
# (2026-08-24 사용자: 초기 계산값은 원복용, 사용자 수정 1세트가 최종본).
|
||||
|
||||
def key_of(item: StructureInstance) -> str:
|
||||
return f"{item.type_id}@{round(item.anchor_m(), 3)}"
|
||||
|
||||
# 옛 이관이 만든 「기타 구조물」(이름 없는 측점 · 메모 없음) — 지우지 않고 알림만(280-3)
|
||||
legacy_etc = [
|
||||
round(item.anchor_m(), 3)
|
||||
for item in existing
|
||||
if item.type_id == "etc"
|
||||
and key_of(item) in migrated_before
|
||||
and str(item.options.get("name") or "기타 구조물") == "기타 구조물"
|
||||
and not item.memo
|
||||
]
|
||||
# 종단 정본이 지금 심는 자리를 다 못 세움 = 낡음(저장 도중 끊김 · 다른 탭 [저장] 도중) —
|
||||
# 낡은 측점을 「기타」로 옮기지 않음(280-3)
|
||||
if any(all(abs(at - s) >= SAME_STATION_M for s in standing) for at in planted):
|
||||
return JSONResponse(
|
||||
content={
|
||||
"status": "success",
|
||||
"migrated": 0,
|
||||
"revision": revision,
|
||||
"pipe_facilities": 0,
|
||||
"skipped": "stale_longitudinal",
|
||||
"legacy_etc": legacy_etc,
|
||||
}
|
||||
)
|
||||
plan = migrate_irregular_stations(legacy, planted)
|
||||
|
||||
occupied = {key_of(item) for item in existing}
|
||||
fresh = [
|
||||
item
|
||||
@@ -360,6 +400,7 @@ async def migrate_structures(project_id: UUID, payload: StructureMigrateRequest)
|
||||
"migrated": moved_pipes,
|
||||
"revision": revision,
|
||||
"pipe_facilities": moved_pipes,
|
||||
"legacy_etc": legacy_etc,
|
||||
}
|
||||
)
|
||||
new_revision = save_structures(
|
||||
@@ -381,6 +422,7 @@ async def migrate_structures(project_id: UUID, payload: StructureMigrateRequest)
|
||||
"migrated": len(fresh) + moved_pipes,
|
||||
"revision": new_revision,
|
||||
"pipe_facilities": moved_pipes,
|
||||
"legacy_etc": legacy_etc,
|
||||
}
|
||||
)
|
||||
except LookupError:
|
||||
|
||||
Reference in New Issue
Block a user