ASI-Engineer commited on
Commit
e6e0066
·
verified ·
1 Parent(s): c63ff33

Upload folder using huggingface_hub

Browse files
Files changed (2) hide show
  1. README.md +225 -35
  2. src/gradio_ui.py +55 -69
README.md CHANGED
@@ -1,49 +1,239 @@
1
- ---
2
- title: Employee Turnover Prediction API
3
- emoji: 👔
4
- colorFrom: blue
5
- colorTo: purple
6
- sdk: docker
7
- pinned: true
8
- license: mit
9
- app_port: 8000
10
- ---
11
 
12
- # Employee Turnover Prediction API 🚀
13
 
14
- API de prédiction du turnover des employés avec XGBoost + SMOTE.
15
 
16
- ## 🎯 Fonctionnalités
17
-
18
- - Prédiction de turnover (0 = reste, 1 = part)
19
- - 📊 Probabilités et niveau de risque (Low/Medium/High)
 
20
  - 🔐 Authentification API Key
21
- - 📝 Logs structurés JSON
22
- - 🛡️ Rate limiting (20 req/min)
23
- - 📚 Documentation OpenAPI/Swagger
24
 
25
- ## 🔗 Endpoints
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- - **Docs** : `/docs` - Documentation interactive
28
- - **Health** : `/health` - Status de l'API
29
- - **Predict** : `/predict` - Prédiction de turnover
30
 
31
- ## 🚀 Utilisation
32
 
33
  ```bash
34
- # Health check
35
- curl https://asi-engineer-employee-turnover-api.hf.space/health
 
 
 
36
 
37
- # Prédiction
38
- curl -X POST https://asi-engineer-employee-turnover-api.hf.space/predict \
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  -H "Content-Type: application/json" \
40
- -d '{
41
- "satisfaction_employee_environnement": 3,
42
- "satisfaction_employee_nature_travail": 4,
43
- ...
44
- }'
45
  ```
46
 
