fix(B05·B06): 노선 [확인] 이월은 사용자 입력만 · 추천 균형 종단 1차 종단 · 확정 노선 = 새 초기값(74-3·74-4·74-6)

- [확인] 체인 — 옛 설계 통째 이월 대신 사용자 입력 칸(CARRY_INPUT_KEYS)만 누가거리로 얹고 전 측점을 새 계획고로 다시 셈 · 새 노선에 없는 누가거리 행 안 만듦
- 「종단 반영」 켬으로 [적용]한 추천 그대로 [확인] = 서버가 같은 코드(route_profile)로 균형 종단을 다시 셈해 base_pvi 로 · 요청엔 켬/끔 표시만
- [확인] 성공 뒤 initial_snapshot 다시 뜸(save_initial_snapshot 재사용)
- [확인] 안내 글(title) · 「종단 반영」 체크박스를 갈래 알약 줄 오른쪽 토글 단추로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYJJRCfHfdCGNhmXTjQKV7
This commit is contained in:
2026-10-07 18:32:33 +09:00
co-authored by Claude Opus 5.5
parent 93869d3314
commit 98a15432b8
10 changed files with 346 additions and 49 deletions
+39 -10
View File
@@ -15,6 +15,7 @@ WF1(지표면 분석·자동 확정)이 끝나면 사용자가 화면에 없어
의존성이 없는 순수 함수 시그니처라 서버 내부 호출이 가능하다.
"""
import asyncio
import logging
import time
from pathlib import Path
@@ -393,6 +394,7 @@ async def run_redesign_chain(
project_id: UUID,
surface_model_id: int,
selection: dict[str, Any],
profile_target: tuple[list[float], list[float]] | None = None,
) -> str | None:
"""B04 재확정 후 — **사용자 입력을 유지한 채** 새 지표면 기준으로 B05·B06 재계산·저장.
@@ -406,6 +408,9 @@ async def run_redesign_chain(
측점은 확정 시 기본값으로 채워진다.
경로가 아예 없으면 신규 자동 체인(계획노선 정본 기본값)으로 되돌아간다.
`profile_target`(누가거리 · 계획고)이 오면 1차 종단을 그 계획고로 세운다 — 노선 추천
「종단 반영」 켬으로 [적용]한 노선의 균형 종단(PLAN 74-4). 없으면 계획고 = 지반고.
"""
from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path
from B05_Profile.B05_Profile_Repository import get_latest_route
@@ -416,7 +421,8 @@ async def run_redesign_chain(
get_longitudinal_section,
)
from B06_Section.B06_Section_Repository_Bulk import merge_cross_section_designs
from B06_Section.B06_Section_Router_Confirm import confirm_sections
from B06_Section.B06_Section_Router_Confirm import _rowless_station_chainages, confirm_sections
from B06_Section.B06_Section_Router_Design import CARRY_INPUT_KEYS, compute_default_designs
from B06_Section.B06_Section_Schema import SectionConfirmRequest
from common_util.common_util_initial_snapshot import (
clear_designing,
@@ -557,14 +563,40 @@ async def run_redesign_chain(
marks.append(("배수·관·측점 재생성", time.perf_counter()))
# 5) 이제 측점이 새 노선 기준으로 다 섰다 — 옛 사용자 설계를 **누가거리로** 얹는다.
# 묶어 쓰므로 행 수와 무관하게 왕복 두 번이다(`merge_cross_section_designs`).
if profile_target is not None:
from B05_Profile.B05_Profile_Router_RouteInitial import apply_profile_target
await apply_profile_target(project_id, new_route_id, profile_target)
marks.append(("균형 종단 반영", time.perf_counter()))
old_options = ((old_longitudinal or {}).get("data") or {}).get("options") or {}
standard = old_options.get("standard_cross_section") or None
# 5) 이제 측점이 새 노선 기준으로 다 섰다 — 옛 사용자 **입력**만 누가거리로 얹고 전 측점을
# 새 계획고로 다시 셈한다(PLAN 74-3). 결과 칸(계획고 · 면적 · design_line …)을 통째
# 옮기면 옛 노선 단면이 새 노선에 남아 누가토량이 두 배가 됐다(74-1). 새 노선에 없는
# 누가거리는 행을 만들지 않는다.
try:
entries = [
(float(record["chainage_m"]), record["design"])
inputs = {
round(float(record["chainage_m"]), 3): {
key: record["design"][key]
for key in CARRY_INPUT_KEYS
if record["design"].get(key) is not None
}
for record in old_designs
if isinstance(record.get("design"), dict)
]
}
async with pool.acquire() as connection:
new_longitudinal = await get_longitudinal_section(
connection, project_id, new_route_id
)
long_path = str((new_longitudinal or {}).get("longitudinal_file_path") or "")
chainages = await asyncio.to_thread(
_rowless_station_chainages, project_root, long_path, []
)
entries = await asyncio.to_thread(
compute_default_designs, project_root, long_path, chainages, standard, inputs
)
async with pool.acquire() as connection:
await connection.begin()
try:
@@ -587,11 +619,8 @@ async def run_redesign_chain(
marks.append(("설계 이월", time.perf_counter()))
old_options = ((old_longitudinal or {}).get("data") or {}).get("options") or {}
section_request = (
SectionConfirmRequest(standard_cross_section=old_options["standard_cross_section"])
if old_options.get("standard_cross_section")
else None
SectionConfirmRequest(standard_cross_section=standard) if standard else None
)
sections_result = await confirm_sections(
project_id, new_route_id, section_request, mark_stage_complete=False