ipns-poc-eval / src /controllers /_proposal_controller.py
Aryan Jain
decimal upto 2 places
90dfc4a
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.services import ProposalService
from src.models import ProposalStatus, GateCriteria
class Proposal(BaseModel):
id: UUID
rfp_id: UUID
name: str
tep: str
gate_criteria: GateCriteria
status: ProposalStatus
ai_score: Optional[float] = None
final_score: Optional[float] = None
created_at: datetime
updated_at: datetime
@field_validator("ai_score", "final_score")
@classmethod
def round_to_two_decimals(cls, v):
if v is not None:
return round(v, 2)
return v
class ProposalRequest(BaseModel):
rfp_id: UUID
name: str
tep: str
gate_criteria: GateCriteria
status: ProposalStatus
ai_score: Optional[float] = None
final_score: Optional[float] = None
class CreateProposalRequest(BaseModel):
name: str
tep: str
gate_criteria: GateCriteria
status: ProposalStatus
ai_score: Optional[float] = None
final_score: Optional[float] = None
class ProposalUpdateRequest(BaseModel):
rfp_id: Optional[UUID] = None
name: Optional[str] = None
tep: Optional[str] = None
gate_criteria: Optional[GateCriteria] = None
status: Optional[ProposalStatus] = None
ai_score: Optional[float] = None
final_score: Optional[float] = None
class UpdateProposalRequest(BaseModel):
name: Optional[str] = None
tep: Optional[str] = None
gate_criteria: Optional[GateCriteria] = None
status: Optional[ProposalStatus] = None
ai_score: Optional[float] = None
final_score: Optional[float] = None
class ProposalDeleteResponse(BaseModel):
status: str
class ResponseProposal(BaseModel):
status: str
data: Optional[List[Proposal]]
class ProposalController:
def __init__(self):
self.__proposal_service = ProposalService
self.router = APIRouter()
# self.router.add_api_route(
# "/proposals",
# self.get_proposals,
# methods=["GET"],
# response_model=ResponseProposal,
# tags=["Proposals"],
# )
# self.router.add_api_route(
# "/proposals/{proposal_id}",
# self.get_proposals_by_id,
# methods=["GET"],
# response_model=ResponseProposal,
# tags=["Proposals by ID"],
# )
self.router.add_api_route(
"/rfps/{rfp_id}/proposals",
self.get_proposals_by_rfp_id,
methods=["GET"],
response_model=ResponseProposal,
tags=["Proposals by RFP ID"],
)
self.router.add_api_route(
"/rfps/{rfp_id}/proposals/{proposal_id}",
self.get_proposals_by_proposal_and_rfp_id,
methods=["GET"],
response_model=ResponseProposal,
tags=["Proposals by Proposal and RFP ID"],
)
# self.router.add_api_route(
# "/proposals",
# self.create_proposal,
# methods=["POST"],
# response_model=ResponseProposal,
# tags=["Proposals"],
# )
self.router.add_api_route(
"/rfps/{rfp_id}/proposals",
self.create_proposal_by_rfp_id,
methods=["POST"],
response_model=ResponseProposal,
tags=["Proposals by RFP ID"],
)
# self.router.add_api_route(
# "/proposals/{proposal_id}",
# self.update_proposal,
# methods=["PUT"],
# response_model=ResponseProposal,
# tags=["Proposals by ID"],
# )
self.router.add_api_route(
"/rfps/{rfp_id}/proposals/{proposal_id}",
self.update_proposal_by_proposal_and_rfp_id,
methods=["PUT"],
response_model=ResponseProposal,
tags=["Proposals by Proposal and RFP ID"],
)
# self.router.add_api_route(
# "/proposals/{proposal_id}",
# self.delete_proposal,
# methods=["DELETE"],
# response_model=ProposalDeleteResponse,
# tags=["Proposals by ID"],
# )
self.router.add_api_route(
"/rfps/{rfp_id}/proposals",
self.delete_proposals_by_rfp_id,
methods=["DeLETE"],
response_model=ProposalDeleteResponse,
tags=["Proposals by RFP ID"],
)
self.router.add_api_route(
"/rfps/{rfp_id}/proposals/{proposal_id}",
self.delete_proposals_by_proposal_and_rfp_id,
methods=["DeLETE"],
response_model=ProposalDeleteResponse,
tags=["Proposals by Proposal and RFP ID"],
)
async def get_proposals(self):
async with self.__proposal_service() as service:
try:
proposals = await service.get_proposals()
return ResponseProposal(
status="success", data=[Proposal(**p) for p in proposals]
)
except Exception as e:
logger.exception(e)
raise HTTPException(
status_code=500, detail="Failed to retrieve proposals."
)
async def get_proposals_by_id(
self,
proposal_id: str = Path(...),
):
async with self.__proposal_service() as service:
try:
proposals = await service.get_proposals(proposal_id=proposal_id)
return ResponseProposal(
status="success", data=[Proposal(**p) for p in proposals]
)
except Exception as e:
logger.exception(e)
raise HTTPException(
status_code=500, detail="Failed to retrieve proposals."
)
async def get_proposals_by_rfp_id(
self,
rfp_id: str = Path(...),
):
async with self.__proposal_service() as service:
try:
proposals = await service.get_proposals(rfp_id=rfp_id)
return ResponseProposal(
status="success", data=[Proposal(**p) for p in proposals]
)
except Exception as e:
logger.exception(e)
raise HTTPException(
status_code=500, detail="Failed to retrieve proposals."
)
async def get_proposals_by_proposal_and_rfp_id(
self,
rfp_id: str = Path(...),
proposal_id: str = Path(...),
):
async with self.__proposal_service() as service:
try:
proposals = await service.get_proposals(
rfp_id=rfp_id, proposal_id=proposal_id
)
return ResponseProposal(
status="success", data=[Proposal(**p) for p in proposals]
)
except Exception as e:
logger.exception(e)
raise HTTPException(
status_code=500, detail="Failed to retrieve proposals."
)
async def create_proposal(self, proposal: ProposalRequest):
async with self.__proposal_service() as service:
try:
new_proposal = await service.create_proposal(
proposal.model_dump(mode="json")
)
return ResponseProposal(
status="success", data=[Proposal(**new_proposal)]
)
except Exception as e:
logger.exception(e)
raise HTTPException(
status_code=500, detail="Failed to create proposal."
)
async def create_proposal_by_rfp_id(
self, proposal: CreateProposalRequest, rfp_id: str = Path(...)
):
async with self.__proposal_service() as service:
try:
data = proposal.model_dump(exclude_unset=True, mode="json")
data["rfp_id"] = rfp_id
new_proposal = await service.create_proposal(proposal=data)
return ResponseProposal(
status="success", data=[Proposal(**new_proposal)]
)
except Exception as e:
logger.exception(e)
raise HTTPException(
status_code=500, detail="Failed to create proposal."
)
async def update_proposal(
self, proposal: ProposalUpdateRequest, proposal_id: str = Path(...)
):
async with self.__proposal_service() as service:
try:
updated_proposal = await service.update_proposal(
proposal_id=proposal_id,
proposal=proposal.model_dump(exclude_unset=True),
)
return ResponseProposal(
status="success", data=[Proposal(**updated_proposal)]
)
except Exception as e:
logger.exception(e)
raise HTTPException(
status_code=500, detail="Failed to update proposal."
)
async def update_proposal_by_proposal_and_rfp_id(
self,
proposal: UpdateProposalRequest,
rfp_id: str = Path(...),
proposal_id: str = Path(...),
):
async with self.__proposal_service() as service:
try:
data = proposal.model_dump(exclude_unset=True, mode="json")
data["rfp_id"] = rfp_id
updated_proposal = await service.update_proposal(
proposal_id=proposal_id,
proposal=proposal.model_dump(exclude_unset=True),
)
return ResponseProposal(
status="success", data=[Proposal(**updated_proposal)]
)
except Exception as e:
logger.exception(e)
raise HTTPException(
status_code=500, detail="Failed to update proposal."
)
async def delete_proposal(self, proposal_id: str = Path(...)):
async with self.__proposal_service() as service:
try:
status = await service.delete_proposal(proposal_id=proposal_id)
if not status:
raise HTTPException(status_code=404, detail="Proposal not found.")
return ProposalDeleteResponse(status="success")
except Exception as e:
logger.exception(e)
raise HTTPException(
status_code=500, detail="Failed to delete proposal."
)
async def delete_proposals_by_rfp_id(self, rfp_id: str = Path(...)):
async with self.__proposal_service() as service:
try:
status = await service.delete_proposal(rfp_id=rfp_id)
if not status:
raise HTTPException(status_code=404, detail="Proposal not found.")
return ProposalDeleteResponse(status="success")
except Exception as e:
logger.exception(e)
raise HTTPException(
status_code=500, detail="Failed to delete proposal."
)
async def delete_proposals_by_proposal_and_rfp_id(
self, rfp_id: str = Path(...), proposal_id: str = Path(...)
):
async with self.__proposal_service() as service:
try:
status = await service.delete_proposal(
rfp_id=rfp_id, proposal_id=proposal_id
)
if not status:
raise HTTPException(status_code=404, detail="Proposal not found.")
return ProposalDeleteResponse(status="success")
except Exception as e:
logger.exception(e)
raise HTTPException(
status_code=500, detail="Failed to delete proposal."
)