ASI-Engineer commited on
Commit
7508477
·
verified ·
1 Parent(s): fb4529d

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. README.md +252 -51
  2. src/gradio_ui.py +20 -18
  3. src/schemas.py +21 -13
README.md CHANGED
@@ -1,73 +1,274 @@
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: 7860
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
- - 📦 **Nouveau** : Endpoint batch pour traiter vos fichiers CSV directement
20
- - 📊 Probabilités et niveau de risque (Low/Medium/High)
21
  - 🔐 Authentification API Key
22
- - 📝 Logs structurés JSON
23
- - 🛡️ Rate limiting (20 req/min)
24
- - 📚 Documentation OpenAPI/Swagger
25
 
26
- ## 🔗 Endpoints
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
- | Endpoint | Description |
29
- |----------|-------------|
30
- | `/docs` | Documentation interactive Swagger |
31
- | `/health` | Status de l'API |
32
- | `/ui` | Interface Gradio interactive |
33
- | `/predict` | Prédiction unitaire (JSON) |
34
- | `/predict/batch` | Prédiction batch (3 fichiers CSV) |
35
 
36
- ## 🚀 Utilisation
 
 
 
 
 
 
37
 
38
- ### Prédiction unitaire
39
  ```bash
40
- curl -X POST https://asi-engineer-oc-p5-dev.hf.space/predict \
 
 
41
  -H "Content-Type: application/json" \
42
- -d '{
43
- "nombre_participation_pee": 0,
44
- "nb_formations_suivies": 2,
45
- "satisfaction_employee_environnement": 3,
46
- ...
47
- }'
48
  ```
49
 
50
- ### Prédiction batch (fichiers CSV)
 
 
51
  ```bash
52
- curl -X POST https://asi-engineer-oc-p5-dev.hf.space/predict/batch \
53
- -F "sondage_file=@extrait_sondage.csv" \
54
- -F "eval_file=@extrait_eval.csv" \
55
- -F "sirh_file=@extrait_sirh.csv"
 
 
 
 
 
56
  ```
57
 
58
- **Réponse :**
59
- ```json
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  {
61
  "total_employees": 1470,
62
- "predictions": [...],
 
 
 
63
  "summary": {
64
  "total_stay": 1169,
65
  "total_leave": 301,
66
- "high_risk_count": 222
 
 
67
  }
68
  }
69
  ```
70
 
71
- ## 📚 Documentation complète
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
 
