estimation service refactor

This commit is contained in:
ipu 2025-07-25 17:43:23 +03:00
parent aaba8753ef
commit 18c95083c9
4 changed files with 367 additions and 62 deletions

View file

@ -1,5 +1,6 @@
from pydantic import BaseModel, Field
from typing import Optional, List, Dict, Any
from datetime import date
class InsuranceChatRequest(BaseModel):
message: str = Field(..., description="User message")
@ -20,19 +21,90 @@ class InsuranceChatResponse(BaseModel):
sources: List[Source] = []
history: List[HistoryItem] = []
# New estimation models matching the specified format
class Applicant(BaseModel):
applicant: int
firstName: str
lastName: str
midName: str
phone: str
gender: str
dob: date
nicotine: bool
weight: float
heightFt: int
heightIn: int
class Plan(BaseModel):
id: int
coverage: int
tier: str
class Medication(BaseModel):
applicant: int
name: str
rxcui: str
dosage: str
frequency: str
description: str
class IssueDetail(BaseModel):
key: str
description: str
class Issue(BaseModel):
key: str
details: List[IssueDetail]
class Condition(BaseModel):
key: str
description: str
class PHQ(BaseModel):
treatment: bool
invalid: bool
pregnancy: bool
effectiveDate: date
disclaimer: bool
signature: str
medications: List[Medication]
issues: List[Issue]
conditions: List[Condition]
class Address(BaseModel):
address1: str
address2: str
city: str
state: str
zipcode: str
class EstimationRequest(BaseModel):
uid: str = Field(..., description="Application unique identifier")
applicants: List[Dict[str, Any]] = Field(..., description="List of applicants")
plans: List[Dict[str, Any]] = Field(..., description="List of insurance plans")
phq: Dict[str, Any] = Field(..., description="Personal Health Questionnaire data")
income: Optional[float] = Field(0, description="Annual income")
address: Optional[Dict[str, Any]] = Field({}, description="Address information")
uid: str
applicants: List[Applicant]
plans: List[Plan]
phq: PHQ
income: float
address: Address
class EstimationDetails(BaseModel):
dtq: bool
reason: str
tier: int
total_price: float
class EstimationResult(BaseModel):
name: str
applicant_type: str
age: int
bmi: float
tier: int
rx_spend: float
message: str
class EstimationResponse(BaseModel):
uid: str
status: str
data: Dict[str, Any]
external: Optional[Dict[str, Any]] = None
details: EstimationDetails
results: List[EstimationResult]
class SessionCreateResponse(BaseModel):
session_id: str

View file

