Spaces:
Sleeping
Sleeping
api
Browse files- DataSetSample.xlsx +0 -0
- app.py +256 -2
DataSetSample.xlsx
CHANGED
Binary files a/DataSetSample.xlsx and b/DataSetSample.xlsx differ
|
|
app.py
CHANGED
@@ -3,12 +3,13 @@ from fastapi import FastAPI, File, HTTPException, status
|
|
3 |
from fastapi.responses import JSONResponse
|
4 |
from pydantic import BaseModel
|
5 |
import json
|
6 |
-
from typing import List, Dict, Any
|
7 |
import pandas as pd
|
8 |
import numpy as np
|
9 |
from sklearn.metrics.pairwise import cosine_similarity
|
10 |
from scipy import sparse
|
11 |
-
from datetime import datetime
|
|
|
12 |
import warnings
|
13 |
import os
|
14 |
import logging
|
@@ -524,3 +525,256 @@ async def get_customer_analysis(customer_id: str):
|
|
524 |
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
525 |
detail=f"Error processing request: {str(e)}"
|
526 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
from fastapi.responses import JSONResponse
|
4 |
from pydantic import BaseModel
|
5 |
import json
|
6 |
+
from typing import List, Dict, Any, Optional
|
7 |
import pandas as pd
|
8 |
import numpy as np
|
9 |
from sklearn.metrics.pairwise import cosine_similarity
|
10 |
from scipy import sparse
|
11 |
+
from datetime import datetime, timedelta
|
12 |
+
from statistics import mean
|
13 |
import warnings
|
14 |
import os
|
15 |
import logging
|
|
|
525 |
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
526 |
detail=f"Error processing request: {str(e)}"
|
527 |
)
|
528 |
+
class CardRecommendation(BaseModel):
|
529 |
+
name: str
|
530 |
+
annual_fee: float
|
531 |
+
rewards_rate: float
|
532 |
+
benefits: str
|
533 |
+
|
534 |
+
class InvestmentRecommendation(BaseModel):
|
535 |
+
type: str
|
536 |
+
risk_level: str
|
537 |
+
expected_return: float
|
538 |
+
min_investment: float
|
539 |
+
|
540 |
+
class RealEstateRecommendation(BaseModel):
|
541 |
+
type: str
|
542 |
+
location: str
|
543 |
+
price: float
|
544 |
+
monthly_payment: float
|
545 |
+
roi_potential: float
|
546 |
+
|
547 |
+
class FinancialAnalysisResponse(BaseModel):
|
548 |
+
monthly_avg_spend: float
|
549 |
+
spend_trend: float
|
550 |
+
credit_score_range: str
|
551 |
+
credit_score_change: str
|
552 |
+
investment_potential: float
|
553 |
+
spending_categories: List[Dict[str, float]]
|
554 |
+
spending_insights: List[str]
|
555 |
+
recent_transactions: List[Dict[str, Any]]
|
556 |
+
card_recommendations: List[CardRecommendation]
|
557 |
+
investment_recommendations: List[InvestmentRecommendation]
|
558 |
+
real_estate_recommendations: List[RealEstateRecommendation]
|
559 |
+
financial_health_score: int
|
560 |
+
action_items: List[str]
|
561 |
+
|
562 |
+
@app.get("/financial-analysis/{customer_id}", response_model=FinancialAnalysisResponse)
|
563 |
+
async def get_financial_analysis(customer_id: str):
|
564 |
+
"""
|
565 |
+
Get comprehensive financial analysis and recommendations for a customer
|
566 |
+
|
567 |
+
Parameters:
|
568 |
+
- customer_id: The ID of the customer
|
569 |
+
|
570 |
+
Returns:
|
571 |
+
- JSON object containing financial analysis and personalized recommendations
|
572 |
+
"""
|
573 |
+
try:
|
574 |
+
# Validate customer
|
575 |
+
if customer_id not in purchase_history['Customer_Id'].unique():
|
576 |
+
raise HTTPException(
|
577 |
+
status_code=status.HTTP_404_NOT_FOUND,
|
578 |
+
detail="Customer not found"
|
579 |
+
)
|
580 |
+
|
581 |
+
# Get customer profile
|
582 |
+
customer_profile = customer_profiles[customer_profiles['Customer_Id'] == customer_id].iloc[0]
|
583 |
+
customer_transactions = purchase_history[purchase_history['Customer_Id'] == customer_id]
|
584 |
+
|
585 |
+
# Calculate basic financial metrics
|
586 |
+
current_month_spending = customer_transactions[
|
587 |
+
customer_transactions['Purchase_Date'] >= datetime.now() - timedelta(days=30)
|
588 |
+
]['Amount (In Dollars)'].sum()
|
589 |
+
|
590 |
+
previous_month_spending = customer_transactions[
|
591 |
+
(customer_transactions['Purchase_Date'] >= datetime.now() - timedelta(days=60)) &
|
592 |
+
(customer_transactions['Purchase_Date'] < datetime.now() - timedelta(days=30))
|
593 |
+
]['Amount (In Dollars)'].sum()
|
594 |
+
|
595 |
+
monthly_avg_spend = customer_transactions.groupby(
|
596 |
+
customer_transactions['Purchase_Date'].dt.to_period('M')
|
597 |
+
)['Amount (In Dollars)'].mean().mean()
|
598 |
+
|
599 |
+
# Calculate spend trend
|
600 |
+
spend_trend = ((current_month_spending - previous_month_spending) / previous_month_spending * 100) if previous_month_spending > 0 else 0
|
601 |
+
|
602 |
+
# Calculate spending categories
|
603 |
+
spending_categories = customer_transactions.groupby('Category')['Amount (In Dollars)'].sum().reset_index()
|
604 |
+
spending_categories_list = [
|
605 |
+
{"category": row['Category'], "amount": float(row['Amount (In Dollars)'])}
|
606 |
+
for _, row in spending_categories.iterrows()
|
607 |
+
]
|
608 |
+
|
609 |
+
# Generate spending insights
|
610 |
+
spending_insights = []
|
611 |
+
|
612 |
+
# Category-based insights
|
613 |
+
for category in spending_categories.itertuples():
|
614 |
+
category_avg = category.Amount / len(customer_transactions['Purchase_Date'].dt.to_period('M').unique())
|
615 |
+
if category_avg > monthly_avg_spend * 0.3:
|
616 |
+
spending_insights.append(f"High spending in {category.Category}: ${category_avg:.2f}/month")
|
617 |
+
|
618 |
+
# Age-based recommendations
|
619 |
+
age = customer_profile['Age']
|
620 |
+
income = customer_profile['Income per year (in dollars)']
|
621 |
+
|
622 |
+
# Determine credit score range (simulated based on age and income)
|
623 |
+
base_score = min(max((age * 10 + income / 1000) / 2, 300), 850)
|
624 |
+
credit_score_range = f"{int(base_score-25)}-{int(base_score+25)}"
|
625 |
+
credit_score_change = "↑ improving" if age > 25 and income > 50000 else "stable"
|
626 |
+
|
627 |
+
# Calculate investment potential (simplified)
|
628 |
+
monthly_income = income / 12
|
629 |
+
investment_potential = max(0, monthly_income - monthly_avg_spend * 1.2)
|
630 |
+
|
631 |
+
# Generate recommendations based on age and income
|
632 |
+
card_recommendations = []
|
633 |
+
investment_recommendations = []
|
634 |
+
real_estate_recommendations = []
|
635 |
+
|
636 |
+
# Credit Card Recommendations
|
637 |
+
if income < 50000:
|
638 |
+
card_recommendations.append(CardRecommendation(
|
639 |
+
name="Cash Back Starter Card",
|
640 |
+
annual_fee=0,
|
641 |
+
rewards_rate=1.5,
|
642 |
+
benefits="No annual fee, 1.5% cash back on all purchases"
|
643 |
+
))
|
644 |
+
elif income < 100000:
|
645 |
+
card_recommendations.append(CardRecommendation(
|
646 |
+
name="Premium Rewards Card",
|
647 |
+
annual_fee=95,
|
648 |
+
rewards_rate=2.5,
|
649 |
+
benefits="Travel insurance, cash back on all purchases, airport lounge access"
|
650 |
+
))
|
651 |
+
else:
|
652 |
+
card_recommendations.append(CardRecommendation(
|
653 |
+
name="Elite Travel Card",
|
654 |
+
annual_fee=495,
|
655 |
+
rewards_rate=3.0,
|
656 |
+
benefits="Comprehensive travel benefits, concierge service, premium insurance"
|
657 |
+
))
|
658 |
+
|
659 |
+
# Investment Recommendations
|
660 |
+
if age < 30:
|
661 |
+
investment_recommendations.extend([
|
662 |
+
InvestmentRecommendation(
|
663 |
+
type="Index Fund",
|
664 |
+
risk_level="High",
|
665 |
+
expected_return=10.0,
|
666 |
+
min_investment=1000.0
|
667 |
+
),
|
668 |
+
InvestmentRecommendation(
|
669 |
+
type="Tech Growth ETF",
|
670 |
+
risk_level="High",
|
671 |
+
expected_return=12.0,
|
672 |
+
min_investment=2000.0
|
673 |
+
)
|
674 |
+
])
|
675 |
+
elif age < 50:
|
676 |
+
investment_recommendations.extend([
|
677 |
+
InvestmentRecommendation(
|
678 |
+
type="Balanced Fund",
|
679 |
+
risk_level="Medium",
|
680 |
+
expected_return=8.0,
|
681 |
+
min_investment=5000.0
|
682 |
+
),
|
683 |
+
InvestmentRecommendation(
|
684 |
+
type="Dividend Growth Stocks",
|
685 |
+
risk_level="Medium",
|
686 |
+
expected_return=7.0,
|
687 |
+
min_investment=10000.0
|
688 |
+
)
|
689 |
+
])
|
690 |
+
else:
|
691 |
+
investment_recommendations.extend([
|
692 |
+
InvestmentRecommendation(
|
693 |
+
type="Bond Fund",
|
694 |
+
risk_level="Low",
|
695 |
+
expected_return=5.0,
|
696 |
+
min_investment=10000.0
|
697 |
+
),
|
698 |
+
InvestmentRecommendation(
|
699 |
+
type="Income Fund",
|
700 |
+
risk_level="Low",
|
701 |
+
expected_return=4.0,
|
702 |
+
min_investment=25000.0
|
703 |
+
)
|
704 |
+
])
|
705 |
+
|
706 |
+
# Real Estate Recommendations
|
707 |
+
if income > 75000:
|
708 |
+
mortgage_capacity = (income * 4) * 0.8 # 80% of 4x annual income
|
709 |
+
monthly_payment = (mortgage_capacity * 0.05) / 12 # Simplified mortgage calculation
|
710 |
+
|
711 |
+
real_estate_recommendations.extend([
|
712 |
+
RealEstateRecommendation(
|
713 |
+
type="Starter Home",
|
714 |
+
location="Suburban Area",
|
715 |
+
price=mortgage_capacity,
|
716 |
+
monthly_payment=monthly_payment,
|
717 |
+
roi_potential=5.0
|
718 |
+
)
|
719 |
+
])
|
720 |
+
|
721 |
+
if income > 150000:
|
722 |
+
real_estate_recommendations.append(
|
723 |
+
RealEstateRecommendation(
|
724 |
+
type="Investment Property",
|
725 |
+
location="Urban Center",
|
726 |
+
price=mortgage_capacity * 0.7,
|
727 |
+
monthly_payment=monthly_payment * 0.7,
|
728 |
+
roi_potential=8.0
|
729 |
+
)
|
730 |
+
)
|
731 |
+
|
732 |
+
# Calculate financial health score
|
733 |
+
savings_ratio = max(0, min(1, (income - monthly_avg_spend * 12) / income))
|
734 |
+
diversity_score = len(spending_categories) / 10
|
735 |
+
stability_score = min(1, len(customer_transactions) / 100)
|
736 |
+
|
737 |
+
financial_health_score = int((savings_ratio * 0.4 + diversity_score * 0.3 + stability_score * 0.3) * 100)
|
738 |
+
|
739 |
+
# Generate action items
|
740 |
+
action_items = []
|
741 |
+
if savings_ratio < 0.2:
|
742 |
+
action_items.append("Increase monthly savings to at least 20% of income")
|
743 |
+
if monthly_avg_spend > monthly_income * 0.7:
|
744 |
+
action_items.append("Review monthly expenses to reduce spending")
|
745 |
+
if len(investment_recommendations) > 0:
|
746 |
+
action_items.append(f"Consider investing in {investment_recommendations[0].type}")
|
747 |
+
|
748 |
+
# Get recent transactions
|
749 |
+
recent_transactions = [
|
750 |
+
{
|
751 |
+
"date": row['Purchase_Date'].strftime('%Y-%m-%d'),
|
752 |
+
"amount": float(row['Amount (In Dollars)'])
|
753 |
+
}
|
754 |
+
for _, row in customer_transactions.sort_values('Purchase_Date', ascending=False).head(10).iterrows()
|
755 |
+
]
|
756 |
+
|
757 |
+
return {
|
758 |
+
"monthly_avg_spend": float(monthly_avg_spend),
|
759 |
+
"spend_trend": float(spend_trend),
|
760 |
+
"credit_score_range": credit_score_range,
|
761 |
+
"credit_score_change": credit_score_change,
|
762 |
+
"investment_potential": float(investment_potential),
|
763 |
+
"spending_categories": spending_categories_list,
|
764 |
+
"spending_insights": spending_insights,
|
765 |
+
"recent_transactions": recent_transactions,
|
766 |
+
"card_recommendations": card_recommendations,
|
767 |
+
"investment_recommendations": investment_recommendations,
|
768 |
+
"real_estate_recommendations": real_estate_recommendations,
|
769 |
+
"financial_health_score": financial_health_score,
|
770 |
+
"action_items": action_items
|
771 |
+
}
|
772 |
+
|
773 |
+
except HTTPException:
|
774 |
+
raise
|
775 |
+
except Exception as e:
|
776 |
+
logger.error(f"Error processing financial analysis for customer {customer_id}: {str(e)}")
|
777 |
+
raise HTTPException(
|
778 |
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
779 |
+
detail=f"Error processing request: {str(e)}"
|
780 |
+
)
|