import streamlit as st import pandas as pd import numpy as np import plotly.graph_objects as go import plotly.express as px from datetime import datetime import time import json import logging from typing import Dict, Union from pathlib import Path from src.utils.model_utils import ModelPredictor from src.utils.data_preprocessing import validate_input_data from src.utils import monitoring # --- Basic Setup --- logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) st.set_page_config( page_title="Advanced Fraud Detection System", page_icon="🛡️", layout="wide", initial_sidebar_state="expanded" ) # --- Load CSS from external file --- def load_css(file_path: Path): with open(file_path) as f: st.markdown(f"", unsafe_allow_html=True) # --- Model & State Management --- @st.cache_resource def get_predictor(): """Cached function to load the model predictor once.""" try: predictor = ModelPredictor() return predictor except Exception as e: st.error(f"🚨 Critical Error: Could not load the model. Details: {e}") return None def create_sample_transaction() -> Dict: """Generates a random sample transaction for demonstration.""" np.random.seed(int(time.time())) sample = {'Time': np.random.randint(0, 172800), 'Amount': round(np.random.uniform(1, 1000), 2)} for i in range(1, 29): sample[f'V{i}'] = np.random.normal(0, 1) return sample # --- UI Page Functions --- def single_prediction_page(predictor: ModelPredictor): st.markdown('

🔍 Real-Time Transaction Analysis

Analyze individual transactions for fraud patterns.

', unsafe_allow_html=True) col1, col2 = st.columns([2, 1]) with col1: st.markdown('
', unsafe_allow_html=True) st.subheader("💳 Transaction Input") input_method = st.radio("Select input method:", ["✍️ Manual Entry", "🎲 Sample Transaction", "📋 JSON Import"], horizontal=True, key="input_method") # Form for Manual Entry if input_method == "✍️ Manual Entry": with st.form("transaction_form"): # Your form code remains the same... st.markdown("**⏰ Timing & Amount**") c1, c2 = st.columns(2) time_val = c1.number_input("Time (seconds since first transaction)", min_value=0, value=3600, help="Time elapsed in seconds.") amount_val = c2.number_input("Amount ($)", min_value=0.01, value=100.0, format="%.2f") st.markdown("**🔢 Anonymized Features (V1-V28)**") v_features = {} with st.expander("Enter V1-V28 Features"): cols = st.columns(4) for i in range(1, 29): with cols[(i-1)%4]: v_features[f'V{i}'] = st.number_input(f"V{i}", value=0.0, format="%.6f", key=f"v{i}") submitted = st.form_submit_button("🚀 Analyze Transaction", use_container_width=True, type="primary") if submitted: st.session_state.transaction_data = {'Time': time_val, 'Amount': amount_val, **v_features} st.session_state.analysis_triggered = True # Logic for other input methods elif input_method == "🎲 Sample Transaction": if st.button("🎲 Generate & Analyze Sample", use_container_width=True, type="primary"): st.session_state.transaction_data = create_sample_transaction() st.session_state.analysis_triggered = True elif input_method == "📋 JSON Import": json_input = st.text_area("Paste JSON data here:", height=250, placeholder='{"Time": 3600, "Amount": 100.0, ...}') if st.button("🔍 Process JSON", use_container_width=True, type="primary"): if json_input: try: st.session_state.transaction_data = json.loads(json_input) st.session_state.analysis_triggered = True except json.JSONDecodeError: st.error("❌ Invalid JSON format.") st.markdown('
', unsafe_allow_html=True) with col2: st.markdown('
', unsafe_allow_html=True) st.subheader("🎯 Analysis Results") if st.session_state.get('analysis_triggered', False): transaction_data = st.session_state.transaction_data if not validate_input_data(transaction_data): st.error("❌ Invalid input data. Please check all required fields.") else: try: with st.spinner("🧠 AI analyzing transaction..."): start_time = time.time() prediction, probability = predictor.predict_single(transaction_data, use_optimal_threshold=True) processing_time = time.time() - start_time monitoring.log_prediction(transaction_data, prediction, probability, processing_time) if prediction == 1: st.markdown('
🚨 FRAUD DETECTED 🚨
', unsafe_allow_html=True) else: st.markdown('
✅ LEGITIMATE TRANSACTION
', unsafe_allow_html=True) # ENHANCEMENT: Add Risk Level and Gauge Chart if probability > 0.8: risk_level, risk_color = "High", "red" elif probability > 0.5: risk_level, risk_color = "Medium", "orange" else: risk_level, risk_color = "Low", "green" st.metric("⚠️ Risk Level", risk_level) fig = go.Figure(go.Indicator( mode = "gauge+number", value = probability, domain = {'x': [0, 1], 'y': [0, 1]}, title = {'text': "Fraud Score", 'font': {'size': 20, 'color': 'white'}}, number = {'suffix': "%", 'valueformat': '.1f', 'font': {'size': 30, 'color': 'white'}}, gauge = { 'axis': {'range': [None, 1], 'tickwidth': 1, 'tickcolor': "white", 'tickformat': '.0%'}, 'bar': {'color': risk_color, 'thickness': 0.3}, 'bgcolor': "#4A4A4A", 'steps': [ {'range': [0, 0.5], 'color': '#3A683B'}, {'range': [0.5, 0.8], 'color': '#75602A'}, {'range': [0.8, 1], 'color': '#753228'}], 'threshold': { 'line': {'color': "#FFD700", 'width': 3}, 'thickness': 0.75, 'value': predictor.optimal_threshold }})) fig.update_layout(paper_bgcolor="rgba(0,0,0,0)", font={'color': "white"}) st.plotly_chart(fig, use_container_width=True) st.caption(f"Model Threshold: {predictor.optimal_threshold:.2%}") st.success(f"⚡ Processed in {processing_time*1000:.1f}ms") except Exception as e: st.error(f"💥 Analysis error: {e}") # Reset the trigger st.session_state.analysis_triggered = False else: st.info("👆 Enter transaction details and click analyze to see results.") st.markdown('
', unsafe_allow_html=True) def batch_processing_page(predictor: ModelPredictor): st.markdown('

