File size: 13,708 Bytes
3efedb0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
import sys
import os

# Add the project root directory to the path
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.path.append(project_root)

print(f"Project root: {project_root}")
print(f"Python path: {sys.path}")

# Import models
from src.api.loan_model import LoanApprovalModel
from src.api.attrition_model import AttritionModel
from src.api.diabetes_model import DiabetesModel
from src.api.liver_model import LiverDiseaseModel
from fastapi import FastAPI, HTTPException, Depends
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import List, Optional, Dict, Union, Any
from src.data.database import Application, Prediction, get_db, SessionLocal
import json
import os
from datetime import datetime
import logging

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = FastAPI(
    title="Multi-Model Prediction API",
    description="API for predicting loan approval, employee attrition, diabetes risk, and liver disease",
    version="1.0.0"
)

# Add CORS middleware
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # Allows all origins
    allow_credentials=True,
    allow_methods=["*"],  # Allows all methods
    allow_headers=["*"],  # Allows all headers
)

# Set up model paths
model_dir = os.path.join(project_root, "models")
print(f"Model directory: {model_dir}")

# Load the trained model
loan_model = LoanApprovalModel(model_dir=model_dir)

# Fix the import for liver disease model
try:
    from src.api.liver_disease_model import LiverDiseaseModel
except ImportError:
    try:
        from liver_disease_model import LiverDiseaseModel
    except ImportError:
        print("Warning: Could not import LiverDiseaseModel")

# Load models
try:
    attrition_model = AttritionModel()
    diabetes_model = DiabetesModel()
    liver_model = LiverDiseaseModel()  # Use the correct class name
    logger.info("All models loaded successfully")
except Exception as e:
    logger.error(f"Error loading models: {str(e)}")
    # Continue execution even if model loading fails
    # Models will be initialized as needed

# Define response model for predictions
class PredictionResponse(BaseModel):
    prediction: bool
    probability: float
    feature_importance: Optional[List[float]] = None

# Define request model for attrition prediction
class AttritionPredictionRequest(BaseModel):
    Age: int
    DistanceFromHome: int
    EnvironmentSatisfaction: int
    JobLevel: int
    JobSatisfaction: int
    MonthlyIncome: int
    OverTime: str
    TotalWorkingYears: int
    WorkLifeBalance: int
    YearsAtCompany: int

class LoanFeatures(BaseModel):
    no_of_dependents: int
    education: str
    self_employed: str
    income_annum: float
    loan_amount: float
    loan_term: int
    cibil_score: int
    residential_assets_value: float
    commercial_assets_value: float
    luxury_assets_value: float
    bank_asset_value: float

