gopalswami's picture
Implement evaluation status API and enhance RFP service with deficiency check logic
2a79756
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.")