Spaces:
Sleeping
Sleeping
Download src/deployment/app.py from useifabdelhady/FraudDetection: direct link, hf CLI and curl.
- Browser
- Download file 15.5 kB
-
https://huggingface.co/spaces/useifabdelhady/FraudDetection/resolve/main/src/deployment/app.py
- Command line
-
hf download hf://spaces/useifabdelhady/FraudDetection/src/deployment/app.py
-
curl -L -o app.py https://huggingface.co/spaces/useifabdelhady/FraudDetection/resolve/main/src/deployment/app.py
15.5 kB
| 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"<style>{f.read()}</style>", unsafe_allow_html=True) | |
| # --- Model & State Management --- | |
| 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('<div class="feature-card"><h2>π Real-Time Transaction Analysis</h2><p>Analyze individual transactions for fraud patterns.</p></div>', unsafe_allow_html=True) | |
| col1, col2 = st.columns([2, 1]) | |
| with col1: | |
| st.markdown('<div class="feature-card">', 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('</div>', unsafe_allow_html=True) | |
| with col2: | |
| st.markdown('<div class="feature-card">', 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('<div class="fraud-alert">π¨ FRAUD DETECTED π¨</div>', unsafe_allow_html=True) | |
| else: | |
| st.markdown('<div class="safe-alert">β LEGITIMATE TRANSACTION</div>', 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('</div>', unsafe_allow_html=True) | |
| def batch_processing_page(predictor: ModelPredictor): | |
| st.markdown('<div class="feature-card"><h2>π Bulk Transaction Processing</h2><p>Upload a CSV file to process multiple transactions at once.</p></div>', 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('<div class="feature-card"><h2>π Real-Time Session Monitoring</h2><p>Track prediction patterns for your current session.</p></div>', 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('<div class="main-header"><h1>π‘οΈ Advanced Fraud Detection System</h1><p>AI-Powered Transaction Security Analysis</p></div>', 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('<div class="sidebar-nav"><h3 style="color: white; text-align: center;">π§ Navigation</h3></div>', 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() |