|
|
import streamlit as st |
|
|
import pandas as pd |
|
|
import numpy as np |
|
|
import plotly.express as px |
|
|
import datetime |
|
|
import time |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
st.set_page_config( |
|
|
page_title="WDE Accountability Dashboard", |
|
|
page_icon="π€ ", |
|
|
layout="wide" |
|
|
) |
|
|
|
|
|
|
|
|
WY_DISTRICTS = [ |
|
|
"Laramie County SD #1", |
|
|
"Natrona County SD #1", |
|
|
"Sheridan County SD #2", |
|
|
"Teton County SD #1", |
|
|
"Albany County SD #1" |
|
|
] |
|
|
|
|
|
|
|
|
@st.cache_data(ttl=3600) |
|
|
def load_data(simulation_date): |
|
|
""" |
|
|
Simulates fetching data from a Data Warehouse updated nightly. |
|
|
""" |
|
|
data = [] |
|
|
|
|
|
|
|
|
for dist in WY_DISTRICTS: |
|
|
|
|
|
for school_level in ['Elementary', 'Middle', 'High']: |
|
|
school_name = f"{dist.split(' ')[0]} {school_level}" |
|
|
|
|
|
|
|
|
for i in range(50): |
|
|
student_id = f"WY-{np.random.randint(100000, 999999)}" |
|
|
|
|
|
|
|
|
math_score = np.random.normal(500, 50) + (20 if "Teton" in dist else 0) |
|
|
ela_score = np.random.normal(510, 45) |
|
|
attendance_rate = np.clip(np.random.normal(92, 5), 50, 100) |
|
|
|
|
|
data.append({ |
|
|
"District": dist, |
|
|
"School": school_name, |
|
|
"Student_ID": student_id, |
|
|
"Grade_Level": np.random.choice([3, 4, 5, 6, 7, 8, 9, 10, 11]), |
|
|
"WY_TOPP_Math": int(math_score), |
|
|
"WY_TOPP_ELA": int(ela_score), |
|
|
"Attendance_Pct": round(attendance_rate, 1), |
|
|
"At_Risk": "Yes" if attendance_rate < 85 or math_score < 450 else "No", |
|
|
"Last_Updated": simulation_date |
|
|
}) |
|
|
|
|
|
return pd.DataFrame(data) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
USERS = { |
|
|
"admin": {"password": "password123", "role": "State_Admin", "access": "All"}, |
|
|
"laramie_supt": {"password": "wyo", "role": "District_Admin", "access": "Laramie County SD #1"}, |
|
|
"teton_principal": {"password": "mountains", "role": "School_Admin", "access": "Teton Elementary"}, |
|
|
} |
|
|
|
|
|
def login(): |
|
|
st.markdown("## π WDE Secure Login") |
|
|
|
|
|
if "authenticated" not in st.session_state: |
|
|
st.session_state["authenticated"] = False |
|
|
|
|
|
if not st.session_state["authenticated"]: |
|
|
username = st.text_input("Username") |
|
|
password = st.text_input("Password", type="password") |
|
|
|
|
|
if st.button("Login"): |
|
|
if username in USERS and USERS[username]["password"] == password: |
|
|
st.session_state["authenticated"] = True |
|
|
st.session_state["user"] = username |
|
|
st.session_state["role"] = USERS[username]["role"] |
|
|
st.session_state["access"] = USERS[username]["access"] |
|
|
st.rerun() |
|
|
else: |
|
|
st.error("Invalid credentials") |
|
|
else: |
|
|
return True |
|
|
return False |
|
|
|
|
|
def logout(): |
|
|
st.session_state["authenticated"] = False |
|
|
st.rerun() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main_dashboard(): |
|
|
|
|
|
st.sidebar.title("Navigation") |
|
|
st.sidebar.write(f"Logged in as: **{st.session_state['user']}**") |
|
|
st.sidebar.write(f"Role: *{st.session_state['role']}*") |
|
|
|
|
|
if st.sidebar.button("Logout"): |
|
|
logout() |
|
|
|
|
|
st.sidebar.markdown("---") |
|
|
|
|
|
|
|
|
today = datetime.date.today() |
|
|
st.sidebar.info(f"π
Data Current As Of: \n{today} 03:00 AM MST") |
|
|
|
|
|
|
|
|
df = load_data(today) |
|
|
|
|
|
|
|
|
|
|
|
if st.session_state["access"] != "All": |
|
|
if st.session_state["role"] == "District_Admin": |
|
|
df = df[df["District"] == st.session_state["access"]] |
|
|
elif st.session_state["role"] == "School_Admin": |
|
|
df = df[df["School"] == st.session_state["access"]] |
|
|
|
|
|
|
|
|
st.title("ποΈ Wyoming Accountability & Analytics") |
|
|
|
|
|
|
|
|
col1, col2, col3, col4 = st.columns(4) |
|
|
col1.metric("Total Students", f"{len(df)}") |
|
|
col2.metric("Avg Math Score", f"{int(df['WY_TOPP_Math'].mean())}") |
|
|
col3.metric("Avg Attendance", f"{df['Attendance_Pct'].mean():.1f}%") |
|
|
|
|
|
at_risk_count = len(df[df["At_Risk"] == "Yes"]) |
|
|
col4.metric("At-Risk Students", f"{at_risk_count}", delta="-Action Required", delta_color="inverse") |
|
|
|
|
|
st.markdown("---") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
st.subheader("π Performance Analytics") |
|
|
|
|
|
view_type = st.radio("Analyze by:", ["District", "School", "Grade_Level"], horizontal=True) |
|
|
|
|
|
|
|
|
agg_df = df.groupby(view_type)[["WY_TOPP_Math", "WY_TOPP_ELA", "Attendance_Pct"]].mean().reset_index() |
|
|
|
|
|
|
|
|
fig = px.bar( |
|
|
agg_df, |
|
|
x=view_type, |
|
|
y=["WY_TOPP_Math", "WY_TOPP_ELA"], |
|
|
barmode='group', |
|
|
title=f"Average WY-TOPP Scores by {view_type}", |
|
|
color_discrete_sequence=["#F2A900", "#53565A"] |
|
|
) |
|
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
|
|
|
|
|
c1, c2 = st.columns(2) |
|
|
|
|
|
with c1: |
|
|
st.subheader("Attendance vs. Performance") |
|
|
fig2 = px.scatter( |
|
|
df, |
|
|
x="Attendance_Pct", |
|
|
y="WY_TOPP_Math", |
|
|
color="At_Risk", |
|
|
hover_data=["Student_ID", "School"], |
|
|
title="Correlation: Attendance vs Math Scores" |
|
|
) |
|
|
st.plotly_chart(fig2, use_container_width=True) |
|
|
|
|
|
with c2: |
|
|
st.subheader("At-Risk Distribution") |
|
|
risk_dist = df['At_Risk'].value_counts() |
|
|
fig3 = px.pie(values=risk_dist, names=risk_dist.index, hole=0.4, color_discrete_sequence=['#2ecc71', '#e74c3c']) |
|
|
st.plotly_chart(fig3, use_container_width=True) |
|
|
|
|
|
|
|
|
st.subheader("π Student Data Details") |
|
|
|
|
|
|
|
|
text_search = st.text_input("Search Student ID", "") |
|
|
grade_filter = st.multiselect("Filter by Grade", sorted(df["Grade_Level"].unique())) |
|
|
|
|
|
filtered_df = df.copy() |
|
|
if text_search: |
|
|
filtered_df = filtered_df[filtered_df["Student_ID"].str.contains(text_search)] |
|
|
if grade_filter: |
|
|
filtered_df = filtered_df[filtered_df["Grade_Level"].isin(grade_filter)] |
|
|
|
|
|
|
|
|
st.dataframe( |
|
|
filtered_df.style.map(lambda x: 'color: red; font-weight: bold' if isinstance(x, (int, float)) and x < 85 else '', subset=['Attendance_Pct']), |
|
|
use_container_width=True, |
|
|
hide_index=True |
|
|
) |
|
|
|
|
|
|
|
|
if st.button("π₯ Export Report to CSV"): |
|
|
st.toast("Export started... Logged in Audit Trail.", icon="β
") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
if login(): |
|
|
main_dashboard() |