This commit is contained in:
2026-07-10 16:41:22 +09:00
parent bbda38a013
commit 50d75fcfcd
47 changed files with 6309 additions and 350 deletions
+95 -37
View File
@@ -3,8 +3,10 @@
import asyncio
import logging
from pathlib import Path
from typing import Any
from uuid import UUID
import aiomysql
from fastapi import APIRouter
from fastapi.responses import JSONResponse
@@ -30,6 +32,63 @@ logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/projects", tags=["B04 Surface Analysis"])
async def save_surface_analysis_to_db(
connection: aiomysql.Connection,
*,
project_id: UUID,
input_file_id: int,
analysis_result: dict[str, Any],
source_filters: list[str],
) -> list[int]:
"""WF1 분석 결과를 DB에 저장한다.
이 함수는 트랜잭션을 시작하거나 종료하지 않는다. 호출자는 같은 커넥션에서
begin/commit/rollback을 한 번만 수행해야 한다.
"""
input_file = await get_input_file(connection, project_id, input_file_id)
processed = analysis_result["processed"]
processed_cloud_id = await create_processed_point_cloud(
connection,
input_file_id=input_file_id,
project_id=project_id,
process_type="structured",
processed_file_path=processed["processed_file_path"],
converted_format=None,
converted_file_path=processed["converted_file_path"],
point_count=processed["point_count"],
bounds=processed["bounds"],
statistics=processed["statistics"],
classification_summary=None,
processing_params={"filters": source_filters},
)
surface_model_ids: list[int] = []
for model in analysis_result["models"]:
model_id = await create_surface_model(
connection,
project_id=project_id,
model_type=model["model_type"],
source_file_id=input_file_id,
processed_cloud_id=processed_cloud_id,
crs_epsg=input_file["crs_epsg"],
resolution_m=model["resolution_m"],
model_file_path=model["model_file_path"],
generation_params=model["generation_params"],
)
surface_model_ids.append(model_id)
for layer in model["layers"]:
await create_terrain_layer(
connection,
surface_model_id=model_id,
layer_name=layer["layer_name"],
geometry_type=layer["geometry_type"],
layer_file_path=layer["file_path"],
file_format=layer["file_format"],
file_size_mb=None,
statistics=None,
)
return surface_model_ids
@router.post("/{project_id}/surface/analyze", response_model=SurfaceAnalyzeResponse)
async def analyze_surface(
project_id: UUID, request: SurfaceAnalyzeRequest
@@ -67,46 +126,13 @@ async def analyze_surface(
# DB 기록 (트랜잭션)
await connection.begin()
try:
processed = result["processed"]
processed_cloud_id = await create_processed_point_cloud(
surface_model_ids = await save_surface_analysis_to_db(
connection,
input_file_id=request.input_file_id,
project_id=project_id,
process_type="structured",
processed_file_path=processed["processed_file_path"],
converted_format=None,
converted_file_path=processed["converted_file_path"],
point_count=processed["point_count"],
bounds=processed["bounds"],
statistics=processed["statistics"],
classification_summary=None,
processing_params={"filters": source_filters},
input_file_id=request.input_file_id,
analysis_result=result,
source_filters=source_filters,
)
surface_model_ids: list[int] = []
for model in result["models"]:
model_id = await create_surface_model(
connection,
project_id=project_id,
model_type=model["model_type"],
source_file_id=request.input_file_id,
processed_cloud_id=processed_cloud_id,
crs_epsg=input_file["crs_epsg"],
resolution_m=model["resolution_m"],
model_file_path=model["model_file_path"],
generation_params=model["generation_params"],
)
surface_model_ids.append(model_id)
for layer in model["layers"]:
await create_terrain_layer(
connection,
surface_model_id=model_id,
layer_name=layer["layer_name"],
geometry_type=layer["geometry_type"],
layer_file_path=layer["file_path"],
file_format=layer["file_format"],
file_size_mb=None,
statistics=None,
)
await connection.commit()
except Exception:
await connection.rollback()
@@ -147,3 +173,35 @@ async def get_surface_models(project_id: UUID) -> SurfaceModelListResponse | JSO
status_code=500,
content={"status": "error", "message": "모델 목록 조회 중 오류가 발생했습니다."},
)
@router.get("/{project_id}/surface/status")
async def get_wf1_analysis_status(project_id: UUID) -> dict:
"""WF1 분석 상태를 조회한다."""
pool = get_db_pool()
try:
async with pool.acquire() as connection:
async with connection.cursor(aiomysql.DictCursor) as cursor:
await cursor.execute(
"""
SELECT COUNT(*) as model_count
FROM surface_models
WHERE project_id = %s
""",
(str(project_id),),
)
row = await cursor.fetchone()
model_count = int(row["model_count"]) if row else 0
status = "completed" if model_count > 0 else "in_progress"
return {
"project_id": str(project_id),
"status": status,
"model_count": model_count,
}
except Exception:
logger.exception("WF1 분석 상태 조회 실패: project_id=%s", project_id)
return JSONResponse(
status_code=500,
content={"status": "error", "message": "분석 상태 조회 중 오류가 발생했습니다."},
)