""" 스풀 관리 API 엔드포인트 """ from fastapi import APIRouter, Depends, HTTPException, Form from sqlalchemy.orm import Session from sqlalchemy import text from typing import List, Optional from datetime import datetime from ..database import get_db from ..services.spool_manager import SpoolManager, classify_pipe_with_spool router = APIRouter() @router.get("/") async def get_spools_info(): return { "message": "스풀 관리 API", "features": [ "스풀 식별자 생성", "에리어/스풀 넘버 관리", "스풀별 파이프 그룹핑", "스풀 유효성 검증" ] } @router.post("/validate-identifier") async def validate_spool_identifier( spool_identifier: str = Form(...), db: Session = Depends(get_db) ): """스풀 식별자 유효성 검증""" spool_manager = SpoolManager() validation_result = spool_manager.validate_spool_identifier(spool_identifier) return { "spool_identifier": spool_identifier, "validation": validation_result, "timestamp": datetime.now().isoformat() } @router.post("/generate-identifier") async def generate_spool_identifier( dwg_name: str = Form(...), area_number: str = Form(...), spool_number: str = Form(...), db: Session = Depends(get_db) ): """새로운 스풀 식별자 생성""" try: spool_manager = SpoolManager() spool_identifier = spool_manager.generate_spool_identifier( dwg_name, area_number, spool_number ) # 중복 확인 duplicate_check = db.execute( text("SELECT id FROM spools WHERE spool_identifier = :spool_id"), {"spool_id": spool_identifier} ).fetchone() return { "success": True, "spool_identifier": spool_identifier, "is_duplicate": bool(duplicate_check), "components": { "dwg_name": dwg_name, "area_number": spool_manager.format_area_number(area_number), "spool_number": spool_manager.format_spool_number(spool_number) } } except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) @router.get("/next-spool-number") async def get_next_spool_number( dwg_name: str, area_number: str, db: Session = Depends(get_db) ): """다음 사용 가능한 스풀 넘버 추천""" try: # 기존 스풀들 조회 existing_spools_query = text(""" SELECT spool_identifier FROM spools WHERE dwg_name = :dwg_name """) result = db.execute(existing_spools_query, {"dwg_name": dwg_name}) existing_spools = [row[0] for row in result.fetchall()] spool_manager = SpoolManager() next_spool = spool_manager.get_next_spool_number( dwg_name, area_number, existing_spools ) return { "dwg_name": dwg_name, "area_number": spool_manager.format_area_number(area_number), "next_spool_number": next_spool, "existing_spools_count": len(existing_spools) } except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) @router.post("/materials/{material_id}/assign-spool") async def assign_spool_to_material( material_id: int, area_number: str = Form(...), spool_number: str = Form(...), db: Session = Depends(get_db) ): """자재에 스풀 정보 할당""" try: # 자재 정보 조회 material_query = text(""" SELECT m.id, m.file_id, f.project_id, f.dwg_name FROM materials m JOIN files f ON m.file_id = f.id WHERE m.id = :material_id """) material = db.execute(material_query, {"material_id": material_id}).fetchone() if not material: raise HTTPException(status_code=404, detail="자재를 찾을 수 없습니다") # 스풀 식별자 생성 spool_manager = SpoolManager() area_formatted = spool_manager.format_area_number(area_number) spool_formatted = spool_manager.format_spool_number(spool_number) spool_identifier = spool_manager.generate_spool_identifier( material.dwg_name, area_formatted, spool_formatted ) # 자재 업데이트 update_query = text(""" UPDATE materials SET area_number = :area_number, spool_number = :spool_number, spool_identifier = :spool_identifier, spool_input_required = FALSE, spool_validated = TRUE, updated_at = NOW() WHERE id = :material_id """) db.execute(update_query, { "material_id": material_id, "area_number": area_formatted, "spool_number": spool_formatted, "spool_identifier": spool_identifier }) # 스풀 테이블에 등록 (없다면) spool_insert_query = text(""" INSERT INTO spools (project_id, dwg_name, area_number, spool_number, spool_identifier, created_at) VALUES (:project_id, :dwg_name, :area_number, :spool_number, :spool_identifier, NOW()) ON CONFLICT (project_id, dwg_name, area_number, spool_number) DO NOTHING """) db.execute(spool_insert_query, { "project_id": material.project_id, "dwg_name": material.dwg_name, "area_number": area_formatted, "spool_number": spool_formatted, "spool_identifier": spool_identifier }) db.commit() return { "success": True, "material_id": material_id, "spool_identifier": spool_identifier, "message": "스풀 정보가 할당되었습니다" } except ValueError as e: db.rollback() raise HTTPException(status_code=400, detail=str(e)) except Exception as e: db.rollback() raise HTTPException(status_code=500, detail=f"스풀 할당 실패: {str(e)}") @router.get("/project/{project_id}/spools") async def get_project_spools( project_id: int, db: Session = Depends(get_db) ): """프로젝트의 모든 스풀 조회""" query = text(""" SELECT s.*, COUNT(m.id) as material_count, SUM(m.quantity) as total_quantity FROM spools s LEFT JOIN materials m ON s.spool_identifier = m.spool_identifier WHERE s.project_id = :project_id GROUP BY s.id ORDER BY s.dwg_name, s.area_number, s.spool_number """) result = db.execute(query, {"project_id": project_id}) spools = result.fetchall() return { "project_id": project_id, "spools_count": len(spools), "spools": [ { "id": spool.id, "spool_identifier": spool.spool_identifier, "dwg_name": spool.dwg_name, "area_number": spool.area_number, "spool_number": spool.spool_number, "material_count": spool.material_count or 0, "total_quantity": float(spool.total_quantity or 0), "status": spool.status, "created_at": spool.created_at } for spool in spools ] }