FraudDetection / app.py
Ahmedhassan54's picture
Update app.py
84eec6d verified
"""
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
)