Aryan Jain commited on
Commit
9c62b5f
·
1 Parent(s): ef8c55b

add evaluation

Browse files
alembic/versions/c70330d677c0_add_evaluation_criteria.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """add evaluation_criteria
2
+
3
+ Revision ID: c70330d677c0
4
+ Revises: e825902b2e8a
5
+ Create Date: 2025-06-02 19:38:53.073958
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+
13
+ from sqlalchemy.dialects import postgresql
14
+
15
+
16
+ # revision identifiers, used by Alembic.
17
+ revision: str = 'c70330d677c0'
18
+ down_revision: Union[str, None] = 'e825902b2e8a'
19
+ branch_labels: Union[str, Sequence[str], None] = None
20
+ depends_on: Union[str, Sequence[str], None] = None
21
+
22
+
23
+ def upgrade() -> None:
24
+ """Upgrade schema."""
25
+ op.create_table(
26
+ "evaluation_criteria_details",
27
+ sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
28
+ sa.Column("rfp_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("rfps.id", ondelete="CASCADE"), nullable=False),
29
+ sa.Column("evaluation_criteria", sa.String(), nullable=True),
30
+ sa.Column("evaluation_criteria_type", sa.Enum("EVALUATION", "GATE", name="evaluationcriteriatype"), nullable=False),
31
+ sa.Column("created_at", sa.DateTime(), nullable=False),
32
+ sa.Column("updated_at", sa.DateTime(), nullable=False),
33
+ )
34
+
35
+
36
+ def downgrade() -> None:
37
+ """Downgrade schema."""
38
+ op.drop_table("evaluation_criteria_details")
src/controllers/__init__.py CHANGED
@@ -4,6 +4,7 @@ from ._proposal_controller import ProposalController
4
  from ._proposal_ai_analysis_controller import ProposalAIController
5
  from ._proposal_detailed_analysis_controller import ProposalDetailedController
6
  from ._letter_controller import LetterController
 
7
 
8
  api_router = APIRouter()
9
 
@@ -12,6 +13,7 @@ api_router.include_router(ProposalController().router, prefix="/proposal", tags=
12
  api_router.include_router(ProposalAIController().router, prefix="/proposal_ai_analysis", tags=["Proposal AI Analysis"])
13
  api_router.include_router(ProposalDetailedController().router, prefix="/proposal_detailed_analysis", tags=["Proposal Detailed Analysis"])
14
  api_router.include_router(LetterController().router, prefix="/letter", tags=["Letter"])
 
15
 
16
  __all__ = ["api_router"]
17
  __version__ = "0.1.0"
 
4
  from ._proposal_ai_analysis_controller import ProposalAIController
5
  from ._proposal_detailed_analysis_controller import ProposalDetailedController
6
  from ._letter_controller import LetterController
7
+ from ._evaluation_controller import EvaluationController
8
 
9
  api_router = APIRouter()
10
 
 
13
  api_router.include_router(ProposalAIController().router, prefix="/proposal_ai_analysis", tags=["Proposal AI Analysis"])
14
  api_router.include_router(ProposalDetailedController().router, prefix="/proposal_detailed_analysis", tags=["Proposal Detailed Analysis"])
15
  api_router.include_router(LetterController().router, prefix="/letter", tags=["Letter"])
16
+ api_router.include_router(EvaluationController().router, prefix="/evaluation_criteria", tags=["Evaluation Criteria"])
17
 
18
  __all__ = ["api_router"]
19
  __version__ = "0.1.0"
src/controllers/_evaluation_controller.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, HTTPException, Query
2
+ from pydantic import BaseModel
3
+ from typing import List, Optional
4
+ from uuid import UUID
5
+ from datetime import datetime
6
+
7
+ from src.config import logger
8
+ from src.services import EvaluationService
9
+ from src.models import EvaluationCriteriaType
10
+
11
+ class EvaluationCriteria(BaseModel):
12
+ id: UUID
13
+ rfp_id: UUID
14
+ evaluation_criteria: str
15
+ evaluation_criteria_type: EvaluationCriteriaType
16
+ created_at: datetime
17
+ updated_at: datetime
18
+
19
+ class EvaluationResponse(BaseModel):
20
+ status: str
21
+ data: List[EvaluationCriteria]
22
+
23
+ class EvaluationRequest(BaseModel):
24
+ rfp_id: UUID
25
+ evaluation_criteria: str
26
+ evaluation_criteria_type: EvaluationCriteriaType
27
+
28
+ class UpdateEvaluationRequest(BaseModel):
29
+ id: UUID
30
+ rfp_id: Optional[UUID] = None
31
+ evaluation_criteria: Optional[str] = None
32
+ evaluation_criteria_type: Optional[EvaluationCriteriaType] = None
33
+
34
+ class DeleteResponse(BaseModel):
35
+ status: str
36
+
37
+ class EvaluationController:
38
+ def __init__(self):
39
+ self.evaluation_service = EvaluationService
40
+ self.router = APIRouter()
41
+ self.router.add_api_route(
42
+ "/get_evaluation_criteria",
43
+ self.get_criteria,
44
+ methods=["GET"],
45
+ response_model=EvaluationResponse
46
+ )
47
+ self.router.add_api_route(
48
+ "/create_evaluation_criteria",
49
+ self.create_criteria,
50
+ methods=["POST"],
51
+ response_model=EvaluationResponse
52
+ )
53
+ self.router.add_api_route(
54
+ "/update_evaluation_criteria",
55
+ self.update_criteria,
56
+ methods=["PUT"],
57
+ response_model=EvaluationResponse
58
+ )
59
+ self.router.add_api_route(
60
+ "/delete_evaluation_criteria",
61
+ self.delete_criteria,
62
+ methods=["DELETE"],
63
+ response_model=DeleteResponse
64
+ )
65
+
66
+ async def get_criteria(self, id: Optional[str] = Query(None), rfp_id: Optional[str] = Query(None), evaluation_criteria_type: Optional[EvaluationCriteriaType] = Query(None)):
67
+ try:
68
+ async with self.evaluation_service() as service:
69
+ result = await service.get_criteria(id=id, rfp_id=rfp_id, evaluation_criteria_type=evaluation_criteria_type)
70
+ return EvaluationResponse(status="success", data=[EvaluationCriteria(**r) for r in result])
71
+ except Exception as e:
72
+ logger.error(e)
73
+ raise HTTPException(status_code=500, detail="Error fetching evaluation criteria")
74
+
75
+ async def create_criteria(self, criteria: EvaluationRequest):
76
+ try:
77
+ async with self.evaluation_service() as service:
78
+ result = await service.create_criteria(criteria.model_dump(exclude_unset=True, mode="json"))
79
+ return EvaluationResponse(status="success", data=[EvaluationCriteria(**r) for r in result])
80
+ except Exception as e:
81
+ logger.error(e)
82
+ raise HTTPException(status_code=500, detail="Error creating evaluation criteria")
83
+
84
+ async def update_criteria(self, criteria: UpdateEvaluationRequest):
85
+ try:
86
+ async with self.evaluation_service() as service:
87
+ result = await service.update_criteria(criteria.model_dump(exclude_unset=True, mode="json"))
88
+ return EvaluationResponse(status="success", data=[EvaluationCriteria(**r) for r in result])
89
+ except Exception as e:
90
+ logger.error(e)
91
+ raise HTTPException(status_code=500, detail="Error updating evaluation criteria")
92
+
93
+ async def delete_criteria(self, id: Optional[str] = Query(None), rfp_id: Optional[str] = Query(None)):
94
+ try:
95
+ if not id and not rfp_id:
96
+ return DeleteResponse(status="Failed to delete evaluation criteria")
97
+ async with self.evaluation_service() as service:
98
+ await service.delete_criteria(id=id, rfp_id=rfp_id)
99
+ return DeleteResponse(status="success")
100
+ except Exception as e:
101
+ logger.error(e)
102
+ raise HTTPException(status_code=500, detail="Error deleting evaluation criteria")
src/models/__init__.py CHANGED
@@ -11,6 +11,7 @@ from ._proposal_ai_analysis import (
11
  from ._proposal_detailed_analysis import ProposalDetailAnalysis, OperationCriteria
12
  from ._proposal_query_models import QueryType, CreateOrUpdateType
13
  from ._letters import Letter, LetterType
 
14
 
15
  __all__ = [
16
  "Base",
@@ -29,6 +30,8 @@ __all__ = [
29
  "GateCriteria",
30
  "Letter",
31
  "LetterType",
 
 
32
  ]
33
  __version__ = "0.1.0"
34
  __author__ = "Aryan Jain"
 
11
  from ._proposal_detailed_analysis import ProposalDetailAnalysis, OperationCriteria
12
  from ._proposal_query_models import QueryType, CreateOrUpdateType
13
  from ._letters import Letter, LetterType
14
+ from ._evaluation import EvaluationCriteria, EvaluationCriteriaType
15
 
16
  __all__ = [
17
  "Base",
 
30
  "GateCriteria",
31
  "Letter",
32
  "LetterType",
33
+ "EvaluationCriteria",
34
+ "EvaluationCriteriaType",
35
  ]
36
  __version__ = "0.1.0"
37
  __author__ = "Aryan Jain"
src/models/_evaluation.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from enum import Enum as PyEnum
2
+
3
+ from sqlalchemy import (
4
+ Column,
5
+ DateTime,
6
+ Enum,
7
+ Float,
8
+ ForeignKey,
9
+ Integer,
10
+ String,
11
+ func,
12
+ )
13
+ from sqlalchemy.dialects.postgresql import UUID
14
+ from pydantic import BaseModel
15
+ from ._base import Base
16
+
17
+ class EvaluationCriteriaType(PyEnum):
18
+ EVALUATION = "EVALUATION"
19
+ GATE = "GATE"
20
+
21
+ class EvaluationCriteria(Base):
22
+ __tablename__ = "evaluation_criteria_details"
23
+ id = Column(UUID(as_uuid=True), primary_key=True, nullable=False)
24
+ rfp_id = Column(UUID(as_uuid=True), ForeignKey("rfps.id", ondelete="CASCADE"), nullable=False)
25
+ evaluation_criteria = Column(String, nullable=True)
26
+ evaluation_criteria_type = Column(Enum(EvaluationCriteriaType), nullable=False)
27
+ created_at = Column(DateTime, nullable=False, default=func.now())
28
+ updated_at = Column(DateTime, nullable=False, default=func.now(), onupdate=func.now())
src/repositories/__init__.py CHANGED
@@ -3,6 +3,7 @@ from ._rfp_repository import RFPRepository
3
  from ._proposal_repository import ProposalRepository
4
  from ._proposal_detailed_analysis_repository import ProposalDetailedAnalysisRepository
5
  from ._letter_repository import LetterRepository
 
6
 
7
  __all__ = [
8
  "BaseRepository",
@@ -10,6 +11,7 @@ __all__ = [
10
  "ProposalRepository",
11
  "ProposalDetailedAnalysisRepository",
12
  "LetterRepository",
 
13
  ]
14
  __version__ = "0.1.0"
15
  __author__ = "Aryan Jain"
 
3
  from ._proposal_repository import ProposalRepository
4
  from ._proposal_detailed_analysis_repository import ProposalDetailedAnalysisRepository
5
  from ._letter_repository import LetterRepository
6
+ from ._evaluation_repository import EvaluationRepository
7
 
8
  __all__ = [
9
  "BaseRepository",
 
11
  "ProposalRepository",
12
  "ProposalDetailedAnalysisRepository",
13
  "LetterRepository",
14
+ "EvaluationRepository",
15
  ]
16
  __version__ = "0.1.0"
17
  __author__ = "Aryan Jain"
src/repositories/_evaluation_repository.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ._base_repository import BaseRepository
2
+ from src.models import EvaluationCriteria, EvaluationCriteriaType
3
+
4
+ import uuid
5
+ from sqlalchemy import select
6
+
7
+ class EvaluationRepository(BaseRepository):
8
+ def __init__(self):
9
+ super().__init__(EvaluationCriteria)
10
+
11
+ async def __aenter__(self):
12
+ return self
13
+
14
+ async def __aexit__(self, exc_type, exc_value, traceback):
15
+ pass
16
+
17
+ async def get_criteria(self, id: str = None, rfp_id: str = None, evaluation_criteria_type: EvaluationCriteriaType = None):
18
+ async with self.get_session() as session:
19
+ query = select(EvaluationCriteria)
20
+ if id:
21
+ query = query.where(EvaluationCriteria.id == id)
22
+ if rfp_id:
23
+ query = query.where(EvaluationCriteria.rfp_id == rfp_id)
24
+ if evaluation_criteria_type:
25
+ query = query.where(EvaluationCriteria.evaluation_criteria_type == evaluation_criteria_type)
26
+ output = await session.execute(query)
27
+ results = output.scalars().all()
28
+ return [{k: v for k, v in result.__dict__.items() if not k.startswith('_')} for result in results]
29
+
30
+ async def create_criteria(self, evaluation_criteria: dict):
31
+ async with self.get_session() as session:
32
+ evaluation_criteria_type = evaluation_criteria["evaluation_criteria_type"]
33
+ rfp_id = evaluation_criteria["rfp_id"]
34
+ query = select(EvaluationCriteria).where(EvaluationCriteria.evaluation_criteria_type == evaluation_criteria_type, EvaluationCriteria.rfp_id == rfp_id)
35
+ output = await session.execute(query)
36
+ instances = output.scalars().all()
37
+ if instances:
38
+ return False
39
+ evaluation_criteria["id"] = str(uuid.uuid4())
40
+ instance = EvaluationCriteria(**evaluation_criteria)
41
+ session.add(instance)
42
+ await session.commit()
43
+ await session.refresh(instance)
44
+ return [{k: v for k, v in instance.__dict__.items() if not k.startswith('_')}]
45
+
46
+ async def update_criteria(self, evaluation_criteria: dict):
47
+ async with self.get_session() as session:
48
+ id = evaluation_criteria["id"]
49
+ query = select(EvaluationCriteria).where(EvaluationCriteria.id == id)
50
+ output = await session.execute(query)
51
+ instance = output.scalars().one()
52
+ rfp_id = instance.rfp_id
53
+ if "evaluation_criteria_type" in evaluation_criteria:
54
+ query = select(EvaluationCriteria).where(EvaluationCriteria.evaluation_criteria_type == evaluation_criteria["evaluation_criteria_type"], EvaluationCriteria.rfp_id == rfp_id)
55
+ output = await session.execute(query)
56
+ instances = output.scalars().all()
57
+ if instances:
58
+ return False
59
+ for k, v in evaluation_criteria.items():
60
+ setattr(instance, k, v)
61
+ await session.commit()
62
+ await session.refresh(instance)
63
+ return [{k: v for k, v in instance.__dict__.items() if not k.startswith('_')}]
64
+
65
+ async def delete_criteria(self, id: str = None, rfp_id: str = None):
66
+ async with self.get_session() as session:
67
+ query = select(EvaluationCriteria)
68
+ if id:
69
+ query = query.where(EvaluationCriteria.id == id)
70
+ if rfp_id:
71
+ query = query.where(EvaluationCriteria.rfp_id == rfp_id)
72
+ output = await session.execute(query)
73
+ instances = output.scalars().all()
74
+ for instance in instances:
75
+ await session.delete(instance)
76
+ await session.commit()
77
+ return True
src/services/__init__.py CHANGED
@@ -7,6 +7,7 @@ from ._proposal_ai_analysis_service import (
7
  )
8
  from ._proposal_detailed_analysis_service import ProposalDetailedAnalysisService
9
  from ._letter_service import LetterService
 
10
 
11
  __all__ = [
12
  "RFPService",
@@ -16,6 +17,7 @@ __all__ = [
16
  "CreateOrUpdateType",
17
  "ProposalDetailedAnalysisService",
18
  "LetterService",
 
19
  ]
20
  __version__ = "0.1.0"
21
  __author__ = "Aryan Jain"
 
7
  )
8
  from ._proposal_detailed_analysis_service import ProposalDetailedAnalysisService
9
  from ._letter_service import LetterService
10
+ from ._evaluation_service import EvaluationService
11
 
12
  __all__ = [
13
  "RFPService",
 
17
  "CreateOrUpdateType",
18
  "ProposalDetailedAnalysisService",
19
  "LetterService",
20
+ "EvaluationService",
21
  ]
22
  __version__ = "0.1.0"
23
  __author__ = "Aryan Jain"
src/services/_evaluation_service.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from src.repositories import EvaluationRepository
2
+ from src.models import EvaluationCriteriaType
3
+
4
+ class EvaluationService:
5
+ def __init__(self):
6
+ self._evaluation_repository = EvaluationRepository
7
+
8
+ async def __aenter__(self):
9
+ return self
10
+
11
+ async def __aexit__(self, exc_type, exc_value, traceback):
12
+ pass
13
+
14
+ async def get_criteria(self, id: str = None, rfp_id: str = None, evaluation_criteria_type: EvaluationCriteriaType = None):
15
+ async with self._evaluation_repository() as repository:
16
+ return await repository.get_criteria(id=id, rfp_id=rfp_id, evaluation_criteria_type=evaluation_criteria_type)
17
+
18
+ async def create_criteria(self, evaluation_criteria: dict):
19
+ async with self._evaluation_repository() as repository:
20
+ return await repository.create_criteria(evaluation_criteria)
21
+
22
+ async def update_criteria(self, evaluation_criteria: dict):
23
+ async with self._evaluation_repository() as repository:
24
+ return await repository.update_criteria(evaluation_criteria)
25
+
26
+ async def delete_criteria(self, id: str = None, rfp_id: str = None):
27
+ async with self._evaluation_repository() as repository:
28
+ return await repository.delete_criteria(id=id, rfp_id=rfp_id)