Spaces:
Running
Running
changes in display of info
Browse files- src/streamlit_app.py +40 -25
src/streamlit_app.py
CHANGED
|
@@ -3,42 +3,57 @@ import requests
|
|
| 3 |
|
| 4 |
API_URL = "https://kleb38-oc-p5.hf.space"
|
| 5 |
|
| 6 |
-
st.set_page_config(page_title="FUTURISYS —
|
| 7 |
-
st.title("👥 FUTURISYS —
|
| 8 |
|
| 9 |
-
|
| 10 |
|
| 11 |
-
# ───
|
| 12 |
|
| 13 |
def afficher_resultat(data):
|
| 14 |
prediction = data["statut_employe"]
|
| 15 |
score = data["probability_score"]
|
| 16 |
facteurs = data["top_5_factors"]
|
| 17 |
|
| 18 |
-
# Couleur selon le risque
|
| 19 |
if "HIGH" in prediction:
|
| 20 |
st.error(f"🚨 {prediction}")
|
| 21 |
else:
|
| 22 |
st.success(f"✅ {prediction}")
|
| 23 |
|
| 24 |
-
st.metric("
|
| 25 |
-
st.markdown(f"*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
| 27 |
-
st.subheader("Top 5 facteurs SHAP")
|
| 28 |
for rang, (facteur, details) in enumerate(facteurs.items()):
|
| 29 |
interpretation = details["interpretation"]
|
| 30 |
valeur = details["feature_value"]
|
| 31 |
-
st.markdown(f"**{rang+1}. {facteur}** — {interpretation} *(valeur : {valeur})*")
|
| 32 |
|
| 33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
with tab1:
|
| 36 |
-
st.header("
|
| 37 |
|
| 38 |
col1, col2, col3 = st.columns(3)
|
| 39 |
|
| 40 |
with col1:
|
| 41 |
-
st.subheader("
|
| 42 |
genre = st.selectbox("Genre", ["M", "F"])
|
| 43 |
age = st.number_input("Âge", min_value=18, max_value=65, value=35)
|
| 44 |
statut_marital = st.selectbox("Statut marital", ["Célibataire", "Marié(e)", "Divorcé(e)"])
|
|
@@ -51,7 +66,7 @@ with tab1:
|
|
| 51 |
frequence_deplacement = st.selectbox("Fréquence de déplacement", ["Jamais", "Occasionnel", "Fréquent"])
|
| 52 |
|
| 53 |
with col2:
|
| 54 |
-
st.subheader("
|
| 55 |
departement = st.selectbox("Département", ["Commercial", "Consulting", "RH"])
|
| 56 |
poste = st.selectbox("Poste", [
|
| 57 |
"Cadre Commercial", "Consultant", "Directeur RH",
|
|
@@ -72,7 +87,7 @@ with tab1:
|
|
| 72 |
])
|
| 73 |
|
| 74 |
with col3:
|
| 75 |
-
st.subheader("Satisfaction")
|
| 76 |
satisfaction_env = st.slider("Satisfaction environnement", 1, 4, 3)
|
| 77 |
satisfaction_travail = st.slider("Satisfaction nature du travail", 1, 4, 3)
|
| 78 |
satisfaction_equipe = st.slider("Satisfaction équipe", 1, 4, 3)
|
|
@@ -83,7 +98,7 @@ with tab1:
|
|
| 83 |
nb_participation_pee = st.number_input("Participations PEE", min_value=0, max_value=6, value=3)
|
| 84 |
nb_employes_sous_resp = st.number_input("Employés sous responsabilité", min_value=0, max_value=20, value=0)
|
| 85 |
|
| 86 |
-
if st.button("🔮
|
| 87 |
payload = {
|
| 88 |
"Genre": genre,
|
| 89 |
"Statut Marital": statut_marital,
|
|
@@ -112,25 +127,25 @@ with tab1:
|
|
| 112 |
"Augmentation salaire précédente": augmentation
|
| 113 |
}
|
| 114 |
|
| 115 |
-
with st.spinner("
|
| 116 |
try:
|
| 117 |
response = requests.post(f"{API_URL}/predict", json=payload)
|
| 118 |
if response.status_code == 200:
|
| 119 |
afficher_resultat(response.json())
|
| 120 |
else:
|
| 121 |
-
st.error(f"
|
| 122 |
except Exception as e:
|
| 123 |
-
st.error(f"
|
| 124 |
|
| 125 |
-
# ───
|
| 126 |
|
| 127 |
with tab2:
|
| 128 |
-
st.header("
|
| 129 |
|
| 130 |
-
id_employee = st.number_input("
|
| 131 |
|
| 132 |
-
if st.button("🔍
|
| 133 |
-
with st.spinner("
|
| 134 |
try:
|
| 135 |
response = requests.get(f"{API_URL}/predict/{id_employee}")
|
| 136 |
if response.status_code == 200:
|
|
@@ -138,6 +153,6 @@ with tab2:
|
|
| 138 |
elif response.status_code == 404:
|
| 139 |
st.warning(f"⚠️ {response.json()['detail']}")
|
| 140 |
else:
|
| 141 |
-
st.error(f"
|
| 142 |
except Exception as e:
|
| 143 |
-
st.error(f"
|
|
|
|
| 3 |
|
| 4 |
API_URL = "https://kleb38-oc-p5.hf.space"
|
| 5 |
|
| 6 |
+
st.set_page_config(page_title="FUTURISYS — HR Prediction", page_icon="👥", layout="wide")
|
| 7 |
+
st.title("👥 FUTURISYS — HR Departure Prediction")
|
| 8 |
|
| 9 |
+
tab2, tab1 = st.tabs(["🔍 Search by ID", "📝 Manual Prediction"])
|
| 10 |
|
| 11 |
+
# ─── Common functions ─────────────────────────────────────────────────────────
|
| 12 |
|
| 13 |
def afficher_resultat(data):
|
| 14 |
prediction = data["statut_employe"]
|
| 15 |
score = data["probability_score"]
|
| 16 |
facteurs = data["top_5_factors"]
|
| 17 |
|
|
|
|
| 18 |
if "HIGH" in prediction:
|
| 19 |
st.error(f"🚨 {prediction}")
|
| 20 |
else:
|
| 21 |
st.success(f"✅ {prediction}")
|
| 22 |
|
| 23 |
+
st.metric("Probability score", f"{score * 100:.1f}%")
|
| 24 |
+
st.markdown(f"*Strategic threshold: {data['model_threshold']} — {data['note']}*")
|
| 25 |
+
|
| 26 |
+
st.subheader("Top 5 SHAP factors")
|
| 27 |
+
col_rank, col_name, col_interp, col_val = st.columns([0.5, 2, 3.5, 1.5])
|
| 28 |
+
col_rank.markdown("**#**")
|
| 29 |
+
col_name.markdown("**Feature**")
|
| 30 |
+
col_interp.markdown("**Interpretation**")
|
| 31 |
+
col_val.markdown("**Value**")
|
| 32 |
+
st.divider()
|
| 33 |
|
|
|
|
| 34 |
for rang, (facteur, details) in enumerate(facteurs.items()):
|
| 35 |
interpretation = details["interpretation"]
|
| 36 |
valeur = details["feature_value"]
|
|
|
|
| 37 |
|
| 38 |
+
decreases = "decreases" in interpretation.lower()
|
| 39 |
+
color = "#00c853" if decreases else "#d50000"
|
| 40 |
+
arrow = "↓" if decreases else "↑"
|
| 41 |
+
|
| 42 |
+
col_rank, col_name, col_interp, col_val = st.columns([0.5, 2, 3.5, 1.5])
|
| 43 |
+
col_rank.markdown(f"**{rang + 1}**")
|
| 44 |
+
col_name.markdown(f"`{facteur}`")
|
| 45 |
+
col_interp.markdown(f'<span style="color:{color}; font-weight:600">{arrow} {interpretation}</span>', unsafe_allow_html=True)
|
| 46 |
+
col_val.markdown(f"`{valeur}`")
|
| 47 |
+
|
| 48 |
+
# ─── Tab 1 : Manual form ──────────────────────────────────────────────────────
|
| 49 |
|
| 50 |
with tab1:
|
| 51 |
+
st.header("Enter employee data")
|
| 52 |
|
| 53 |
col1, col2, col3 = st.columns(3)
|
| 54 |
|
| 55 |
with col1:
|
| 56 |
+
st.subheader("Personal information")
|
| 57 |
genre = st.selectbox("Genre", ["M", "F"])
|
| 58 |
age = st.number_input("Âge", min_value=18, max_value=65, value=35)
|
| 59 |
statut_marital = st.selectbox("Statut marital", ["Célibataire", "Marié(e)", "Divorcé(e)"])
|
|
|
|
| 66 |
frequence_deplacement = st.selectbox("Fréquence de déplacement", ["Jamais", "Occasionnel", "Fréquent"])
|
| 67 |
|
| 68 |
with col2:
|
| 69 |
+
st.subheader("Position and experience")
|
| 70 |
departement = st.selectbox("Département", ["Commercial", "Consulting", "RH"])
|
| 71 |
poste = st.selectbox("Poste", [
|
| 72 |
"Cadre Commercial", "Consultant", "Directeur RH",
|
|
|
|
| 87 |
])
|
| 88 |
|
| 89 |
with col3:
|
| 90 |
+
st.subheader("Satisfaction scores")
|
| 91 |
satisfaction_env = st.slider("Satisfaction environnement", 1, 4, 3)
|
| 92 |
satisfaction_travail = st.slider("Satisfaction nature du travail", 1, 4, 3)
|
| 93 |
satisfaction_equipe = st.slider("Satisfaction équipe", 1, 4, 3)
|
|
|
|
| 98 |
nb_participation_pee = st.number_input("Participations PEE", min_value=0, max_value=6, value=3)
|
| 99 |
nb_employes_sous_resp = st.number_input("Employés sous responsabilité", min_value=0, max_value=20, value=0)
|
| 100 |
|
| 101 |
+
if st.button("🔮 Predict departure risk", type="primary"):
|
| 102 |
payload = {
|
| 103 |
"Genre": genre,
|
| 104 |
"Statut Marital": statut_marital,
|
|
|
|
| 127 |
"Augmentation salaire précédente": augmentation
|
| 128 |
}
|
| 129 |
|
| 130 |
+
with st.spinner("Predicting..."):
|
| 131 |
try:
|
| 132 |
response = requests.post(f"{API_URL}/predict", json=payload)
|
| 133 |
if response.status_code == 200:
|
| 134 |
afficher_resultat(response.json())
|
| 135 |
else:
|
| 136 |
+
st.error(f"API error: {response.status_code} — {response.text}")
|
| 137 |
except Exception as e:
|
| 138 |
+
st.error(f"Could not reach the API: {e}")
|
| 139 |
|
| 140 |
+
# ─── Tab 2 : Search by ID ─────────────────────────────────────────────────────
|
| 141 |
|
| 142 |
with tab2:
|
| 143 |
+
st.header("Search an employee by ID")
|
| 144 |
|
| 145 |
+
id_employee = st.number_input("Employee ID", min_value=1, value=1, step=1)
|
| 146 |
|
| 147 |
+
if st.button("🔍 Search and predict", type="primary"):
|
| 148 |
+
with st.spinner("Searching..."):
|
| 149 |
try:
|
| 150 |
response = requests.get(f"{API_URL}/predict/{id_employee}")
|
| 151 |
if response.status_code == 200:
|
|
|
|
| 153 |
elif response.status_code == 404:
|
| 154 |
st.warning(f"⚠️ {response.json()['detail']}")
|
| 155 |
else:
|
| 156 |
+
st.error(f"API error: {response.status_code}")
|
| 157 |
except Exception as e:
|
| 158 |
+
st.error(f"Could not reach the API: {e}")
|