"""M02 마스터 템플릿 API — 시스템 층(계약 `6_계약.md` 서버 길 첫 묶음). ⚠ 권한은 등록하는 쪽(`main.py`)이 `system_admin_only` 로 붙임. """ from __future__ import annotations import json from pathlib import Path from typing import Any from fastapi import APIRouter, HTTPException from pydantic import BaseModel from common_util import common_util_spreadsheet as spreadsheet from common_util.common_util_json import atomic_write_json from M02_MasterTemplete import M02_MasterTemplete_Store as store router = APIRouter(prefix="/api/m02", tags=["M02 MasterTemplete"]) class SaveBody(BaseModel): 판: str = "" 문서: Any def _call(fn, *args): try: return fn(*args) except store.StoreError as e: raise HTTPException(status_code=e.status, detail=e.detail) from e @router.get("/templates") def get_templates() -> list[dict]: return _call(store.list_all) @router.get("/structures/numbers") def get_structure_numbers() -> dict: return _call(store.structure_numbers) @router.post("/structures/{column}") def post_structure(column: str) -> dict: """구조물 도면 새로 — 열 id 하나 · 도번 자동 · A1 도각 + 빈 산출근거 표 · 이미 있으면 409.""" return _call(store.create_structure, column) @router.get("/templates/{kind}/{name}") def get_template(kind: str, name: str) -> dict: return _call(store.read, kind, name) @router.put("/templates/{kind}/{name}") def put_template(kind: str, name: str, body: SaveBody) -> dict: return _call(store.write, kind, name, body.판, body.문서) @router.delete("/templates/{kind}/{name}") def delete_template(kind: str, name: str, 판: str | None = None) -> dict: _call(store.delete, kind, name, 판) return {"ok": True} # ── ⚠ 임시(스프레드시트 시험 단추 · 사용자 지시 · 1단계 검증 뒤 지움) ────────── # 저장 자리 = `tmp/spreadsheet_trial.json`(git 밖) · 없으면 시트 셋 빈 통합문서. TRIAL_PATH = Path(__file__).resolve().parent.parent / "tmp" / "spreadsheet_trial.json" def _trial_doc() -> dict[str, Any]: sheets = [{"id": f"s{i}", "이름": f"시트{i}", "칸": {}} for i in (1, 2, 3)] return {"종류": "통합문서", "판": 1, "열": "시험", "서식": [{}], "시트": sheets, "활성": "s1"} @router.get("/spreadsheet-trial") def get_spreadsheet_trial() -> dict: if not TRIAL_PATH.is_file(): return {"문서": _trial_doc()} return {"문서": json.loads(TRIAL_PATH.read_text(encoding="utf-8"))} class TrialBody(BaseModel): 문서: dict[str, Any] @router.put("/spreadsheet-trial") def put_spreadsheet_trial(body: TrialBody) -> dict: try: doc = spreadsheet.with_server_values(body.문서) except spreadsheet.RecalcError as e: raise HTTPException(status_code=503, detail=str(e)) from e atomic_write_json(TRIAL_PATH, doc) return {"문서": doc}