97 lines
3.1 KiB
Python
97 lines
3.1 KiB
Python
"""Page 3: Staffing & Enrollment."""
|
|
|
|
import duckdb
|
|
from dash import html, dcc
|
|
import plotly.graph_objects as go
|
|
|
|
from admin_analytics.dashboard.queries import (
|
|
query_staff_composition,
|
|
query_student_staff_ratios,
|
|
query_growth_index,
|
|
)
|
|
|
|
_NO_DATA = html.Div(
|
|
"No IPEDS staff data loaded. Run: admin-analytics ingest ipeds",
|
|
style={"textAlign": "center", "padding": "40px", "color": "#888"},
|
|
)
|
|
|
|
|
|
def layout(conn: duckdb.DuckDBPyConnection):
|
|
staff_df = query_staff_composition(conn)
|
|
if staff_df.height == 0:
|
|
return _NO_DATA
|
|
|
|
staff_pd = staff_df.to_pandas()
|
|
|
|
# Staff composition stacked area
|
|
comp_fig = go.Figure()
|
|
for col, label, color in [
|
|
("faculty_total", "Faculty", "#00539F"),
|
|
("management_total", "Management", "#E07A5F"),
|
|
("other_staff", "Other Staff", "#7FB069"),
|
|
]:
|
|
comp_fig.add_trace(go.Scatter(
|
|
x=staff_pd["year"], y=staff_pd[col],
|
|
mode="lines", name=label,
|
|
stackgroup="one",
|
|
line={"color": color},
|
|
))
|
|
comp_fig.update_layout(
|
|
title="Staff Composition Over Time",
|
|
xaxis_title="Year", yaxis_title="Headcount",
|
|
template="plotly_white", height=420,
|
|
)
|
|
|
|
# Student-to-staff ratios
|
|
ratio_df = query_student_staff_ratios(conn)
|
|
ratio_fig = go.Figure()
|
|
if ratio_df.height > 0:
|
|
ratio_pd = ratio_df.to_pandas()
|
|
ratio_fig.add_trace(go.Scatter(
|
|
x=ratio_pd["year"], y=ratio_pd["students_per_staff"],
|
|
mode="lines+markers", name="Students per Staff",
|
|
line={"color": "#00539F"},
|
|
))
|
|
ratio_fig.add_trace(go.Scatter(
|
|
x=ratio_pd["year"], y=ratio_pd["students_per_faculty"],
|
|
mode="lines+markers", name="Students per Faculty",
|
|
line={"color": "#FFD200"},
|
|
))
|
|
ratio_fig.update_layout(
|
|
title="Student-to-Staff Ratios",
|
|
xaxis_title="Year", yaxis_title="Ratio",
|
|
template="plotly_white", height=380,
|
|
)
|
|
|
|
# Growth index
|
|
growth_df = query_growth_index(conn)
|
|
growth_fig = go.Figure()
|
|
if growth_df.height > 0:
|
|
growth_pd = growth_df.to_pandas()
|
|
growth_fig.add_trace(go.Scatter(
|
|
x=growth_pd["year"], y=growth_pd["mgmt_index"],
|
|
mode="lines+markers", name="Management Growth",
|
|
line={"color": "#E07A5F"},
|
|
))
|
|
growth_fig.add_trace(go.Scatter(
|
|
x=growth_pd["year"], y=growth_pd["enrollment_index"],
|
|
mode="lines+markers", name="Enrollment Growth",
|
|
line={"color": "#00539F"},
|
|
))
|
|
growth_fig.add_hline(y=100, line_dash="dot", line_color="#ccc")
|
|
growth_fig.update_layout(
|
|
title="Management vs Enrollment Growth (Indexed, Base Year = 100)",
|
|
xaxis_title="Year", yaxis_title="Index",
|
|
template="plotly_white", height=380,
|
|
)
|
|
|
|
return html.Div([
|
|
dcc.Graph(figure=comp_fig),
|
|
html.Div(
|
|
[
|
|
html.Div(dcc.Graph(figure=ratio_fig), style={"flex": "1"}),
|
|
html.Div(dcc.Graph(figure=growth_fig), style={"flex": "1"}),
|
|
],
|
|
style={"display": "flex", "gap": "16px"},
|
|
),
|
|
])
|