Spaces:
Runtime error
Runtime error
add requirements and app for HF Spaces deployment
Browse files- app.py +141 -0
- app/app.py +141 -0
- requirements.txt +8 -0
app.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import joblib
|
| 3 |
+
import numpy as np
|
| 4 |
+
import sys
|
| 5 |
+
sys.path.insert(0, '.')
|
| 6 |
+
from src.data_loader import load_data, preprocess, split_and_scale
|
| 7 |
+
from src.llm_advisor import get_burnout_advice, get_burnout_chat_response
|
| 8 |
+
|
| 9 |
+
# Load models
|
| 10 |
+
@st.cache_resource
|
| 11 |
+
def load_models():
|
| 12 |
+
xgb_model = joblib.load('models/xgboost_model.pkl')
|
| 13 |
+
scaler = joblib.load('models/scaler.pkl')
|
| 14 |
+
feature_cols = joblib.load('models/feature_cols.pkl')
|
| 15 |
+
return xgb_model, scaler, feature_cols
|
| 16 |
+
|
| 17 |
+
def get_top_risk_factors(user_input, feature_cols, model):
|
| 18 |
+
importance = model.feature_importances_
|
| 19 |
+
user_values = [user_input[f] for f in feature_cols]
|
| 20 |
+
|
| 21 |
+
# Weight importance by how extreme the value is (normalized 0-10)
|
| 22 |
+
risk_factors = {}
|
| 23 |
+
for i, col in enumerate(feature_cols):
|
| 24 |
+
val = user_values[i]
|
| 25 |
+
imp = importance[i]
|
| 26 |
+
# Higher stress/shouting/lost vacation = bad, higher sleep/flow = good
|
| 27 |
+
bad_features = ['DAILY_STRESS', 'LOST_VACATION', 'DAILY_SHOUTING', 'BMI_RANGE']
|
| 28 |
+
if col in bad_features:
|
| 29 |
+
score = imp * (val / 10)
|
| 30 |
+
else:
|
| 31 |
+
score = imp * (1 - val / 10)
|
| 32 |
+
risk_factors[col] = score
|
| 33 |
+
|
| 34 |
+
top = sorted(risk_factors.items(), key=lambda x: x[1], reverse=True)[:3]
|
| 35 |
+
return {k: f"score {user_input[k]}/10" for k, _ in top}
|
| 36 |
+
|
| 37 |
+
def main():
|
| 38 |
+
st.set_page_config(page_title="Burnout Tracker", page_icon="🔥", layout="wide")
|
| 39 |
+
|
| 40 |
+
st.title("🔥 Burnout Risk Tracker")
|
| 41 |
+
st.markdown("*Predict your burnout risk and get personalized advice*")
|
| 42 |
+
|
| 43 |
+
xgb_model, scaler, feature_cols = load_models()
|
| 44 |
+
|
| 45 |
+
# Initialize session state
|
| 46 |
+
if 'conversation_history' not in st.session_state:
|
| 47 |
+
st.session_state.conversation_history = []
|
| 48 |
+
if 'risk_score' not in st.session_state:
|
| 49 |
+
st.session_state.risk_score = None
|
| 50 |
+
if 'advice' not in st.session_state:
|
| 51 |
+
st.session_state.advice = None
|
| 52 |
+
|
| 53 |
+
col1, col2 = st.columns([1, 1])
|
| 54 |
+
|
| 55 |
+
with col1:
|
| 56 |
+
st.subheader("Your Daily Lifestyle Inputs")
|
| 57 |
+
|
| 58 |
+
user_input = {}
|
| 59 |
+
user_input['DAILY_STRESS'] = st.slider("Daily Stress Level", 0, 10, 5)
|
| 60 |
+
user_input['SLEEP_HOURS'] = st.slider("Sleep Hours", 0, 10, 7)
|
| 61 |
+
user_input['LOST_VACATION'] = st.slider("Unused Vacation Days", 0, 10, 2)
|
| 62 |
+
user_input['TODO_COMPLETED'] = st.slider("Tasks Completed Today", 0, 10, 5)
|
| 63 |
+
user_input['FLOW'] = st.slider("Flow State at Work", 0, 10, 5)
|
| 64 |
+
user_input['TIME_FOR_PASSION'] = st.slider("Time for Passions", 0, 10, 3)
|
| 65 |
+
user_input['WEEKLY_MEDITATION'] = st.slider("Weekly Meditation Sessions", 0, 10, 2)
|
| 66 |
+
user_input['DAILY_SHOUTING'] = st.slider("Daily Emotional Outbursts", 0, 10, 1)
|
| 67 |
+
user_input['FRUITS_VEGGIES'] = st.slider("Fruits & Veggies Servings", 0, 10, 5)
|
| 68 |
+
user_input['DAILY_STEPS'] = st.slider("Daily Steps (thousands)", 0, 10, 5)
|
| 69 |
+
user_input['SUFFICIENT_INCOME'] = st.slider("Income Sufficiency", 0, 10, 5)
|
| 70 |
+
user_input['SOCIAL_NETWORK'] = st.slider("Social Network Strength", 0, 10, 5)
|
| 71 |
+
user_input['ACHIEVEMENT'] = st.slider("Sense of Achievement", 0, 10, 5)
|
| 72 |
+
user_input['SUPPORTING_OTHERS'] = st.slider("Supporting Others", 0, 10, 5)
|
| 73 |
+
user_input['PLACES_VISITED'] = st.slider("New Places Visited", 0, 10, 3)
|
| 74 |
+
user_input['CORE_CIRCLE'] = st.slider("Close Relationships", 0, 10, 5)
|
| 75 |
+
user_input['PERSONAL_AWARDS'] = st.slider("Personal Awards/Recognition", 0, 10, 3)
|
| 76 |
+
user_input['DONATION'] = st.slider("Charitable Giving", 0, 10, 2)
|
| 77 |
+
user_input['BMI_RANGE'] = st.slider("BMI Range (1=underweight, 4=obese)", 1, 4, 2)
|
| 78 |
+
user_input['LIVE_VISION'] = st.slider("Life Vision Clarity", 0, 10, 5)
|
| 79 |
+
user_input['AGE'] = st.selectbox("Age Range", [0, 1, 2, 3],
|
| 80 |
+
format_func=lambda x: ['<20', '21-35', '36-50', '51+'][x])
|
| 81 |
+
user_input['GENDER'] = st.selectbox("Gender", [0, 1],
|
| 82 |
+
format_func=lambda x: ['Female', 'Male'][x])
|
| 83 |
+
|
| 84 |
+
if st.button("🔍 Assess My Burnout Risk", type="primary"):
|
| 85 |
+
input_array = np.array([[user_input[f] for f in feature_cols]])
|
| 86 |
+
input_scaled = scaler.transform(input_array)
|
| 87 |
+
|
| 88 |
+
risk_score = xgb_model.predict_proba(input_scaled)[0][1]
|
| 89 |
+
st.session_state.risk_score = risk_score
|
| 90 |
+
st.session_state.conversation_history = []
|
| 91 |
+
|
| 92 |
+
top_risk_factors = get_top_risk_factors(user_input, feature_cols, xgb_model)
|
| 93 |
+
|
| 94 |
+
with st.spinner("Getting personalized advice from AI coach..."):
|
| 95 |
+
advice = get_burnout_advice(risk_score, top_risk_factors, user_input)
|
| 96 |
+
st.session_state.advice = advice
|
| 97 |
+
st.session_state.risk_context = f"Risk score: {risk_score:.1%}, Top factors: {top_risk_factors}"
|
| 98 |
+
|
| 99 |
+
with col2:
|
| 100 |
+
if st.session_state.risk_score is not None:
|
| 101 |
+
risk_score = st.session_state.risk_score
|
| 102 |
+
|
| 103 |
+
# Risk meter
|
| 104 |
+
st.subheader("Your Burnout Risk")
|
| 105 |
+
if risk_score > 0.7:
|
| 106 |
+
st.error(f"🚨 High Risk: {risk_score:.1%}")
|
| 107 |
+
elif risk_score > 0.4:
|
| 108 |
+
st.warning(f"⚠️ Moderate Risk: {risk_score:.1%}")
|
| 109 |
+
else:
|
| 110 |
+
st.success(f"✅ Low Risk: {risk_score:.1%}")
|
| 111 |
+
|
| 112 |
+
st.progress(float(risk_score))
|
| 113 |
+
|
| 114 |
+
# AI advice
|
| 115 |
+
st.subheader("AI Coach Advice")
|
| 116 |
+
st.markdown(st.session_state.advice)
|
| 117 |
+
|
| 118 |
+
st.divider()
|
| 119 |
+
|
| 120 |
+
# Chat
|
| 121 |
+
st.subheader("💬 Chat with Your AI Coach")
|
| 122 |
+
|
| 123 |
+
for msg in st.session_state.conversation_history:
|
| 124 |
+
if msg['role'] == 'user':
|
| 125 |
+
st.chat_message("user").write(msg['content'])
|
| 126 |
+
else:
|
| 127 |
+
st.chat_message("assistant").write(msg['content'])
|
| 128 |
+
|
| 129 |
+
if prompt := st.chat_input("Ask your coach anything..."):
|
| 130 |
+
st.chat_message("user").write(prompt)
|
| 131 |
+
with st.spinner("Thinking..."):
|
| 132 |
+
response, st.session_state.conversation_history = get_burnout_chat_response(
|
| 133 |
+
st.session_state.conversation_history,
|
| 134 |
+
prompt,
|
| 135 |
+
st.session_state.risk_context
|
| 136 |
+
)
|
| 137 |
+
st.chat_message("assistant").write(response)
|
| 138 |
+
st.rerun()
|
| 139 |
+
|
| 140 |
+
if __name__ == '__main__':
|
| 141 |
+
main()
|
app/app.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import joblib
|
| 3 |
+
import numpy as np
|
| 4 |
+
import sys
|
| 5 |
+
sys.path.insert(0, '.')
|
| 6 |
+
from src.data_loader import load_data, preprocess, split_and_scale
|
| 7 |
+
from src.llm_advisor import get_burnout_advice, get_burnout_chat_response
|
| 8 |
+
|
| 9 |
+
# Load models
|
| 10 |
+
@st.cache_resource
|
| 11 |
+
def load_models():
|
| 12 |
+
xgb_model = joblib.load('models/xgboost_model.pkl')
|
| 13 |
+
scaler = joblib.load('models/scaler.pkl')
|
| 14 |
+
feature_cols = joblib.load('models/feature_cols.pkl')
|
| 15 |
+
return xgb_model, scaler, feature_cols
|
| 16 |
+
|
| 17 |
+
def get_top_risk_factors(user_input, feature_cols, model):
|
| 18 |
+
importance = model.feature_importances_
|
| 19 |
+
user_values = [user_input[f] for f in feature_cols]
|
| 20 |
+
|
| 21 |
+
# Weight importance by how extreme the value is (normalized 0-10)
|
| 22 |
+
risk_factors = {}
|
| 23 |
+
for i, col in enumerate(feature_cols):
|
| 24 |
+
val = user_values[i]
|
| 25 |
+
imp = importance[i]
|
| 26 |
+
# Higher stress/shouting/lost vacation = bad, higher sleep/flow = good
|
| 27 |
+
bad_features = ['DAILY_STRESS', 'LOST_VACATION', 'DAILY_SHOUTING', 'BMI_RANGE']
|
| 28 |
+
if col in bad_features:
|
| 29 |
+
score = imp * (val / 10)
|
| 30 |
+
else:
|
| 31 |
+
score = imp * (1 - val / 10)
|
| 32 |
+
risk_factors[col] = score
|
| 33 |
+
|
| 34 |
+
top = sorted(risk_factors.items(), key=lambda x: x[1], reverse=True)[:3]
|
| 35 |
+
return {k: f"score {user_input[k]}/10" for k, _ in top}
|
| 36 |
+
|
| 37 |
+
def main():
|
| 38 |
+
st.set_page_config(page_title="Burnout Tracker", page_icon="🔥", layout="wide")
|
| 39 |
+
|
| 40 |
+
st.title("🔥 Burnout Risk Tracker")
|
| 41 |
+
st.markdown("*Predict your burnout risk and get personalized advice*")
|
| 42 |
+
|
| 43 |
+
xgb_model, scaler, feature_cols = load_models()
|
| 44 |
+
|
| 45 |
+
# Initialize session state
|
| 46 |
+
if 'conversation_history' not in st.session_state:
|
| 47 |
+
st.session_state.conversation_history = []
|
| 48 |
+
if 'risk_score' not in st.session_state:
|
| 49 |
+
st.session_state.risk_score = None
|
| 50 |
+
if 'advice' not in st.session_state:
|
| 51 |
+
st.session_state.advice = None
|
| 52 |
+
|
| 53 |
+
col1, col2 = st.columns([1, 1])
|
| 54 |
+
|
| 55 |
+
with col1:
|
| 56 |
+
st.subheader("Your Daily Lifestyle Inputs")
|
| 57 |
+
|
| 58 |
+
user_input = {}
|
| 59 |
+
user_input['DAILY_STRESS'] = st.slider("Daily Stress Level", 0, 10, 5)
|
| 60 |
+
user_input['SLEEP_HOURS'] = st.slider("Sleep Hours", 0, 10, 7)
|
| 61 |
+
user_input['LOST_VACATION'] = st.slider("Unused Vacation Days", 0, 10, 2)
|
| 62 |
+
user_input['TODO_COMPLETED'] = st.slider("Tasks Completed Today", 0, 10, 5)
|
| 63 |
+
user_input['FLOW'] = st.slider("Flow State at Work", 0, 10, 5)
|
| 64 |
+
user_input['TIME_FOR_PASSION'] = st.slider("Time for Passions", 0, 10, 3)
|
| 65 |
+
user_input['WEEKLY_MEDITATION'] = st.slider("Weekly Meditation Sessions", 0, 10, 2)
|
| 66 |
+
user_input['DAILY_SHOUTING'] = st.slider("Daily Emotional Outbursts", 0, 10, 1)
|
| 67 |
+
user_input['FRUITS_VEGGIES'] = st.slider("Fruits & Veggies Servings", 0, 10, 5)
|
| 68 |
+
user_input['DAILY_STEPS'] = st.slider("Daily Steps (thousands)", 0, 10, 5)
|
| 69 |
+
user_input['SUFFICIENT_INCOME'] = st.slider("Income Sufficiency", 0, 10, 5)
|
| 70 |
+
user_input['SOCIAL_NETWORK'] = st.slider("Social Network Strength", 0, 10, 5)
|
| 71 |
+
user_input['ACHIEVEMENT'] = st.slider("Sense of Achievement", 0, 10, 5)
|
| 72 |
+
user_input['SUPPORTING_OTHERS'] = st.slider("Supporting Others", 0, 10, 5)
|
| 73 |
+
user_input['PLACES_VISITED'] = st.slider("New Places Visited", 0, 10, 3)
|
| 74 |
+
user_input['CORE_CIRCLE'] = st.slider("Close Relationships", 0, 10, 5)
|
| 75 |
+
user_input['PERSONAL_AWARDS'] = st.slider("Personal Awards/Recognition", 0, 10, 3)
|
| 76 |
+
user_input['DONATION'] = st.slider("Charitable Giving", 0, 10, 2)
|
| 77 |
+
user_input['BMI_RANGE'] = st.slider("BMI Range (1=underweight, 4=obese)", 1, 4, 2)
|
| 78 |
+
user_input['LIVE_VISION'] = st.slider("Life Vision Clarity", 0, 10, 5)
|
| 79 |
+
user_input['AGE'] = st.selectbox("Age Range", [0, 1, 2, 3],
|
| 80 |
+
format_func=lambda x: ['<20', '21-35', '36-50', '51+'][x])
|
| 81 |
+
user_input['GENDER'] = st.selectbox("Gender", [0, 1],
|
| 82 |
+
format_func=lambda x: ['Female', 'Male'][x])
|
| 83 |
+
|
| 84 |
+
if st.button("🔍 Assess My Burnout Risk", type="primary"):
|
| 85 |
+
input_array = np.array([[user_input[f] for f in feature_cols]])
|
| 86 |
+
input_scaled = scaler.transform(input_array)
|
| 87 |
+
|
| 88 |
+
risk_score = xgb_model.predict_proba(input_scaled)[0][1]
|
| 89 |
+
st.session_state.risk_score = risk_score
|
| 90 |
+
st.session_state.conversation_history = []
|
| 91 |
+
|
| 92 |
+
top_risk_factors = get_top_risk_factors(user_input, feature_cols, xgb_model)
|
| 93 |
+
|
| 94 |
+
with st.spinner("Getting personalized advice from AI coach..."):
|
| 95 |
+
advice = get_burnout_advice(risk_score, top_risk_factors, user_input)
|
| 96 |
+
st.session_state.advice = advice
|
| 97 |
+
st.session_state.risk_context = f"Risk score: {risk_score:.1%}, Top factors: {top_risk_factors}"
|
| 98 |
+
|
| 99 |
+
with col2:
|
| 100 |
+
if st.session_state.risk_score is not None:
|
| 101 |
+
risk_score = st.session_state.risk_score
|
| 102 |
+
|
| 103 |
+
# Risk meter
|
| 104 |
+
st.subheader("Your Burnout Risk")
|
| 105 |
+
if risk_score > 0.7:
|
| 106 |
+
st.error(f"🚨 High Risk: {risk_score:.1%}")
|
| 107 |
+
elif risk_score > 0.4:
|
| 108 |
+
st.warning(f"⚠️ Moderate Risk: {risk_score:.1%}")
|
| 109 |
+
else:
|
| 110 |
+
st.success(f"✅ Low Risk: {risk_score:.1%}")
|
| 111 |
+
|
| 112 |
+
st.progress(float(risk_score))
|
| 113 |
+
|
| 114 |
+
# AI advice
|
| 115 |
+
st.subheader("AI Coach Advice")
|
| 116 |
+
st.markdown(st.session_state.advice)
|
| 117 |
+
|
| 118 |
+
st.divider()
|
| 119 |
+
|
| 120 |
+
# Chat
|
| 121 |
+
st.subheader("💬 Chat with Your AI Coach")
|
| 122 |
+
|
| 123 |
+
for msg in st.session_state.conversation_history:
|
| 124 |
+
if msg['role'] == 'user':
|
| 125 |
+
st.chat_message("user").write(msg['content'])
|
| 126 |
+
else:
|
| 127 |
+
st.chat_message("assistant").write(msg['content'])
|
| 128 |
+
|
| 129 |
+
if prompt := st.chat_input("Ask your coach anything..."):
|
| 130 |
+
st.chat_message("user").write(prompt)
|
| 131 |
+
with st.spinner("Thinking..."):
|
| 132 |
+
response, st.session_state.conversation_history = get_burnout_chat_response(
|
| 133 |
+
st.session_state.conversation_history,
|
| 134 |
+
prompt,
|
| 135 |
+
st.session_state.risk_context
|
| 136 |
+
)
|
| 137 |
+
st.chat_message("assistant").write(response)
|
| 138 |
+
st.rerun()
|
| 139 |
+
|
| 140 |
+
if __name__ == '__main__':
|
| 141 |
+
main()
|
requirements.txt
CHANGED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
streamlit
|
| 2 |
+
xgboost
|
| 3 |
+
scikit-learn
|
| 4 |
+
pandas
|
| 5 |
+
numpy
|
| 6 |
+
groq
|
| 7 |
+
python-dotenv
|
| 8 |
+
joblib
|