""" 간단한 리비전 관리 API 복잡한 dependency 없이 핵심 기능만 제공 """ from fastapi import APIRouter, Depends, HTTPException, UploadFile, File as FastAPIFile from sqlalchemy.orm import Session from typing import Dict, List, Any, Optional import json from ..database import get_db from ..services.simple_revision_service import SimpleRevisionService from ..services.file_upload_service import FileUploadService from ..models import File, Material from ..utils.logger import get_logger logger = get_logger(__name__) router = APIRouter(prefix="/simple-revision", tags=["Simple Revision"]) @router.post("/compare") async def compare_revisions( current_file_id: int, previous_file_id: int, category: str, username: str = "system", db: Session = Depends(get_db) ): """ 두 리비전 간의 자재 비교 Args: current_file_id: 현재 파일 ID previous_file_id: 이전 파일 ID category: 비교할 카테고리 (PIPE, FITTING, FLANGE, VALVE, GASKET, BOLT, SUPPORT, SPECIAL, UNCLASSIFIED) username: 비교 수행자 """ try: service = SimpleRevisionService(db) result = service.compare_revisions( current_file_id, previous_file_id, category, username ) return { "success": True, "message": f"{category} 카테고리 리비전 비교 완료", "data": result } except Exception as e: logger.error(f"리비전 비교 실패: {e}") raise HTTPException(status_code=500, detail=f"리비전 비교 실패: {str(e)}") @router.get("/comparison/{comparison_id}") async def get_comparison_result( comparison_id: int, db: Session = Depends(get_db) ): """저장된 비교 결과 조회""" try: service = SimpleRevisionService(db) result = service.get_comparison_result(comparison_id) if not result: raise HTTPException(status_code=404, detail="비교 결과를 찾을 수 없습니다") return { "success": True, "data": result } except HTTPException: raise except Exception as e: logger.error(f"비교 결과 조회 실패: {e}") raise HTTPException(status_code=500, detail=f"비교 결과 조회 실패: {str(e)}") @router.post("/upload-revision") async def upload_revision_file( file: UploadFile = FastAPIFile(...), job_no: str = "default", previous_file_id: int = None, username: str = "system", db: Session = Depends(get_db) ): """ 리비전 파일 업로드 및 자동 비교 Args: file: 업로드할 BOM 파일 job_no: 작업 번호 previous_file_id: 비교할 이전 파일 ID username: 업로드 사용자 """ try: # 1. 파일 업로드 upload_service = FileUploadService(db) upload_result = await upload_service.process_upload( file=file, job_no=job_no, username=username, is_revision=True ) current_file_id = upload_result['file_id'] # 2. 이전 파일이 지정되지 않은 경우 최신 파일 찾기 if not previous_file_id: previous_file = db.query(File).filter( File.job_no == job_no, File.id != current_file_id, File.is_active == True ).order_by(File.upload_date.desc()).first() if previous_file: previous_file_id = previous_file.id else: return { "success": True, "message": "첫 번째 파일이므로 비교할 이전 파일이 없습니다", "data": { "file_id": current_file_id, "comparison_results": [] } } # 3. 각 카테고리별로 자동 비교 수행 categories = [ 'PIPE', 'FITTING', 'FLANGE', 'VALVE', 'GASKET', 'BOLT', 'SUPPORT', 'SPECIAL', 'UNCLASSIFIED' ] revision_service = SimpleRevisionService(db) comparison_results = [] for category in categories: try: # 해당 카테고리에 자재가 있는지 확인 current_materials = db.query(Material).filter( Material.file_id == current_file_id, Material.classified_category == category ).count() previous_materials = db.query(Material).filter( Material.file_id == previous_file_id, Material.classified_category == category ).count() if current_materials > 0 or previous_materials > 0: # 비교 수행 comparison_result = revision_service.compare_revisions( current_file_id, previous_file_id, category, username ) comparison_results.append(comparison_result) except Exception as e: logger.warning(f"{category} 카테고리 비교 실패: {e}") continue return { "success": True, "message": f"리비전 파일 업로드 및 비교 완료 ({len(comparison_results)}개 카테고리)", "data": { "file_id": current_file_id, "previous_file_id": previous_file_id, "comparison_results": comparison_results } } except Exception as e: logger.error(f"리비전 업로드 실패: {e}") raise HTTPException(status_code=500, detail=f"리비전 업로드 실패: {str(e)}") @router.get("/categories/{file_id}") async def get_available_categories( file_id: int, db: Session = Depends(get_db) ): """파일의 사용 가능한 카테고리 목록 조회""" try: # 파일에 포함된 카테고리별 자재 수 조회 categories_query = db.query( Material.classified_category, db.func.count(Material.id).label('count') ).filter( Material.file_id == file_id ).group_by(Material.classified_category).all() categories = [] for category, count in categories_query: if category and count > 0: categories.append({ 'category': category, 'count': count }) return { "success": True, "data": { "file_id": file_id, "categories": categories } } except Exception as e: logger.error(f"카테고리 조회 실패: {e}") raise HTTPException(status_code=500, detail=f"카테고리 조회 실패: {str(e)}") @router.get("/changed-materials/{current_file_id}/{previous_file_id}") async def get_changed_materials_only( current_file_id: int, previous_file_id: int, categories: str = None, # 쉼표로 구분된 카테고리 목록 username: str = "system", db: Session = Depends(get_db) ): """변경된 자재만 필터링하여 반환""" try: service = SimpleRevisionService(db) # 카테고리 파싱 category_list = None if categories: category_list = [cat.strip().upper() for cat in categories.split(',')] result = service.get_changed_materials_only( current_file_id, previous_file_id, category_list, username ) return { "success": True, "message": f"변경된 자재 조회 완료 ({result['total_changed_materials']}개 변경)", "data": result } except Exception as e: logger.error(f"변경된 자재 조회 실패: {e}") raise HTTPException(status_code=500, detail=f"변경된 자재 조회 실패: {str(e)}") @router.get("/history/{job_no}") async def get_revision_history( job_no: str, db: Session = Depends(get_db) ): """작업 번호별 리비전 히스토리 조회""" try: from ..models import SimpleRevisionComparison comparisons = db.query(SimpleRevisionComparison).filter( SimpleRevisionComparison.job_no == job_no ).order_by(SimpleRevisionComparison.created_at.desc()).all() history = [] for comp in comparisons: history.append({ 'comparison_id': comp.id, 'category': comp.category, 'created_at': comp.created_at.isoformat(), 'created_by': comp.created_by_username, 'summary': { 'added': comp.added_count, 'removed': comp.removed_count, 'changed': comp.changed_count, 'unchanged': comp.unchanged_count } }) return { "success": True, "data": { "job_no": job_no, "history": history } } except Exception as e: logger.error(f"리비전 히스토리 조회 실패: {e}") raise HTTPException(status_code=500, detail=f"리비전 히스토리 조회 실패: {str(e)}")