""" PIPE 스냅샷 Excel 내보내기 API 라우터 확정된 Cutting Plan의 고정된 Excel 내보내기 기능 """ import logging from typing import Dict, Any from fastapi import APIRouter, Depends, HTTPException, status, Response from fastapi.responses import StreamingResponse from sqlalchemy.orm import Session from io import BytesIO from ..database import get_db from ..services.pipe_snapshot_excel_service import get_pipe_snapshot_excel_service, PipeSnapshotExcelService logger = logging.getLogger(__name__) router = APIRouter(prefix="/pipe-excel", tags=["pipe-excel"]) @router.get("/export-finalized/{job_no}") async def export_finalized_cutting_plan( job_no: str, db: Session = Depends(get_db) ): """ 확정된 Cutting Plan Excel 내보내기 - 스냅샷 데이터 기준으로 고정된 Excel 생성 - 리비전과 무관하게 동일한 데이터 제공 """ try: service = get_pipe_snapshot_excel_service(db) result = service.export_finalized_cutting_plan(job_no) if not result["success"]: if "확정된 Cutting Plan이 없습니다" in result["message"]: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=result["message"] ) else: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=result["message"] ) # Excel 파일 스트리밍 응답 excel_buffer = result["excel_buffer"] filename = result["filename"] # 파일 다운로드를 위한 헤더 설정 headers = { 'Content-Disposition': f'attachment; filename="{filename}"', 'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' } return StreamingResponse( BytesIO(excel_buffer.getvalue()), media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', headers=headers ) except HTTPException: raise except Exception as e: logger.error(f"Failed to export finalized cutting plan: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"확정된 Excel 내보내기 실패: {str(e)}" ) @router.get("/check-finalization/{job_no}") async def check_finalization_status( job_no: str, db: Session = Depends(get_db) ): """ Cutting Plan 확정 상태 확인 - 확정된 Excel 내보내기 가능 여부 확인 """ try: service = get_pipe_snapshot_excel_service(db) result = service.check_finalization_status(job_no) return result except Exception as e: logger.error(f"Failed to check finalization status: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"확정 상태 확인 실패: {str(e)}" ) @router.get("/preview-finalized/{job_no}") async def preview_finalized_data( job_no: str, db: Session = Depends(get_db) ): """ 확정된 데이터 미리보기 - Excel 생성 전 데이터 확인용 """ try: from ..services.pipe_issue_snapshot_service import get_pipe_issue_snapshot_service # 스냅샷 상태 확인 snapshot_service = get_pipe_issue_snapshot_service(db) snapshot_info = snapshot_service.get_snapshot_info(job_no) if not snapshot_info["has_snapshot"] or not snapshot_info["is_locked"]: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="확정된 Cutting Plan이 없습니다." ) # 스냅샷 데이터 조회 snapshot_id = snapshot_info["snapshot_id"] segments = snapshot_service.get_snapshot_segments(snapshot_id) # 구역별/도면별 통계 area_stats = {} drawing_stats = {} material_stats = {} for segment in segments: # 구역별 통계 area = segment.get("area", "미할당") if area not in area_stats: area_stats[area] = {"count": 0, "total_length": 0} area_stats[area]["count"] += 1 area_stats[area]["total_length"] += segment.get("length_mm", 0) # 도면별 통계 drawing = segment.get("drawing_name", "UNKNOWN") if drawing not in drawing_stats: drawing_stats[drawing] = {"count": 0, "total_length": 0} drawing_stats[drawing]["count"] += 1 drawing_stats[drawing]["total_length"] += segment.get("length_mm", 0) # 재질별 통계 material = segment.get("material_grade", "UNKNOWN") if material not in material_stats: material_stats[material] = {"count": 0, "total_length": 0} material_stats[material]["count"] += 1 material_stats[material]["total_length"] += segment.get("length_mm", 0) return { "job_no": job_no, "snapshot_info": snapshot_info, "preview_data": { "total_segments": len(segments), "area_stats": area_stats, "drawing_stats": drawing_stats, "material_stats": material_stats }, "sample_segments": segments[:10] if segments else [], # 처음 10개만 미리보기 "can_export": True, "message": "확정된 데이터 미리보기가 준비되었습니다." } except HTTPException: raise except Exception as e: logger.error(f"Failed to preview finalized data: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"데이터 미리보기 실패: {str(e)}" ) @router.post("/export-temp/{job_no}") async def export_temp_cutting_plan( job_no: str, db: Session = Depends(get_db) ): """ 임시 Cutting Plan Excel 내보내기 (구현 예정) - 현재 작업 중인 데이터 기준 - 리비전 시 변경될 수 있는 데이터 """ try: # TODO: 임시 Excel 내보내기 구현 raise HTTPException( status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="임시 Excel 내보내기 기능은 구현 예정입니다." ) except HTTPException: raise except Exception as e: logger.error(f"Failed to export temp cutting plan: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"임시 Excel 내보내기 실패: {str(e)}" ) @router.get("/download-history/{job_no}") async def get_download_history( job_no: str, db: Session = Depends(get_db) ): """ Excel 다운로드 이력 조회 (구현 예정) - 확정된 Excel 다운로드 기록 - 다운로드 시간 및 사용자 추적 """ try: # TODO: 다운로드 이력 추적 구현 return { "job_no": job_no, "download_history": [], "message": "다운로드 이력 추적 기능은 구현 예정입니다." } except Exception as e: logger.error(f"Failed to get download history: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"다운로드 이력 조회 실패: {str(e)}" )