"""B07 [초기화](요구 298-2 · 300 C1 · C3) — 그 페이지 사용자 값 · 설정을 지움. POST /api/projects/{id}/design-drawings/reset 본문 없음 기준 = 저장본 없이 지금 B06 정본으로 그린 원본(떠 두지 않음 · 앞 단계 저장 = 저절로 새 기준). 지움 = 모든 저장본 확정 풂(`confirmed=False` · 도면번호 · json 남김 = [원본에서 다시 그리기] 꼴) · B07 이 확정으로 올린 측점 설계 → 잠정 · 설정 파일(축척 · 횡단 배치 · 표 배율 · 평면도 종류 · 표식). M02 층(도각 · 표지)은 안 건드림. 단계 = [저장]과 같음(`mark_stage` save). """ import asyncio import logging import shutil from pathlib import Path from typing import Any from uuid import UUID from fastapi import APIRouter from fastapi.responses import JSONResponse from B06_Section.B06_Section_Repository import merge_cross_section_design_by_round from B07_DesignDetail.B07_DesignDetail_Engine_Template import drawing_scales_path from B07_DesignDetail.B07_DesignDetail_Router import _confirmed_source, _designs_by_chainage from B07_DesignDetail.B07_DesignDetail_Router_Support import CROSS_LAYOUT_FILE from B07_DesignDetail.B07_DesignDetail_Router_Support_Io import ( _design_root, _read_manifest, _write_manifest, ) from B07_DesignDetail.B07_DesignDetail_Router_Support_Plan import _kinds_dir, _marks_path from common_util.common_util_workflow_state import mark_stage from config.config_db import get_db_pool logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/projects", tags=["B07 Design Detail"]) def reset_design_files(project_root: Path) -> dict[str, Any]: """저장본 풂 + 설정 파일 지움 → {released: 풀린 장 수, cleared: 지운 설정 이름}.""" manifest = _read_manifest(project_root) released = 0 for entry in manifest["drawings"].values(): if entry.get("confirmed"): entry["confirmed"] = False released += 1 if released: _write_manifest(project_root, manifest) cleared = [] for path in ( drawing_scales_path(project_root), _design_root(project_root) / CROSS_LAYOUT_FILE, _marks_path(project_root), ): if path.is_file(): path.unlink() cleared.append(path.name) if _kinds_dir(project_root).is_dir(): shutil.rmtree(_kinds_dir(project_root)) cleared.append(_kinds_dir(project_root).name) return {"released": released, "cleared": cleared} @router.post("/{project_id}/design-drawings/reset") async def reset_design_drawings(project_id: UUID) -> Any: try: route_id, project_root, _longitudinal_path, _bypass = await _confirmed_source(project_id) result = await asyncio.to_thread(reset_design_files, project_root) designs = await _designs_by_chainage(route_id) confirmed = [chainage for chainage, d in designs.items() if d.get("status") == "confirmed"] pool = get_db_pool() async with pool.acquire() as connection: await connection.begin() try: for chainage in confirmed: await merge_cross_section_design_by_round( connection, route_id=route_id, chainage=chainage, patch={"status": "provisional"}, ) async with connection.cursor() as cursor: await mark_stage(str(project_id), 4, "save", cursor) await connection.commit() except Exception: await connection.rollback() raise return { "status": "success", "project_id": str(project_id), **result, "provisional": len(confirmed), } except FileNotFoundError as exc: return JSONResponse(status_code=404, content={"status": "error", "message": str(exc)}) except PermissionError as exc: return JSONResponse(status_code=409, content={"status": "error", "message": str(exc)}) except Exception: logger.exception("B07 초기화 실패: project_id=%s", project_id) return JSONResponse( status_code=500, content={"status": "error", "message": "상세 설계를 초기화하지 못했습니다."}, )