@ -39,41 +39,191 @@ async def estimate(request: models.EstimationRequest):
detail="Missing required applicants or plans"
)
# Step 1: Run estimation
underwriting_result = run_underwriting(request.applicants, request.phq, request.plans)
# Convert request to the format expected by run_underwriting
applicants_dict = []
for applicant in request.applicants:
applicants_dict.append({
"applicant": applicant.applicant,
"firstName": applicant.firstName,
"lastName": applicant.lastName,
"midName": applicant.midName,
"phone": applicant.phone,
"gender": applicant.gender,
"dob": applicant.dob.strftime("%d/%m/%Y"),
"nicotine": applicant.nicotine,
"weight": applicant.weight,
"heightFt": applicant.heightFt,
"heightIn": applicant.heightIn
})
# Step 2: If DTQ → reject application
phq_dict = {
"treatment": request.phq.treatment,
"invalid": request.phq.invalid,
"pregnancy": request.phq.pregnancy,
"effectiveDate": request.phq.effectiveDate.strftime("%d/%m/%Y"),
"disclaimer": request.phq.disclaimer,
"signature": request.phq.signature,
"medications": [
{
"applicant": med.applicant,
"name": med.name,
"rxcui": med.rxcui,
"dosage": med.dosage,
"frequency": med.frequency,
"description": med.description
} for med in request.phq.medications
],
"issues": [
{
"key": issue.key,
"details": [
{
"key": detail.key,
"description": detail.description
} for detail in issue.details
]
} for issue in request.phq.issues
],
"conditions": [
{
"key": condition.key,
"description": condition.description
} for condition in request.phq.conditions
]
}
plans_dict = [
{
"id": plan.id,
"coverage": plan.coverage,
"tier": plan.tier
} for plan in request.plans
]
# Step 1: Run estimation
underwriting_result = run_underwriting(applicants_dict, phq_dict, plans_dict)
# Step 2: Check if DTQ → reject application
if underwriting_result["combined"].get("dtq"):
# For DTQ cases, call external reject API and return rejected status
reject_response = await reject_application(request.uid)
return models.EstimationResponse(
uid=request.uid,
status="rejected",
data=underwriting_result,
external=reject_response
details=models.EstimationDetails(
dtq=True,
reason="Declined due to high-risk conditions (DTQ triggered).",
tier=int(underwriting_result["combined"]["tier"]),
total_price=underwriting_result["combined"]["total_price"]
),
results=[
models.EstimationResult(
name=result["name"],
applicant_type=result["applicant_type"],
age=result["age"] or 0,
bmi=result["bmi"] or 0.0,
tier=int(result["tier"]),
rx_spend=result["rx_spend"],
message=result["message"]
) for result in underwriting_result["results"]
]
)
# Step 3: Else → assign tier and submit
# Step 3: Else → assign tier and submit to external API
final_tier = underwriting_result["combined"]["tier"]
plans = request.plans.copy()
if plans:
plans[0]["tier"] = f"tier_{str(final_tier).replace('.', '_')}"
plans[0].tier = f"tier_{str(final_tier).replace('.', '_')}"
# Assemble external payload
submission_payload = {
"applicants": request.applicants,
"plans": plans,
"phq": request.phq,
"applicants": [
{
"applicant": applicant.applicant,
"firstName": applicant.firstName,
"lastName": applicant.lastName,
"midName": applicant.midName,
"phone": applicant.phone,
"gender": applicant.gender,
"dob": applicant.dob.strftime("%d/%m/%Y"),
"nicotine": applicant.nicotine,
"weight": applicant.weight,
"heightFt": applicant.heightFt,
"heightIn": applicant.heightIn
} for applicant in request.applicants
],
"plans": [
{
"id": plan.id,
"coverage": plan.coverage,
"tier": plan.tier
} for plan in plans
],
"phq": {
"treatment": request.phq.treatment,
"invalid": request.phq.invalid,
"pregnancy": request.phq.pregnancy,
"effectiveDate": request.phq.effectiveDate.strftime("%d/%m/%Y"),
"disclaimer": request.phq.disclaimer,
"signature": request.phq.signature,
"medications": [
{
"applicant": med.applicant,
"name": med.name,
"rxcui": med.rxcui,
"dosage": med.dosage,
"frequency": med.frequency,
"description": med.description
} for med in request.phq.medications
],
"issues": [
{
"key": issue.key,
"details": [
{
"key": detail.key,
"description": detail.description
} for detail in issue.details
]
} for issue in request.phq.issues
],
"conditions": [
{
"key": condition.key,
"description": condition.description
} for condition in request.phq.conditions
]
},
"income": request.income,
"address": request.address
"address": {
"address1": request.address.address1,
"address2": request.address.address2,
"city": request.address.city,
"state": request.address.state,
"zipcode": request.address.zipcode
}
}
submit_response = await submit_application(submission_payload)
return models.EstimationResponse(
uid=request.uid,
status="submitted",
data=underwriting_result,
external=submit_response
status="accepted",
details=models.EstimationDetails(
dtq=False,
reason=underwriting_result["combined"]["message"],
tier=int(underwriting_result["combined"]["tier"]),
total_price=underwriting_result["combined"]["total_price"]
),
results=[
models.EstimationResult(
name=result["name"],
applicant_type=result["applicant_type"],
age=result["age"] or 0,
bmi=result["bmi"] or 0.0,
tier=int(result["tier"]),
rx_spend=result["rx_spend"],
message=result["message"]
) for result in underwriting_result["results"]
]
)
except HTTPException:
@ -101,4 +251,7 @@ async def submit_application(application_payload: Dict[str, Any]) -> Dict[str, A
f"{settings.INSURANCE_API_BASE_URL}/applications/submit",
json=application_payload
)
return response.json() if response.status_code == 200 else {"error": "Failed to submit application"}
return response.json() if response.status_code == 200 else {"error": "Failed to submit application"}

View file

@ -128,23 +128,45 @@ async def test_estimation():
"uid": "test-application-123",
"applicants": [
{
"applicant": 1,
"firstName": "John",
"dob": "15/03/1985",
"weight": 70,
"lastName": "Doe",
"midName": "M",
"phone": "555-123-4567",
"gender": "male",
"dob": "1985-03-15",
"nicotine": False,
"weight": 70.0,
"heightFt": 5,
"heightIn": 10,
"applicant": 1
"heightIn": 10
}
],
"plans": [
{
"id": 1,
"coverage": 1,
"tier": "tier_1"
}
],
"plans": [{"coverage": 1}],
"phq": {
"effectiveDate": "01/01/2024",
"treatment": False,
"invalid": False,
"pregnancy": False,
"effectiveDate": "2024-01-01",
"disclaimer": True,
"signature": "John Doe",
"medications": [],
"conditions": [],
"issues": []
"issues": [],
"conditions": []
},
"income": 50000,
"address": {}
"income": 50000.0,
"address": {
"address1": "123 Main St",
"address2": "Apt 4B",
"city": "New York",
"state": "NY",
"zipcode": "10001"
}
}
async with httpx.AsyncClient() as client:
@ -153,8 +175,18 @@ async def test_estimation():
if response.status_code == 200:
data = response.json()
print(f"✅ Estimation successful!")
print(f"Application ID: {data['uid']}")
print(f"Status: {data['status']}")
print(f"Details:")
print(f" DTQ: {data['details']['dtq']}")
print(f" Tier: {data['details']['tier']}")
print(f" Total Price: ${data['details']['total_price']}")
print(f" Reason: {data['details']['reason']}")
print(f"Results:")
for i, result in enumerate(data['results'], 1):
print(f" Applicant {i}: {result['name']} ({result['applicant_type']})")
print(f" Age: {result['age']}, BMI: {result['bmi']}")
print(f" Tier: {result['tier']}, Rx Spend: ${result['rx_spend']}")
print(f" Message: {result['message']}")
else:
print(f"❌ Estimation failed with status {response.status_code}")
print(f"Error: {response.text}")