📊 Bulk Transaction Processing

Upload a CSV file to process multiple transactions at once.

', unsafe_allow_html=True) uploaded_file = st.file_uploader("CSV file must contain 'Time', 'Amount', and 'V1'-'V28' columns.", type=['csv'], key="batch_uploader") if uploaded_file is not None: try: df = pd.read_csv(uploaded_file) st.success(f"✅ File '{uploaded_file.name}' uploaded successfully with {len(df):,} rows.") if st.button("🚀 Start Batch Processing", use_container_width=True, type="primary"): if not validate_input_data(df): st.error("❌ Invalid data format. Please check the file columns.") return progress_bar = st.progress(0, text="Starting batch processing...") start_time = time.time() predictions, probabilities = predictor.predict_batch(df, use_optimal_threshold=True) df['Prediction'] = predictions df['Fraud_Probability'] = probabilities # Log results total_time = time.time() - start_time for index, row in df.iterrows(): row_processing_time = total_time / len(df) if len(df) > 0 else 0 monitoring.log_prediction(row.to_dict(), row['Prediction'], row['Fraud_Probability'], row_processing_time) progress_bar.progress(100, text="✅ Batch processing complete!") # Store results in session state to prevent re-running st.session_state.batch_results = df except Exception as e: st.error(f"💥 An error occurred: {e}") st.exception(e) # ENHANCEMENT: Display a comprehensive results dashboard if 'batch_results' in st.session_state: st.markdown("---") st.subheader("📊 Batch Processing Dashboard") results_df = st.session_state.batch_results fraud_count = results_df['Prediction'].sum() fraud_amount = results_df[results_df['Prediction'] == 1]['Amount'].sum() # Summary Metrics col1, col2, col3, col4 = st.columns(4) col1.metric("Processed", f"{len(results_df):,}") col2.metric("🚨 Fraud Detected", f"{fraud_count:,}", f"{fraud_count/len(results_df):.2%}") col3.metric("💸 Amount at Risk", f"${fraud_amount:,.2f}") col4.metric("Total Amount", f"${results_df['Amount'].sum():,.2f}") # Charts c1, c2 = st.columns(2) with c1: fig_pie = px.pie(results_df, names='Prediction', title='Transaction Classification', color_discrete_map={0: '#51cf66', 1: '#ff6b6b'}, labels={0: 'Legitimate', 1: 'Fraud'}) fig_pie.update_layout(paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)") st.plotly_chart(fig_pie, use_container_width=True) with c2: fig_hist = px.histogram(results_df, x='Fraud_Probability', color='Prediction', title='Distribution of Fraud Scores', nbins=50, color_discrete_map={0: '#51cf66', 1: '#ff6b6b'}) fig_hist.update_layout(paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)") st.plotly_chart(fig_hist, use_container_width=True) # Download Buttons & Data Table with st.expander("📥 Download Results & View Data", expanded=True): display_cols = ['Time', 'Amount', 'Prediction', 'Fraud_Probability'] st.dataframe(results_df[display_cols].sort_values('Fraud_Probability', ascending=False), use_container_width=True) c1, c2 = st.columns(2) c1.download_button( label="📊 Download Full Results (CSV)", data=results_df.to_csv(index=False).encode('utf-8'), file_name=f"full_results_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv", mime='text/csv', use_container_width=True ) c2.download_button( label="🚨 Download Fraud Only (CSV)", data=results_df[results_df['Prediction'] == 1].to_csv(index=False).encode('utf-8'), file_name=f"fraud_only_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv", mime='text/csv', use_container_width=True ) def monitoring_page(): st.markdown('

