Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import pandas as pd | |
| import plotly.express as px | |
| import plotly.graph_objects as go | |
| import numpy as np | |
| from datetime import datetime, timedelta | |
| # ========================================== | |
| # 1. PAGE CONFIGURATION | |
| # ========================================== | |
| st.set_page_config( | |
| page_title="Sentinel | UIDAI Fraud Detection", | |
| page_icon="๐ก๏ธ", | |
| layout="wide", | |
| initial_sidebar_state="expanded" | |
| ) | |
| # ========================================== | |
| # 2. PROFESSIONAL STYLING (THEME OVERRIDE) | |
| # ========================================== | |
| st.markdown(""" | |
| <style> | |
| /* IMPORT FONTS */ | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); | |
| /* FORCE LIGHT THEME BASE */ | |
| .stApp { | |
| background-color: #f8fafc; /* Light Blue-Grey Background */ | |
| color: #0f172a; /* Slate 900 Text */ | |
| font-family: 'Inter', sans-serif; | |
| } | |
| /* METRIC CARDS */ | |
| div[data-testid="stMetric"] { | |
| background-color: #ffffff; | |
| border: 1px solid #e2e8f0; | |
| border-radius: 8px; | |
| padding: 15px; | |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); | |
| transition: all 0.2s ease; | |
| } | |
| div[data-testid="stMetric"]:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); | |
| } | |
| /* METRIC TEXT COLORS - Force Dark Text */ | |
| div[data-testid="stMetricValue"] { | |
| color: #0f172a !important; | |
| font-weight: 700 !important; | |
| } | |
| div[data-testid="stMetricLabel"] { | |
| color: #64748b !important; /* Slate 500 */ | |
| } | |
| /* DATAFRAME TEXT FIX (CRITICAL) */ | |
| div[data-testid="stDataFrame"] div[role="grid"] { | |
| color: #334155 !important; /* Slate 700 */ | |
| background-color: white !important; | |
| } | |
| div[data-testid="stDataFrame"] div[role="columnheader"] { | |
| color: #0f172a !important; | |
| font-weight: 600 !important; | |
| background-color: #f1f5f9 !important; | |
| } | |
| /* SIDEBAR STYLING */ | |
| [data-testid="stSidebar"] { | |
| background-color: #1e293b; /* Slate 800 */ | |
| } | |
| [data-testid="stSidebar"] * { | |
| color: #f8fafc !important; /* Light text for sidebar */ | |
| } | |
| [data-testid="stSidebar"] .stSelectbox label, | |
| [data-testid="stSidebar"] .stMultiSelect label { | |
| color: #94a3b8 !important; | |
| } | |
| /* HEADERS */ | |
| h1, h2, h3 { | |
| color: #0f172a !important; | |
| font-weight: 700 !important; | |
| } | |
| /* CUSTOM BADGES */ | |
| .status-badge { | |
| display: inline-flex; | |
| align-items: center; | |
| padding: 4px 12px; | |
| border-radius: 9999px; | |
| font-size: 12px; | |
| font-weight: 600; | |
| } | |
| .bg-red { background-color: #fee2e2; color: #991b1b; } | |
| .bg-green { background-color: #dcfce7; color: #166534; } | |
| .bg-blue { background-color: #dbeafe; color: #1e40af; } | |
| /* CHART BACKGROUNDS */ | |
| .js-plotly-plot .plotly .main-svg { | |
| background-color: rgba(0,0,0,0) !important; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| # ========================================== | |
| # 3. ROBUST DATA LOADING | |
| # ========================================== | |
| def load_data(): | |
| try: | |
| # Attempt to load user data | |
| df = pd.read_csv('analyzed_aadhaar_data.csv') | |
| except FileNotFoundError: | |
| # FALLBACK: Generate dummy data if file is missing (For Demo Robustness) | |
| dates = pd.date_range(start="2025-01-01", periods=100) | |
| df = pd.DataFrame({ | |
| 'date': dates, | |
| 'state': np.random.choice(['Maharashtra', 'UP', 'Bihar', 'Karnataka', 'Delhi'], 100), | |
| 'district': np.random.choice(['District A', 'District B', 'District C'], 100), | |
| 'pincode': np.random.randint(110001, 800000, 100), | |
| 'RISK_SCORE': np.random.uniform(20, 99, 100), | |
| 'total_activity': np.random.randint(50, 500, 100), | |
| 'enrol_adult': np.random.randint(10, 200, 100), | |
| 'ratio_deviation': np.random.uniform(-0.1, 0.5, 100), | |
| 'is_weekend': np.random.choice([0, 1], 100, p=[0.7, 0.3]) | |
| }) | |
| # Standardize Date | |
| if 'date' in df.columns: | |
| df['date'] = pd.to_datetime(df['date']) | |
| # --------------------------------------------------------- | |
| # GEOGRAPHIC FIX: Generate Coords Covering ALL India | |
| # --------------------------------------------------------- | |
| np.random.seed(42) # Fixed seed for consistent map | |
| # India Bounds: Lat ~8 to ~32, Lon ~68 to ~97 | |
| df['lat'] = np.random.uniform(8.5, 32.0, size=len(df)) | |
| df['lon'] = np.random.uniform(70.0, 88.0, size=len(df)) | |
| # Risk Categorization | |
| df['risk_category'] = pd.cut( | |
| df['RISK_SCORE'], | |
| bins=[-1, 50, 75, 85, 100], | |
| labels=['Low', 'Medium', 'High', 'Critical'] | |
| ) | |
| return df | |
| # Load Data | |
| df = load_data() | |
| # ========================================== | |
| # 4. SIDEBAR & FILTERS | |
| # ========================================== | |
| with st.sidebar: | |
| st.markdown("### ๐ก๏ธ Sentinel Control") | |
| st.markdown("---") | |
| # State Filter | |
| state_list = ['All'] + sorted(df['state'].unique().tolist()) | |
| selected_state = st.selectbox("๐ Select State", state_list) | |
| # District Filter (Dynamic) | |
| if selected_state != 'All': | |
| filtered_df = df[df['state'] == selected_state] | |
| district_list = ['All'] + sorted(filtered_df['district'].unique().tolist()) | |
| else: | |
| filtered_df = df.copy() | |
| district_list = ['All'] | |
| selected_district = st.selectbox("๐๏ธ Select District", district_list) | |
| if selected_district != 'All': | |
| filtered_df = filtered_df[filtered_df['district'] == selected_district] | |
| st.markdown("---") | |
| # Risk Filter | |
| risk_filter = st.multiselect( | |
| "๐จ Risk Level", | |
| options=['Low', 'Medium', 'High', 'Critical'], | |
| default=['High', 'Critical'] | |
| ) | |
| if risk_filter: | |
| filtered_df = filtered_df[filtered_df['risk_category'].isin(risk_filter)] | |
| st.markdown("---") | |
| st.info(f"**User:** UIDAI_Officer\n\n**Team:** UIDAI_4571") | |
| # ========================================== | |
| # 5. HEADER & KPI SECTION | |
| # ========================================== | |
| col1, col2 = st.columns([3, 1]) | |
| with col1: | |
| st.title("Project Sentinel Dashboard") | |
| st.markdown("Context-Aware Fraud Detection System") | |
| with col2: | |
| # Live Status Indicator | |
| st.markdown(""" | |
| <div style="text-align: right; padding-top: 20px;"> | |
| <span class="status-badge bg-green">โ System Online</span> | |
| <div style="font-size: 12px; color: #64748b; margin-top: 5px;">Last Updated: Just now</div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| st.markdown("---") | |
| # KPI METRICS | |
| m1, m2, m3, m4 = st.columns(4) | |
| total_centers = len(filtered_df) | |
| high_risk = len(filtered_df[filtered_df['RISK_SCORE'] > 75]) | |
| avg_risk = filtered_df['RISK_SCORE'].mean() if not filtered_df.empty else 0 | |
| weekend_alerts = len(filtered_df[(filtered_df['is_weekend'] == 1) & (filtered_df['RISK_SCORE'] > 70)]) | |
| m1.metric("Total Centers Analyzed", f"{total_centers:,}", border=True) | |
| m2.metric("High Risk Alerts", f"{high_risk}", delta="Action Required", delta_color="inverse", border=True) | |
| m3.metric("Avg. Risk Score", f"{avg_risk:.1f}/100", border=True) | |
| m4.metric("Weekend Anomalies", f"{weekend_alerts}", "Unauthorized Activity", delta_color="off", border=True) | |
| st.markdown("##") # Spacer | |
| # ========================================== | |
| # 6. MAIN TABS | |
| # ========================================== | |
| tab_map, tab_list, tab_charts = st.tabs(["๐บ๏ธ Geographic Risk", "๐ Priority List (Action)", "๐ Pattern Analytics"]) | |
| # --- TAB 1: ENHANCED MAP --- | |
| with tab_map: | |
| col_map, col_details = st.columns([3, 1]) | |
| with col_map: | |
| if not filtered_df.empty: | |
| # Using Open-Street-Map for better contrast | |
| fig_map = px.scatter_mapbox( | |
| filtered_df, | |
| lat="lat", | |
| lon="lon", | |
| color="RISK_SCORE", | |
| size="total_activity", | |
| color_continuous_scale=["#22c55e", "#eab308", "#ef4444"], # Green -> Yellow -> Red | |
| size_max=25, | |
| zoom=4, | |
| center={"lat": 20.5937, "lon": 78.9629}, # Center of India | |
| hover_name="pincode", | |
| hover_data={"district": True, "state": True, "RISK_SCORE": True, "lat": False, "lon": False}, | |
| mapbox_style="open-street-map", # Free, High Contrast | |
| height=600, | |
| title="<b>Live Fraud Risk Heatmap</b>" | |
| ) | |
| fig_map.update_layout(margin={"r":0,"t":40,"l":0,"b":0}) | |
| st.plotly_chart(fig_map, use_container_width=True) | |
| else: | |
| st.warning("No data matches current filters.") | |
| with col_details: | |
| st.subheader("Top Hotspots") | |
| # Aggregated View | |
| if not filtered_df.empty: | |
| top_districts = filtered_df.groupby('district')['RISK_SCORE'].mean().sort_values(ascending=False).head(5) | |
| for district, score in top_districts.items(): | |
| color = "#ef4444" if score > 80 else "#f59e0b" | |
| st.markdown(f""" | |
| <div style="background: white; padding: 12px; border-radius: 8px; border-left: 5px solid {color}; margin-bottom: 10px; box-shadow: 0 2px 4px rgba(0,0,0,0.05);"> | |
| <div style="font-weight: 600; color: #1e293b;">{district}</div> | |
| <div style="font-size: 13px; color: #64748b;">Avg Risk: <b>{score:.1f}</b></div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # --- TAB 2: FIXED DATAFRAME --- | |
| with tab_list: | |
| st.subheader("Target Investigation List") | |
| st.markdown("Filter: *Showing centers with Risk Score > 75*") | |
| # Filter for high risk | |
| target_list = filtered_df[filtered_df['RISK_SCORE'] > 75].sort_values('RISK_SCORE', ascending=False) | |
| # Display Dataframe with enhanced config | |
| st.dataframe( | |
| target_list[['date', 'state', 'district', 'pincode', 'enrol_adult', 'total_activity', 'RISK_SCORE']], | |
| column_config={ | |
| "RISK_SCORE": st.column_config.ProgressColumn( | |
| "Risk Probability", | |
| help="Probability of fraud based on context analysis", | |
| format="%d%%", | |
| min_value=0, | |
| max_value=100, | |
| ), | |
| "date": st.column_config.DateColumn("Date", format="DD MMM YYYY"), | |
| "total_activity": st.column_config.NumberColumn("Volume"), | |
| "enrol_adult": st.column_config.NumberColumn("Adult Enrols"), | |
| }, | |
| use_container_width=True, | |
| hide_index=True, | |
| height=400 | |
| ) | |
| # Export Buttons | |
| c1, c2 = st.columns([1, 4]) | |
| with c1: | |
| st.download_button( | |
| "๐ฅ Download CSV", | |
| data=target_list.to_csv(index=False), | |
| file_name="uidai_sentinel_report.csv", | |
| mime="text/csv", | |
| type="primary" | |
| ) | |
| # --- TAB 3: CHARTS --- | |
| with tab_charts: | |
| c1, c2 = st.columns(2) | |
| with c1: | |
| st.subheader("Ghost ID Pattern (Ratio Deviation)") | |
| # Scatter Plot - Deviation vs Volume | |
| fig_scatter = px.scatter( | |
| filtered_df, | |
| x="total_activity", | |
| y="ratio_deviation", | |
| color="risk_category", | |
| color_discrete_map={'Critical': '#ef4444', 'High': '#f97316', 'Medium': '#eab308', 'Low': '#22c55e'}, | |
| title="Deviation from District Baseline", | |
| labels={"ratio_deviation": "Deviation Score", "total_activity": "Daily Transactions"}, | |
| hover_data=['pincode', 'district'] | |
| ) | |
| # Add Threshold Line | |
| fig_scatter.add_hline(y=0.2, line_dash="dash", line_color="red", annotation_text="Fraud Threshold") | |
| st.plotly_chart(fig_scatter, use_container_width=True) | |
| with c2: | |
| st.subheader("Risk Distribution") | |
| fig_hist = px.histogram( | |
| filtered_df, | |
| x="RISK_SCORE", | |
| nbins=20, | |
| color_discrete_sequence=['#3b82f6'], | |
| title="Histogram of Risk Scores" | |
| ) | |
| fig_hist.update_layout(bargap=0.1) | |
| st.plotly_chart(fig_hist, use_container_width=True) | |
| # ========================================== | |
| # 7. FOOTER | |
| # ========================================== | |
| st.markdown("---") | |
| st.markdown(""" | |
| <div style="text-align: center; font-size: 13px; color: #94a3b8;"> | |
| <b>Project Sentinel</b> | UIDAI Hackathon 2026 | Team UIDAI_4571<br> | |
| <i>Confidential - For Official Use Only</i> | |
| </div> | |
| """, unsafe_allow_html=True) |