B05 페이지 초안
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
"""B03 업로드 이후 WF1 백그라운드 분석·자동 확정 서비스."""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from uuid import UUID
|
||||
|
||||
import aiomysql
|
||||
|
||||
from B03_FileInput.B03_FileInput_Email import (
|
||||
send_analysis_completion_email,
|
||||
send_analysis_error_email,
|
||||
)
|
||||
from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path
|
||||
from common_util.common_util_storage import resolve_stored_project_path
|
||||
from common_util.common_util_surface_confirmation import surface_confirmation_defaults
|
||||
from common_util.common_util_workflow_state import fail_stage, start_stage
|
||||
from config.config_db import get_db_pool
|
||||
from config.config_system import (
|
||||
SEND_ANALYSIS_COMPLETION_EMAIL,
|
||||
SURFACE_MODEL_PRECOMPUTE,
|
||||
SURFACE_MODEL_SOURCE_FILTERS,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def _get_project_notification_info(
|
||||
connection: aiomysql.Connection,
|
||||
project_id: UUID,
|
||||
) -> dict[str, Any] | None:
|
||||
async with connection.cursor(aiomysql.DictCursor) as cursor:
|
||||
await cursor.execute(
|
||||
"""
|
||||
SELECT p.id, p.name AS project_name, u.email AS user_email, u.name AS user_name
|
||||
FROM projects p
|
||||
JOIN users u ON u.id = p.user_id
|
||||
WHERE p.id = %s AND p.deleted_at IS NULL AND u.deleted_at IS NULL
|
||||
""",
|
||||
(str(project_id),),
|
||||
)
|
||||
row = await cursor.fetchone()
|
||||
return dict(row) if row else None
|
||||
|
||||
|
||||
async def trigger_wf1_analysis_and_email(
|
||||
*,
|
||||
project_id: UUID,
|
||||
input_file_id: int,
|
||||
user_role: str,
|
||||
) -> None:
|
||||
"""WF1 분석·DB 저장 후 역할에 따라 자동 확정하고 이메일을 발송한다."""
|
||||
pool = get_db_pool()
|
||||
project_info: dict[str, Any] | None = None
|
||||
try:
|
||||
source_filters = list(SURFACE_MODEL_SOURCE_FILTERS)
|
||||
methods = list(SURFACE_MODEL_PRECOMPUTE)
|
||||
params = {
|
||||
"input_file_id": str(input_file_id),
|
||||
"source_filters": source_filters,
|
||||
"methods": methods,
|
||||
"force": False,
|
||||
}
|
||||
async with pool.acquire() as connection:
|
||||
async with connection.cursor() as cursor:
|
||||
await start_stage(cursor, str(project_id), 1, params)
|
||||
await connection.commit()
|
||||
stored_path = await get_project_storage_relative_path(connection, project_id)
|
||||
project_info = await _get_project_notification_info(connection, project_id)
|
||||
from B04_wf1_Surface.B04_wf1_Surface_Repository import get_input_file
|
||||
|
||||
input_file = await get_input_file(connection, project_id, input_file_id)
|
||||
|
||||
project_root = Path(resolve_stored_project_path(stored_path))
|
||||
las_path = project_root / Path(str(input_file["raw_file_path"]))
|
||||
if not las_path.is_file():
|
||||
raise FileNotFoundError("원본 LAS/LAZ 파일을 찾을 수 없습니다.")
|
||||
|
||||
from B04_wf1_Surface.B04_wf1_Surface_Engine import run_surface_analysis
|
||||
from B04_wf1_Surface.B04_wf1_Surface_Repository import save_surface_analysis_to_db
|
||||
from B04_wf1_Surface.B04_wf1_Surface_Router import write_surface_progress
|
||||
|
||||
write_surface_progress(project_root, 5, "analyzing", "WF1 분석을 시작합니다.")
|
||||
|
||||
def _on_progress(percent: int, stage: str, message: str) -> None:
|
||||
write_surface_progress(project_root, percent, stage, message)
|
||||
|
||||
analysis_result = await asyncio.to_thread(
|
||||
run_surface_analysis,
|
||||
project_root,
|
||||
las_path,
|
||||
source_filters=source_filters,
|
||||
methods=methods,
|
||||
force=False,
|
||||
on_progress=_on_progress,
|
||||
)
|
||||
|
||||
auto_confirmation_error: str | None = None
|
||||
auto_confirmed = False
|
||||
async with pool.acquire() as connection:
|
||||
await connection.begin()
|
||||
try:
|
||||
await save_surface_analysis_to_db(
|
||||
connection,
|
||||
project_id=project_id,
|
||||
input_file_id=input_file_id,
|
||||
analysis_result=analysis_result,
|
||||
source_filters=source_filters,
|
||||
)
|
||||
if user_role != "SYSTEM_ADMIN":
|
||||
from B04_wf1_Surface.B04_wf1_Surface_Service import (
|
||||
confirm_surface_selection,
|
||||
find_surface_model_for_selection,
|
||||
)
|
||||
|
||||
selection = surface_confirmation_defaults()
|
||||
try:
|
||||
model_id = await find_surface_model_for_selection(
|
||||
connection, project_id, selection
|
||||
)
|
||||
except LookupError as exc:
|
||||
auto_confirmation_error = str(exc)
|
||||
logger.error(
|
||||
"WF1 자동 확정 보류: project_id=%s reason=%s",
|
||||
project_id,
|
||||
auto_confirmation_error,
|
||||
)
|
||||
else:
|
||||
await confirm_surface_selection(connection, project_id, model_id, selection)
|
||||
auto_confirmed = True
|
||||
await connection.commit()
|
||||
except Exception:
|
||||
await connection.rollback()
|
||||
raise
|
||||
|
||||
if auto_confirmation_error:
|
||||
progress_stage = "awaiting_confirmation"
|
||||
progress_message = f"WF1 자동 확정 보류 — {auto_confirmation_error}"
|
||||
elif auto_confirmed:
|
||||
progress_stage = "completed"
|
||||
progress_message = "지표면 모델 자동 확정 완료 — 노선 설계로 이동하세요."
|
||||
else:
|
||||
progress_stage = "awaiting_confirmation"
|
||||
progress_message = "WF1 분석이 완료되었습니다. 사용할 모델을 확정하세요."
|
||||
write_surface_progress(project_root, 100, progress_stage, progress_message)
|
||||
|
||||
if SEND_ANALYSIS_COMPLETION_EMAIL and project_info and project_info.get("user_email"):
|
||||
await send_analysis_completion_email(
|
||||
project_id=project_id,
|
||||
project_name=str(project_info.get("project_name") or project_id),
|
||||
to_email=str(project_info["user_email"]),
|
||||
analysis_result=analysis_result,
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.exception("WF1 백그라운드 분석 실패: project_id=%s", project_id)
|
||||
async with pool.acquire() as connection, connection.cursor() as cursor:
|
||||
await fail_stage(cursor, str(project_id), 1, str(exc))
|
||||
await connection.commit()
|
||||
if project_info and project_info.get("user_email"):
|
||||
await send_analysis_error_email(
|
||||
project_id=project_id,
|
||||
project_name=str(project_info.get("project_name") or project_id),
|
||||
to_email=str(project_info["user_email"]),
|
||||
error_message=str(exc),
|
||||
)
|
||||
Reference in New Issue
Block a user