EVMS 완전 자동화: - 공기 지연 AI 예측 (SPI 기반 준공일 예측) - 기성청구 가능 금액 자동 산출 - 매일 자정 EVMS 스냅샷 자동 생성 (APScheduler) - 매일 07:00 GONGSA 아침 브리핑 자동 생성 준공도서 패키지: - 준공 요약 + 품질시험 목록 + 검측 이력 + 인허가 현황 → ZIP 번들 - 준공 준비 체크리스트 API - 4종 HTML 템플릿 (WeasyPrint PDF 출력) Vision AI Level 3: - 설계 도면 vs 현장 사진 비교 보조 판독 (Claude Vision) - 철근 배근, 거푸집 치수 1차 분석 설계도서 파싱: - PDF 이미지/텍스트에서 공종·수량·규격 자동 추출 - Pandoc HWP 출력 지원 발주처 전용 포털: - 토큰 기반 읽기 전용 API - 공사 현황 대시보드, 공정률 추이 차트 에이전트 협업 고도화: - 협업 시나리오 (concrete_pour, excavation, weekly_report) - GONGSA→PUMJIL→ANJEON 순차 처리 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
103 lines
3.0 KiB
Python
103 lines
3.0 KiB
Python
"""
|
|
설계도서 파싱 + HWP 출력 API
|
|
"""
|
|
import uuid
|
|
from fastapi import APIRouter, UploadFile, File, HTTPException, Form
|
|
from fastapi.responses import Response
|
|
from pydantic import BaseModel
|
|
from sqlalchemy import select
|
|
|
|
from app.deps import CurrentUser, DB
|
|
from app.models.project import Project
|
|
from app.services.document_parser import (
|
|
parse_design_document_text,
|
|
parse_design_document_image,
|
|
convert_to_hwp,
|
|
)
|
|
from app.services.pdf_service import _render_html, _html_to_pdf
|
|
|
|
router = APIRouter(prefix="/projects/{project_id}/documents", tags=["설계도서"])
|
|
|
|
ALLOWED_IMAGE_TYPES = {"image/jpeg", "image/png", "image/webp"}
|
|
MAX_SIZE_MB = 20
|
|
|
|
|
|
async def _get_project_or_404(project_id: uuid.UUID, db: DB) -> Project:
|
|
r = await db.execute(select(Project).where(Project.id == project_id))
|
|
p = r.scalar_one_or_none()
|
|
if not p:
|
|
raise HTTPException(status_code=404, detail="프로젝트를 찾을 수 없습니다")
|
|
return p
|
|
|
|
|
|
@router.post("/parse-image")
|
|
async def parse_document_image(
|
|
project_id: uuid.UUID,
|
|
db: DB,
|
|
current_user: CurrentUser,
|
|
file: UploadFile = File(..., description="도면 이미지 (JPG/PNG)"),
|
|
):
|
|
"""
|
|
설계 도면 이미지 → 공종/수량/규격 자동 추출 (Claude Vision)
|
|
"""
|
|
await _get_project_or_404(project_id, db)
|
|
|
|
content_type = file.content_type or "image/jpeg"
|
|
if content_type not in ALLOWED_IMAGE_TYPES:
|
|
raise HTTPException(status_code=400, detail="JPG, PNG, WEBP 이미지만 지원합니다")
|
|
|
|
image_data = await file.read()
|
|
if len(image_data) > MAX_SIZE_MB * 1024 * 1024:
|
|
raise HTTPException(status_code=400, detail=f"파일 크기가 {MAX_SIZE_MB}MB를 초과합니다")
|
|
|
|
result = await parse_design_document_image(image_data, content_type)
|
|
return result
|
|
|
|
|
|
@router.post("/parse-text")
|
|
async def parse_document_text(
|
|
project_id: uuid.UUID,
|
|
db: DB,
|
|
current_user: CurrentUser,
|
|
file: UploadFile = File(..., description="설계도서 텍스트 파일 (TXT/MD)"),
|
|
):
|
|
"""
|
|
설계도서 텍스트 파일 → 공종/수량/규격 자동 추출
|
|
"""
|
|
await _get_project_or_404(project_id, db)
|
|
|
|
content = await file.read()
|
|
text = content.decode("utf-8", errors="replace")
|
|
result = await parse_design_document_text(text)
|
|
return result
|
|
|
|
|
|
class HWPRequest(BaseModel):
|
|
html_content: str
|
|
filename: str = "document.hwp"
|
|
|
|
|
|
@router.post("/export-hwp")
|
|
async def export_hwp(
|
|
project_id: uuid.UUID,
|
|
data: HWPRequest,
|
|
db: DB,
|
|
current_user: CurrentUser,
|
|
):
|
|
"""
|
|
HTML → HWP 변환 (Pandoc 필요)
|
|
보고서나 일보를 HWP 형식으로 내보냅니다.
|
|
"""
|
|
await _get_project_or_404(project_id, db)
|
|
|
|
try:
|
|
hwp_bytes = convert_to_hwp(data.html_content)
|
|
except RuntimeError as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
return Response(
|
|
content=hwp_bytes,
|
|
media_type="application/octet-stream",
|
|
headers={"Content-Disposition": f"attachment; filename*=UTF-8''{data.filename}"},
|
|
)
|