from fastapi import APIRouter, HTTPException, Query, Path from pydantic import BaseModel, field_validator from typing import List, Optional from uuid import UUID from datetime import datetime from src.config import logger from src.models import EvaluationType from src.services import ProposalEvaluationService class Evaluation(BaseModel): id: UUID proposal_id: UUID evaluation_type: EvaluationType ai_score: Optional[float] = None adjusted_score: Optional[float] = None created_at: datetime updated_at: datetime @field_validator("ai_score", "adjusted_score") @classmethod def round_to_two_decimals(cls, v): if v is not None: return round(v, 2) return v class Response(BaseModel): status: str data: Optional[List[Evaluation]] = None class RequestEvaluation(BaseModel): evaluation_type: Optional[EvaluationType] = None ai_score: Optional[float] = None adjusted_score: Optional[float] = None class DeleteResponse(BaseModel): status: str class CreateEvaluation(BaseModel): evaluation_type: EvaluationType ai_score: Optional[float] = None adjusted_score: Optional[float] = None class CreateEvaluationRequest(BaseModel): data: List[CreateEvaluation] class UpdateEvaluation(BaseModel): evaluation_type: Optional[EvaluationType] = None ai_score: Optional[float] = None adjusted_score: Optional[float] = None class ProposalEvaluationController: def __init__(self): self.service = ProposalEvaluationService self.router = APIRouter() self.router.add_api_route( "/rfps/{rfp_id}/proposals/{proposal_id}/evaluations", self.get_evaluation_by_proposal_and_rfp_id, methods=["GET"], response_model=Response, tags=["Evaluations by Proposal and RFP ID"], ) self.router.add_api_route( "/rfps/{rfp_id}/proposals/{proposal_id}/evaluations/{id}", self.get_evaluation_by_proposal_and_rfp_id_and_id, methods=["GET"], response_model=Response, tags=["Evaluations by Proposal and RFP ID and ID"], ) self.router.add_api_route( "/rfps/{rfp_id}/proposals/{proposal_id}/evaluations", self.create_evaluation_by_proposal_and_rfp_id, methods=["POST"], response_model=Response, tags=["Evaluations by Proposal and RFP ID"], ) self.router.add_api_route( "/rfps/{rfp_id}/proposals/{proposal_id}/evaluations/{id}", self.update_evaluation_by_proposal_and_rfp_id_and_id, methods=["PUT"], response_model=Response, tags=["Evaluations by Proposal and RFP ID and ID"], ) self.router.add_api_route( "/rfps/{rfp_id}/proposals/{proposal_id}/evaluations", self.delete_evaluation_by_proposal_and_rfp_id, methods=["DELETE"], response_model=DeleteResponse, tags=["Evaluations by Proposal and RFP ID"], ) self.router.add_api_route( "/rfps/{rfp_id}/proposals/{proposal_id}/evaluations/{id}", self.delete_evaluation_by_proposal_and_rfp_id_and_id, methods=["DELETE"], response_model=DeleteResponse, tags=["Evaluations by Proposal and RFP ID and ID"], ) async def get_evaluation_by_proposal_and_rfp_id( self, rfp_id: str = Path(...), proposal_id: str = Path(...), evaluation_type: Optional[EvaluationType] = Query(None), ): try: async with self.service() as service: results = await service.get_evaluations( proposal_id=proposal_id, evaluation_type=evaluation_type ) return Response( status="success", data=[Evaluation(**result) for result in results], ) except Exception as e: logger.exception(e) raise HTTPException(status_code=500, detail="Failed to retrieve proposals.") async def get_evaluation_by_proposal_and_rfp_id_and_id( self, rfp_id: str = Path(...), proposal_id: str = Path(...), id: str = Path(...) ): try: async with self.service() as service: result = await service.get_evaluations(proposal_id=proposal_id, id=id) return Response( status="success", data=[Evaluation(**result) for result in result], ) except Exception as e: logger.exception(e) raise HTTPException(status_code=500, detail="Failed to retrieve proposals.") async def create_evaluation_by_proposal_and_rfp_id( self, evaluation: CreateEvaluationRequest, rfp_id: str = Path(...), proposal_id: str = Path(...), ): try: async with self.service() as service: result = await service.create_evaluations( proposal_id=proposal_id, evaluations=evaluation.model_dump(mode="json", exclude_unset=True)[ "data" ], ) return Response( status="success", data=[Evaluation(**result) for result in result], ) except Exception as e: logger.exception(e) raise HTTPException(status_code=500, detail="Failed to retrieve proposals.") async def update_evaluation_by_proposal_and_rfp_id_and_id( self, evaluation: UpdateEvaluation, rfp_id: str = Path(...), proposal_id: str = Path(...), id: str = Path(...), ): try: async with self.service() as service: result = await service.update_evaluations( proposal_id=proposal_id, id=id, evaluations=evaluation.model_dump(mode="json", exclude_unset=True), ) return Response( status="success", data=[Evaluation(**result) for result in result], ) except Exception as e: logger.exception(e) raise HTTPException(status_code=500, detail="Failed to retrieve proposals.") async def delete_evaluation_by_proposal_and_rfp_id( self, rfp_id: str = Path(...), proposal_id: str = Path(...) ): try: async with self.service() as service: result = await service.delete_evaluations(proposal_id=proposal_id) return DeleteResponse(status="success") except Exception as e: logger.exception(e) raise HTTPException(status_code=500, detail="Failed to retrieve proposals.") async def delete_evaluation_by_proposal_and_rfp_id_and_id( self, rfp_id: str = Path(...), proposal_id: str = Path(...), id: str = Path(...) ): try: async with self.service() as service: result = await service.delete_evaluations( proposal_id=proposal_id, id=id ) return DeleteResponse(status="success") except Exception as e: logger.exception(e) raise HTTPException(status_code=500, detail="Failed to retrieve proposals.")