import streamlit as st import pandas as pd import requests import plotly.express as px from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from xgboost import XGBClassifier from sklearn.preprocessing import LabelEncoder from sklearn.metrics import accuracy_score, f1_score import sqlite3 import base64 import logging import os import numpy as np # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Database connection DB_PATH = "crime_records.db" def get_db_connection(): if not os.path.exists(DB_PATH): logger.error(f"Database file {DB_PATH} does not exist") raise Exception(f"Database file {DB_PATH} not found. Run init_db.py to create it.") conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row return conn # Data analysis function def load_crime_data(search_query=""): try: conn = get_db_connection() query = "SELECT LOWER(crime_type) AS crime_type, LOWER(description) AS description, LOWER(location) AS location, date, LOWER(officer_in_charge) AS officer_in_charge, LOWER(status) AS status FROM Crimes" if search_query: query += " WHERE LOWER(crime_type) LIKE ? OR LOWER(location) LIKE ?" df = pd.read_sql(query, conn, params=(f"%{search_query.lower()}%", f"%{search_query.lower()}%")) else: df = pd.read_sql(query, conn) conn.close() if df.empty: logger.warning("No data found in Crimes table") st.warning("No crime data available. Please add crimes via 'Add Crime'.") return df except Exception as e: logger.error(f"Error loading crime data: {e}") st.error(f"Error loading crime data: {e}. Ensure the database and Crimes table are initialized.") return pd.DataFrame() # ML model training and prediction def train_ml_models(): df = load_crime_data() if df.empty or len(df) < 5: logger.warning("Insufficient data for ML training") st.error("Insufficient data for ML training. Please add at least 5 crime records.") return None, None, None, None, None, 0, 0, 0, 0 df = df.dropna() if len(df['status'].unique()) < 2: logger.warning("Only one status value found; ML models require multiple classes") st.error("ML models require at least two different status values (e.g., 'open' and 'closed').") return None, None, None, None, None, 0, 0, 0, 0 le_crime = LabelEncoder() le_location = LabelEncoder() le_status = LabelEncoder() df['crime_type_encoded'] = le_crime.fit_transform(df['crime_type']) df['location_encoded'] = le_location.fit_transform(df['location']) df['status_encoded'] = le_status.fit_transform(df['status']) features = ['crime_type_encoded', 'location_encoded'] X = df[features] y = df['status_encoded'] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # Random Forest rf_model = RandomForestClassifier(n_estimators=100, random_state=42) rf_model.fit(X_train, y_train) rf_pred = rf_model.predict(X_test) rf_accuracy = accuracy_score(y_test, rf_pred) rf_f1 = f1_score(y_test, rf_pred, average='weighted') # XGBoost xgb_model = XGBClassifier(use_label_encoder=False, eval_metric='mlogloss', random_state=42) xgb_model.fit(X_train, y_train) xgb_pred = xgb_model.predict(X_test) xgb_accuracy = accuracy_score(y_test, xgb_pred) xgb_f1 = f1_score(y_test, xgb_pred, average='weighted') return rf_model, xgb_model, le_crime, le_location, le_status, rf_accuracy, rf_f1, xgb_accuracy, xgb_f1 # Function to set modern styling with background image and white text def set_background_and_text(image_file): try: with open(image_file, "rb") as image: encoded_image = base64.b64encode(image.read()).decode() css = f""" """ st.markdown(css, unsafe_allow_html=True) logger.info(f"Modern styling and background image set for {image_file}") except FileNotFoundError: logger.error(f"Background image {image_file} not found") st.warning(f"Background image for {image_file} not found.") css = """ """ st.markdown(css, unsafe_allow_html=True) # Initialize session state for user authentication if 'user' not in st.session_state: st.session_state.user = None st.session_state.role = None if 'page' not in st.session_state: st.session_state.page = "Login" # Role-based menu options ROLE_MENUS = { "admin": ["Dashboard", "Add Crime", "View Crimes", "Add FIR", "View FIRs", "Data Analysis", "ML Predictions", "Sign Up", "Login", "Logout"], "police": ["Dashboard", "View Crimes", "Add FIR", "View FIRs", "ML Predictions", "Login", "Logout"], None: ["Login", "Sign Up"] } # Sidebar navigation st.sidebar.header("User Authentication") auth_choice = st.sidebar.selectbox("Action", ["Login", "Sign Up", "Logout"] if st.session_state.user else ["Login", "Sign Up"], key="auth_choice") # Signup if auth_choice == "Sign Up": st.header("Sign Up") username = st.text_input("Username", help="Enter a unique username") password = st.text_input("Password", type="password", help="Enter a secure password") role = st.selectbox("Role", ["admin", "police"], help="Select user role: admin or police") if st.button(" Register", key="signup", help="Create a new user account"): if username and password: try: response = requests.post("http://localhost:8000/api/users", json={"username": username, "password": password, "role": role}) response.raise_for_status() st.success("User registered successfully! Please log in.") st.session_state.page = "Login" except requests.RequestException as e: st.error(f"Error: {e}") else: st.error("Please fill in all fields.") # Login elif auth_choice == "Login": st.header("Login") username = st.text_input("Username", help="Enter your username") password = st.text_input("Password", type="password", help="Enter your password") if st.button(" Login", key="login", help="Log in to the system"): if username and password: try: response = requests.post("http://localhost:8000/api/login", json={"username": username, "password": password}) response.raise_for_status() data = response.json() st.session_state.user = data["username"] st.session_state.role = data["role"] st.success(f"Logged in as {data['username']} ({data['role']})") st.session_state.page = "Dashboard" except requests.RequestException as e: st.error(f"Error: {e}") else: st.error("Please fill in all fields.") # Logout elif auth_choice == "Logout": st.session_state.user = None st.session_state.role = None st.session_state.page = "Login" st.success("Logged out successfully!") st.rerun() # Set background image and modern styling background_images = { "Dashboard": "static\mainmenu.jpg", "Add Crime": "static\AddCrimes.jpg", "View Crimes": "static\ViewCrimes.jpg", "Add FIR": "static\AddFirs.jpg", "View FIRs": "static\ViewFirs.jpg", "Data Analysis": "static\mainmenu.jpg", "ML Predictions": "static\AddCrimes.jpg", "Sign Up": "static\ViewCrimes.jpg", "Login": "static\ViewFirs.jpg" } # Apply background and styling if st.session_state.page in background_images: set_background_and_text(background_images[st.session_state.page]) else: set_background_and_text(None) # Main menu navigation (only shown if logged in) if st.session_state.user: st.sidebar.header("Navigation") menu = ROLE_MENUS[st.session_state.role] choice = st.sidebar.selectbox("Menu", menu, key="main_menu") else: choice = st.session_state.page # Main app logic if choice == "Dashboard" and st.session_state.user: st.header("Dashboard") st.write(f"Welcome, {st.session_state.user} ({st.session_state.role.title()})! Navigate using the sidebar.") elif choice == "Add Crime" and st.session_state.user and st.session_state.role == "admin": st.header("Add Crime") crime_type = st.text_input("Crime Type", help="e.g., Cyber Crimes, Theft, Assault") description = st.text_area("Description", help="Brief description of the crime") location = st.text_input("Location", help="e.g., New York, Chicago") date = st.date_input("Date") officer = st.text_input("Officer In Charge", help="Name of the assigned officer") if st.button(" Submit Crime", key="add_crime", help="Save the crime record"): if crime_type and description and location and officer: crime_data = { "crime_type": crime_type.lower(), "description": description.lower(), "location": location.lower(), "date": str(date), "officer_in_charge": officer.lower() } try: response = requests.post("http://localhost:8000/api/crimes", json=crime_data) response.raise_for_status() st.success("Crime added successfully!") except requests.RequestException as e: st.error(f"Error: {e}") else: st.error("Please fill in all fields.") elif choice == "View Crimes" and st.session_state.user: st.header("View Crimes") with st.container(): st.markdown('
', unsafe_allow_html=True) search_query = st.text_input("Search Crimes", placeholder="Search by crime type or location...", help="e.g., Cyber Crimes, New York") if st.button(" Search", key="search_crimes"): try: response = requests.get("http://localhost:8000/api/crimes", params={"search": search_query}) response.raise_for_status() crimes = response.json() df = pd.DataFrame(crimes) for col in ['crime_type', 'description', 'location', 'officer_in_charge', 'status']: if col in df.columns: df[col] = df[col].str.title() st.dataframe(df) except requests.RequestException as e: st.error(f"Error loading crimes: {e}") st.markdown('
', unsafe_allow_html=True) elif choice == "Add FIR" and st.session_state.user: st.header("Add FIR") crime_id = st.number_input("Crime ID", min_value=1, step=1, help="ID of the associated crime") complainant_name = st.text_input("Complainant Name", help="Name of the person filing the FIR") complainant_contact = st.text_input("Complainant Contact", help="Email or phone number") filing_date = st.date_input("Filing Date") if st.button(" Submit FIR", key="add_fir", help="Save the FIR record"): if complainant_name and complainant_contact: fir_data = { "crime_id": crime_id, "complainant_name": complainant_name.lower(), "complainant_contact": complainant_contact.lower(), "filing_date": str(filing_date) } try: response = requests.post("http://localhost:8000/api/firs", json=fir_data) response.raise_for_status() st.success("FIR added successfully!") except requests.RequestException as e: st.error(f"Error: {e}") else: st.error("Please fill in all fields.") elif choice == "View FIRs" and st.session_state.user: st.header("View FIRs") with st.container(): st.markdown('
', unsafe_allow_html=True) search_query = st.text_input("Search FIRs", placeholder="Search by complainant name or contact...", help="e.g., Alice Brown, alice@example.com") if st.button(" Search", key="search_firs"): try: response = requests.get("http://localhost:8000/api/firs", params={"search": search_query}) response.raise_for_status() firs = response.json() df = pd.DataFrame(firs) for col in ['complainant_name', 'complainant_contact']: if col in df.columns: df[col] = df[col].str.title() st.dataframe(df) except requests.RequestException as e: st.error(f"Error loading FIRs: {e}") st.markdown('
', unsafe_allow_html=True) elif choice == "Data Analysis" and st.session_state.user and st.session_state.role == "admin": st.header("Data Analysis") with st.container(): st.markdown('
', unsafe_allow_html=True) search_query = st.text_input("Filter Crimes", placeholder="Filter by crime type or location...", help="e.g., Cyber Crimes, New York") if st.button(" Filter", key="filter_analysis"): df = load_crime_data(search_query) else: df = load_crime_data() st.markdown('
', unsafe_allow_html=True) if not df.empty: st.subheader("Crime Type Distribution") fig = px.histogram(df, x="crime_type", title="Distribution of Crime Types") fig.update_layout( title_font_color="white", xaxis_title_font_color="white", yaxis_title_font_color="white", font_color="white", plot_bgcolor="rgba(0,0,0,0)", paper_bgcolor="rgba(0,0,0,0)" ) st.plotly_chart(fig) st.subheader("Crime Trends Over Time") df['date'] = pd.to_datetime(df['date']) df['year_month'] = df['date'].dt.to_period('M') trend_data = df.groupby('year_month').size().reset_index(name='count') trend_data['year_month'] = trend_data['year_month'].astype(str) fig = px.line(trend_data, x='year_month', y='count', title="Crime Trends Over Time") fig.update_layout( title_font_color="white", xaxis_title_font_color="white", yaxis_title_font_color="white", font_color="white", plot_bgcolor="rgba(0,0,0,0)", paper_bgcolor="rgba(0,0,0,0)" ) st.plotly_chart(fig) else: st.error("No crime data available for analysis. Please add crimes or check the database.") elif choice == "ML Predictions" and st.session_state.user: st.header("Predict Crime Status") st.markdown(""" This feature uses advanced machine learning models (Random Forest and XGBoost) to predict whether a crime case is likely to be 'Open' or 'Closed' based on its type and location. The prediction can help prioritize investigations or allocate resources effectively. Select a model, crime type, and location, then click Predict to see the result. """) try: rf_model, xgb_model, le_crime, le_location, le_status, rf_accuracy, rf_f1, xgb_accuracy, xgb_f1 = train_ml_models() if rf_model is None: st.error("No data available for ML predictions. Please add at least 5 crime records with varied statuses (e.g., 'open' and 'closed').") else: # Model Performance st.subheader("Model Performance") st.write(f"**Random Forest**: Accuracy: {rf_accuracy:.2f}, F1-Score: {rf_f1:.2f} (measures model reliability)") st.write(f"**XGBoost**: Accuracy: {xgb_accuracy:.2f}, F1-Score: {xgb_f1:.2f} (measures model reliability)") st.markdown("**Accuracy**: Percentage of correct predictions. **F1-Score**: Balances precision and recall for robust evaluation.") # Training Data Preview st.subheader("Training Data Preview") df = load_crime_data() if not df.empty: preview_df = df[['crime_type', 'location', 'status']].copy() preview_df.columns = ['Crime Type', 'Location', 'Status'] preview_df = preview_df.apply(lambda x: x.str.title() if x.dtype == "object" else x) st.dataframe(preview_df, height=200) else: st.warning("No data available to display.") # Prediction Inputs st.subheader("Make a Prediction") model_choice = st.selectbox("Select Model", ["Random Forest", "XGBoost"], help="Choose the machine learning model for prediction") crime_types = sorted(set(load_crime_data()['crime_type'])) locations = sorted(set(load_crime_data()['location'])) if len(crime_types) == 0 or len(locations) == 0: st.error("No crime data available for prediction. Please add crimes via 'Add Crime'.") else: crime_type = st.selectbox("Crime Type", [t.title() for t in crime_types], help="e.g., Cyber Crimes, Theft") location = st.selectbox("Location", [t.title() for t in locations], help="e.g., New York, Chicago") if st.button(" Predict", key="predict", help="Predict the crime status"): crime_type_encoded = le_crime.transform([crime_type.lower()])[0] location_encoded = le_location.transform([location.lower()])[0] model = rf_model if model_choice == "Random Forest" else xgb_model input_data = np.array([[crime_type_encoded, location_encoded]]) prediction = model.predict(input_data)[0] status = le_status.inverse_transform([prediction])[0] # Get prediction probabilities probs = model.predict_proba(input_data)[0] prob_dict = {le_status.inverse_transform([i])[0].title(): f"{prob*100:.1f}%" for i, prob in enumerate(probs)} # Get feature importance feature_names = ['Crime Type', 'Location'] if model_choice == "Random Forest": importance = model.feature_importances_ else: # XGBoost importance = model.feature_importances_ importance_dict = {name: f"{imp*100:.1f}%" for name, imp in zip(feature_names, importance)} # Display results in a styled container st.markdown('
', unsafe_allow_html=True) st.write(f"**Predicted Status**: {status.title()}") st.write("**Confidence Scores**:") for status, prob in prob_dict.items(): st.write(f"- {status}: {prob}") st.write("**Feature Importance**: (How much each input affects the prediction)") for name, imp in importance_dict.items(): st.write(f"- {name}: {imp}") st.markdown('
', unsafe_allow_html=True) except Exception as e: st.error(f"Error in ML Predictions: {e}. Ensure the database is initialized with sufficient data.") elif choice in ["Add Crime", "Data Analysis"] and st.session_state.user and st.session_state.role != "admin": st.error("Access Denied: This feature is restricted to admin users.") elif choice not in ["Login", "Sign Up"] and not st.session_state.user: st.error("Please log in to access this feature.")