Update app.py
Browse files
app.py
CHANGED
|
@@ -64,14 +64,10 @@ CLUSTER_PROFILES = {
|
|
| 64 |
}
|
| 65 |
|
| 66 |
# Defaults for hidden vars when user adjusts sliders manually (TOTAL means)
|
| 67 |
-
HIDDEN_DEFAULTS = {
|
| 68 |
-
"Voice": 4.23,
|
| 69 |
-
"DecisionAutonomy": 4.50,
|
| 70 |
-
"Workload": 4.13,
|
| 71 |
-
}
|
| 72 |
-
|
| 73 |
VISIBLE_DRIVERS = ["Engagement", "SupportiveGM", "WellBeing", "WorkEnvironment"]
|
| 74 |
|
|
|
|
| 75 |
# =========================
|
| 76 |
# Helpers
|
| 77 |
# =========================
|
|
@@ -82,8 +78,7 @@ def clamp_1_5(x: float) -> float:
|
|
| 82 |
|
| 83 |
def build_X(vals: dict) -> pd.DataFrame:
|
| 84 |
"""Build 1-row DataFrame in exact model feature order."""
|
| 85 |
-
|
| 86 |
-
return pd.DataFrame([[row[f] for f in FEATURES]], columns=FEATURES)
|
| 87 |
|
| 88 |
|
| 89 |
def prob_at_risk(X: pd.DataFrame) -> float:
|
|
@@ -117,13 +112,11 @@ def make_driver_plot_midlevel(Engagement, SupportiveGM, WellBeing, WorkEnvironme
|
|
| 117 |
values = [Engagement, SupportiveGM, WellBeing, WorkEnvironment]
|
| 118 |
colors = ["seagreen" if v >= threshold else "firebrick" for v in values]
|
| 119 |
|
| 120 |
-
# Deterministic layout
|
| 121 |
fig, ax = plt.subplots(figsize=(11, 5), constrained_layout=True)
|
| 122 |
-
|
| 123 |
ax.bar(drivers, values, color=colors)
|
| 124 |
-
ax.axhline(threshold, linestyle="--", linewidth=2)
|
| 125 |
|
| 126 |
-
|
| 127 |
ax.text(
|
| 128 |
0.99,
|
| 129 |
threshold,
|
|
@@ -164,12 +157,9 @@ def predict_dashboard(
|
|
| 164 |
|
| 165 |
# hidden
|
| 166 |
Voice = clamp_1_5(HIDDEN_DEFAULTS["Voice"] if Voice is None else Voice)
|
| 167 |
-
DecisionAutonomy = clamp_1_5(
|
| 168 |
-
HIDDEN_DEFAULTS["DecisionAutonomy"] if DecisionAutonomy is None else DecisionAutonomy
|
| 169 |
-
)
|
| 170 |
Workload = clamp_1_5(HIDDEN_DEFAULTS["Workload"] if Workload is None else Workload)
|
| 171 |
|
| 172 |
-
# Table: risk status by management level
|
| 173 |
rows = []
|
| 174 |
for mgmt in [1, 2, 3, 4]:
|
| 175 |
vals = {
|
|
@@ -193,21 +183,14 @@ def predict_dashboard(
|
|
| 193 |
|
| 194 |
df_table = pd.DataFrame(rows)
|
| 195 |
|
| 196 |
-
# Headline = Mid-level status
|
| 197 |
mid_status = df_table.loc[df_table["Management Level"] == MGMT_LABELS[2], "Risk Status"].iloc[0]
|
| 198 |
headline = f"Mid-level managers: **{mid_status}**"
|
| 199 |
|
| 200 |
-
# Plot only the 4 drivers (mid-level focused)
|
| 201 |
fig = make_driver_plot_midlevel(Engagement, SupportiveGM, WellBeing, WorkEnvironment)
|
| 202 |
-
|
| 203 |
return headline, df_table, fig
|
| 204 |
|
| 205 |
|
| 206 |
def apply_cluster(cluster_name: str):
|
| 207 |
-
"""
|
| 208 |
-
Load cluster means into the 4 sliders AND adjust hidden vars to that cluster's means,
|
| 209 |
-
then run prediction dashboard.
|
| 210 |
-
"""
|
| 211 |
prof = CLUSTER_PROFILES[cluster_name]
|
| 212 |
|
| 213 |
Engagement = prof["Engagement"]
|
|
@@ -229,15 +212,23 @@ def apply_cluster(cluster_name: str):
|
|
| 229 |
Workload=Workload,
|
| 230 |
)
|
| 231 |
|
| 232 |
-
# Return sliders first so UI updates smoothly
|
| 233 |
return Engagement, SupportiveGM, WellBeing, WorkEnvironment, headline, table, fig
|
| 234 |
|
| 235 |
|
| 236 |
# =========================
|
| 237 |
-
# Gradio UI (
|
| 238 |
# =========================
|
| 239 |
CSS = """
|
| 240 |
#headline_box { min-height: 44px; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 241 |
"""
|
| 242 |
|
| 243 |
with gr.Blocks() as demo:
|
|
@@ -262,21 +253,21 @@ with gr.Blocks() as demo:
|
|
| 262 |
btn_predict = gr.Button("Predict (manual)")
|
| 263 |
|
| 264 |
with gr.Column(scale=1, min_width=520):
|
| 265 |
-
# Use elem_id on the Markdown itself (no gr.Box in Gradio 6)
|
| 266 |
headline = gr.Markdown(elem_id="headline_box")
|
| 267 |
|
| 268 |
-
#
|
| 269 |
-
|
|
|
|
|
|
|
|
|
|
| 270 |
plot = gr.Plot(height=420)
|
| 271 |
|
| 272 |
-
# Manual predict
|
| 273 |
btn_predict.click(
|
| 274 |
fn=predict_dashboard,
|
| 275 |
inputs=[Engagement, SupportiveGM, WellBeing, WorkEnvironment],
|
| 276 |
outputs=[headline, table, plot],
|
| 277 |
)
|
| 278 |
|
| 279 |
-
# Cluster buttons (load means + predict)
|
| 280 |
btn_c1.click(
|
| 281 |
fn=lambda: apply_cluster("Cluster 1"),
|
| 282 |
inputs=[],
|
|
|
|
| 64 |
}
|
| 65 |
|
| 66 |
# Defaults for hidden vars when user adjusts sliders manually (TOTAL means)
|
| 67 |
+
HIDDEN_DEFAULTS = {"Voice": 4.23, "DecisionAutonomy": 4.50, "Workload": 4.13}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
VISIBLE_DRIVERS = ["Engagement", "SupportiveGM", "WellBeing", "WorkEnvironment"]
|
| 69 |
|
| 70 |
+
|
| 71 |
# =========================
|
| 72 |
# Helpers
|
| 73 |
# =========================
|
|
|
|
| 78 |
|
| 79 |
def build_X(vals: dict) -> pd.DataFrame:
|
| 80 |
"""Build 1-row DataFrame in exact model feature order."""
|
| 81 |
+
return pd.DataFrame([[vals[f] for f in FEATURES]], columns=FEATURES)
|
|
|
|
| 82 |
|
| 83 |
|
| 84 |
def prob_at_risk(X: pd.DataFrame) -> float:
|
|
|
|
| 112 |
values = [Engagement, SupportiveGM, WellBeing, WorkEnvironment]
|
| 113 |
colors = ["seagreen" if v >= threshold else "firebrick" for v in values]
|
| 114 |
|
| 115 |
+
# Deterministic layout (reduces jitter)
|
| 116 |
fig, ax = plt.subplots(figsize=(11, 5), constrained_layout=True)
|
|
|
|
| 117 |
ax.bar(drivers, values, color=colors)
|
|
|
|
| 118 |
|
| 119 |
+
ax.axhline(threshold, linestyle="--", linewidth=2)
|
| 120 |
ax.text(
|
| 121 |
0.99,
|
| 122 |
threshold,
|
|
|
|
| 157 |
|
| 158 |
# hidden
|
| 159 |
Voice = clamp_1_5(HIDDEN_DEFAULTS["Voice"] if Voice is None else Voice)
|
| 160 |
+
DecisionAutonomy = clamp_1_5(HIDDEN_DEFAULTS["DecisionAutonomy"] if DecisionAutonomy is None else DecisionAutonomy)
|
|
|
|
|
|
|
| 161 |
Workload = clamp_1_5(HIDDEN_DEFAULTS["Workload"] if Workload is None else Workload)
|
| 162 |
|
|
|
|
| 163 |
rows = []
|
| 164 |
for mgmt in [1, 2, 3, 4]:
|
| 165 |
vals = {
|
|
|
|
| 183 |
|
| 184 |
df_table = pd.DataFrame(rows)
|
| 185 |
|
|
|
|
| 186 |
mid_status = df_table.loc[df_table["Management Level"] == MGMT_LABELS[2], "Risk Status"].iloc[0]
|
| 187 |
headline = f"Mid-level managers: **{mid_status}**"
|
| 188 |
|
|
|
|
| 189 |
fig = make_driver_plot_midlevel(Engagement, SupportiveGM, WellBeing, WorkEnvironment)
|
|
|
|
| 190 |
return headline, df_table, fig
|
| 191 |
|
| 192 |
|
| 193 |
def apply_cluster(cluster_name: str):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
prof = CLUSTER_PROFILES[cluster_name]
|
| 195 |
|
| 196 |
Engagement = prof["Engagement"]
|
|
|
|
| 212 |
Workload=Workload,
|
| 213 |
)
|
| 214 |
|
|
|
|
| 215 |
return Engagement, SupportiveGM, WellBeing, WorkEnvironment, headline, table, fig
|
| 216 |
|
| 217 |
|
| 218 |
# =========================
|
| 219 |
+
# Gradio UI (CSS locks layout without Dataframe height=)
|
| 220 |
# =========================
|
| 221 |
CSS = """
|
| 222 |
#headline_box { min-height: 44px; }
|
| 223 |
+
|
| 224 |
+
/* fixed-height container for the dataframe to prevent page reflow */
|
| 225 |
+
#table_box {
|
| 226 |
+
height: 240px;
|
| 227 |
+
overflow: auto;
|
| 228 |
+
border: 1px solid rgba(0,0,0,0.08);
|
| 229 |
+
border-radius: 10px;
|
| 230 |
+
padding: 6px;
|
| 231 |
+
}
|
| 232 |
"""
|
| 233 |
|
| 234 |
with gr.Blocks() as demo:
|
|
|
|
| 253 |
btn_predict = gr.Button("Predict (manual)")
|
| 254 |
|
| 255 |
with gr.Column(scale=1, min_width=520):
|
|
|
|
| 256 |
headline = gr.Markdown(elem_id="headline_box")
|
| 257 |
|
| 258 |
+
# Wrap dataframe in a fixed-height container using HTML
|
| 259 |
+
gr.HTML('<div id="table_box">')
|
| 260 |
+
table = gr.Dataframe(label="Risk Status by Management Level")
|
| 261 |
+
gr.HTML("</div>")
|
| 262 |
+
|
| 263 |
plot = gr.Plot(height=420)
|
| 264 |
|
|
|
|
| 265 |
btn_predict.click(
|
| 266 |
fn=predict_dashboard,
|
| 267 |
inputs=[Engagement, SupportiveGM, WellBeing, WorkEnvironment],
|
| 268 |
outputs=[headline, table, plot],
|
| 269 |
)
|
| 270 |
|
|
|
|
| 271 |
btn_c1.click(
|
| 272 |
fn=lambda: apply_cluster("Cluster 1"),
|
| 273 |
inputs=[],
|