@app.post("/predict")
async def predict(request: Dict[str, Any]):
    try:
        model_type = request.get("model_type", "").lower()
        features = request.get("features", {})
        
        if model_type == "liver":
            result = liver_model.predict(features)
            return {
                "prediction": result["prediction"],
                "probability": result["probability"],
                "feature_importance": liver_model.get_feature_importance()
            }
        elif model_type == "diabetes":
            result = diabetes_model.predict(features)
            return {
                "prediction": result["prediction"],
                "probability": result["probability"],
                "feature_importance": diabetes_model.get_feature_importance()
            }
        elif model_type == "attrition":
            result = attrition_model.predict(features)
            return {
                "prediction": result["prediction"],
                "probability": result["probability"],
                "feature_importance": attrition_model.get_feature_importance()
            }
        elif model_type == "loan":
            result = loan_model.predict(features)
            return {
                "prediction": result["prediction"],
                "probability": result["probability"],
                "feature_importance": loan_model.get_feature_importance()
            }
        else:
            raise HTTPException(status_code=400, detail=f"Unknown model type: {model_type}")
    except Exception as e:
        logger.error(f"Error in prediction: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))

@app.post("/predict/attrition", response_model=PredictionResponse)
async def predict_attrition(request: AttritionPredictionRequest):
    try:
        # Convert request to dictionary
        features = request.dict()
        
        # Make prediction
        result = attrition_model.predict(features)
        
        return result
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

# Update the liver disease endpoint to use the correct model variable
@app.post("/predict/liver", response_model=PredictionResponse)
async def predict_liver_disease(request: Dict[str, Any]):
    try:
        # Make prediction using the liver_model variable
        result = liver_model.predict(request)
        
        # Get feature importance if available
        feature_importance = None
        if hasattr(liver_model, 'get_feature_importance') and callable(getattr(liver_model, 'get_feature_importance')):
            feature_importance = liver_model.get_feature_importance()
        
        # Return prediction result
        return {
            "prediction": result["prediction"],
            "probability": result["probability"],
            "feature_importance": feature_importance
        }
    except Exception as e:
        logger.error(f"Error in liver disease prediction: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))

# Add this endpoint if it doesn't exist
@app.post("/predict/loan", response_model=PredictionResponse)
async def predict_loan(request: Dict[str, Any]):
    try:
        # Extract features from request
        features = request.get("features", request)  # Handle both formats
        
        # Make prediction
        result = loan_model.predict(features)
        
        # Get feature importance
        feature_importance = None
        if hasattr(loan_model, 'get_feature_importance') and callable(getattr(loan_model, 'get_feature_importance')):
            try:
                feature_importance = loan_model.get_feature_importance()
            except Exception as e:
                logger.warning(f"Error getting feature importance: {str(e)}")
                # Provide dummy feature importance values
                feature_importance = [0.2, 0.15, 0.15, 0.1, 0.1, 0.08, 0.07, 0.05, 0.05, 0.03, 0.02]
        else:
            # If method doesn't exist, provide dummy values
            logger.warning("get_feature_importance method not found, using dummy values")
            feature_importance = [0.2, 0.15, 0.15, 0.1, 0.1, 0.08, 0.07, 0.05, 0.05, 0.03, 0.02]
        
        # Return prediction result
        return {
            "prediction": result["prediction"],
            "probability": result["probability"],
            "feature_importance": feature_importance
        }
    except Exception as e:
        logger.error(f"Error in loan prediction: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))

@app.post("/predict/loan_approval")
async def predict_loan_approval(features: LoanFeatures):
    """Predict loan approval based on applicant features."""
    try:
        # Convert Pydantic model to dict
        features_dict = features.dict()
        
        # Log input features for debugging
        logger.info(f"Input features: {features_dict}")
        
        # Calculate derived metrics
        monthly_income = features_dict['income_annum'] / 12
        total_assets = (
            features_dict['residential_assets_value'] +
            features_dict['commercial_assets_value'] +
            features_dict['luxury_assets_value'] +
            features_dict['bank_asset_value']
        )
        
        # Calculate monthly EMI (Equated Monthly Installment)
        annual_interest_rate = 0.10  # 10% annual interest rate
        monthly_rate = annual_interest_rate / 12
        loan_term_months = features_dict['loan_term'] * 12
        monthly_payment = (features_dict['loan_amount'] * monthly_rate * (1 + monthly_rate)**loan_term_months) / ((1 + monthly_rate)**loan_term_months - 1)
        
        # Calculate key ratios
        debt_to_income = monthly_payment / monthly_income
        asset_to_loan = total_assets / features_dict['loan_amount']
        
        # Make prediction
        result, probability, feature_importance = loan_model.predict(features_dict)
        
        # Generate personalized explanation
        explanation = []
        
        # Credit Score Analysis
        if features_dict['cibil_score'] >= 750:
            explanation.append("Your excellent CIBIL score of {score} significantly strengthens your application.".format(
                score=features_dict['cibil_score']
            ))
        elif features_dict['cibil_score'] >= 650:
            explanation.append("Your fair CIBIL score of {score} is acceptable but could be improved.".format(
                score=features_dict['cibil_score']
            ))
        else:
            explanation.append("Your CIBIL score of {score} is below the preferred threshold.".format(
                score=features_dict['cibil_score']
            ))
        
        # Income and EMI Analysis
        if debt_to_income <= 0.3:
            explanation.append("Your monthly loan payment (₹{emi:,.2f}) represents {ratio:.1%} of your monthly income (₹{income:,.2f}), which is very manageable.".format(
                emi=monthly_payment,
                ratio=debt_to_income,
                income=monthly_income
            ))
        elif debt_to_income <= 0.5:
            explanation.append("Your monthly loan payment (₹{emi:,.2f}) represents {ratio:.1%} of your monthly income (₹{income:,.2f}), which is moderate but acceptable.".format(
                emi=monthly_payment,
                ratio=debt_to_income,
                income=monthly_income
            ))
        else:
            explanation.append("Your monthly loan payment (₹{emi:,.2f}) represents {ratio:.1%} of your monthly income (₹{income:,.2f}), which is relatively high.".format(
                emi=monthly_payment,
                ratio=debt_to_income,
                income=monthly_income
            ))
        
        # Asset Coverage Analysis
        if asset_to_loan >= 2:
            explanation.append("Your total assets (₹{assets:,.2f}) provide excellent coverage at {ratio:.1f}x the loan amount.".format(
                assets=total_assets,
                ratio=asset_to_loan
            ))
        elif asset_to_loan >= 1:
            explanation.append("Your total assets (₹{assets:,.2f}) adequately cover the loan amount at {ratio:.1f}x.".format(
                assets=total_assets,
                ratio=asset_to_loan
            ))
        else:
            explanation.append("Your total assets (₹{assets:,.2f}) provide limited coverage at {ratio:.1f}x the loan amount.".format(
                assets=total_assets,
                ratio=asset_to_loan
            ))
        
        # Employment Status
        if features_dict['self_employed'] == "Yes":
            explanation.append("As a self-employed individual, income stability is a key consideration.")
        else:
            explanation.append("Your salaried employment status provides income stability.")
        
        # Education
        if features_dict['education'] == "Graduate":
            explanation.append("Your graduate education is viewed favorably.")
        
        # Dependents
        if features_dict['no_of_dependents'] > 2:
            explanation.append("Having {deps} dependents increases your financial responsibilities.".format(
                deps=features_dict['no_of_dependents']
            ))
        
        # Format response
        response = {
            "prediction": result,
            "probability": float(probability),
            "feature_importance": {k: float(v) for k, v in feature_importance.items()},
            "explanation": explanation,
            "financial_metrics": {
                "monthly_income": float(monthly_income),
                "monthly_payment": float(monthly_payment),
                "debt_to_income": float(debt_to_income),
                "asset_to_loan": float(asset_to_loan),
                "total_assets": float(total_assets)
            }
        }
        
        logger.info(f"Prediction response: {response}")
        return response
        
    except Exception as e:
        logger.error(f"Error making prediction: {str(e)}")
        logger.exception("Detailed traceback:")
        raise HTTPException(status_code=500, detail=str(e))

@app.get("/health")
async def health_check():
    return {"status": "healthy"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="127.0.0.1", port=8000)