"""B07 계획평면도 종류 · 대피소 자리 라우터 (PLAN 60장). 종류 = 정보 조각 고르기 목록. 더하기 · 지우기는 파일 하나씩(`plan_kinds/{id}.json`)이다. 대피소 · 사토장 자리는 구조물 정본 밖에서 사용자가 더 정한 자리(`plan_marks.json`)다. """ import asyncio import logging from typing import Any from uuid import UUID from fastapi import APIRouter from fastapi.responses import JSONResponse from pydantic import BaseModel, Field from B07_DesignDetail.B07_DesignDetail_Engine_Cad_PlanKinds import PIECES from B07_DesignDetail.B07_DesignDetail_Router_Frame import _project_root from B07_DesignDetail.B07_DesignDetail_Router_Support_Plan import ( add_kind, delete_kind, list_kinds, read_marks, write_marks, ) logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/projects", tags=["B07 Design Detail"]) class PlanKindRequest(BaseModel): name: str pieces: list[str] = Field(default_factory=list) class PlanMark(BaseModel): chainage_m: float = Field(ge=0) refuge: bool = False spoil: bool = False class PlanMarksRequest(BaseModel): marks: list[PlanMark] = Field(default_factory=list) def _state(root: Any) -> dict[str, Any]: return { "status": "success", "pieces": [{"id": piece, "name": name} for piece, name, _locked in PIECES], "kinds": list_kinds(root), "marks": read_marks(root), } def _error(status: int, exc: Exception) -> JSONResponse: return JSONResponse(status_code=status, content={"status": "error", "message": str(exc)}) @router.get("/{project_id}/plan-kinds") async def get_plan_kinds(project_id: UUID) -> Any: """고를 수 있는 조각 · 지금 종류 · 대피소 자리.""" root = await _project_root(project_id) return await asyncio.to_thread(_state, root) @router.post("/{project_id}/plan-kinds") async def post_plan_kind(project_id: UUID, request: PlanKindRequest) -> Any: """종류 더하기 — 조각을 골라 이름을 붙인다.""" try: root = await _project_root(project_id) await asyncio.to_thread(add_kind, root, request.name, request.pieces) return await asyncio.to_thread(_state, root) except ValueError as exc: return _error(400, exc) @router.delete("/{project_id}/plan-kinds/{kind_id}") async def delete_plan_kind(project_id: UUID, kind_id: str) -> Any: """종류 지우기 — 기본 넷도 지울 수 있다.""" try: root = await _project_root(project_id) await asyncio.to_thread(delete_kind, root, kind_id) return await asyncio.to_thread(_state, root) except ValueError as exc: return _error(400, exc) except FileNotFoundError as exc: return _error(404, exc) @router.put("/{project_id}/plan-marks") async def put_plan_marks(project_id: UUID, request: PlanMarksRequest) -> Any: """사용자가 정한 대피소 · 사토장 자리를 통째로 갈아 쓴다.""" try: root = await _project_root(project_id) marks = [mark.model_dump() for mark in request.marks] await asyncio.to_thread(write_marks, root, marks) return await asyncio.to_thread(_state, root) except ValueError as exc: return _error(400, exc)