"""
Gradio Web Interface for Spend Analyzer MCP - Local Version
"""
import gradio as gr
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import json
import os
from typing import Dict, List, Optional, Tuple
from datetime import datetime, timedelta
import logging
import time
class SpendAnalyzerInterface:
def __init__(self):
self.current_analysis = None
self.user_sessions = {}
self.logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
# Load demo data if available
self.demo_data = self._load_demo_data()
def _load_demo_data(self):
"""Load demo data for testing"""
try:
if os.path.exists('demo_data.json'):
with open('demo_data.json', 'r') as f:
return json.load(f)
except Exception as e:
self.logger.warning(f"Could not load demo data: {e}")
# Fallback demo data
return {
"transactions": [
{
"date": "2024-01-15",
"description": "STARBUCKS COFFEE",
"amount": -4.50,
"category": "Food & Dining"
},
{
"date": "2024-01-14",
"description": "AMAZON PURCHASE",
"amount": -29.99,
"category": "Shopping"
},
{
"date": "2024-01-13",
"description": "SALARY DEPOSIT",
"amount": 3000.00,
"category": "Income"
},
{
"date": "2024-01-12",
"description": "GROCERY STORE",
"amount": -85.67,
"category": "Food & Dining"
},
{
"date": "2024-01-11",
"description": "GAS STATION",
"amount": -45.00,
"category": "Gas & Transport"
}
]
}
def create_interface(self):
"""Create the main Gradio interface"""
with gr.Blocks(
title="Spend Analyzer MCP - Local Demo",
css="""
.main-header { text-align: center; margin: 20px 0; }
.status-box { padding: 10px; border-radius: 5px; margin: 10px 0; }
.success-box { background-color: #d4edda; border: 1px solid #c3e6cb; }
.error-box { background-color: #f8d7da; border: 1px solid #f5c6cb; }
.warning-box { background-color: #fff3cd; border: 1px solid #ffeaa7; }
.demo-box { background-color: #e7f3ff; border: 1px solid #b3d9ff; }
"""
) as interface:
gr.Markdown("# 💰 Spend Analyzer MCP - Local Demo", elem_classes=["main-header"])
gr.Markdown("*Analyze your bank statements with AI-powered insights*")
# Demo notice
gr.HTML('
🚀 Demo Mode: This is a local demonstration. Upload real PDFs or use the demo data to explore features.
')
with gr.Tabs():
# Tab 1: Demo Data
with gr.TabItem("🎯 Demo Data"):
self._create_demo_tab()
# Tab 2: PDF Upload
with gr.TabItem("📄 PDF Upload"):
self._create_pdf_tab()
# Tab 3: Analysis Dashboard
with gr.TabItem("📊 Analysis Dashboard"):
self._create_dashboard_tab()
# Tab 4: AI Chat
with gr.TabItem("🤖 AI Financial Advisor"):
self._create_chat_tab()
# Tab 5: Settings
with gr.TabItem("⚙️ Settings"):
self._create_settings_tab()
return interface
def _create_demo_tab(self):
"""Create demo data tab"""
gr.Markdown("## 🎯 Demo Data & Quick Start")
gr.Markdown("*Load sample financial data to explore the features*")
with gr.Row():
with gr.Column():
gr.Markdown("### Sample Transactions")
demo_transactions = gr.JSON(
value=self.demo_data["transactions"],
label="Demo Transaction Data"
)
load_demo_btn = gr.Button("📊 Load Demo Data", variant="primary", size="lg")
with gr.Column():
demo_status = gr.HTML()
gr.Markdown("### Features to Try")
gr.Markdown("""
1. **Load Demo Data** - Click the button to analyze sample transactions
2. **View Dashboard** - See charts and financial summaries
3. **Chat with AI** - Ask questions about spending patterns
4. **Upload PDFs** - Try uploading your own bank statements
5. **Configure Settings** - Set budgets and preferences
""")
# Event handler
load_demo_btn.click(
fn=self._load_demo_data_handler,
outputs=[demo_status]
)
def _create_pdf_tab(self):
"""Create PDF upload tab"""
gr.Markdown("## 📄 Upload Bank Statement PDFs")
gr.Markdown("*Upload your bank statement PDFs for analysis*")
with gr.Row():
with gr.Column():
pdf_upload = gr.File(
label="Upload Bank Statement PDFs",
file_count="multiple",
file_types=[".pdf"]
)
analyze_pdf_btn = gr.Button("📊 Analyze PDFs", variant="primary")
with gr.Column():
pdf_status = gr.HTML()
pdf_results = gr.JSON(label="Analysis Results", visible=False)
# Event handler
analyze_pdf_btn.click(
fn=self._analyze_pdf_files,
inputs=[pdf_upload],
outputs=[pdf_status, pdf_results]
)
def _create_dashboard_tab(self):
"""Create analysis dashboard tab"""
gr.Markdown("## 📊 Financial Analysis Dashboard")
with gr.Row():
refresh_btn = gr.Button("🔄 Refresh Dashboard")
export_btn = gr.Button("📤 Export Analysis")
# Summary cards
with gr.Row():
total_income = gr.Number(label="Total Income ($)", interactive=False)
total_expenses = gr.Number(label="Total Expenses ($)", interactive=False)
net_cashflow = gr.Number(label="Net Cash Flow ($)", interactive=False)
transaction_count = gr.Number(label="Total Transactions", interactive=False)
# Charts
with gr.Row():
with gr.Column():
spending_by_category = gr.Plot(label="Spending by Category")
monthly_trends = gr.Plot(label="Daily Spending Trends")
with gr.Column():
budget_alerts = gr.HTML(label="Budget Alerts")
recommendations = gr.HTML(label="Recommendations")
# Detailed data
with gr.Accordion("Detailed Transaction Data", open=False):
transaction_table = gr.Dataframe(
headers=["Date", "Description", "Amount", "Category"],
interactive=False,
label="Recent Transactions"
)
# Event handlers
refresh_btn.click(
fn=self._refresh_dashboard,
outputs=[total_income, total_expenses, net_cashflow, transaction_count,
spending_by_category, monthly_trends, budget_alerts, recommendations,
transaction_table]
)
export_btn.click(
fn=self._export_analysis,
outputs=[gr.File(label="Analysis Export")]
)
def _create_chat_tab(self):
"""Create AI chat tab"""
gr.Markdown("## 🤖 AI Financial Advisor")
gr.Markdown("*Ask questions about your spending patterns and get insights*")
with gr.Row():
with gr.Column(scale=3):
chatbot = gr.Chatbot(
label="Financial Advisor Chat",
height=400,
show_label=True,
type="messages"
)
with gr.Row():
msg_input = gr.Textbox(
placeholder="Ask about your spending patterns, budgets, or financial goals...",
label="Your Question",
scale=4
)
send_btn = gr.Button("Send", variant="primary", scale=1)
# Quick question buttons
with gr.Row():
budget_btn = gr.Button("💰 Budget Analysis", size="sm")
trends_btn = gr.Button("📈 Spending Trends", size="sm")
tips_btn = gr.Button("💡 Save Money Tips", size="sm")
unusual_btn = gr.Button("🚨 Unusual Activity", size="sm")
with gr.Column(scale=1):
chat_status = gr.HTML()
# Analysis context
gr.Markdown("### Current Analysis Context")
context_info = gr.JSON(
label="Available Data",
value={"status": "Load demo data or upload PDFs to start"}
)
# Event handlers
send_btn.click(
fn=self._handle_chat_message,
inputs=[msg_input, chatbot],
outputs=[chatbot, msg_input, chat_status]
)
msg_input.submit(
fn=self._handle_chat_message,
inputs=[msg_input, chatbot],
outputs=[chatbot, msg_input, chat_status]
)
# Quick question handlers
budget_btn.click(
lambda: "How am I doing with my budget this month?",
outputs=[msg_input]
)
trends_btn.click(
lambda: "What are my spending trends over the last few days?",
outputs=[msg_input]
)
tips_btn.click(
lambda: "What are some specific ways I can save money based on my spending?",
outputs=[msg_input]
)
unusual_btn.click(
lambda: "Are there any unusual transactions I should be aware of?",
outputs=[msg_input]
)
def _create_settings_tab(self):
"""Create settings tab"""
gr.Markdown("## ⚙️ Settings & Configuration")
with gr.Tabs():
with gr.TabItem("Budget Settings"):
gr.Markdown("### Set Monthly Budget Limits")
with gr.Row():
with gr.Column():
budget_categories = gr.CheckboxGroup(
choices=["Food & Dining", "Shopping", "Gas & Transport",
"Utilities", "Entertainment", "Healthcare", "Other"],
label="Categories to Budget",
value=["Food & Dining", "Shopping", "Gas & Transport"]
)
budget_amounts = gr.JSON(
label="Budget Amounts ($)",
value={
"Food & Dining": 500,
"Shopping": 300,
"Gas & Transport": 200,
"Utilities": 150,
"Entertainment": 100,
"Healthcare": 200,
"Other": 100
}
)
save_budgets_btn = gr.Button("💾 Save Budget Settings", variant="primary")
with gr.Column():
budget_status = gr.HTML()
current_budgets = gr.JSON(label="Current Budget Settings")
with gr.TabItem("Export Settings"):
gr.Markdown("### Data Export Options")
export_format = gr.Radio(
choices=["JSON", "CSV"],
label="Export Format",
value="JSON"
)
include_raw_data = gr.Checkbox(
label="Include raw transaction data",
value=True
)
include_analysis = gr.Checkbox(
label="Include analysis results",
value=True
)
# Event handlers
save_budgets_btn.click(
fn=self._save_budget_settings,
inputs=[budget_categories, budget_amounts],
outputs=[budget_status, current_budgets]
)
# Implementation methods
def _load_demo_data_handler(self):
"""Load demo data and create analysis"""
try:
# Simulate processing
time.sleep(1)
# Create mock analysis from demo data
transactions = self.demo_data["transactions"]
total_income = sum(t["amount"] for t in transactions if t["amount"] > 0)
total_expenses = abs(sum(t["amount"] for t in transactions if t["amount"] < 0))
# Group by category
categories = {}
for t in transactions:
if t["amount"] < 0: # Only expenses
cat = t["category"]
if cat not in categories:
categories[cat] = {"total": 0, "count": 0}
categories[cat]["total"] += abs(t["amount"])
categories[cat]["count"] += 1
spending_insights = []
for cat, data in categories.items():
spending_insights.append({
"category": cat,
"total_amount": data["total"],
"transaction_count": data["count"],
"percentage_of_total": (data["total"] / total_expenses) * 100 if total_expenses > 0 else 0
})
self.current_analysis = {
"financial_summary": {
"total_income": total_income,
"total_expenses": total_expenses,
"net_cash_flow": total_income - total_expenses
},
"spending_insights": spending_insights,
"recommendations": [
"Your Food & Dining expenses are significant. Consider cooking more meals at home.",
"Great job maintaining a positive cash flow!",
"Track your daily expenses to identify more saving opportunities."
],
"transaction_count": len(transactions)
}
return '✅ Demo data loaded successfully! Check the Dashboard tab to see your analysis.
'
except Exception as e:
return f'❌ Error loading demo data: {str(e)}
'
def _analyze_pdf_files(self, files):
"""Analyze uploaded PDF files (mock implementation)"""
try:
if not files:
return '❌ No files uploaded
', {}
# Mock PDF analysis
result = {
'processed_files': [],
'total_transactions': 0
}
for file in files:
# Mock processing
file_result = {
'filename': file.name,
'status': 'success',
'message': 'PDF parsing not implemented in demo mode'
}
result['processed_files'].append(file_result)
status_html = f'⚠️ PDF processing is not implemented in demo mode. Use the Demo Data tab instead.
'
return status_html, result
except Exception as e:
error_html = f'❌ Error: {str(e)}
'
return error_html, {}
def _refresh_dashboard(self):
"""Refresh dashboard with current analysis"""
if not self.current_analysis:
return (0, 0, 0, 0, None, None,
'⚠️ No analysis data available. Load demo data first!
',
'⚠️ Load demo data to see recommendations
',
pd.DataFrame())
try:
summary = self.current_analysis.get('financial_summary', {})
insights = self.current_analysis.get('spending_insights', [])
# Summary metrics
total_income = summary.get('total_income', 0)
total_expenses = summary.get('total_expenses', 0)
net_cashflow = summary.get('net_cash_flow', 0)
transaction_count = self.current_analysis.get('transaction_count', 0)
# Create spending by category chart
if insights:
categories = [insight['category'] for insight in insights]
amounts = [insight['total_amount'] for insight in insights]
spending_chart = px.pie(
values=amounts,
names=categories,
title="Spending by Category"
)
spending_chart.update_layout(height=400)
else:
spending_chart = None
# Create daily trends chart
transactions = self.demo_data.get("transactions", [])
if transactions:
# Group by date
daily_spending = {}
for t in transactions:
if t["amount"] < 0: # Only expenses
date = t["date"]
if date not in daily_spending:
daily_spending[date] = 0
daily_spending[date] += abs(t["amount"])
dates = list(daily_spending.keys())
amounts = list(daily_spending.values())
trends_chart = px.line(
x=dates,
y=amounts,
title="Daily Spending Trends",
labels={"x": "Date", "y": "Amount ($)"}
)
trends_chart.update_layout(height=400)
else:
trends_chart = None
# Budget alerts
alert_html = '✅ All spending within reasonable limits
'
# Recommendations
recommendations = self.current_analysis.get('recommendations', [])
if recommendations:
rec_html = '💡 Recommendations:
'
for rec in recommendations:
rec_html += f'- {rec}
'
rec_html += '
'
else:
rec_html = 'No specific recommendations at this time.
'
# Transaction table
transaction_data = []
for t in transactions:
transaction_data.append([
t["date"],
t["description"],
f"${t['amount']:.2f}",
t["category"]
])
transaction_df = pd.DataFrame(
transaction_data,
columns=["Date", "Description", "Amount", "Category"]
)
return (total_income, total_expenses, net_cashflow, transaction_count,
spending_chart, trends_chart, alert_html, rec_html, transaction_df)
except Exception as e:
error_msg = f'❌ Dashboard error: {str(e)}
'
return (0, 0, 0, 0, None, None, error_msg, error_msg, pd.DataFrame())
def _export_analysis(self):
"""Export current analysis data"""
if not self.current_analysis:
return None
try:
import tempfile
# Create temporary file
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
json.dump(self.current_analysis, f, indent=2, default=str)
return f.name
except Exception as e:
self.logger.error(f"Export error: {e}")
return None
def _handle_chat_message(self, message, chat_history):
"""Handle chat messages with AI advisor"""
if not message.strip():
return chat_history, "", '⚠️ Please enter a message
'
try:
# Add user message to chat
chat_history = chat_history or []
chat_history.append([message, None])
# Generate response based on analysis
if self.current_analysis:
summary = self.current_analysis.get('financial_summary', {})
insights = self.current_analysis.get('spending_insights', [])
recommendations = self.current_analysis.get('recommendations', [])
if 'budget' in message.lower():
ai_response = f"Based on your analysis, you have a net cash flow of ${summary.get('net_cash_flow', 0):.2f}. Your total expenses are ${summary.get('total_expenses', 0):.2f} against an income of ${summary.get('total_income', 0):.2f}."
elif 'trend' in message.lower():
if insights:
top_category = insights[0]
ai_response = f"Your top spending category is {top_category['category']} at ${top_category['total_amount']:.2f} ({top_category['percentage_of_total']:.1f}% of total spending)."
else:
ai_response = "I need more transaction data to analyze your spending trends effectively."
elif 'save' in message.lower() or 'tip' in message.lower():
if recommendations:
ai_response = f"Here are some personalized recommendations: {'. '.join(recommendations[:2])}"
else:
ai_response = "Based on your spending patterns, consider tracking your largest expense categories and setting monthly budgets."
elif 'unusual' in message.lower() or 'activity' in message.lower():
ai_response = "I've analyzed your transactions and they appear normal. All transactions seem consistent with typical spending patterns."
else:
ai_response = f"I can see you have {self.current_analysis.get('transaction_count', 0)} transactions analyzed. Feel free to ask about your budget, spending trends, saving tips, or unusual activity!"
status_html = '✅ Response generated
'
else:
ai_response = "I don't have any financial data to analyze yet. Please load the demo data or upload PDFs first!"
status_html = '⚠️ No data available
'
# Update chat history with AI response
chat_history[-1][1] = ai_response
return chat_history, "", status_html
except Exception as e:
error_response = f"I'm sorry, I encountered an error: {str(e)}"
if chat_history:
chat_history[-1][1] = error_response
return chat_history, "", '❌ Chat Error
'
def _save_budget_settings(self, categories, amounts):
"""Save budget settings"""
try:
# Filter amounts for selected categories
budget_settings = {cat: amounts.get(cat, 0) for cat in categories}
# Store in user session
self.user_sessions['budgets'] = budget_settings
status_html = '✅ Budget settings saved
'
return status_html, budget_settings
except Exception as e:
error_html = f'❌ Error saving budgets: {str(e)}
'
return error_html, {}
# Launch the interface
def launch_interface():
"""Launch the Gradio interface"""
interface = SpendAnalyzerInterface()
app = interface.create_interface()
print("🚀 Starting Spend Analyzer MCP - Local Demo")
print("📊 Demo mode: Use the Demo Data tab to get started")
print("🌐 Opening in browser...")
app.launch(
server_name="0.0.0.0",
server_port=7861,
share=False,
debug=True,
show_error=True,
inbrowser=True
)
if __name__ == "__main__":
launch_interface()