from fastapi import Path from fastapi import APIRouter, HTTPException, Query from pydantic import BaseModel, Field, field_validator from typing import List, Optional, Dict, Any from uuid import UUID from src.config import logger from src.services import RFPService from datetime import datetime from src.models import RFPStatus, AnalysisType class RFP(BaseModel): id: UUID rfp_number: str name: str received_proposals: int evaluated_count: int awaiting_evaluation: int target_award_date: Optional[datetime] = None ko_name: Optional[str] = None award_type: Optional[str] = None google_drive_id: Optional[str] = None status: RFPStatus comparative_weights_locked: Optional[bool] = False created_at: datetime updated_at: datetime class ResponseRFP(BaseModel): status: str data: Optional[List[RFP]] class RFPRequest(BaseModel): rfp_number: str name: str received_proposals: int evaluated_count: int target_award_date: Optional[datetime] = None ko_name: Optional[str] = None award_type: Optional[str] = None google_drive_id: Optional[str] = None status: RFPStatus comparative_weights_locked: Optional[bool] = False awaiting_evaluation: int class RFPUpdateRequest(BaseModel): rfp_number: Optional[str] = None name: Optional[str] = None received_proposals: Optional[int] = None evaluated_count: Optional[int] = None target_award_date: Optional[datetime] = None ko_name: Optional[str] = None award_type: Optional[str] = None google_drive_id: Optional[str] = None status: Optional[RFPStatus] = None comparative_weights_locked: Optional[bool] = None awaiting_evaluation: Optional[int] = None class RFPDeleteResponse(BaseModel): status: str class EvaluationStatusResponse(BaseModel): status: bool class EvaluationStatus(BaseModel): data: EvaluationStatusResponse class RFPController: def __init__(self): self.__rfp_service = RFPService self.router = APIRouter() self.router.add_api_route( "/rfps", self.get_rfps, methods=["GET"], response_model=ResponseRFP, tags=["RFPs"], ) self.router.add_api_route( "/rfps/{rfp_id}", self.get_rfps_by_id, methods=["GET"], response_model=ResponseRFP, tags=["RFPs by ID"], ) self.router.add_api_route( "/rfps", self.create_rfp, methods=["POST"], response_model=ResponseRFP, tags=["RFPs"], ) self.router.add_api_route( "/rfps/{rfp_id}", self.update_rfp, methods=["PUT"], response_model=ResponseRFP, tags=["RFPs by ID"], ) self.router.add_api_route( "/rfps/{rfp_id}", self.delete_rfp, methods=["DELETE"], response_model=RFPDeleteResponse, tags=["RFPs by ID"], ) self.router.add_api_route( "/rfps/{rfp_id}/evaluation-status", self.get_evaluation_status, methods=["GET"], response_model=EvaluationStatus, tags=["RFPs Evaluation Status"], ) async def get_rfps(self, status: Optional[RFPStatus] = Query(None)): async with self.__rfp_service() as service: try: rfps = await service.get_rfps(status=status) return ResponseRFP(status="success", data=[RFP(**rfp) for rfp in rfps]) except HTTPException as e: logger.warning(e) raise e except Exception as e: logger.exception(e) raise HTTPException(status_code=500, detail="Failed to retrieve RFPs.") async def get_rfps_by_id(self, rfp_id: str = Path(...)): async with self.__rfp_service() as service: try: rfps = await service.get_rfps(rfp_id=rfp_id) return ResponseRFP(status="success", data=[RFP(**rfp) for rfp in rfps]) except HTTPException as e: logger.warning(e) raise e except Exception as e: logger.exception(e) raise HTTPException(status_code=500, detail="Failed to retrieve RFPs.") async def create_rfp(self, rfp: RFPRequest): async with self.__rfp_service() as service: try: rfp = await service.create_rfp(rfp.model_dump(exclude_unset=True)) return ResponseRFP(status="success", data=[RFP(**rfp)]) except HTTPException as e: logger.warning(e) raise e except Exception as e: logger.exception(e) raise HTTPException(status_code=500, detail="Failed to create RFP.") async def update_rfp(self, rfp: RFPUpdateRequest, rfp_id: str = Path(...)): async with self.__rfp_service() as service: try: rfp = await service.update_rfp( rfp_id=rfp_id, rfp=rfp.model_dump(exclude_unset=True) ) return ResponseRFP(status="success", data=[RFP(**rfp)]) except HTTPException as e: logger.warning(e) raise e except Exception as e: logger.exception(e) raise HTTPException(status_code=500, detail="Failed to update RFP.") async def delete_rfp(self, rfp_id: str = Path(...)): async with self.__rfp_service() as service: try: status = await service.delete_rfp(rfp_id) if not status: raise HTTPException(status_code=404, detail="RFP not found.") return RFPDeleteResponse(status="success") except HTTPException as e: logger.warning(e) raise e except Exception as e: logger.exception(e) raise HTTPException(status_code=500, detail="Failed to delete RFP.") async def get_evaluation_status(self, rfp_id: str = Path(...)): async with self.__rfp_service() as service: try: has_deficiencies = await service.check_deficiencies(rfp_id) return EvaluationStatus(data=EvaluationStatusResponse(status=has_deficiencies)) except HTTPException as e: logger.warning(e) raise e except Exception as e: logger.exception(e) raise HTTPException(status_code=500, detail="Failed to check evaluation status.")