""" 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:

' 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()