refactor(B06): 재생성 옵션 도우미 = B06_Section_Router_Regenerate_Options.py 로 나눔(698 → 640 · 139-6)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F3mdb91125mPoC6tpsQx4P
This commit is contained in:
2026-10-09 15:20:31 +09:00
co-authored by Claude Opus 5.5
parent 6772597e0b
commit 8e1d33eaeb
2 changed files with 76 additions and 62 deletions
+4 -62
View File
@@ -12,7 +12,6 @@ from fastapi import APIRouter, Depends
from fastapi.responses import JSONResponse
from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path
from B05_Profile.B05_Profile_Engine_Grade import GradeDesignOptions, resolve_grade_options
from B05_Profile.B05_Profile_Engine_Grade_Profile import rebuild_alignment_profile
from B05_Profile.B05_Profile_Engine_Sections import (
prune_stale_cross_files,
@@ -59,6 +58,10 @@ from B06_Section.B06_Section_Router_Design import (
from B06_Section.B06_Section_Router_Design import (
stored_standard_cross_section as _stored_standard_cross_section,
)
from B06_Section.B06_Section_Router_Regenerate_Options import ( # noqa: F401 — 시험이 여기서 씀
_regeneration_grade_options,
_regeneration_options,
)
from B06_Section.B06_Section_Schema import (
CompanyStandardListResponse,
CompanyStandardProject,
@@ -371,67 +374,6 @@ async def get_section_detail(
)
def _regeneration_grade_options(stage_params: dict[str, Any] | None) -> GradeDesignOptions | None:
"""재생성 시 종단 계획선 기준을 확정 당시 저장 파라미터(stage 2)로 재구성한다.
grade_options 없이 재생성하면 종단 계획선(profile_alignment)이 통째로 소실돼 계획
횡단도선·유토곡선·테이블이 사라진다(2026-08-06 사용자 보고 — 옛 재계산 버튼 사고의
원인). B04 파일입력→B06 자동 계산 파이프라인이 쓰는 같은 엔진 경로를 재활용한다.
"""
if not stage_params:
return None
options = stage_params.get("options") or {}
grade_class = options.get("grade_class")
if not grade_class:
return None
stored = {
key: stage_params.get(key)
for key in (
"max_grade_pct",
"min_vertical_radius_m",
"min_tangent_length_m",
"balance_segment_length_m",
"start_elevation_offset_m",
"end_elevation_offset_m",
# 횡단배수 최소고 강제 스위치도 확정 당시 값을 따라야 한다 — 빠뜨리면 재생성이
# 기본(해제)으로 돌아가 켜 둔 계획선이 내려앉는다(2026-09-01).
"enforce_pipe_clearance",
)
if stage_params.get(key) is not None
}
# 설계속도는 B05가 저장해 둔 값을 그대로 따른다 — 없으면 종류별 기본(20).
speed = options.get("design_speed_kph")
return resolve_grade_options(
str(grade_class),
terrain_type=str(options.get("terrain_type") or "normal"),
paved=bool(options.get("paved", False)),
design_speed_kph=int(speed) if speed is not None else None,
stored=stored,
)
def _regeneration_options(
stored_options: dict[str, Any] | None,
stage_params: dict[str, Any] | None,
cross_half_width_m: float,
) -> SectionGenerationOptions:
"""DB 저장 옵션(단일 소스) → stage 2 params → config 순으로 유지하고 반폭만 교체한다."""
defaults = SectionGenerationOptions()
stored = stored_options or {}
params = stage_params or {}
def pick(key: str, default: float) -> float:
return stored.get(key) or params.get(key) or default
return SectionGenerationOptions(
station_interval_m=pick("station_interval_m", defaults.station_interval_m),
cross_half_width_m=cross_half_width_m,
cross_sample_interval_m=pick("cross_sample_interval_m", defaults.cross_sample_interval_m),
long_sample_interval_m=pick("long_sample_interval_m", defaults.long_sample_interval_m),
include_endpoint=defaults.include_endpoint,
)
@router.post("/{project_id}/sections/{route_id}/regenerate", response_model=SectionDetailResponse)
async def regenerate_sections(
project_id: UUID, route_id: int, request: SectionRegenerateRequest
@@ -0,0 +1,72 @@
"""B06 재생성 옵션 — 저장 옵션 · stage 2 파라미터 → 종단 계획선 기준 · 측점 생성 옵션.
`B06_Section_Router.py` 에서 700줄 제한으로 분리(139-6). 라우터가 그대로 다시 씀.
"""
from __future__ import annotations
from typing import Any
from B05_Profile.B05_Profile_Engine_Grade import GradeDesignOptions, resolve_grade_options
from B05_Profile.B05_Profile_Engine_Sections_Core import SectionGenerationOptions
def _regeneration_grade_options(stage_params: dict[str, Any] | None) -> GradeDesignOptions | None:
"""재생성 시 종단 계획선 기준을 확정 당시 저장 파라미터(stage 2)로 재구성한다.
grade_options 없이 재생성하면 종단 계획선(profile_alignment)이 통째로 소실돼 계획
횡단도선·유토곡선·테이블이 사라진다(2026-08-06 사용자 보고 — 옛 재계산 버튼 사고의
원인). B04 파일입력→B06 자동 계산 파이프라인이 쓰는 같은 엔진 경로를 재활용한다.
"""
if not stage_params:
return None
options = stage_params.get("options") or {}
grade_class = options.get("grade_class")
if not grade_class:
return None
stored = {
key: stage_params.get(key)
for key in (
"max_grade_pct",
"min_vertical_radius_m",
"min_tangent_length_m",
"balance_segment_length_m",
"start_elevation_offset_m",
"end_elevation_offset_m",
# 횡단배수 최소고 강제 스위치도 확정 당시 값을 따라야 한다 — 빠뜨리면 재생성이
# 기본(해제)으로 돌아가 켜 둔 계획선이 내려앉는다(2026-09-01).
"enforce_pipe_clearance",
)
if stage_params.get(key) is not None
}
# 설계속도는 B05가 저장해 둔 값을 그대로 따른다 — 없으면 종류별 기본(20).
speed = options.get("design_speed_kph")
return resolve_grade_options(
str(grade_class),
terrain_type=str(options.get("terrain_type") or "normal"),
paved=bool(options.get("paved", False)),
design_speed_kph=int(speed) if speed is not None else None,
stored=stored,
)
def _regeneration_options(
stored_options: dict[str, Any] | None,
stage_params: dict[str, Any] | None,
cross_half_width_m: float,
) -> SectionGenerationOptions:
"""DB 저장 옵션(단일 소스) → stage 2 params → config 순으로 유지하고 반폭만 교체한다."""
defaults = SectionGenerationOptions()
stored = stored_options or {}
params = stage_params or {}
def pick(key: str, default: float) -> float:
return stored.get(key) or params.get(key) or default
return SectionGenerationOptions(
station_interval_m=pick("station_interval_m", defaults.station_interval_m),
cross_half_width_m=cross_half_width_m,
cross_sample_interval_m=pick("cross_sample_interval_m", defaults.cross_sample_interval_m),
long_sample_interval_m=pick("long_sample_interval_m", defaults.long_sample_interval_m),
include_endpoint=defaults.include_endpoint,
)