260705_2
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
"""B06 종횡단 생성 FastAPI 라우터."""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path
|
||||
from B05_wf2_Route.B05_wf2_Route_Repository import get_latest_route
|
||||
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Engine import run_section_generation
|
||||
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Engine_Section import SectionGenerationOptions
|
||||
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Repository import (
|
||||
confirm_sections_for_route,
|
||||
create_longitudinal_section,
|
||||
delete_sections_for_route,
|
||||
get_longitudinal_section,
|
||||
insert_cross_sections,
|
||||
)
|
||||
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Schema import (
|
||||
SectionConfirmResponse,
|
||||
SectionGenerateRequest,
|
||||
SectionGenerateResponse,
|
||||
SectionSummaryResponse,
|
||||
)
|
||||
from common_util.common_util_storage import resolve_stored_project_path
|
||||
from config.config_db import get_db_pool
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
router = APIRouter(prefix="/api/projects", tags=["B06 Profile Cross"])
|
||||
|
||||
|
||||
def _build_options(request: SectionGenerateRequest) -> SectionGenerationOptions:
|
||||
"""요청의 옵션(미지정은 config 기본값)으로 SectionGenerationOptions를 만든다."""
|
||||
defaults = SectionGenerationOptions()
|
||||
return SectionGenerationOptions(
|
||||
station_interval_m=request.station_interval_m or defaults.station_interval_m,
|
||||
cross_half_width_m=request.cross_half_width_m or defaults.cross_half_width_m,
|
||||
cross_sample_interval_m=request.cross_sample_interval_m or defaults.cross_sample_interval_m,
|
||||
long_sample_interval_m=request.long_sample_interval_m or defaults.long_sample_interval_m,
|
||||
include_endpoint=defaults.include_endpoint,
|
||||
)
|
||||
|
||||
|
||||
@router.post("/{project_id}/sections/generate", response_model=SectionGenerateResponse)
|
||||
async def generate_sections(
|
||||
project_id: UUID, request: SectionGenerateRequest
|
||||
) -> SectionGenerateResponse | JSONResponse:
|
||||
"""확정 경로에서 종횡단을 생성·저장하고 DB에 기록한다."""
|
||||
pool = get_db_pool()
|
||||
try:
|
||||
async with pool.acquire() as connection:
|
||||
stored_path = await get_project_storage_relative_path(connection, project_id)
|
||||
project_root = Path(resolve_stored_project_path(stored_path))
|
||||
|
||||
latest = await get_latest_route(connection, project_id)
|
||||
if not latest or latest["id"] != request.route_id:
|
||||
return JSONResponse(
|
||||
status_code=404,
|
||||
content={"status": "error", "message": "대상 경로를 찾을 수 없습니다."},
|
||||
)
|
||||
route_data_path = latest["route_data_path"]
|
||||
|
||||
options = _build_options(request)
|
||||
design = await asyncio.to_thread(
|
||||
run_section_generation,
|
||||
project_root,
|
||||
route_data_path,
|
||||
request.filter_key,
|
||||
request.method,
|
||||
request.smooth,
|
||||
options=options,
|
||||
crs=request.crs,
|
||||
)
|
||||
|
||||
await connection.begin()
|
||||
try:
|
||||
await delete_sections_for_route(connection, request.route_id)
|
||||
longitudinal_id = await create_longitudinal_section(
|
||||
connection,
|
||||
project_id=project_id,
|
||||
route_id=request.route_id,
|
||||
data=design["longitudinal"]["data"],
|
||||
longitudinal_file_path=design["longitudinal"]["file_path"],
|
||||
)
|
||||
await insert_cross_sections(
|
||||
connection,
|
||||
project_id=project_id,
|
||||
route_id=request.route_id,
|
||||
sections=design["cross_sections"],
|
||||
)
|
||||
await connection.commit()
|
||||
except Exception:
|
||||
await connection.rollback()
|
||||
raise
|
||||
|
||||
return SectionGenerateResponse(
|
||||
project_id=str(project_id),
|
||||
route_id=request.route_id,
|
||||
longitudinal_id=longitudinal_id,
|
||||
cross_section_count=len(design["cross_sections"]),
|
||||
length_m=design["longitudinal"]["data"]["length_m"],
|
||||
longitudinal_file_path=design["longitudinal"]["file_path"],
|
||||
)
|
||||
except LookupError as exc:
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": str(exc)})
|
||||
except FileNotFoundError as exc:
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": str(exc)})
|
||||
except (OSError, ValueError) as exc:
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": str(exc)})
|
||||
except Exception:
|
||||
logger.exception("B06 종횡단 생성 실패: project_id=%s", project_id)
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
content={"status": "error", "message": "종횡단 생성 처리 중 오류가 발생했습니다."},
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{project_id}/sections/{route_id}", response_model=SectionSummaryResponse)
|
||||
async def get_sections(project_id: UUID, route_id: int) -> SectionSummaryResponse | JSONResponse:
|
||||
"""경로의 종단면 요약을 조회한다."""
|
||||
pool = get_db_pool()
|
||||
try:
|
||||
async with pool.acquire() as connection:
|
||||
longitudinal = await get_longitudinal_section(connection, project_id, route_id)
|
||||
return SectionSummaryResponse(
|
||||
project_id=str(project_id), route_id=route_id, longitudinal=longitudinal
|
||||
)
|
||||
except Exception:
|
||||
logger.exception("B06 종횡단 조회 실패: project_id=%s", project_id)
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
content={"status": "error", "message": "종횡단 조회 중 오류가 발생했습니다."},
|
||||
)
|
||||
|
||||
|
||||
@router.post("/{project_id}/sections/{route_id}/confirm", response_model=SectionConfirmResponse)
|
||||
async def confirm_sections(project_id: UUID, route_id: int) -> SectionConfirmResponse | JSONResponse:
|
||||
"""경로의 종횡단면을 확정(CONFIRMED)한다."""
|
||||
pool = get_db_pool()
|
||||
try:
|
||||
async with pool.acquire() as connection:
|
||||
existing = await get_longitudinal_section(connection, project_id, route_id)
|
||||
if not existing:
|
||||
return JSONResponse(
|
||||
status_code=404,
|
||||
content={"status": "error", "message": "확정할 종횡단이 없습니다."},
|
||||
)
|
||||
await connection.begin()
|
||||
try:
|
||||
await confirm_sections_for_route(connection, route_id)
|
||||
await connection.commit()
|
||||
except Exception:
|
||||
await connection.rollback()
|
||||
raise
|
||||
return SectionConfirmResponse(project_id=str(project_id), route_id=route_id)
|
||||
except Exception:
|
||||
logger.exception("B06 종횡단 확정 실패: project_id=%s", project_id)
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
content={"status": "error", "message": "종횡단 확정 처리 중 오류가 발생했습니다."},
|
||||
)
|
||||
Reference in New Issue
Block a user