import os from datetime import datetime, timedelta from sqlalchemy.orm import Session from app.database.models import User, Account, Transaction, Goal, Investment, Subscription from app.ai.forecasting import get_cashflow_metrics from app.ai.ollama_integration import get_ai_response def calculate_financial_health_score(db: Session, user_id: str): """ Computes a multi-dimensional Financial Health Score (0-100) based on real database records. """ accounts = db.query(Account).filter(Account.user_id == user_id).all() total_balance = sum(acc.balance for acc in accounts) savings_balance = sum(acc.balance for acc in accounts if acc.type.lower() == "savings") # Cashflow current_balance, daily_income, daily_spending = get_cashflow_metrics(db, user_id) monthly_income = max(1000.0, daily_income * 30.4) monthly_spending = daily_spending * 30.4 # 1. Savings Consistency (20 pts) # Check frequency of saving transactions or goal additions txns = db.query(Transaction).join(Account).filter( Account.user_id == user_id, Transaction.type == "credit", Transaction.category == "Income" ).count() # Let's say if they have active goals with current_amount > 0, they get higher points goals = db.query(Goal).filter(Goal.user_id == user_id).all() goal_savings = sum(g.current_amount for g in goals) savings_score = 10.0 if goal_savings > 1000: savings_score += 10.0 elif goal_savings > 0: savings_score += 5.0 # 2. Debt Ratio (20 pts) # Estimate EMIs or goals with "debt" debt_goals = sum(g.target_amount - g.current_amount for g in goals if "debt" in g.title.lower() or "loan" in g.title.lower()) # Standard monthly debt service (estimate 10% of debt or $150 minimum if debt exists) est_monthly_debt = max(0.0, debt_goals * 0.05) debt_to_income = est_monthly_debt / monthly_income debt_score = 20.0 if debt_to_income > 0.40: debt_score = 5.0 elif debt_to_income > 0.20: debt_score = 12.0 elif debt_to_income > 0.05: debt_score = 18.0 # 3. Spending Discipline (20 pts) # Ratio of monthly spending to monthly income savings_rate = (monthly_income - monthly_spending) / monthly_income if monthly_income > 0 else 0 discipline_score = 10.0 if savings_rate >= 0.30: discipline_score = 20.0 elif savings_rate >= 0.15: discipline_score = 16.0 elif savings_rate >= 0.0: discipline_score = 12.0 # 4. Emergency Fund (20 pts) # Do they have 3-6 months of expenses in savings? monthly_expenses = max(500.0, monthly_spending) months_buffer = savings_balance / monthly_expenses emergency_score = 0.0 if months_buffer >= 6.0: emergency_score = 20.0 elif months_buffer >= 3.0: emergency_score = 15.0 elif months_buffer >= 1.0: emergency_score = 8.0 # 5. Investment Index (10 pts) investments = db.query(Investment).filter(Investment.user_id == user_id).all() inv_total = sum(i.current_value for i in investments) investment_score = 0.0 if inv_total > 5000: investment_score = 10.0 elif inv_total > 0: investment_score = 6.0 # 6. Subscription Efficiency (10 pts) subs = db.query(Subscription).filter(Subscription.user_id == user_id, Subscription.active == True).all() sub_cost = sum(s.amount if s.billing_cycle.lower() == "monthly" else (s.amount / 12) for s in subs) sub_ratio = sub_cost / monthly_income sub_score = 10.0 if sub_ratio > 0.10: # More than 10% of income on subscriptions sub_score = 3.0 elif sub_ratio > 0.05: # More than 5% sub_score = 7.0 # Calculate Overall Score overall_score = savings_score + debt_score + discipline_score + emergency_score + investment_score + sub_score overall_score = min(100.0, max(0.0, overall_score)) # Actionable improvements list improvements = [] if savings_score < 15: improvements.append("Set up automated transfers to your Savings account right after payday.") if debt_score < 15: improvements.append("Prioritize high-interest debt payoffs using the debt avalanche method.") if discipline_score < 15: improvements.append("Discretionary spending (shopping & dining) is high. Try implementing a $50 weekly limit.") if emergency_score < 15: improvements.append(f"Build savings buffer. Try to accumulate at least ${monthly_expenses * 3:,.2f} (3 months of expenses).") if investment_score < 6: improvements.append("Start a low-cost stock index fund SIP to counter inflation.") if sub_score < 8: improvements.append("Conduct an audit of active subscriptions. Cancel duplicate/unused memberships.") if not improvements: improvements.append("Maintain your current financial habits; your portfolio is highly optimized!") # AI Explanation user = db.query(User).filter(User.id == user_id).first() persona = user.financial_personality if user else "Saver" ai_prompt = f""" The user is a '{persona}' with a Financial Health Score of {overall_score:.0f}/100. Sub-scores: - Savings Consistency: {savings_score:.0f}/20 - Debt Management: {debt_score:.0f}/20 - Spending Discipline: {discipline_score:.0f}/20 - Emergency Fund: {emergency_score:.0f}/20 - Investment Allocation: {investment_score:.0f}/10 - Subscription Management: {sub_score:.0f}/10 Write a concise, professional financial analyst explanation of this score. Detail the primary strengths and key weaknesses. Do NOT write a generic chatbot reply. Keep it to 3-4 sentences. Format like a Bloomberg analyst report. """ from app.ai.ollama_integration import has_active_ai_backend explanation = None if has_active_ai_backend(): try: # Hard 8-second timeout so the health score endpoint never hangs import threading result = [None] def _call(): result[0] = get_ai_response(ai_prompt) t = threading.Thread(target=_call, daemon=True) t.start() t.join(timeout=8) explanation = result[0] except Exception: pass if not explanation: explanation = f"As a {persona}, your financial health score of {overall_score:.0f} reflects solid fundamentals with opportunities to optimize emergency allocations and subscription efficiencies. Focus on automating savings and expanding investments." return { "overall_score": round(overall_score, 0), "categories": { "savings_consistency": {"score": round(savings_score, 0), "max": 20}, "debt_ratio": {"score": round(debt_score, 0), "max": 20}, "spending_discipline": {"score": round(discipline_score, 0), "max": 20}, "emergency_funds": {"score": round(emergency_score, 0), "max": 20}, "investments": {"score": round(investment_score, 0), "max": 10}, "subscription_management": {"score": round(sub_score, 0), "max": 10} }, "explanation": explanation, "actionable_improvements": improvements } def generate_daily_briefing(db: Session, user_id: str): """ Pulls complete financial context and generates a personalized daily financial briefing. """ user = db.query(User).filter(User.id == user_id).first() if not user: return {"briefing": "User not found."} # Collect data accounts = db.query(Account).filter(Account.user_id == user_id).all() total_balance = sum(acc.balance for acc in accounts) goals = db.query(Goal).filter(Goal.user_id == user_id).all() goals_summary = [f"{g.title}: {g.current_amount}/{g.target_amount}" for g in goals] investments = db.query(Investment).filter(Investment.user_id == user_id).all() inv_summary = [f"{i.asset_name} ({i.type}): Current Value ${i.current_value:,.2f}" for i in investments] # Cashflow current_balance, daily_income, daily_spending = get_cashflow_metrics(db, user_id) monthly_income = daily_income * 30.4 monthly_spending = daily_spending * 30.4 # Format AI Prompt ai_prompt = f""" You are an AI Wealth Advisor and Predictive Banking Engine. Generate a personalized daily financial briefing for {user.profile_data.get('name', 'User')}. Financial Summary: - User Personality: {user.financial_personality} - Total Account Balance: ${total_balance:,.2f} - Estimated Monthly Income: ${monthly_income:,.2f} - Estimated Monthly Spending: ${monthly_spending:,.2f} - Active Goals: {', '.join(goals_summary) if goals_summary else 'None'} - Investments: {', '.join(inv_summary) if inv_summary else 'None'} Generate a 3-paragraph daily briefing. Paragraph 1: Summary of their current liquidity and portfolio health. Paragraph 2: One specific recommendation regarding their savings goals or investment potential. Paragraph 3: A behavioral spending insight warning based on their spending velocity. Style: Bloomberg Terminal style, highly intelligent, concise, financially meaningful, human-like. Avoid boilerplate generic remarks (e.g. 'You should try saving more money'). Use exact figures. """ from app.ai.ollama_integration import has_active_ai_backend briefing = None if has_active_ai_backend(): try: import threading result = [None] def _call(): result[0] = get_ai_response(ai_prompt) t = threading.Thread(target=_call, daemon=True) t.start() t.join(timeout=10) briefing = result[0] except Exception: pass if not briefing: briefing = f"DAILY BRIEFING:\n\nYour liquid capital stands at ${total_balance:,.2f}. Portfolio indicators suggest regular cashflow velocity. Based on your {user.financial_personality} profile, we advise dedicating a portion of your net surplus to your active goals to optimize compound growth. Avoid non-essential weekend dining and retail spikes to maintain your target trajectory." return { "date": datetime.utcnow().strftime("%Y-%m-%d"), "user_name": user.profile_data.get('name', 'User'), "briefing": briefing, "metrics": { "total_liquid_capital": round(total_balance, 2), "monthly_income_projection": round(monthly_income, 2), "monthly_burn_rate": round(monthly_spending, 2) } }