burnout-tracker / app.py
Katherine
file organization / deletion
e3188b7
Raw
History Blame Contribute Delete
9.2 kB
import streamlit as st
import numpy as np
import sys
import time
sys.path.insert(0, '.')
from data.data_loader import load_data, preprocess, split_and_scale
from src.llm_advisor import get_burnout_advice, get_burnout_chat_response
@st.cache_resource
def load_models():
from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
df = load_data()
X, y, feature_cols = preprocess(df)
X_train, X_temp, y_train, y_temp = train_test_split(X, y, test_size=0.3, random_state=42)
X_val, _, y_val, _ = train_test_split(X_temp, y_temp, test_size=0.5, random_state=42)
scaler = StandardScaler()
X_train_s = scaler.fit_transform(X_train)
X_val_s = scaler.transform(X_val)
model = XGBClassifier(
n_estimators=100, max_depth=4, learning_rate=0.05,
subsample=0.7, colsample_bytree=0.7, min_child_weight=5,
reg_alpha=0.1, reg_lambda=1.0, eval_metric='logloss',
early_stopping_rounds=15, random_state=42,
)
model.fit(X_train_s, y_train, eval_set=[(X_val_s, y_val)], verbose=False)
return model, scaler, feature_cols
def get_top_risk_factors(user_input, feature_cols, model):
"""Return top-3 risk factors using XGBoost feature importances."""
importances = model.feature_importances_
scores = {}
for col, imp in zip(feature_cols, importances):
if col not in user_input:
continue
val = user_input[col]
# Recovery/wellness features: low value = higher risk contribution
recovery_features = [
'SLEEP_HOURS', 'WEEKLY_MEDITATION', 'TIME_FOR_PASSION', 'FLOW',
'ACHIEVEMENT', 'SOCIAL_NETWORK', 'CORE_CIRCLE', 'LIVE_VISION',
'RECOVERY_SCORE', 'SOCIAL_SUPPORT_SCORE', 'LIFESTYLE_SCORE', 'HEALTH_HABITS',
]
if col in recovery_features:
scores[col] = imp * (1 - val / 10)
else:
scores[col] = imp * (val / 10)
top3 = sorted(scores.items(), key=lambda x: x[1], reverse=True)[:3]
return {k: f"score {user_input.get(k, '?')}/10" for k, _ in top3}
def main():
st.set_page_config(page_title="Burnout Risk Tracker", page_icon="πŸ”₯", layout="wide")
st.title("πŸ”₯ Burnout Risk Tracker")
st.markdown(
"*Answer questions about your **recovery and lifestyle habits** β€” "
"the model predicts whether you're showing early burnout risk.*"
)
xgb_model, scaler, feature_cols = load_models()
if 'conversation_history' not in st.session_state:
st.session_state.conversation_history = []
if 'risk_score' not in st.session_state:
st.session_state.risk_score = None
if 'advice' not in st.session_state:
st.session_state.advice = None
col1, col2 = st.columns([1, 1])
with col1:
st.subheader("Your Lifestyle & Recovery Habits")
st.caption(
"These questions cover your wellness behaviors β€” not your current stress level. "
"The model infers burnout risk from how you're living, not how you're feeling right now."
)
user_input = {}
st.markdown("**Sleep & Recovery**")
user_input['SLEEP_HOURS'] = st.slider("Sleep hours per night", 0, 10, 7)
user_input['WEEKLY_MEDITATION'] = st.slider("Meditation sessions per week", 0, 10, 2)
user_input['TIME_FOR_PASSION'] = st.slider("Time for hobbies / passions", 0, 10, 3)
st.markdown("**Work & Productivity**")
user_input['TODO_COMPLETED'] = st.slider("Daily tasks completed", 0, 10, 5)
user_input['FLOW'] = st.slider("Flow state at work", 0, 10, 5)
user_input['ACHIEVEMENT'] = st.slider("Sense of achievement", 0, 10, 5)
user_input['LIVE_VISION'] = st.slider("Clarity of life vision", 0, 10, 5)
st.markdown("**Social & Support**")
user_input['SOCIAL_NETWORK'] = st.slider("Strength of social network", 0, 10, 5)
user_input['CORE_CIRCLE'] = st.slider("Close / trusted relationships", 0, 10, 5)
user_input['SUPPORTING_OTHERS'] = st.slider("Supporting others regularly", 0, 10, 5)
st.markdown("**Health & Lifestyle**")
user_input['FRUITS_VEGGIES'] = st.slider("Fruit & veg servings per day", 0, 10, 5)
user_input['DAILY_STEPS'] = st.slider("Daily steps (thousands)", 0, 10, 5)
user_input['SUFFICIENT_INCOME'] = st.slider("Income sufficiency", 0, 10, 5)
user_input['BMI_RANGE'] = st.slider("BMI range (1=under, 4=obese)", 1, 4, 2)
st.markdown("**Personal Growth**")
user_input['PERSONAL_AWARDS'] = st.slider("Personal awards / recognition", 0, 10, 3)
user_input['DONATION'] = st.slider("Charitable giving", 0, 10, 2)
user_input['PLACES_VISITED'] = st.slider("New places visited recently", 0, 10, 3)
st.markdown("**About you**")
user_input['AGE'] = st.selectbox(
"Age range", [0, 1, 2, 3],
format_func=lambda x: ['Under 20', '21–35', '36–50', '51+'][x]
)
user_input['GENDER'] = st.selectbox(
"Gender", [0, 1],
format_func=lambda x: ['Female', 'Male'][x]
)
# Fill in engineered features so the input vector is complete
user_input['RECOVERY_SCORE'] = (user_input['SLEEP_HOURS']
+ user_input['TIME_FOR_PASSION']
+ user_input['WEEKLY_MEDITATION'])
user_input['SOCIAL_SUPPORT_SCORE'] = (user_input['SOCIAL_NETWORK']
+ user_input['CORE_CIRCLE'])
user_input['LIFESTYLE_SCORE'] = (user_input['FLOW']
+ user_input['ACHIEVEMENT']
+ user_input['LIVE_VISION']
+ user_input['TIME_FOR_PASSION'])
user_input['HEALTH_HABITS'] = (user_input['FRUITS_VEGGIES']
+ user_input['SLEEP_HOURS']
+ user_input['TODO_COMPLETED'])
if st.button("πŸ” Assess My Burnout Risk", type="primary"):
try:
input_array = np.array([[user_input.get(f, 0) for f in feature_cols]])
input_scaled = scaler.transform(input_array)
t0 = time.perf_counter()
risk_score = xgb_model.predict_proba(input_scaled)[0][1]
inference_ms = (time.perf_counter() - t0) * 1000
st.session_state.risk_score = risk_score
st.session_state.inference_time_ms = inference_ms
st.session_state.conversation_history = []
top_risk_factors = get_top_risk_factors(user_input, feature_cols, xgb_model)
with st.spinner("Getting personalised advice from AI coach..."):
advice = get_burnout_advice(risk_score, top_risk_factors, user_input)
st.session_state.advice = advice
st.session_state.risk_context = (
f"Risk score: {risk_score:.1%}, "
f"Top factors: {top_risk_factors}"
)
except Exception as e:
st.error(f"Something went wrong during prediction: {e}")
with col2:
if st.session_state.risk_score is not None:
risk_score = st.session_state.risk_score
st.caption(f"⚑ Model inference: {st.session_state.inference_time_ms:.2f} ms")
st.subheader("Your Burnout Risk")
if risk_score > 0.7:
st.error(f"🚨 High Risk: {risk_score:.1%}")
elif risk_score > 0.4:
st.warning(f"⚠️ Moderate Risk: {risk_score:.1%}")
else:
st.success(f"βœ… Low Risk: {risk_score:.1%}")
st.progress(float(risk_score))
st.subheader("AI Coach Advice")
st.markdown(st.session_state.advice)
st.divider()
st.subheader("πŸ’¬ Chat with Your AI Coach")
for msg in st.session_state.conversation_history:
role = msg['role']
st.chat_message(role).write(msg['content'])
if prompt := st.chat_input("Ask your coach anything..."):
st.chat_message("user").write(prompt)
with st.spinner("Thinking..."):
try:
response, st.session_state.conversation_history = get_burnout_chat_response(
st.session_state.conversation_history,
prompt,
st.session_state.risk_context,
)
except Exception as e:
response = f"Sorry, I couldn't reach the AI coach right now ({e})."
st.chat_message("assistant").write(response)
st.rerun()
if __name__ == '__main__':
main()