Files
Aislo/M02_MasterTemplete/M02_MasterTemplete_Router_Drawing.py
T

109 lines
4.7 KiB
Python

"""M02 도면 양식 서버 길 — 외부 도각 파일 불러오기 · 자리표 키 목록 (PLAN 10-3).
양식을 읽고 쓰는 길은 `M02_MasterTemplete_Router.py`(시스템 층)·`_Router_Layers.py`(층)가 맡는다.
여기는 도면 양식 편집 화면만 쓰는 두 길 — B07 의 불러오기 엔진 · 표제란 값을 그대로 다시 쓴다.
"""
import asyncio
import logging
from uuid import UUID
import ezdxf
from fastapi import APIRouter, File, UploadFile
from fastapi.responses import JSONResponse
from B07_DesignDetail.B07_DesignDetail_Engine_Frame_Import import import_frame_file
from B07_DesignDetail.B07_DesignDetail_Engine_Template import (
frame_document,
validate_template_entities,
)
from B07_DesignDetail.B07_DesignDetail_Router import title_block_fields
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/m02", tags=["M02 MasterTemplete"])
# 도각 파일 상한 — A1 도각 한 장은 보통 1MB 아래다. 큰 도면 전체를 올리는 실수를 막는다.
_IMPORT_MAX_BYTES = 20 * 1024 * 1024
# 자리표 `{{키}}` 로 쓸 수 있는 키 — 도면을 그릴 때 채워지는 것만 둔다.
# 프로젝트 값은 B07 `title_block_fields` 가 · 도면 값은 도면을 그리는 엔진이 채운다.
DRAWING_FIELDS: tuple[tuple[str, str, str], ...] = (
("공사명", "프로젝트", "프로젝트 이름"),
("위치", "프로젝트", "사업 위치"),
("시행청", "프로젝트", "발주 기관"),
("연도기번", "프로젝트", "프로젝트 번호"),
("사업량", "프로젝트", "사업량"),
("설계일자", "프로젝트", "설계 일자"),
("용역회사", "프로젝트", "회사 이름"),
("설계자", "프로젝트", "설계자 이름"),
("과업책임자", "프로젝트", "과업책임자 이름"),
("분야별책임자", "프로젝트", "분야별책임자 이름"),
("회사로고", "프로젝트", "회사 로고 그림"),
("설계자서명", "프로젝트", "설계자 서명 그림"),
("과업책임자서명", "프로젝트", "과업책임자 서명 그림"),
("분야별책임자서명", "프로젝트", "분야별책임자 서명 그림"),
("도면명", "도면", "도면 이름"),
("도면번호", "도면", "도면 목록 순번"),
("축척_A1", "도면", "A1 축척 분모"),
("축척_A3", "도면", "A3 축척 분모"),
)
@router.post("/drawing-import")
async def import_drawing_file(file: UploadFile = File(...)) -> JSONResponse:
"""외부 도각 파일(DXF·DWG)을 읽어 **편집 화면에 실을 도면**으로 돌려준다 — 저장하지 않는다."""
try:
data = await file.read()
if not data:
raise ValueError("빈 파일입니다.")
if len(data) > _IMPORT_MAX_BYTES:
raise ValueError("도각 파일이 너무 큽니다(20MB 넘음).")
entities = await asyncio.to_thread(import_frame_file, file.filename or "", data)
validate_template_entities(entities)
return JSONResponse(
{
"status": "success",
"drawing": frame_document(entities),
"entity_count": len(entities),
}
)
except ValueError as exc:
return JSONResponse(status_code=400, content={"status": "error", "message": str(exc)})
except (OSError, UnicodeError, ezdxf.DXFError):
# 엉터리 파일 — 읽는 라이브러리가 ValueError 아닌 것으로 던짐(DXF 아님 · 글자 깨짐).
# 던진 글에 임시 폴더 경로가 섞여 있어 그대로 내지 않는다.
return JSONResponse(
status_code=400,
content={
"status": "error",
"message": "도각 파일을 읽지 못했습니다 — DXF 가 아니거나 깨진 파일입니다.",
},
)
except Exception:
logger.exception("M02 도각 불러오기 실패: %s", file.filename)
return JSONResponse(
status_code=500,
content={"status": "error", "message": "도각 파일을 읽지 못했습니다."},
)
@router.get("/drawing-fields")
async def get_drawing_fields(project_id: UUID | None = None) -> JSONResponse:
"""자리표 키 목록. project_id 를 주면 그 프로젝트의 표제란 값도 함께(미리보기용)."""
try:
values = await title_block_fields(project_id) if project_id else {}
except Exception:
logger.exception("M02 자리표 값 조회 실패: project_id=%s", project_id)
values = {}
return JSONResponse(
{
"status": "success",
"fields": [
{"key": key, "source": source, "label": label}
for key, source, label in DRAWING_FIELDS
],
"values": values,
}
)