73
- Voir [GitHub Repository](https://github.com/chaton59/OC_P5) pour la documentation complète.
 
 
1
+ # 🚀 Employee Turnover Prediction API - v2.2.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.2.0** :
8
+ - 📦 **Endpoint batch CSV** : Envoyez directement vos 3 fichiers CSV bruts
9
+ - 🔧 Correction du preprocessing (scaling + ordre des colonnes)
10
+ - 📊 Prédictions plus précises (~90% accuracy)
11
+
12
+ **✨ v2.1.0** :
13
+ - 📝 Logging structuré JSON
14
+ - 🛡️ Rate limiting (20 req/min par IP)
15
+ - ⚡ Gestion d'erreurs améliorée
 
 
 
 
 
16
  - 🔐 Authentification API Key
 
 
 
17
 
18
+ ## 🏗️ Architecture
19
+
20
+ ```
21
+ OC_P5/
22
+ ├── app.py # Point d'entrée FastAPI
23
+ ├── src/
24
+ │ ├── auth.py # Authentification API Key
25
+ │ ├── config.py # Configuration centralisée
26
+ │ ├── logger.py # Logging structuré (NOUVEAU)
27
+ │ ├── models.py # Chargement modèle HF Hub
28
+ │ ├── preprocessing.py # Pipeline preprocessing
29
+ │ ├── rate_limit.py # Rate limiting (NOUVEAU)
30
+ │ └── schemas.py # Validation Pydantic
31
+ ├── tests/ # Suite pytest (33 tests, 88% couverture)
32
+ ├── logs/ # Logs JSON (NOUVEAU)
33
+ │ ├── api.log # Tous les logs
34
+ │ └── error.log # Erreurs uniquement
35
+ ├── docs/ # Documentation
36
+ ├── ml_model/ # Scripts training
37
+ └── data/ # Données sources
38
+ ```
39
+
40
+ ## 🚀 Installation
41
+
42
+ ### Prérequis
43
+ - Python 3.12+
44
+ - Poetry 1.7+
45
+ - Git
46
+
47
+ ### Setup rapide
48
+
49
+ ```bash
50
+ # 1. Cloner le repo
51
+ git clone https://github.com/chaton59/OC_P5.git
52
+ cd OC_P5
53
+
54
+ # 2. Installer les dépendances
55
+ poetry install
56
+
57
+ # 3. Configurer l'environnement
58
+ cp .env.example .env
59
+ # Éditer .env avec vos valeurs
60
+
61
+ # 4. Lancer l'API
62
+ poetry run uvicorn app:app --reload
63
+
64
+ # 5. Accéder à la documentation
65
+ # http://localhost:8000/docs
66
+ ```
67
+
68
+ ## 📝 Configuration (.env)
69
+
70
+ ```bash
71
+ # Mode développement (désactive auth + active logs détaillés)
72
+ DEBUG=true
73
+
74
+ # API Key (requis en production)
75
+ API_KEY=your-secret-key-here
76
+
77
+ # Logging (DEBUG, INFO, WARNING, ERROR, CRITICAL)
78
+ LOG_LEVEL=INFO
79
 
80
+ # HuggingFace Model
81
+ HF_MODEL_REPO=ASI-Engineer/employee-turnover-model
82
+ MODEL_FILENAME=model/model.pkl
83
+ ```
 
 
 
84
 
85
+ ## 🔒 Authentification
86
+
87
+ ### Mode DEBUG (développement)
88
+ ```bash
89
+ # L'API Key n'est PAS requise
90
+ curl http://localhost:8000/predict -H "Content-Type: application/json" -d '{...}'
91
+ ```
92
 
93
+ ### Mode PRODUCTION
94
  ```bash
95
+ # L'API Key est REQUISE
96
+ curl http://localhost:8000/predict \
97
+ -H "X-API-Key: your-secret-key" \
98
  -H "Content-Type: application/json" \
99
+ -d '{...}'
 
 
 
 
 
100
  ```
101
 
102
+ ## 📡 Endpoints
103
+
104
+ ### 🏥 Health Check
105
  ```bash
106
+ GET /health
107
+
108
+ # Réponse
109
+ {
110
+ "status": "healthy",
111
+ "model_loaded": true,
112
+ "model_type": "Pipeline",
113
+ "version": "2.2.0"
114
+ }
115
  ```
116
 
117
+ ### 🔮 Prédiction unitaire
118
+ ```bash
119
+ POST /predict
120
+ Content-Type: application/json
121
+ X-API-Key: your-key (en production)
122
+
123
+ # Payload (tous les champs d'un employé)
124
+ {
125
+ "nombre_participation_pee": 0,
126
+ "nb_formations_suivies": 2,
127
+ "satisfaction_employee_environnement": 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
+ ### 📦 Prédiction batch (NOUVEAU)
141
+ ```bash
142
+ POST /predict/batch
143
+ X-API-Key: your-key (en production)
144
+
145
+ # Envoi des 3 fichiers CSV bruts
146
+ curl -X POST "http://localhost:8000/predict/batch" \
147
+ -H "X-API-Key: your-key" \
148
+ -F "sondage_file=@data/extrait_sondage.csv" \
149
+ -F "eval_file=@data/extrait_eval.csv" \
150
+ -F "sirh_file=@data/extrait_sirh.csv"
151
+
152
+ # Réponse
153
  {
154
  "total_employees": 1470,
155
+ "predictions": [
156
+ {"employee_id": 1, "prediction": 1, "probability_leave": 0.84, "risk_level": "High"},
157
+ {"employee_id": 2, "prediction": 0, "probability_leave": 0.11, "risk_level": "Low"}
158
+ ],
159
  "summary": {
160
  "total_stay": 1169,
161
  "total_leave": 301,
162
+ "high_risk_count": 222,
163
+ "medium_risk_count": 233,
164
+ "low_risk_count": 1015
165
  }
166
  }
167
  ```
168
 
169
+ ## 📊 Logging
170
+
171
+ ### Logs structurés JSON
172
+
173
+ **Fichiers** :
174
+ - `logs/api.log` : Tous les logs
175
+ - `logs/error.log` : Erreurs uniquement
176
+
177
+ **Format** :
178
+ ```json
179
+ {
180
+ "timestamp": "2025-12-26T10:30:45",
181
+ "level": "INFO",
182
+ "logger": "employee_turnover_api",
183
+ "message": "Request POST /predict",
184
+ "method": "POST",
185
+ "path": "/predict",
186
+ "status_code": 200,
187
+ "duration_ms": 23.45,
188
+ "client_host": "127.0.0.1"
189
+ }
190
+ ```
191
+
192
+ ## 🛡️ Rate Limiting
193
+
194
+ **Configuration** :
195
+ - **Développement** : Désactivé (DEBUG=true)
196
+ - **Production** : 20 requêtes/minute par IP ou API Key
197
+
198
+ **En cas de dépassement** :
199
+ ```json
200
+ {
201
+ "error": "Rate limit exceeded",
202
+ "message": "20 per 1 minute"
203
+ }
204
+ ```
205
+
206
+ ## ✅ Tests
207
+
208
+ ```bash
209
+ # Tous les tests
210
+ poetry run pytest tests/ -v
211
+
212
+ # Avec couverture
213
+ poetry run pytest tests/ --cov --cov-report=html
214
+
215
+ # Voir rapport HTML
216
+ open htmlcov/index.html
217
+ ```
218
+
219
+ **Résultats** :
220
+ - ✅ 33 tests passés
221
+ - 📊 88% de couverture globale
222
+
223
+ ## 🚀 Déploiement
224
+
225
+ ### Variables d'environnement requises
226
+ ```bash
227
+ DEBUG=false
228
+ API_KEY=<votre-clé-sécurisée>
229
+ LOG_LEVEL=INFO
230
+ ```
231
+
232
+ ### HuggingFace Spaces
233
+ Prêt pour déploiement avec `app.py` et `requirements.txt`
234
+
235
+ ## 📚 Documentation
236
+
237
+ - **API Interactive** : http://localhost:8000/docs
238
+ - **ReDoc** : http://localhost:8000/redoc
239
+ - **Guide complet** : [docs/API_GUIDE.md](docs/API_GUIDE.md)
240
+ - **Standards** : [docs/standards.md](docs/standards.md)
241
+ - **Couverture tests** : [docs/TEST_COVERAGE.md](docs/TEST_COVERAGE.md)
242
+
243
+ ## 📦 Dépendances principales
244
+
245
+ - **FastAPI** 0.115.14 : Framework web
246
+ - **Pydantic** 2.12.5 : Validation données
247
+ - **XGBoost** 2.1.3 : Modèle ML
248
+ - **SlowAPI** 0.1.9 : Rate limiting
249
+ - **python-json-logger** 4.0.0 : Logs structurés
250
+ - **pytest** 9.0.2 : Tests
251
+
252
+ ## 🔄 Changelog
253
+
254
+ ### v2.2.0 (27 décembre 2025)
255
+ - 📦 Nouvel endpoint `/predict/batch` pour traitement CSV direct
256
+ - 🔧 Fix preprocessing : ajout du scaling des features
257
+ - 🔧 Fix preprocessing : correction de l'ordre des colonnes
258
+ - 📊 Amélioration précision des prédictions (~90%)
259
+
260
+ ### v2.1.0 (26 décembre 2025)
261
+ - ✨ Système de logging structuré JSON
262
+ - 🛡️ Rate limiting avec SlowAPI
263
+ - ⚡ Amélioration gestion d'erreurs
264
+ - 📊 Monitoring des performances
265
+
266
+ ### v2.0.0 (26 décembre 2025)
267
+ - ✅ Suite de tests complète (36 tests)
268
+ - 🔐 Authentification API Key
269
+ - 📊 88% de couverture de code
270
+
271
+ ## 👥 Auteurs
272
 
273
+ - **Projet** : OpenClassrooms P5
274
+ - **Repo** : [github.com/chaton59/OC_P5](https://github.com/chaton59/OC_P5)
src/gradio_ui.py CHANGED
@@ -198,7 +198,7 @@ curl -X POST https://asi-engineer-oc-p5-dev.hf.space/predict \\
198
  "departement": "Commercial",
199
  "poste": "Manager",
200
  "nombre_experiences_precedentes": 3,
201
- "nombre_heures_travailless": 45,
202
  "annee_experience_totale": 10,
203
  "annees_dans_l_entreprise": 5,
204
  "annees_dans_le_poste_actuel": 2
@@ -239,7 +239,7 @@ data = {
239
  "departement": "Commercial",
240
  "poste": "Manager",
241
  "nombre_experiences_precedentes": 3,
242
- "nombre_heures_travailless": 45,
243
  "annee_experience_totale": 10,
244
  "annees_dans_l_entreprise": 5,
245
  "annees_dans_le_poste_actuel": 2
@@ -316,16 +316,18 @@ def create_gradio_interface():
316
  with gr.Column():
317
  gr.Markdown("#### 📋 Données Sondage")
318
  nombre_participation_pee = gr.Slider(
319
- 0, 10, value=0, step=1, label="Participations PEE"
320
  )
321
  nb_formations_suivies = gr.Slider(
322
- 0, 10, value=2, step=1, label="Formations suivies"
323
  )
324
- nombre_employee_sous_responsabilite = gr.Slider(
325
- 0, 20, value=0, step=1, label="Employés sous responsabilité"
 
 
326
  )
327
  distance_domicile_travail = gr.Slider(
328
- 0, 50, value=15, step=1, label="Distance domicile (km)"
329
  )
330
  niveau_education = gr.Slider(
331
  1, 5, value=3, step=1, label="Niveau éducation (1-5)"
@@ -354,7 +356,7 @@ def create_gradio_interface():
354
  0, 15, value=2, step=1, label="Années depuis promotion"
355
  )
356
  annes_sous_responsable_actuel = gr.Slider(
357
- 0, 20, value=3, step=1, label="Années sous responsable"
358
  )
359
 
360
  # Colonne EVALUATION
@@ -364,7 +366,7 @@ def create_gradio_interface():
364
  1, 4, value=3, step=1, label="Satisfaction environnement"
365
  )
366
  note_evaluation_precedente = gr.Slider(
367
- 1, 5, value=3, step=1, label="Évaluation précédente"
368
  )
369
  niveau_hierarchique_poste = gr.Slider(
370
  1, 5, value=2, step=1, label="Niveau hiérarchique"
@@ -379,7 +381,7 @@ def create_gradio_interface():
379
  1, 4, value=3, step=1, label="Équilibre pro/perso"
380
  )
381
  note_evaluation_actuelle = gr.Slider(
382
- 1, 5, value=3, step=1, label="Évaluation actuelle"
383
  )
384
  heure_supplementaires = gr.Radio(
385
  ["Oui", "Non"], value="Non", label="Heures supplémentaires"
@@ -395,11 +397,11 @@ def create_gradio_interface():
395
  # Colonne SIRH
396
  with gr.Column():
397
  gr.Markdown("#### 👤 Données SIRH")
398
- age = gr.Slider(18, 65, value=35, step=1, label="Âge")
399
  genre = gr.Radio(["M", "F"], value="M", label="Genre")
400
  revenu_mensuel = gr.Slider(
401
- 1500,
402
- 15000,
403
  value=4500,
404
  step=100,
405
  label="Revenu mensuel (€)",
@@ -430,19 +432,19 @@ def create_gradio_interface():
430
  label="Poste",
431
  )
432
  nombre_experiences_precedentes = gr.Slider(
433
- 0, 10, value=2, step=1, label="Expériences précédentes"
434
  )
435
- nombre_heures_travailless = gr.Slider(
436
- 35, 80, value=40, step=1, label="Heures travaillées/sem"
437
  )
438
  annee_experience_totale = gr.Slider(
439
  0, 40, value=10, step=1, label="Années d'expérience totale"
440
  )
441
  annees_dans_l_entreprise = gr.Slider(
442
- 0, 30, value=5, step=1, label="Années dans l'entreprise"
443
  )
444
  annees_dans_le_poste_actuel = gr.Slider(
445
- 0, 20, value=2, step=1, label="Années dans le poste"
446
  )
447
 
448
  # Bouton et résultat
 
198
  "departement": "Commercial",
199
  "poste": "Manager",
200
  "nombre_experiences_precedentes": 3,
201
+ "nombre_heures_travailless": 80,
202
  "annee_experience_totale": 10,
203
  "annees_dans_l_entreprise": 5,
204
  "annees_dans_le_poste_actuel": 2
 
239
  "departement": "Commercial",
240
  "poste": "Manager",
241
  "nombre_experiences_precedentes": 3,
242
+ "nombre_heures_travailless": 80,
243
  "annee_experience_totale": 10,
244
  "annees_dans_l_entreprise": 5,
245
  "annees_dans_le_poste_actuel": 2
 
316
  with gr.Column():
317
  gr.Markdown("#### 📋 Données Sondage")
318
  nombre_participation_pee = gr.Slider(
319
+ 0, 3, value=0, step=1, label="Participations PEE"
320
  )
321
  nb_formations_suivies = gr.Slider(
322
+ 0, 6, value=2, step=1, label="Formations suivies"
323
  )
324
+ nombre_employee_sous_responsabilite = gr.Number(
325
+ value=1,
326
+ label="Employés sous responsabilité",
327
+ interactive=False,
328
  )
329
  distance_domicile_travail = gr.Slider(
330
+ 1, 30, value=10, step=1, label="Distance domicile (km)"
331
  )
332
  niveau_education = gr.Slider(
333
  1, 5, value=3, step=1, label="Niveau éducation (1-5)"
 
356
  0, 15, value=2, step=1, label="Années depuis promotion"
357
  )
358
  annes_sous_responsable_actuel = gr.Slider(
359
+ 0, 17, value=3, step=1, label="Années sous responsable"
360
  )
361
 
362
  # Colonne EVALUATION
 
366
  1, 4, value=3, step=1, label="Satisfaction environnement"
367
  )
368
  note_evaluation_precedente = gr.Slider(
369
+ 1, 4, value=3, step=1, label="Évaluation précédente"
370
  )
371
  niveau_hierarchique_poste = gr.Slider(
372
  1, 5, value=2, step=1, label="Niveau hiérarchique"
 
381
  1, 4, value=3, step=1, label="Équilibre pro/perso"
382
  )
383
  note_evaluation_actuelle = gr.Slider(
384
+ 3, 4, value=3, step=1, label="Évaluation actuelle"
385
  )
386
  heure_supplementaires = gr.Radio(
387
  ["Oui", "Non"], value="Non", label="Heures supplémentaires"
 
397
  # Colonne SIRH
398
  with gr.Column():
399
  gr.Markdown("#### 👤 Données SIRH")
400
+ age = gr.Slider(18, 60, value=35, step=1, label="Âge")
401
  genre = gr.Radio(["M", "F"], value="M", label="Genre")
402
  revenu_mensuel = gr.Slider(
403
+ 1000,
404
+ 20000,
405
  value=4500,
406
  step=100,
407
  label="Revenu mensuel (€)",
 
432
  label="Poste",
433
  )
434
  nombre_experiences_precedentes = gr.Slider(
435
+ 0, 9, value=2, step=1, label="Expériences précédentes"
436
  )
437
+ nombre_heures_travailless = gr.Number(
438
+ value=80, label="Heures travaillées/sem", interactive=False
439
  )
440
  annee_experience_totale = gr.Slider(
441
  0, 40, value=10, step=1, label="Années d'expérience totale"
442
  )
443
  annees_dans_l_entreprise = gr.Slider(
444
+ 0, 40, value=5, step=1, label="Années dans l'entreprise"
445
  )
446
  annees_dans_le_poste_actuel = gr.Slider(
447
+ 0, 18, value=2, step=1, label="Années dans le poste"
448
  )
449
 
450
  # Bouton et résultat
src/schemas.py CHANGED
@@ -78,16 +78,19 @@ class EmployeeInput(BaseModel):
78
 
79
  # === Données SONDAGE ===
80
  nombre_participation_pee: int = Field(
81
- ..., ge=0, description="Nombre de participations au PEE"
82
  )
83
  nb_formations_suivies: int = Field(
84
- ..., ge=0, le=10, description="Nombre de formations suivies"
85
  )
86
  nombre_employee_sous_responsabilite: int = Field(
87
- ..., ge=0, description="Nombre d'employés sous responsabilité"
 
 
 
88
  )
89
  distance_domicile_travail: int = Field(
90
- ..., ge=0, le=50, description="Distance domicile-travail en km"
91
  )
92
  niveau_education: int = Field(
93
  ..., ge=1, le=5, description="Niveau d'éducation (1-5)"
@@ -101,7 +104,7 @@ class EmployeeInput(BaseModel):
101
  ..., ge=0, description="Années depuis la dernière promotion"
102
  )
103
  annes_sous_responsable_actuel: int = Field(
104
- ..., ge=0, description="Années sous le responsable actuel"
105
  )
106
 
107
  # === Données EVALUATION ===
@@ -109,7 +112,7 @@ class EmployeeInput(BaseModel):
109
  ..., ge=1, le=4, description="Satisfaction environnement (1-4)"
110
  )
111
  note_evaluation_precedente: int = Field(
112
- ..., ge=1, le=5, description="Note évaluation précédente (1-5)"
113
  )
114
  niveau_hierarchique_poste: int = Field(
115
  ..., ge=1, le=5, description="Niveau hiérarchique (1-5)"
@@ -124,7 +127,7 @@ class EmployeeInput(BaseModel):
124
  ..., ge=1, le=4, description="Satisfaction équilibre pro/perso (1-4)"
125
  )
126
  note_evaluation_actuelle: int = Field(
127
- ..., ge=1, le=5, description="Note évaluation actuelle (1-5)"
128
  )
129
  heure_supplementaires: Literal["Oui", "Non"] = Field(
130
  ..., description="Fait des heures supplémentaires"
@@ -134,26 +137,31 @@ class EmployeeInput(BaseModel):
134
  )
135
 
136
  # === Données SIRH ===
137
- age: int = Field(..., ge=18, le=70, description="Âge de l'employé")
138
  genre: GenreEnum = Field(..., description="Genre")
139
- revenu_mensuel: float = Field(..., ge=1000, description="Revenu mensuel (€)")
 
 
140
  statut_marital: StatutMaritalEnum = Field(..., description="Statut marital")
141
  departement: DepartementEnum = Field(..., description="Département")
142
  poste: PosteEnum = Field(..., description="Intitulé du poste")
143
  nombre_experiences_precedentes: int = Field(
144
- ..., ge=0, description="Nombre d'expériences précédentes"
145
  )
146
  nombre_heures_travailless: int = Field(
147
- ..., ge=35, le=80, description="Nombre d'heures travaillées par semaine"
 
 
 
148
  )
149
  annee_experience_totale: int = Field(
150
  ..., ge=0, description="Années d'expérience totale"
151
  )
152
  annees_dans_l_entreprise: int = Field(
153
- ..., ge=0, description="Années dans l'entreprise"
154
  )
155
  annees_dans_le_poste_actuel: int = Field(
156
- ..., ge=0, description="Années dans le poste actuel"
157
  )
158
 
159
  @field_validator("augementation_salaire_precedente")
 
78
 
79
  # === Données SONDAGE ===
80
  nombre_participation_pee: int = Field(
81
+ ..., ge=0, le=3, description="Nombre de participations au PEE (0-3)"
82
  )
83
  nb_formations_suivies: int = Field(
84
+ ..., ge=0, le=6, description="Nombre de formations suivies (0-6)"
85
  )
86
  nombre_employee_sous_responsabilite: int = Field(
87
+ default=1,
88
+ ge=1,
89
+ le=1,
90
+ description="Nombre d'employés sous responsabilité (fixe: 1)",
91
  )
92
  distance_domicile_travail: int = Field(
93
+ ..., ge=1, le=30, description="Distance domicile-travail en km (1-30)"
94
  )
95
  niveau_education: int = Field(
96
  ..., ge=1, le=5, description="Niveau d'éducation (1-5)"
 
104
  ..., ge=0, description="Années depuis la dernière promotion"
105
  )
106
  annes_sous_responsable_actuel: int = Field(
107
+ ..., ge=0, le=17, description="Années sous le responsable actuel (0-17)"
108
  )
109
 
110
  # === Données EVALUATION ===
 
112
  ..., ge=1, le=4, description="Satisfaction environnement (1-4)"
113
  )
114
  note_evaluation_precedente: int = Field(
115
+ ..., ge=1, le=4, description="Note évaluation précédente (1-4)"
116
  )
117
  niveau_hierarchique_poste: int = Field(
118
  ..., ge=1, le=5, description="Niveau hiérarchique (1-5)"
 
127
  ..., ge=1, le=4, description="Satisfaction équilibre pro/perso (1-4)"
128
  )
129
  note_evaluation_actuelle: int = Field(
130
+ ..., ge=3, le=4, description="Note évaluation actuelle (3-4)"
131
  )
132
  heure_supplementaires: Literal["Oui", "Non"] = Field(
133
  ..., description="Fait des heures supplémentaires"
 
137
  )
138
 
139
  # === Données SIRH ===
140
+ age: int = Field(..., ge=18, le=60, description="Âge de l'employé (18-60)")
141
  genre: GenreEnum = Field(..., description="Genre")
142
+ revenu_mensuel: float = Field(
143
+ ..., ge=1000, le=20000, description="Revenu mensuel (€) (1000-20000)"
144
+ )
145
  statut_marital: StatutMaritalEnum = Field(..., description="Statut marital")
146
  departement: DepartementEnum = Field(..., description="Département")
147
  poste: PosteEnum = Field(..., description="Intitulé du poste")
148
  nombre_experiences_precedentes: int = Field(
149
+ ..., ge=0, le=9, description="Nombre d'expériences précédentes (0-9)"
150
  )
151
  nombre_heures_travailless: int = Field(
152
+ default=80,
153
+ ge=80,
154
+ le=80,
155
+ description="Nombre d'heures travaillées par semaine (fixe: 80)",
156
  )
157
  annee_experience_totale: int = Field(
158
  ..., ge=0, description="Années d'expérience totale"
159
  )
160
  annees_dans_l_entreprise: int = Field(
161
+ ..., ge=0, le=40, description="Années dans l'entreprise (0-40)"
162
  )
163
  annees_dans_le_poste_actuel: int = Field(
164
+ ..., ge=0, le=18, description="Années dans le poste actuel (0-18)"
165
  )
166
 
167
  @field_validator("augementation_salaire_precedente")