47
- ## 📚 Documentation complète
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
- Voir [GitHub Repository](https://github.com/chaton59/OC_P5) pour la documentation complète.
 
 
1
+ # 🚀 Employee Turnover Prediction API - v2.1.0
 
 
 
 
 
 
 
 
 
2
 
3
+ ## 📊 Vue d'ensemble
4
 
5
+ API REST de prédiction du turnover des employés basée sur un modèle XGBoost avec SMOTE.
6
 
7
+ **✨ Nouveautés v2.1.0** :
8
+ - 📝 Logging structuré JSON
9
+ - 🛡️ Rate limiting (20 req/min par IP)
10
+ - Gestion d'erreurs améliorée
11
+ - 📊 Monitoring des performances
12
  - 🔐 Authentification API Key
 
 
 
13
 
14
+ ## 🏗️ Architecture
15
+
16
+ ```
17
+ OC_P5/
18
+ ├── app.py # Point d'entrée FastAPI
19
+ ├── src/
20
+ │ ├── auth.py # Authentification API Key
21
+ │ ├── config.py # Configuration centralisée
22
+ │ ├── logger.py # Logging structuré (NOUVEAU)
23
+ │ ├── models.py # Chargement modèle HF Hub
24
+ │ ├── preprocessing.py # Pipeline preprocessing
25
+ │ ├── rate_limit.py # Rate limiting (NOUVEAU)
26
+ │ └── schemas.py # Validation Pydantic
27
+ ├── tests/ # Suite pytest (33 tests, 88% couverture)
28
+ ├── logs/ # Logs JSON (NOUVEAU)
29
+ │ ├── api.log # Tous les logs
30
+ │ └── error.log # Erreurs uniquement
31
+ ├── docs/ # Documentation
32
+ ├── ml_model/ # Scripts training
33
+ └── data/ # Données sources
34
+ ```
35
+
36
+ ## 🚀 Installation
37
+
38
+ ### Prérequis
39
+ - Python 3.12+
40
+ - Poetry 1.7+
41
+ - Git
42
+
43
+ ### Setup rapide
44
+
45
+ ```bash
46
+ # 1. Cloner le repo
47
+ git clone https://github.com/chaton59/OC_P5.git
48
+ cd OC_P5
49
+
50
+ # 2. Installer les dépendances
51
+ poetry install
52
+
53
+ # 3. Configurer l'environnement
54
+ cp .env.example .env
55
+ # Éditer .env avec vos valeurs
56
+
57
+ # 4. Lancer l'API
58
+ poetry run uvicorn app:app --reload
59
 
60
+ # 5. Accéder à la documentation
61
+ # http://localhost:8000/docs
62
+ ```
63
 
64
+ ## 📝 Configuration (.env)
65
 
66
  ```bash
67
+ # Mode développement (désactive auth + active logs détaillés)
68
+ DEBUG=true
69
+
70
+ # API Key (requis en production)
71
+ API_KEY=your-secret-key-here
72
 
73
+ # Logging (DEBUG, INFO, WARNING, ERROR, CRITICAL)
74
+ LOG_LEVEL=INFO
75
+
76
+ # HuggingFace Model
77
+ HF_MODEL_REPO=ASI-Engineer/employee-turnover-model
78
+ MODEL_FILENAME=model/model.pkl
79
+ ```
80
+
81
+ ## 🔒 Authentification
82
+
83
+ ### Mode DEBUG (développement)
84
+ ```bash
85
+ # L'API Key n'est PAS requise
86
+ curl http://localhost:8000/predict -H "Content-Type: application/json" -d '{...}'
87
+ ```
88
+
89
+ ### Mode PRODUCTION
90
+ ```bash
91
+ # L'API Key est REQUISE
92
+ curl http://localhost:8000/predict \
93
+ -H "X-API-Key: your-secret-key" \
94
  -H "Content-Type: application/json" \
95
+ -d '{...}'
 
 
 
 
96
  ```
97
 
98
+ ## 📡 Endpoints
99
+
100
+ ### 🏥 Health Check
101
+ ```bash
102
+ GET /health
103
+
104
+ # Réponse
105
+ {
106
+ "status": "healthy",
107
+ "model_loaded": true,
108
+ "model_type": "Pipeline",
109
+ "version": "2.1.0"
110
+ }
111
+ ```
112
+
113
+ ### 🔮 Prédiction
114
+ ```bash
115
+ POST /predict
116
+ Content-Type: application/json
117
+ X-API-Key: your-key (en production)
118
+
119
+ # Exemple payload (voir docs/API_GUIDE.md pour tous les champs)
120
+ {
121
+ "satisfaction_employee_environnement": 3,
122
+ "satisfaction_employee_nature_travail": 4,
123
+ "satisfaction_employee_equipe": 5,
124
+ "satisfaction_employee_equilibre_pro_perso": 3,
125
+ "note_evaluation_actuelle": 85,
126
+ "annees_depuis_la_derniere_promotion": 2,
127
+ "nombre_formations_realisees": 3,
128
+ ...
129
+ }
130
+
131
+ # Réponse
132
+ {
133
+ "prediction": 0, # 0 = reste, 1 = part
134
+ "probability_0": 0.85, # Probabilité de rester
135
+ "probability_1": 0.15, # Probabilité de partir
136
+ "risk_level": "Low" # Low, Medium, High
137
+ }
138
+ ```
139
+
140
+ ## 📊 Logging
141
+
142
+ ### Logs structurés JSON
143
+
144
+ **Fichiers** :
145
+ - `logs/api.log` : Tous les logs
146
+ - `logs/error.log` : Erreurs uniquement
147
+
148
+ **Format** :
149
+ ```json
150
+ {
151
+ "timestamp": "2025-12-26T10:30:45",
152
+ "level": "INFO",
153
+ "logger": "employee_turnover_api",
154
+ "message": "Request POST /predict",
155
+ "method": "POST",
156
+ "path": "/predict",
157
+ "status_code": 200,
158
+ "duration_ms": 23.45,
159
+ "client_host": "127.0.0.1"
160
+ }
161
+ ```
162
+
163
+ ## 🛡️ Rate Limiting
164
+
165
+ **Configuration** :
166
+ - **Développement** : Désactivé (DEBUG=true)
167
+ - **Production** : 20 requêtes/minute par IP ou API Key
168
+
169
+ **En cas de dépassement** :
170
+ ```json
171
+ {
172
+ "error": "Rate limit exceeded",
173
+ "message": "20 per 1 minute"
174
+ }
175
+ ```
176
+
177
+ ## ✅ Tests
178
+
179
+ ```bash
180
+ # Tous les tests
181
+ poetry run pytest tests/ -v
182
+
183
+ # Avec couverture
184
+ poetry run pytest tests/ --cov --cov-report=html
185
+
186
+ # Voir rapport HTML
187
+ open htmlcov/index.html
188
+ ```
189
+
190
+ **Résultats** :
191
+ - ✅ 33 tests passés
192
+ - 📊 88% de couverture globale
193
+
194
+ ## 🚀 Déploiement
195
+
196
+ ### Variables d'environnement requises
197
+ ```bash
198
+ DEBUG=false
199
+ API_KEY=<votre-clé-sécurisée>
200
+ LOG_LEVEL=INFO
201
+ ```
202
+
203
+ ### HuggingFace Spaces
204
+ Prêt pour déploiement avec `app.py` et `requirements.txt`
205
+
206
+ ## 📚 Documentation
207
+
208
+ - **API Interactive** : http://localhost:8000/docs
209
+ - **ReDoc** : http://localhost:8000/redoc
210
+ - **Guide complet** : [docs/API_GUIDE.md](docs/API_GUIDE.md)
211
+ - **Standards** : [docs/standards.md](docs/standards.md)
212
+ - **Couverture tests** : [docs/TEST_COVERAGE.md](docs/TEST_COVERAGE.md)
213
+
214
+ ## 📦 Dépendances principales
215
+
216
+ - **FastAPI** 0.115.14 : Framework web
217
+ - **Pydantic** 2.12.5 : Validation données
218
+ - **XGBoost** 2.1.3 : Modèle ML
219
+ - **SlowAPI** 0.1.9 : Rate limiting
220
+ - **python-json-logger** 4.0.0 : Logs structurés
221
+ - **pytest** 9.0.2 : Tests
222
+
223
+ ## 🔄 Changelog
224
+
225
+ ### v2.1.0 (26 décembre 2025)
226
+ - ✨ Système de logging structuré JSON
227
+ - 🛡️ Rate limiting avec SlowAPI
228
+ - ⚡ Amélioration gestion d'erreurs
229
+ - 📊 Monitoring des performances
230
+
231
+ ### v2.0.0 (26 décembre 2025)
232
+ - ✅ Suite de tests complète (33 tests)
233
+ - 🔐 Authentification API Key
234
+ - 📊 88% de couverture de code
235
+
236
+ ## 👥 Auteurs
237
 
238
+ - **Projet** : OpenClassrooms P5
239
+ - **Repo** : [github.com/chaton59/OC_P5](https://github.com/chaton59/OC_P5)
src/gradio_ui.py CHANGED
@@ -7,23 +7,11 @@ Cette interface permet de:
7
  - Visualiser la documentation de l'API
8
  - Comprendre les champs requis
9
  """
10
- import os
11
-
12
  import gradio as gr
13
- import httpx
14
-
15
- from src.models import get_model_info
16
-
17
 
18
- # URL de base pour les appels API (localhost en dev, relatif en prod)
19
- def get_api_base_url() -> str:
20
- """Retourne l'URL de base de l'API."""
21
- # En production sur HF Spaces, utiliser le même host
22
- space_host = os.getenv("SPACE_HOST")
23
- if space_host:
24
- return f"https://{space_host}"
25
- # En local
26
- return "http://localhost:8000"
27
 
28
 
29
  def predict_turnover(
@@ -61,67 +49,69 @@ def predict_turnover(
61
  annees_dans_l_entreprise: int,
62
  annees_dans_le_poste_actuel: int,
63
  ) -> str:
64
- """Effectue une prédiction de turnover via l'API REST."""
65
  try:
66
- # Construire le payload pour l'API
67
- payload = {
68
- "nombre_participation_pee": int(nombre_participation_pee),
69
- "nb_formations_suivies": int(nb_formations_suivies),
70
- "nombre_employee_sous_responsabilite": int(
71
  nombre_employee_sous_responsabilite
72
  ),
73
- "distance_domicile_travail": int(distance_domicile_travail),
74
- "niveau_education": int(niveau_education),
75
- "domaine_etude": domaine_etude,
76
- "ayant_enfants": ayant_enfants,
77
- "frequence_deplacement": frequence_deplacement,
78
- "annees_depuis_la_derniere_promotion": int(
79
  annees_depuis_la_derniere_promotion
80
  ),
81
- "annes_sous_responsable_actuel": int(annes_sous_responsable_actuel),
82
- "satisfaction_employee_environnement": int(
83
  satisfaction_employee_environnement
84
  ),
85
- "note_evaluation_precedente": int(note_evaluation_precedente),
86
- "niveau_hierarchique_poste": int(niveau_hierarchique_poste),
87
- "satisfaction_employee_nature_travail": int(
88
  satisfaction_employee_nature_travail
89
  ),
90
- "satisfaction_employee_equipe": int(satisfaction_employee_equipe),
91
- "satisfaction_employee_equilibre_pro_perso": int(
92
  satisfaction_employee_equilibre_pro_perso
93
  ),
94
- "note_evaluation_actuelle": int(note_evaluation_actuelle),
95
- "heure_supplementaires": heure_supplementaires,
96
- "augementation_salaire_precedente": float(augementation_salaire_precedente),
97
- "age": int(age),
98
- "genre": genre,
99
- "revenu_mensuel": float(revenu_mensuel),
100
- "statut_marital": statut_marital,
101
- "departement": departement,
102
- "poste": poste,
103
- "nombre_experiences_precedentes": int(nombre_experiences_precedentes),
104
- "nombre_heures_travailless": int(nombre_heures_travailless),
105
- "annee_experience_totale": int(annee_experience_totale),
106
- "annees_dans_l_entreprise": int(annees_dans_l_entreprise),
107
- "annees_dans_le_poste_actuel": int(annees_dans_le_poste_actuel),
108
- }
109
-
110
- # Appeler l'API REST avec la clé API
111
- api_url = get_api_base_url()
112
- api_key = os.getenv("API_KEY", "")
113
- headers = {"X-API-Key": api_key} if api_key else {}
114
-
115
- with httpx.Client(timeout=30.0) as client:
116
- response = client.post(f"{api_url}/predict", json=payload, headers=headers)
117
- response.raise_for_status()
118
- data = response.json()
119
-
120
- # Formater le résultat
121
- prediction = data["prediction"]
122
- prob_1 = data["probability_1"]
123
- prob_0 = data["probability_0"]
124
- risk_level = data["risk_level"]
 
 
125
 
126
  # Affichage
127
  if risk_level == "High":
@@ -147,10 +137,6 @@ def predict_turnover(
147
  """
148
  return result
149
 
150
- except httpx.HTTPStatusError as e:
151
- return f"❌ **Erreur API**: {e.response.status_code} - {e.response.text}"
152
- except httpx.RequestError as e:
153
- return f"❌ **Erreur de connexion**: {str(e)}"
154
  except Exception as e:
155
  return f"❌ **Erreur**: {str(e)}"
156
 
 
7
  - Visualiser la documentation de l'API
8
  - Comprendre les champs requis
9
  """
 
 
10
  import gradio as gr
 
 
 
 
11
 
12
+ from src.models import get_model_info, load_model
13
+ from src.preprocessing import preprocess_for_prediction
14
+ from src.schemas import EmployeeInput
 
 
 
 
 
 
15
 
16
 
17
  def predict_turnover(
 
49
  annees_dans_l_entreprise: int,
50
  annees_dans_le_poste_actuel: int,
51
  ) -> str:
52
+ """Effectue une prédiction de turnover directement via le modèle."""
53
  try:
54
+ # Créer l'objet EmployeeInput avec validation Pydantic
55
+ employee = EmployeeInput(
56
+ nombre_participation_pee=int(nombre_participation_pee),
57
+ nb_formations_suivies=int(nb_formations_suivies),
58
+ nombre_employee_sous_responsabilite=int(
59
  nombre_employee_sous_responsabilite
60
  ),
61
+ distance_domicile_travail=int(distance_domicile_travail),
62
+ niveau_education=int(niveau_education),
63
+ domaine_etude=domaine_etude,
64
+ ayant_enfants=ayant_enfants,
65
+ frequence_deplacement=frequence_deplacement,
66
+ annees_depuis_la_derniere_promotion=int(
67
  annees_depuis_la_derniere_promotion
68
  ),
69
+ annes_sous_responsable_actuel=int(annes_sous_responsable_actuel),
70
+ satisfaction_employee_environnement=int(
71
  satisfaction_employee_environnement
72
  ),
73
+ note_evaluation_precedente=int(note_evaluation_precedente),
74
+ niveau_hierarchique_poste=int(niveau_hierarchique_poste),
75
+ satisfaction_employee_nature_travail=int(
76
  satisfaction_employee_nature_travail
77
  ),
78
+ satisfaction_employee_equipe=int(satisfaction_employee_equipe),
79
+ satisfaction_employee_equilibre_pro_perso=int(
80
  satisfaction_employee_equilibre_pro_perso
81
  ),
82
+ note_evaluation_actuelle=int(note_evaluation_actuelle),
83
+ heure_supplementaires=heure_supplementaires,
84
+ augementation_salaire_precedente=float(augementation_salaire_precedente),
85
+ age=int(age),
86
+ genre=genre,
87
+ revenu_mensuel=float(revenu_mensuel),
88
+ statut_marital=statut_marital,
89
+ departement=departement,
90
+ poste=poste,
91
+ nombre_experiences_precedentes=int(nombre_experiences_precedentes),
92
+ nombre_heures_travailless=int(nombre_heures_travailless),
93
+ annee_experience_totale=int(annee_experience_totale),
94
+ annees_dans_l_entreprise=int(annees_dans_l_entreprise),
95
+ annees_dans_le_poste_actuel=int(annees_dans_le_poste_actuel),
96
+ )
97
+
98
+ # Preprocessing
99
+ features = preprocess_for_prediction(employee)
100
+
101
+ # Charger le modèle et prédire
102
+ model = load_model()
103
+ prediction = int(model.predict(features)[0])
104
+ proba = model.predict_proba(features)[0]
105
+ prob_0 = float(proba[0])
106
+ prob_1 = float(proba[1])
107
+
108
+ # Déterminer le niveau de risque
109
+ if prob_1 < 0.3:
110
+ risk_level = "Low"
111
+ elif prob_1 < 0.7:
112
+ risk_level = "Medium"
113
+ else:
114
+ risk_level = "High"
115
 
116
  # Affichage
117
  if risk_level == "High":
 
137
  """
138
  return result
139
 
 
 
 
 
140
  except Exception as e:
141
  return f"❌ **Erreur**: {str(e)}"
142