- common_util_spreadsheet_node.ts · common_util_spreadsheet.py · build:spreadsheet — [저장] 때 화면과 같은 TS 엔진을 Node 로 돌려 계산값 정본(브라우저 값 버림 · 못 풀면 503) - M02 새 종류 basis — resources/master_template/basis/<열 id>.json(구조물집계표 열마다) · 층 복사 · 뼈대 검사 · 옛 구조물 문서 산출근거 49 벌 옮김(migrate_basis) · 새 열도 같이 만듦 - 상세 산출근거 카드 = 새 스프레드시트(import() 로 늦게 받음) · basis 읽기 · 저장 - ⚠ 임시 [스프레드시트 시험] 단추 — 빈 통합문서(시트 셋) · 저장 자리 tmp/spreadsheet_trial.json · 1단계 검증 뒤 지움 - 시험 resources/tester/spreadsheet/ — 저장 · 서버 다리 · 크기 예산 · M02 만 늦게 불러옴 · 실무 구조도 xlsx 세 벌 수식 2,699 칸 엑셀 캐시값 대조(엔진 빈 몸이면 건너뜀) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0168FQuoV7vDh5nhnSowW5cp
95 lines
2.9 KiB
Python
95 lines
2.9 KiB
Python
"""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}
|