BankBot-AI / backend /app /ai /budget_planner.py
mohsin-devs's picture
Deploy to HF
a282d4b
Raw
History Blame Contribute Delete
12.6 kB
"""
Smart Budget Planner for BankBot
Categorizes spending and provides budgeting insights
"""
import json
import os
import numpy as np
import pandas as pd
from datetime import datetime, timedelta
from collections import defaultdict
import uuid
BUDGET_FILE = "budgets.json"
# Category keywords for automatic categorization
CATEGORY_KEYWORDS = {
"Food & Dining": ["restaurant", "food", "cafe", "pizza", "burger", "biryani", "zomato", "swiggy", "coffee", "tea", "meal"],
"Shopping": ["shop", "store", "mall", "amazon", "flipkart", "ebay", "retail", "boutique", "apparel", "clothes"],
"Travel": ["uber", "taxi", "bus", "flight", "train", "travel", "hotel", "airline", "booking", "transport"],
"Entertainment": ["movie", "cinema", "game", "netflix", "spotify", "music", "ticket", "concert", "show"],
"Bills & Utilities": ["electricity", "water", "gas", "internet", "mobile", "phone", "bill", "subscription"],
"Healthcare": ["hospital", "doctor", "pharmacy", "medical", "health", "clinic", "medicine"],
"Groceries": ["grocery", "supermarket", "vegetables", "fruits", "milk", "wheat", "bazar"],
"Fitness": ["gym", "yoga", "fitness", "sports", "training", "coach"],
"Insurance": ["insurance", "premium", "policy"],
"Education": ["school", "college", "course", "book", "tuition", "fees"],
"Loan & EMI": ["loan", "emi", "mortgage", "credit"],
"Transfer": ["transfer", "sent", "payment"]
}
class BudgetPlanner:
"""Smart budget planning and expense tracking"""
def __init__(self):
self.budgets = self.load_budgets()
def load_budgets(self):
"""Load saved budgets from file"""
if os.path.exists(BUDGET_FILE):
try:
with open(BUDGET_FILE, "r", encoding="utf-8") as f:
return json.load(f)
except Exception as e:
print(f"Error loading budgets: {e}")
return {}
return {}
def save_budgets(self):
"""Save budgets to file"""
try:
with open(BUDGET_FILE, "w", encoding="utf-8") as f:
json.dump(self.budgets, f, indent=4, ensure_ascii=False)
except Exception as e:
print(f"Error saving budgets: {e}")
def categorize_transaction(self, description, amount=0):
"""
Automatically categorize a transaction based on description
Returns: Category name
"""
description_lower = description.lower()
# Check against keywords
for category, keywords in CATEGORY_KEYWORDS.items():
if any(keyword in description_lower for keyword in keywords):
return category
# Default category
return "Other"
def set_budget_limit(self, username, category, limit):
"""Set budget limit for a spending category"""
if username not in self.budgets:
self.budgets[username] = {}
self.budgets[username][category] = {
"limit": limit,
"created_at": datetime.now().isoformat(),
"alerts": []
}
self.save_budgets()
def analyze_spending(self, username, transactions, period_days=30):
"""
Analyze spending by category for a given period
Returns: Categorized spending data
"""
if not transactions:
return {}
# Filter transactions from last N days
cutoff_date = datetime.now() - timedelta(days=period_days)
recent_txns = []
for txn in transactions:
try:
txn_date = datetime.fromisoformat(txn.get('date', ''))
if txn_date > cutoff_date and txn.get('type') == 'debit':
recent_txns.append(txn)
except:
pass
# Categorize transactions
spending_by_category = defaultdict(float)
categorized_txns = defaultdict(list)
for txn in recent_txns:
category = self.categorize_transaction(
txn.get('description', txn.get('details', '')),
float(txn.get('amount', 0))
)
amount = float(txn.get('amount', 0))
spending_by_category[category] += amount
categorized_txns[category].append({
'date': txn.get('date'),
'amount': amount,
'details': txn.get('details', '')
})
return {
"period_days": period_days,
"spending_by_category": dict(spending_by_category),
"categorized_transactions": dict(categorized_txns),
"total_spending": sum(spending_by_category.values()),
"transaction_count": len(recent_txns)
}
def check_budget_alerts(self, username, spending_analysis):
"""Check if any spending categories exceed their budgets"""
alerts = []
if username not in self.budgets:
return alerts
user_budgets = self.budgets.get(username, {})
spending = spending_analysis.get('spending_by_category', {})
for category, budget_info in user_budgets.items():
if category not in spending:
continue
spent = spending[category]
limit = budget_info.get('limit', 0)
if spent > limit:
percentage = (spent / limit) * 100
alerts.append({
"category": category,
"spent": round(spent, 2),
"limit": limit,
"percentage": round(percentage, 1),
"excess": round(spent - limit, 2),
"severity": "high" if percentage > 150 else "medium" if percentage > 100 else "low",
"timestamp": datetime.now().isoformat()
})
return alerts
def generate_budget_plan(self, username, transactions, monthly_income=50000):
"""Generate recommended budget plan based on spending patterns"""
spending_analysis = self.analyze_spending(username, transactions, period_days=90)
spending = spending_analysis.get('spending_by_category', {})
total_spending = spending_analysis.get('total_spending', 0)
avg_monthly_spending = total_spending / 3 if total_spending > 0 else 0
# Calculate budget percentages (50/30/20 rule variant)
recommended_budgets = {}
if spending:
for category, amount in spending.items():
percentage = (amount / total_spending * 100) if total_spending > 0 else 0
recommended_budget = (percentage / 100) * monthly_income
recommended_budgets[category] = round(recommended_budget, 2)
# Add default categories if not present
default_categories = {
"Food & Dining": monthly_income * 0.08,
"Shopping": monthly_income * 0.10,
"Travel": monthly_income * 0.08,
"Bills & Utilities": monthly_income * 0.15,
"Entertainment": monthly_income * 0.05,
"Savings": monthly_income * 0.20,
}
for category, amount in default_categories.items():
if category not in recommended_budgets:
recommended_budgets[category] = amount
return {
"monthly_income": monthly_income,
"current_monthly_avg": round(avg_monthly_spending, 2),
"recommended_budgets": recommended_budgets,
"savings_potential": round(monthly_income - avg_monthly_spending, 2),
"budget_breakdown": {
"essentials": round(monthly_income * 0.50, 2), # Bills, groceries, insurance
"lifestyle": round(monthly_income * 0.30, 2), # Entertainment, dining, shopping
"savings": round(monthly_income * 0.20, 2) # Emergency fund, investments
}
}
def predict_monthly_spending(self, username, transactions):
"""
Predict future spending using historical data
Returns: Predicted spending for next month
"""
if not transactions:
return {}
# Analyze last 3 months
predictions = {}
for period in [30, 60, 90]:
analysis = self.analyze_spending(username, transactions, period_days=period)
spending = analysis.get('spending_by_category', {})
# Calculate trends
for category, amount in spending.items():
if category not in predictions:
predictions[category] = []
predictions[category].append(amount)
# Calculate averages and trends
predicted_spending = {}
for category, amounts in predictions.items():
if amounts:
predicted_spending[category] = {
"predicted": round(np.mean(amounts), 2),
"trend": "increasing" if amounts[-1] > amounts[0] else "decreasing",
"variance": round(np.std(amounts), 2)
}
return predicted_spending
def get_savings_suggestions(self, username, spending_analysis, monthly_income=50000):
"""Generate specific savings suggestions"""
suggestions = []
spending = spending_analysis.get('spending_by_category', {})
# Check each category and provide suggestions
for category, amount in spending.items():
percentage = (amount / monthly_income) * 100 if monthly_income > 0 else 0
if category == "Food & Dining" and percentage > 10:
reduction = amount - (monthly_income * 0.08)
suggestions.append({
"category": "Food & Dining",
"potential_savings": round(reduction, 2),
"suggestion": f"You can save ₹{round(reduction, 2)} by reducing dining expenses by 10%",
"priority": "high" if reduction > 1000 else "medium"
})
elif category == "Shopping" and percentage > 12:
reduction = amount - (monthly_income * 0.10)
suggestions.append({
"category": "Shopping",
"potential_savings": round(reduction, 2),
"suggestion": f"Reduce impulse purchases to save ₹{round(reduction, 2)} monthly",
"priority": "high" if reduction > 1000 else "medium"
})
elif category == "Entertainment" and percentage > 7:
reduction = amount - (monthly_income * 0.05)
suggestions.append({
"category": "Entertainment",
"potential_savings": round(reduction, 2),
"suggestion": f"Optimize subscriptions and entertainment to save ₹{round(reduction, 2)}",
"priority": "low"
})
# Overall savings tip
total_savings = sum(s.get('potential_savings', 0) for s in suggestions)
if total_savings > 0:
suggestions.append({
"category": "Total Potential Savings",
"potential_savings": round(total_savings, 2),
"suggestion": f"By following these suggestions, you can save ₹{round(total_savings, 2)} per month",
"priority": "high"
})
return suggestions
def get_budget_insights(username, transactions, users_data):
"""Get comprehensive budget insights for a user"""
planner = BudgetPlanner()
user_data = users_data.get(username, {})
monthly_income = user_data.get('monthly_income', 50000)
spending_analysis = planner.analyze_spending(username, transactions)
budget_alerts = planner.check_budget_alerts(username, spending_analysis)
budget_plan = planner.generate_budget_plan(username, transactions, monthly_income)
savings_suggestions = planner.get_savings_suggestions(username, spending_analysis, monthly_income)
predicted_spending = planner.predict_monthly_spending(username, transactions)
return {
"spending_analysis": spending_analysis,
"budget_alerts": budget_alerts,
"budget_plan": budget_plan,
"savings_suggestions": savings_suggestions,
"predicted_spending": predicted_spending
}