refactor(637-1): 예정노선 LAS 올리기 · 짓기 공용 모듈 common_util_lidar_upload — 이름 검사 · 좌표계 · 옮겨 놓기 · 받기 · 갈음 · 짓기(maps=False) · 뒤 일 · 진행 기록 · DB 넣기/고치기만(keep = 갈음 때 id · 확정 · 입력 연결 살림) · 예정노선은 그걸 부름(동작 · 응답 그대로) · 지킴 시험 test_637_lidar(요구 546)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -35,9 +35,9 @@ from pydantic import BaseModel
|
||||
|
||||
from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path
|
||||
from common_util import common_util_contour_tiles as contour_tiles
|
||||
from common_util import common_util_lidar_upload as lidar_upload
|
||||
from common_util.common_util_crs import lonlat_to_tm, tm_epsg_for_lon
|
||||
from common_util.common_util_http_cache import cached_file_response
|
||||
from common_util.common_util_json import atomic_write_json
|
||||
from common_util.common_util_storage import resolve_stored_project_path
|
||||
from config.config_db import get_db_pool
|
||||
from D03_PlanAnalysis import D03_PlanAnalysis_ForestRoad as forest_road
|
||||
@@ -118,7 +118,7 @@ def _query_xy(extra: str | None) -> list[tuple[float, float]]:
|
||||
raise HTTPException(status_code=400, detail="extra 꼴이 다릅니다.") from error
|
||||
|
||||
|
||||
IDLE = {"running": False, "percent": 100, "message": None, "error": None, "interrupted": False}
|
||||
IDLE = lidar_upload.IDLE
|
||||
|
||||
|
||||
def _job_path(root: Path, kind: str) -> Path:
|
||||
@@ -126,81 +126,22 @@ def _job_path(root: Path, kind: str) -> Path:
|
||||
|
||||
|
||||
def _note_job(path: Path | None, job: dict[str, Any]) -> None:
|
||||
"""진행 기록 — 서버가 다시 켜져도 「돌던 일」을 알게(실패해도 일은 계속)."""
|
||||
if path is None:
|
||||
return
|
||||
try:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
atomic_write_json(path, job)
|
||||
except OSError as error:
|
||||
logger.warning("D 진행 기록 실패: %s", error)
|
||||
lidar_upload.note_job(path, job)
|
||||
|
||||
|
||||
def _saved_job(root: Path, kind: str) -> dict[str, Any]:
|
||||
"""기억에 없는 일 — 기록이 「도는 중」이면 서버가 다시 켜져 끊김(percent < 100 ·
|
||||
message 없음)."""
|
||||
try:
|
||||
saved = json.loads(_job_path(root, kind).read_text(encoding="utf-8"))
|
||||
except (OSError, ValueError):
|
||||
return dict(IDLE)
|
||||
if not (isinstance(saved, dict) and saved.get("running")):
|
||||
return dict(IDLE)
|
||||
percent = min(99, int(saved.get("percent") or 0))
|
||||
return {**IDLE, "percent": percent, "interrupted": True}
|
||||
"""기억에 없는 일 — 기록이 「도는 중」이면 서버가 다시 켜져 끊김(공용 `saved_job`)."""
|
||||
return lidar_upload.saved_job(_job_path(root, kind))
|
||||
|
||||
|
||||
def _start_job(project_id: UUID, kind: str, work: Any, root: Path | None = None) -> dict[str, Any]:
|
||||
key = (str(project_id), kind)
|
||||
if (_JOBS.get(key) or {}).get("running"):
|
||||
raise HTTPException(status_code=409, detail="이미 돌고 있습니다.")
|
||||
job = {"running": True, "percent": 0, "message": "시작", "error": None, "interrupted": False}
|
||||
_JOBS[key] = job
|
||||
path = _job_path(root, kind) if root is not None else None
|
||||
_note_job(path, job)
|
||||
|
||||
def report(percent: int, message: str) -> None:
|
||||
job.update(percent=int(percent), message=message)
|
||||
_note_job(path, job)
|
||||
|
||||
async def run() -> None:
|
||||
try:
|
||||
await work(report)
|
||||
job.update(percent=100)
|
||||
except Exception as error: # noqa: BLE001 — 진행률에 까닭을 그대로 올림
|
||||
logger.exception("D %s 실패: project_id=%s", kind, project_id)
|
||||
job["error"] = str(getattr(error, "detail", None) or error)
|
||||
finally:
|
||||
job["running"] = False
|
||||
_note_job(path, job)
|
||||
|
||||
task = asyncio.create_task(run(), name=f"D {kind} {project_id}")
|
||||
_TASKS.add(task)
|
||||
task.add_done_callback(_TASKS.discard)
|
||||
return {"status": "accepted", **job}
|
||||
return lidar_upload.start_job(_JOBS, _TASKS, project_id, kind, work, path, "D")
|
||||
|
||||
|
||||
async def _save_models(project_id: UUID, models: list[dict[str, Any]], epsg: int) -> None:
|
||||
"""지형면 → `surface_models` 행(같은 파일 · 같은 지문이면 id 살림 · 다른 행 그대로)."""
|
||||
from B04_PreProcess.B04_PreProcess_Repository import _existing_models, _upsert_surface_model
|
||||
|
||||
async with get_db_pool().acquire() as connection:
|
||||
await connection.begin()
|
||||
try:
|
||||
existing = await _existing_models(connection, project_id)
|
||||
for model in models:
|
||||
await _upsert_surface_model(
|
||||
connection,
|
||||
existing,
|
||||
project_id=project_id,
|
||||
model=model,
|
||||
source_file_id=None,
|
||||
processed_cloud_id=None,
|
||||
crs_epsg=epsg,
|
||||
)
|
||||
await connection.commit()
|
||||
except Exception:
|
||||
await connection.rollback()
|
||||
raise
|
||||
await lidar_upload.save_models(project_id, models, epsg)
|
||||
|
||||
|
||||
@router.get("/{project_id}/plan/terrain/extent", response_model=None)
|
||||
|
||||
Reference in New Issue
Block a user