UIDAI / app.py
LovnishVerma's picture
Update app.py
86265dd verified
raw
history blame
12.9 kB
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
# ==========================================
@st.cache_data
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)