Spaces:
Running
Running
Upload folder using huggingface_hub
Browse files
README.md
CHANGED
|
@@ -178,7 +178,9 @@ OC_P5/
|
|
| 178 |
│ └── preprocess.py # Preprocessing dataset
|
| 179 |
├── scripts/ # 🔧 Scripts utilitaires
|
| 180 |
│ ├── create_db.py # Création base PostgreSQL
|
| 181 |
-
│
|
|
|
|
|
|
|
| 182 |
├── docs/ # 📚 Documentation (5 fichiers minimaux)
|
| 183 |
│ ├── architecture.md # 🏗️ Vue d'ensemble architecture + schéma BDD
|
| 184 |
│ ├── api_documentation.md # 📡 Endpoints REST + exemples cURL/Python
|
|
@@ -373,6 +375,83 @@ poetry run pytest tests/ -v
|
|
| 373 |
|
| 374 |
---
|
| 375 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 376 |
## 🚀 Utilisation
|
| 377 |
|
| 378 |
### Démarrer l'API Localement
|
|
@@ -761,6 +840,8 @@ poetry run pytest tests/ --cov=. --cov-report=html
|
|
| 761 |
open htmlcov/index.html
|
| 762 |
```
|
| 763 |
|
|
|
|
|
|
|
| 764 |
### Métriques
|
| 765 |
|
| 766 |
| Métrique | Valeur | Détail |
|
|
|
|
| 178 |
│ └── preprocess.py # Preprocessing dataset
|
| 179 |
├── scripts/ # 🔧 Scripts utilitaires
|
| 180 |
│ ├── create_db.py # Création base PostgreSQL
|
| 181 |
+
│ ├── insert_dataset.py # Insertion données (1470 employés)
|
| 182 |
+
│ ├── generate_requirements_hf.sh # Génération requirements.txt pour HF
|
| 183 |
+
│ └── run_local.sh # Lancement local développement
|
| 184 |
├── docs/ # 📚 Documentation (5 fichiers minimaux)
|
| 185 |
│ ├── architecture.md # 🏗️ Vue d'ensemble architecture + schéma BDD
|
| 186 |
│ ├── api_documentation.md # 📡 Endpoints REST + exemples cURL/Python
|
|
|
|
| 375 |
|
| 376 |
---
|
| 377 |
|
| 378 |
+
## 🔧 Scripts Utilitaires
|
| 379 |
+
|
| 380 |
+
Le dossier `scripts/` contient les scripts essentiels pour la gestion de la base de données et le déploiement. **Minimalisme** : 4 fichiers maximum, code principal dans `src/`, tests dans `tests/`.
|
| 381 |
+
|
| 382 |
+
### 🗄️ `create_db.py` - Création de la base de données
|
| 383 |
+
|
| 384 |
+
**Rôle** : Crée la base de données PostgreSQL et les tables nécessaires (étape 4 du projet).
|
| 385 |
+
|
| 386 |
+
```bash
|
| 387 |
+
# Créer les tables (dataset, ml_logs)
|
| 388 |
+
poetry run python scripts/create_db.py
|
| 389 |
+
```
|
| 390 |
+
|
| 391 |
+
**Tables créées** :
|
| 392 |
+
- `dataset` : Stockage des données d'entraînement (features_json, target)
|
| 393 |
+
- `ml_logs` : Logs des prédictions de l'API (inputs, outputs, timestamps)
|
| 394 |
+
|
| 395 |
+
### 📊 `insert_dataset.py` - Insertion du dataset
|
| 396 |
+
|
| 397 |
+
**Rôle** : Charge les 3 fichiers CSV (sondage, eval, sirh), les fusionne et insère 1470 employés dans PostgreSQL (étape 4 du projet).
|
| 398 |
+
|
| 399 |
+
```bash
|
| 400 |
+
# Insérer le dataset complet
|
| 401 |
+
poetry run python scripts/insert_dataset.py
|
| 402 |
+
|
| 403 |
+
# Vérifier l'insertion
|
| 404 |
+
psql -h localhost -U ml_user -d oc_p5_db -c "SELECT COUNT(*) FROM dataset;"
|
| 405 |
+
# Résultat attendu : 1470
|
| 406 |
+
```
|
| 407 |
+
|
| 408 |
+
**Fonctionnalités** :
|
| 409 |
+
- Fusionne automatiquement les 3 sources de données
|
| 410 |
+
- Nettoie les valeurs manquantes (NaN → None)
|
| 411 |
+
- Commits par batch de 100 pour performance
|
| 412 |
+
- Validation de l'intégrité des données
|
| 413 |
+
|
| 414 |
+
### 📦 `generate_requirements_hf.sh` - Requirements pour HF Spaces
|
| 415 |
+
|
| 416 |
+
**Rôle** : Génère un fichier `requirements.txt` minimaliste pour déploiement sur Hugging Face Spaces (étape 1 & 2).
|
| 417 |
+
|
| 418 |
+
```bash
|
| 419 |
+
# Générer requirements.txt optimisé pour HF
|
| 420 |
+
bash scripts/generate_requirements_hf.sh
|
| 421 |
+
```
|
| 422 |
+
|
| 423 |
+
**Pourquoi nécessaire ?** HF Spaces nécessite des dépendances minimales (pas dev/test). Ce script extrait uniquement les packages essentiels depuis `pyproject.toml`.
|
| 424 |
+
|
| 425 |
+
### 🚀 `run_local.sh` - Lancement local
|
| 426 |
+
|
| 427 |
+
**Rôle** : Script de démarrage rapide pour développement local.
|
| 428 |
+
|
| 429 |
+
```bash
|
| 430 |
+
# Lancer l'application en mode développement
|
| 431 |
+
bash scripts/run_local.sh
|
| 432 |
+
```
|
| 433 |
+
|
| 434 |
+
**Actions effectuées** :
|
| 435 |
+
1. Installation des dépendances (Poetry)
|
| 436 |
+
2. Vérification du fichier `.env` (copie `.env.example` si nécessaire)
|
| 437 |
+
3. Lancement de l'interface Gradio sur http://localhost:7860
|
| 438 |
+
|
| 439 |
+
### 📝 Organisation des Scripts
|
| 440 |
+
|
| 441 |
+
**Principe de séparation** :
|
| 442 |
+
- **`scripts/`** : Utilitaires BDD et déploiement uniquement (4 fichiers max)
|
| 443 |
+
- **`src/`** : Code applicatif principal (API, modèles, preprocessing)
|
| 444 |
+
- **`tests/`** : Tests unitaires et fonctionnels (séparé pour clarté)
|
| 445 |
+
- **`.github/workflows/`** : CI/CD (GitHub Actions, pas dans scripts/)
|
| 446 |
+
|
| 447 |
+
**Justifications** (liées aux étapes du projet) :
|
| 448 |
+
- ✅ **create_db.py** + **insert_dataset.py** : Étape 4 (script Python pour créer BDD + insérer dataset)
|
| 449 |
+
- ✅ **generate_requirements_hf.sh** : Étape 1 (requirements.txt à la racine) + Étape 2 (CI/CD, environnements)
|
| 450 |
+
- ✅ **run_local.sh** : Développement local (pas obligatoire mais pratique)
|
| 451 |
+
- ✅ **Tests dans `tests/`** : Étape 5 (scripts de tests + rapport couverture)
|
| 452 |
+
|
| 453 |
+
---
|
| 454 |
+
|
| 455 |
## 🚀 Utilisation
|
| 456 |
|
| 457 |
### Démarrer l'API Localement
|
|
|
|
| 840 |
open htmlcov/index.html
|
| 841 |
```
|
| 842 |
|
| 843 |
+
Pour le détail de l'organisation et des catégories de tests, voir `tests/README.md`.
|
| 844 |
+
|
| 845 |
### Métriques
|
| 846 |
|
| 847 |
| Métrique | Valeur | Détail |
|
api.py
CHANGED
|
@@ -7,7 +7,7 @@ Cette API expose le modèle de prédiction de départ des employés avec :
|
|
| 7 |
- Preprocessing automatique
|
| 8 |
- Health check pour monitoring
|
| 9 |
- Documentation OpenAPI/Swagger automatique
|
| 10 |
-
- Interface Gradio pour utilisation interactive
|
| 11 |
- Endpoint batch pour traitement de fichiers CSV
|
| 12 |
"""
|
| 13 |
import io
|
|
@@ -15,7 +15,6 @@ import time
|
|
| 15 |
from contextlib import asynccontextmanager
|
| 16 |
from typing import Any, Callable
|
| 17 |
|
| 18 |
-
import gradio as gr
|
| 19 |
import pandas as pd
|
| 20 |
from fastapi import Depends, FastAPI, File, HTTPException, Request, Response, UploadFile
|
| 21 |
from fastapi.middleware.cors import CORSMiddleware
|
|
@@ -24,7 +23,6 @@ from slowapi.errors import RateLimitExceeded
|
|
| 24 |
|
| 25 |
from src.auth import verify_api_key
|
| 26 |
from src.config import get_settings
|
| 27 |
-
from src.gradio_ui import create_gradio_interface
|
| 28 |
from src.logger import log_model_load, log_request, logger
|
| 29 |
from src.models import get_model_info, load_model
|
| 30 |
from src.preprocessing import (
|
|
@@ -44,6 +42,7 @@ from src.schemas import (
|
|
| 44 |
# Charger la configuration
|
| 45 |
settings = get_settings()
|
| 46 |
API_VERSION = settings.API_VERSION
|
|
|
|
| 47 |
|
| 48 |
|
| 49 |
def conditional_rate_limit(
|
|
@@ -450,9 +449,16 @@ async def predict_batch(
|
|
| 450 |
)
|
| 451 |
|
| 452 |
|
| 453 |
-
|
| 454 |
-
|
| 455 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 456 |
|
| 457 |
|
| 458 |
if __name__ == "__main__":
|
|
|
|
| 7 |
- Preprocessing automatique
|
| 8 |
- Health check pour monitoring
|
| 9 |
- Documentation OpenAPI/Swagger automatique
|
| 10 |
+
- Interface Gradio optionnelle pour utilisation interactive
|
| 11 |
- Endpoint batch pour traitement de fichiers CSV
|
| 12 |
"""
|
| 13 |
import io
|
|
|
|
| 15 |
from contextlib import asynccontextmanager
|
| 16 |
from typing import Any, Callable
|
| 17 |
|
|
|
|
| 18 |
import pandas as pd
|
| 19 |
from fastapi import Depends, FastAPI, File, HTTPException, Request, Response, UploadFile
|
| 20 |
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
| 23 |
|
| 24 |
from src.auth import verify_api_key
|
| 25 |
from src.config import get_settings
|
|
|
|
| 26 |
from src.logger import log_model_load, log_request, logger
|
| 27 |
from src.models import get_model_info, load_model
|
| 28 |
from src.preprocessing import (
|
|
|
|
| 42 |
# Charger la configuration
|
| 43 |
settings = get_settings()
|
| 44 |
API_VERSION = settings.API_VERSION
|
| 45 |
+
GRADIO_ENABLED = settings.GRADIO_ENABLED
|
| 46 |
|
| 47 |
|
| 48 |
def conditional_rate_limit(
|
|
|
|
| 449 |
)
|
| 450 |
|
| 451 |
|
| 452 |
+
if GRADIO_ENABLED:
|
| 453 |
+
# Importer Gradio uniquement si l'UI est activée pour éviter une dépendance inutile en prod API-only
|
| 454 |
+
import gradio as gr
|
| 455 |
+
|
| 456 |
+
from src.gradio_ui import create_gradio_interface
|
| 457 |
+
|
| 458 |
+
gradio_app = create_gradio_interface()
|
| 459 |
+
app = gr.mount_gradio_app(app, gradio_app, path="/")
|
| 460 |
+
else:
|
| 461 |
+
logger.info("Gradio UI désactivée (GRADIO_ENABLED=False)")
|
| 462 |
|
| 463 |
|
| 464 |
if __name__ == "__main__":
|
app.py
CHANGED
|
@@ -20,8 +20,15 @@ logger = logging.getLogger(__name__)
|
|
| 20 |
# Ajouter le répertoire src au path
|
| 21 |
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))
|
| 22 |
|
|
|
|
|
|
|
| 23 |
if __name__ == "__main__":
|
| 24 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
logger.info("🚀 Démarrage de l'application Gradio...")
|
| 26 |
from src.gradio_ui import launch_standalone
|
| 27 |
|
|
|
|
| 20 |
# Ajouter le répertoire src au path
|
| 21 |
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))
|
| 22 |
|
| 23 |
+
from src.config import get_settings # noqa: E402
|
| 24 |
+
|
| 25 |
if __name__ == "__main__":
|
| 26 |
try:
|
| 27 |
+
settings = get_settings()
|
| 28 |
+
if not settings.GRADIO_ENABLED:
|
| 29 |
+
logger.info("Gradio désactivée (GRADIO_ENABLED=False) - arrêt.")
|
| 30 |
+
sys.exit(0)
|
| 31 |
+
|
| 32 |
logger.info("🚀 Démarrage de l'application Gradio...")
|
| 33 |
from src.gradio_ui import launch_standalone
|
| 34 |
|
src/config.py
CHANGED
|
@@ -14,6 +14,15 @@ from dotenv import load_dotenv
|
|
| 14 |
load_dotenv()
|
| 15 |
|
| 16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
class Settings:
|
| 18 |
"""
|
| 19 |
Configuration de l'application.
|
|
@@ -37,8 +46,9 @@ class Settings:
|
|
| 37 |
MODEL_FILENAME: str = os.getenv("MODEL_FILENAME", "model/model.pkl")
|
| 38 |
|
| 39 |
# ===== ENVIRONNEMENT =====
|
| 40 |
-
DEBUG: bool = os.getenv("DEBUG", "False")
|
| 41 |
LOG_LEVEL: str = os.getenv("LOG_LEVEL", "INFO")
|
|
|
|
| 42 |
|
| 43 |
# ===== BASE DE DONNÉES =====
|
| 44 |
DATABASE_URL: str = os.getenv(
|
|
|
|
| 14 |
load_dotenv()
|
| 15 |
|
| 16 |
|
| 17 |
+
def _str_to_bool(value: str, default: bool = False) -> bool:
|
| 18 |
+
"""Convertit une chaîne d'environnement en booléen robuste."""
|
| 19 |
+
|
| 20 |
+
if value is None:
|
| 21 |
+
return default
|
| 22 |
+
|
| 23 |
+
return value.lower() in {"1", "true", "yes", "y", "on"}
|
| 24 |
+
|
| 25 |
+
|
| 26 |
class Settings:
|
| 27 |
"""
|
| 28 |
Configuration de l'application.
|
|
|
|
| 46 |
MODEL_FILENAME: str = os.getenv("MODEL_FILENAME", "model/model.pkl")
|
| 47 |
|
| 48 |
# ===== ENVIRONNEMENT =====
|
| 49 |
+
DEBUG: bool = _str_to_bool(os.getenv("DEBUG", "False"))
|
| 50 |
LOG_LEVEL: str = os.getenv("LOG_LEVEL", "INFO")
|
| 51 |
+
GRADIO_ENABLED: bool = _str_to_bool(os.getenv("GRADIO_ENABLED", "True"), True)
|
| 52 |
|
| 53 |
# ===== BASE DE DONNÉES =====
|
| 54 |
DATABASE_URL: str = os.getenv(
|