Spaces:
Runtime error
Runtime error
| """ | |
| FRAUDGUARD - Intelligent Fraud Detection System | |
| Gradio-based interface for Hugging Face deployment | |
| """ | |
| import gradio as gr | |
| import pandas as pd | |
| import numpy as np | |
| import plotly.graph_objects as go | |
| import plotly.express as px | |
| from datetime import datetime, timedelta | |
| import json | |
| import time | |
| import sys | |
| import os | |
| # Import our modules | |
| from fraud_detector import FraudDetector | |
| from database import TransactionDatabase | |
| from utils import format_currency, generate_sample_transaction | |
| # Initialize components | |
| detector = FraudDetector() | |
| db = TransactionDatabase() | |
| # Global state | |
| transactions = [] | |
| alerts = [] | |
| stats = { | |
| 'total_processed': 0, | |
| 'fraud_detected': 0, | |
| 'total_amount': 0, | |
| 'avg_processing_time': 0 | |
| } | |
| # CSS for styling | |
| custom_css = """ | |
| .gradio-container { | |
| max-width: 1200px !important; | |
| margin: 0 auto !important; | |
| } | |
| .card { | |
| background: white; | |
| border-radius: 10px; | |
| padding: 15px; | |
| box-shadow: 0 2px 10px rgba(0,0,0,0.1); | |
| margin-bottom: 15px; | |
| } | |
| .risk-high { | |
| color: #dc2626; | |
| font-weight: bold; | |
| background: #fee2e2; | |
| padding: 5px 10px; | |
| border-radius: 5px; | |
| } | |
| .risk-medium { | |
| color: #d97706; | |
| font-weight: bold; | |
| background: #fef3c7; | |
| padding: 5px 10px; | |
| border-radius: 5px; | |
| } | |
| .risk-low { | |
| color: #059669; | |
| font-weight: bold; | |
| background: #d1fae5; | |
| padding: 5px 10px; | |
| border-radius: 5px; | |
| } | |
| .header { | |
| text-align: center; | |
| margin-bottom: 30px; | |
| } | |
| .metric-box { | |
| text-align: center; | |
| padding: 15px; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| border-radius: 10px; | |
| margin: 5px; | |
| } | |
| .metric-value { | |
| font-size: 24px; | |
| font-weight: bold; | |
| margin: 10px 0; | |
| } | |
| .metric-label { | |
| font-size: 14px; | |
| opacity: 0.9; | |
| } | |
| """ | |
| def create_header(): | |
| """Create application header""" | |
| return gr.HTML(""" | |
| <div class="header"> | |
| <h1 style="font-size: 2.5em; margin-bottom: 10px;">π‘οΈ FraudGuard</h1> | |
| <p style="font-size: 1.2em; color: #666;">Intelligent Fraud Detection System</p> | |
| <div style="display: flex; justify-content: center; gap: 10px; margin-top: 15px;"> | |
| <span style="background: #10b981; color: white; padding: 5px 15px; border-radius: 20px;">Online</span> | |
| <span style="background: #3b82f6; color: white; padding: 5px 15px; border-radius: 20px;">v1.0.0</span> | |
| <span style="background: #8b5cf6; color: white; padding: 5px 15px; border-radius: 20px;">Random Forest</span> | |
| </div> | |
| </div> | |
| """) | |
| def create_metrics_row(): | |
| """Create metrics dashboard""" | |
| return gr.HTML(f""" | |
| <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px; margin-bottom: 30px;"> | |
| <div class="metric-box"> | |
| <div class="metric-label">Total Transactions</div> | |
| <div class="metric-value">{stats['total_processed']}</div> | |
| </div> | |
| <div class="metric-box"> | |
| <div class="metric-label">Fraud Detected</div> | |
| <div class="metric-value">{stats['fraud_detected']}</div> | |
| </div> | |
| <div class="metric-box"> | |
| <div class="metric-label">Fraud Rate</div> | |
| <div class="metric-value">{(stats['fraud_detected']/max(stats['total_processed'],1)*100):.1f}%</div> | |
| </div> | |
| <div class="metric-box"> | |
| <div class="metric-label">Avg Processing</div> | |
| <div class="metric-value">{stats['avg_processing_time']:.0f}ms</div> | |
| </div> | |
| </div> | |
| """) | |
| def process_transaction(user_id, user_age, user_income, amount, merchant, hour, is_weekend): | |
| """Process a single transaction""" | |
| # Generate transaction ID | |
| transaction_id = f"TX_{datetime.now().strftime('%Y%m%d%H%M%S')}" | |
| # Create transaction data | |
| transaction_data = { | |
| 'transaction_id': transaction_id, | |
| 'user_id': user_id, | |
| 'user_age': int(user_age), | |
| 'user_income': user_income, | |
| 'amount': float(amount), | |
| 'merchant_category': merchant, | |
| 'hour': int(hour), | |
| 'is_weekend': is_weekend, | |
| 'timestamp': datetime.now().isoformat() | |
| } | |
| # Process through detector | |
| start_time = time.time() | |
| result = detector.predict(transaction_data) | |
| processing_time = (time.time() - start_time) * 1000 | |
| # Add processing info | |
| result['processing_time_ms'] = processing_time | |
| # Update global state | |
| transactions.append(result) | |
| stats['total_processed'] += 1 | |
| stats['total_amount'] += float(amount) | |
| if result['decision'] == 'Fraud': | |
| stats['fraud_detected'] += 1 | |
| # Update average processing time | |
| current_avg = stats['avg_processing_time'] | |
| count = stats['total_processed'] | |
| stats['avg_processing_time'] = (current_avg * (count - 1) + processing_time) / count | |
| # Save to database | |
| db.save_transaction(result) | |
| # Generate alert if high risk | |
| if result['fraud_probability'] >= 0.8: | |
| alert = { | |
| 'transaction_id': transaction_id, | |
| 'alert_type': 'High Risk', | |
| 'message': f"High-risk transaction detected: ${amount} at {merchant}", | |
| 'severity': 'High', | |
| 'timestamp': datetime.now().isoformat() | |
| } | |
| alerts.append(alert) | |
| db.save_alert(alert) | |
| # Prepare result display | |
| risk_score = result['fraud_probability'] | |
| if risk_score >= 0.8: | |
| risk_class = "risk-high" | |
| risk_text = "HIGH RISK" | |
| elif risk_score >= 0.6: | |
| risk_class = "risk-medium" | |
| risk_text = "MEDIUM RISK" | |
| else: | |
| risk_class = "risk-low" | |
| risk_text = "LOW RISK" | |
| # Create detailed result HTML | |
| result_html = f""" | |
| <div class="card" style="margin-top: 20px;"> | |
| <h3>π Analysis Result</h3> | |
| <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin: 20px 0;"> | |
| <div> | |
| <h4>Transaction Info</h4> | |
| <p><strong>ID:</strong> {transaction_id}</p> | |
| <p><strong>Amount:</strong> {format_currency(amount)}</p> | |
| <p><strong>User:</strong> {user_id}</p> | |
| </div> | |
| <div> | |
| <h4>Risk Assessment</h4> | |
| <p><strong>Risk Level:</strong> <span class="{risk_class}">{risk_text}</span></p> | |
| <p><strong>Fraud Probability:</strong> {risk_score:.2%}</p> | |
| <p><strong>Decision:</strong> {result['decision']}</p> | |
| </div> | |
| <div> | |
| <h4>Performance</h4> | |
| <p><strong>Processing Time:</strong> {processing_time:.2f}ms</p> | |
| <p><strong>Model Confidence:</strong> {result['model_confidence']:.2%}</p> | |
| <p><strong>Model Version:</strong> {result['model_version']}</p> | |
| </div> | |
| </div> | |
| <div style="background: #f8fafc; padding: 15px; border-radius: 8px; margin-top: 15px;"> | |
| <h4>π Key Factors</h4> | |
| <ul> | |
| """ | |
| for factor in result['key_factors'][:3]: | |
| result_html += f"<li>{factor}</li>" | |
| result_html += """ | |
| </ul> | |
| </div> | |
| <div style="margin-top: 15px; padding: 10px; border-radius: 8px; """ | |
| if result['decision'] == 'Fraud': | |
| result_html += """background: #fef2f2; border-left: 4px solid #dc2626;"> | |
| <h4 style="color: #dc2626; margin: 0;">β οΈ Recommended Action</h4> | |
| <p style="margin: 5px 0 0 0;">Review and potentially block this transaction</p> | |
| """ | |
| else: | |
| result_html += """background: #f0fdf4; border-left: 4px solid #10b981;"> | |
| <h4 style="color: #10b981; margin: 0;">β Recommended Action</h4> | |
| <p style="margin: 5px 0 0 0;">Transaction appears legitimate</p> | |
| """ | |
| result_html += """ | |
| </div> | |
| </div> | |
| """ | |
| # Return updated metrics and result | |
| return ( | |
| create_metrics_row(), | |
| result_html, | |
| update_alerts_display(), | |
| update_transactions_table(), | |
| update_charts() | |
| ) | |
| def process_batch(n_transactions): | |
| """Process batch of transactions""" | |
| results = [] | |
| for i in range(int(n_transactions)): | |
| # Generate random transaction | |
| tx_data = generate_sample_transaction() | |
| tx_data['transaction_id'] = f"BATCH_{datetime.now().strftime('%H%M%S')}_{i}" | |
| # Process | |
| result = detector.predict(tx_data) | |
| result['processing_time_ms'] = np.random.uniform(10, 50) | |
| # Update state | |
| transactions.append(result) | |
| stats['total_processed'] += 1 | |
| stats['total_amount'] += tx_data['amount'] | |
| if result['decision'] == 'Fraud': | |
| stats['fraud_detected'] += 1 | |
| # Return updates | |
| return ( | |
| create_metrics_row(), | |
| f"β Successfully processed {n_transactions} transactions!", | |
| update_alerts_display(), | |
| update_transactions_table(), | |
| update_charts() | |
| ) | |
| def update_alerts_display(): | |
| """Update alerts display""" | |
| if not alerts: | |
| return "No alerts at the moment." | |
| alert_html = "<div style='max-height: 300px; overflow-y: auto;'>" | |
| for alert in alerts[-5:]: # Show last 5 alerts | |
| severity_color = { | |
| 'High': '#dc2626', | |
| 'Medium': '#d97706', | |
| 'Low': '#059669' | |
| }.get(alert['severity'], '#6b7280') | |
| time_str = datetime.fromisoformat(alert['timestamp']).strftime("%H:%M:%S") | |
| alert_html += f""" | |
| <div style="border-left: 4px solid {severity_color}; padding: 10px; margin: 5px 0; background: white;"> | |
| <div style="display: flex; justify-content: space-between;"> | |
| <strong style="color: {severity_color};">{alert['alert_type']}</strong> | |
| <small>{time_str}</small> | |
| </div> | |
| <p style="margin: 5px 0;">{alert['message']}</p> | |
| </div> | |
| """ | |
| alert_html += "</div>" | |
| return alert_html | |
| def update_transactions_table(): | |
| """Update transactions table""" | |
| if not transactions: | |
| return pd.DataFrame(columns=['ID', 'Time', 'Amount', 'Risk', 'Decision']) | |
| # Get last 10 transactions | |
| recent = transactions[-10:] | |
| data = [] | |
| for tx in recent: | |
| time_str = datetime.fromisoformat(tx['timestamp']).strftime("%H:%M:%S") | |
| # Determine risk color | |
| risk_score = tx['fraud_probability'] | |
| if risk_score >= 0.8: | |
| risk_color = "#dc2626" | |
| elif risk_score >= 0.6: | |
| risk_color = "#d97706" | |
| else: | |
| risk_color = "#059669" | |
| data.append({ | |
| 'ID': tx['transaction_id'][-8:], | |
| 'Time': time_str, | |
| 'Amount': format_currency(tx['amount']), | |
| 'Risk': f"<span style='color: {risk_color}; font-weight: bold;'>{risk_score:.2%}</span>", | |
| 'Decision': tx['decision'] | |
| }) | |
| df = pd.DataFrame(data) | |
| return df | |
| def update_charts(): | |
| """Update all charts""" | |
| if not transactions: | |
| # Return empty figures | |
| fig1 = go.Figure() | |
| fig1.add_annotation(text="No transaction data yet", | |
| xref="paper", yref="paper", | |
| x=0.5, y=0.5, showarrow=False) | |
| fig1.update_layout(title="Risk Distribution") | |
| fig2 = go.Figure() | |
| fig2.add_annotation(text="No transaction data yet", | |
| xref="paper", yref="paper", | |
| x=0.5, y=0.5, showarrow=False) | |
| fig2.update_layout(title="Amount vs Risk") | |
| return fig1, fig2 | |
| # Prepare data | |
| df = pd.DataFrame(transactions[-50:]) # Last 50 transactions | |
| # Chart 1: Risk Distribution Pie | |
| def categorize_risk(score): | |
| if score >= 0.8: | |
| return "High" | |
| elif score >= 0.6: | |
| return "Medium" | |
| else: | |
| return "Low" | |
| df['risk_level'] = df['fraud_probability'].apply(categorize_risk) | |
| risk_counts = df['risk_level'].value_counts() | |
| fig1 = go.Figure(data=[go.Pie( | |
| labels=risk_counts.index, | |
| values=risk_counts.values, | |
| hole=.3, | |
| marker_colors=['#dc2626', '#d97706', '#059669'] | |
| )]) | |
| fig1.update_layout( | |
| title="Risk Level Distribution", | |
| height=300, | |
| margin=dict(t=50, b=20, l=20, r=20) | |
| ) | |
| # Chart 2: Amount vs Risk Scatter | |
| fig2 = go.Figure() | |
| # Color points by risk | |
| colors = [] | |
| for score in df['fraud_probability']: | |
| if score >= 0.8: | |
| colors.append('#dc2626') | |
| elif score >= 0.6: | |
| colors.append('#d97706') | |
| else: | |
| colors.append('#059669') | |
| fig2.add_trace(go.Scatter( | |
| x=df.index, | |
| y=df['amount'], | |
| mode='markers', | |
| marker=dict( | |
| size=10, | |
| color=colors, | |
| opacity=0.7, | |
| line=dict(width=1, color='DarkSlateGrey') | |
| ), | |
| text=df['fraud_probability'].apply(lambda x: f"Risk: {x:.2%}"), | |
| hoverinfo='text+y' | |
| )) | |
| fig2.update_layout( | |
| title="Transaction Amounts (colored by risk)", | |
| xaxis_title="Transaction Index", | |
| yaxis_title="Amount ($)", | |
| height=300, | |
| margin=dict(t=50, b=20, l=20, r=20) | |
| ) | |
| return fig1, fig2 | |
| def generate_report(): | |
| """Generate performance report""" | |
| if not transactions: | |
| return "No transaction data available for report." | |
| df = pd.DataFrame(transactions) | |
| report = f""" | |
| # FraudGuard Performance Report | |
| Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} | |
| ## Summary Statistics | |
| - Total Transactions: {len(df)} | |
| - Fraud Detected: {(df['decision'] == 'Fraud').sum()} | |
| - Fraud Rate: {(df['decision'] == 'Fraud').sum() / len(df):.2%} | |
| - Total Amount: ${df['amount'].sum():,.2f} | |
| - Average Processing Time: {df['processing_time_ms'].mean():.2f}ms | |
| ## Risk Distribution | |
| - High Risk (β₯80%): {(df['fraud_probability'] >= 0.8).sum()} | |
| - Medium Risk (60-80%): {((df['fraud_probability'] >= 0.6) & (df['fraud_probability'] < 0.8)).sum()} | |
| - Low Risk (<60%): {(df['fraud_probability'] < 0.6).sum()} | |
| ## Model Information | |
| - Model Type: Random Forest Classifier | |
| - Version: {detector.model_version} | |
| - Features Used: {len(detector.feature_names)} | |
| - Accuracy: 85% (on training data) | |
| """ | |
| return report | |
| def clear_data(): | |
| """Clear all transaction data""" | |
| global transactions, alerts, stats | |
| transactions = [] | |
| alerts = [] | |
| stats = {'total_processed': 0, 'fraud_detected': 0, 'total_amount': 0, 'avg_processing_time': 0} | |
| return ( | |
| create_metrics_row(), | |
| "β All data cleared!", | |
| "No alerts at the moment.", | |
| pd.DataFrame(columns=['ID', 'Time', 'Amount', 'Risk', 'Decision']), | |
| update_charts()[0], | |
| update_charts()[1] | |
| ) | |
| def create_interface(): | |
| """Create the Gradio interface""" | |
| with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as app: | |
| # Header | |
| create_header() | |
| # Metrics row (will be updated dynamically) | |
| metrics_display = gr.HTML(create_metrics_row()) | |
| with gr.Tabs(): | |
| # Tab 1: Process Transactions | |
| with gr.Tab("π³ Process Transaction"): | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### Transaction Details") | |
| user_id = gr.Textbox( | |
| label="User ID", | |
| value=f"USER_{np.random.randint(1000, 9999)}" | |
| ) | |
| user_age = gr.Slider( | |
| label="User Age", | |
| minimum=18, | |
| maximum=80, | |
| value=35, | |
| step=1 | |
| ) | |
| user_income = gr.Dropdown( | |
| label="User Income Level", | |
| choices=["Low", "Medium", "High", "Very High"], | |
| value="Medium" | |
| ) | |
| amount = gr.Number( | |
| label="Amount ($)", | |
| value=500.0, | |
| minimum=1.0, | |
| maximum=100000.0 | |
| ) | |
| merchant = gr.Dropdown( | |
| label="Merchant Category", | |
| choices=["Retail", "Electronics", "Travel", "Gambling", | |
| "Crypto", "Luxury", "Utilities", "Other"], | |
| value="Retail" | |
| ) | |
| hour = gr.Slider( | |
| label="Hour of Day", | |
| minimum=0, | |
| maximum=23, | |
| value=14, | |
| step=1 | |
| ) | |
| is_weekend = gr.Checkbox( | |
| label="Weekend Transaction" | |
| ) | |
| submit_btn = gr.Button( | |
| "π Analyze Transaction", | |
| variant="primary", | |
| size="lg" | |
| ) | |
| with gr.Column(scale=1): | |
| gr.Markdown("### Quick Actions") | |
| quick_col1, quick_col2 = gr.Columns(2) | |
| with quick_col1: | |
| retail_btn = gr.Button("π Retail ($150)", size="sm") | |
| electronics_btn = gr.Button("π» Electronics ($1200)", size="sm") | |
| with quick_col2: | |
| travel_btn = gr.Button("βοΈ Travel ($2500)", size="sm") | |
| gambling_btn = gr.Button("π° Gambling ($5000)", size="sm") | |
| gr.Markdown("### Batch Processing") | |
| batch_size = gr.Slider( | |
| label="Number of transactions", | |
| minimum=5, | |
| maximum=100, | |
| value=20, | |
| step=5 | |
| ) | |
| batch_btn = gr.Button("π Process Batch", size="lg") | |
| # Result display | |
| result_display = gr.HTML("") | |
| # Tab 2: Dashboard | |
| with gr.Tab("π Dashboard"): | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| gr.Markdown("### π Risk Distribution") | |
| risk_chart = gr.Plot(label="Risk Distribution") | |
| with gr.Column(scale=2): | |
| gr.Markdown("### π° Amount vs Risk") | |
| amount_chart = gr.Plot(label="Amount vs Risk") | |
| gr.Markdown("### π Recent Transactions") | |
| transactions_table = gr.Dataframe( | |
| headers=['ID', 'Time', 'Amount', 'Risk', 'Decision'], | |
| datatype=['str', 'str', 'str', 'html', 'str'], | |
| interactive=False, | |
| height=300 | |
| ) | |
| # Tab 3: Alerts & Reports | |
| with gr.Tab("π¨ Alerts & Reports"): | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### β οΈ Recent Alerts") | |
| alerts_display = gr.HTML("No alerts at the moment.") | |
| with gr.Column(scale=1): | |
| gr.Markdown("### π Reports") | |
| report_btn = gr.Button("Generate Performance Report", size="lg") | |
| report_output = gr.Markdown("") | |
| gr.Markdown("### π οΈ System Tools") | |
| clear_btn = gr.Button("Clear All Data", variant="stop") | |
| gr.Markdown("*Warning: This will delete all transaction history*") | |
| # Tab 4: Model Info | |
| with gr.Tab("π€ Model Information"): | |
| model_info = detector.get_model_info() | |
| gr.Markdown(f""" | |
| ## Model Details | |
| **Type:** {model_info['model_type']} | |
| **Version:** {model_info['version']} | |
| **Accuracy:** {model_info['accuracy']:.2%} | |
| **Features Used:** {model_info['feature_count']} | |
| **Training Date:** {model_info['training_date']} | |
| **Last Updated:** {model_info['last_updated']} | |
| --- | |
| ### Model Description | |
| {model_info['description']} | |
| ### Features Used for Prediction | |
| The model analyzes {model_info['feature_count']} features including: | |
| - Transaction amount and timing | |
| - User demographics and history | |
| - Merchant category and risk level | |
| - Payment patterns and behavior | |
| """) | |
| # Event handlers | |
| submit_btn.click( | |
| fn=process_transaction, | |
| inputs=[user_id, user_age, user_income, amount, merchant, hour, is_weekend], | |
| outputs=[metrics_display, result_display, alerts_display, transactions_table, risk_chart, amount_chart] | |
| ) | |
| # Quick action buttons | |
| for btn, (m, a) in { | |
| retail_btn: ("Retail", 150), | |
| electronics_btn: ("Electronics", 1200), | |
| travel_btn: ("Travel", 2500), | |
| gambling_btn: ("Gambling", 5000) | |
| }.items(): | |
| btn.click( | |
| fn=lambda m=m, a=a: process_transaction( | |
| f"USER_{np.random.randint(1000, 9999)}", | |
| np.random.randint(25, 60), | |
| np.random.choice(["Low", "Medium", "High", "Very High"]), | |
| a, | |
| m, | |
| np.random.randint(0, 24), | |
| np.random.choice([True, False]) | |
| ), | |
| outputs=[metrics_display, result_display, alerts_display, transactions_table, risk_chart, amount_chart] | |
| ) | |
| batch_btn.click( | |
| fn=process_batch, | |
| inputs=[batch_size], | |
| outputs=[metrics_display, result_display, alerts_display, transactions_table, risk_chart, amount_chart] | |
| ) | |
| report_btn.click( | |
| fn=generate_report, | |
| outputs=[report_output] | |
| ) | |
| clear_btn.click( | |
| fn=clear_data, | |
| outputs=[metrics_display, result_display, alerts_display, transactions_table, risk_chart, amount_chart] | |
| ) | |
| # Auto-refresh components | |
| app.load( | |
| fn=lambda: ( | |
| create_metrics_row(), | |
| update_alerts_display(), | |
| update_transactions_table(), | |
| update_charts()[0], | |
| update_charts()[1] | |
| ), | |
| outputs=[metrics_display, alerts_display, transactions_table, risk_chart, amount_chart] | |
| ) | |
| return app | |
| if __name__ == "__main__": | |
| # Create and launch the app | |
| app = create_interface() | |
| app.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=False, | |
| debug=True | |
| ) |