📈 Real-Time Session Monitoring

Track prediction patterns for your current session.

', unsafe_allow_html=True) monitoring.initialize_monitor_state() df_logs = monitoring.get_logs_as_dataframe() if df_logs.empty: st.info("📊 No predictions have been made in this session yet. Analyze a transaction to see monitoring data.") return # ENHANCEMENT: Add interactive charts to the monitoring page st.markdown("---") st.subheader("📊 Session Performance") total_predictions = len(df_logs) fraud_predictions = int(df_logs['prediction'].sum()) col1, col2, col3 = st.columns(3) col1.metric("🔢 Total Predictions", f"{total_predictions:,}") col2.metric("🚨 Fraud Detected", f"{fraud_predictions:,}") col3.metric("⚡ Avg Response Time", f"{df_logs['processing_time_ms'].mean():.1f}ms") c1, c2 = st.columns(2) with c1: df_logs['timestamp_hour'] = df_logs['timestamp'].dt.round('H') hourly_counts = df_logs.groupby('timestamp_hour').size().reset_index(name='count') fig_ts = px.line(hourly_counts, x='timestamp_hour', y='count', title='Prediction Volume Over Time', markers=True) fig_ts.update_layout(paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)") st.plotly_chart(fig_ts, use_container_width=True) with c2: fig_hist = px.histogram(df_logs, x='fraud_probability', title='Distribution of Fraud Scores (This Session)', nbins=25) fig_hist.update_layout(paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)") st.plotly_chart(fig_hist, use_container_width=True) st.markdown("---") st.subheader("📋 Recent Activity Log (This Session)") st.dataframe(df_logs.sort_values(by="timestamp", ascending=False), use_container_width=True) def main(): """Main function to run the Streamlit app.""" css_path = Path(__file__).parent / "style.css" load_css(css_path) st.markdown('

🛡️ Advanced Fraud Detection System

AI-Powered Transaction Security Analysis

', unsafe_allow_html=True) predictor = get_predictor() if predictor is None: st.error("Model failed to load. The application cannot start.") st.stop() with st.sidebar: st.markdown('', unsafe_allow_html=True) page = st.radio("Select a page:", ["🔍 Single Prediction", "📊 Batch Processing", "📈 Model Monitoring"], label_visibility="collapsed") if page == "🔍 Single Prediction": single_prediction_page(predictor) elif page == "📊 Batch Processing": batch_processing_page(predictor) elif page == "📈 Model Monitoring": monitoring_page() if __name__ == "__main__": main()