Guide complet pour utiliser le dashboard Predict W'AI avec son API FastAPI.
cd predictwai-app-main
pip install -r requirements.txt
uvicorn main:app --reload --host 0.0.0.0 --port 8000
L'API sera disponible à http://localhost:8000. La doc interactive Swagger est accessible à http://localhost:8000/docs.
Ouvrez web/index.html avec VS Code Live Server (port 5500 par défaut) ou tout serveur HTTP local.
css/, js/). Assurez-vous que votre serveur sert le dossier web/ comme racine, ou ouvrez web/index.html directement via Live Server depuis la racine du projet.Par défaut le frontend pointe sur /api/v1 (même domaine). Si le backend tourne sur un port différent, modifiez la constante BASE dans js/bundle.js :
// js/bundle.js — ligne ~207
const BASE = 'http://localhost:8000/api/v1';
predictwai-app-main/
├── api/
│ ├── main.py ← Point d'entrée FastAPI + montage statiques
│ └── routers/
│ ├── sections.py ← GET /sections — GeoJSON tronçons
│ ├── predictions.py ← GET /predictions — horizons temporels
│ ├── budget.py ← GET /budget/overview + POST /budget/scenarios
│ ├── dashboard.py ← GET /dashboard/kpis
│ ├── analyze.py ← POST /photos/analyze — pipeline YOLOv8 complet
│ ├── photos.py ← POST /photos/upload + GET /photos/file/{filename}
│ └── copilote.py ← POST /copilote/extract — LLM avec fallback
├── core/
│ ├── exif.py ← Extraction GPS/EXIF (Pillow)
│ ├── detection.py ← Inférence YOLOv8 + remapping classes → D-codes
│ ├── geo.py ← WFS BD TOPO IGN + association photo↔tronçon
│ ├── aggregation.py ← Agrégation scores par tronçon
│ └── optimization.py ← Solveur MILP (PuLP + CBC)
├── ui/ ← Interface Streamlit (alternative web)
│ ├── app.py ← Application Streamlit 6 onglets
│ ├── styles.py ← Design system responsive (3 breakpoints)
│ └── tabs/ ← Modules onglets (detection, scan, overview…)
├── train/ ← Outils d'entraînement YOLOv8
│ ├── auto_train.py ← Pipeline complet : téléchargement + entraînement
│ ├── download_pretrained.py ← Téléchargement checkpoint HuggingFace
│ ├── train.py ← Fine-tuning sur dataset annoté
│ ├── evaluate.py ← Évaluation mAP par classe
│ ├── prepare_dataset.py ← Préparation structure YOLO
│ └── dataset.yaml ← Configuration dataset YOLO
├── models/
│ └── best_last_model.pt ← Modèle YOLOv8 road-damage (non versionné Git)
├── data/
│ └── photos/ ← Photos terrain (servi via /api/v1/photos/file/)
├── config.py ← Constantes globales (classes, seuils, remapping)
└── web/
├── index.html ← SPA frontend
├── css/main.css ← Design system
├── js/bundle.js ← Application JS (tout-en-un, sans bundler)
└── doc.html ← Cette documentation
Le frontend est une SPA vanilla JS sans framework. Chaque écran (Overview, Scan, Predict…) est initialisé à la demande via navigate(). Les données sont chargées depuis l'API FastAPI via fetch().
web/, servie par FastAPI sur le port 8000) et une interface Streamlit (ui/, streamlit run ui/app.py sur le port 8501). Les deux partagent le même backend core/.Tous les endpoints sont préfixés par /api/v1.
Réponse exemple :
{
"n_troncons": 42,
"score_moyen": 58.3,
"n_critique": 5,
"n_urgent": 12,
"n_planifie": 18,
"n_surveille": 7,
"cout_total_estime": 284000,
"n_photos_total": 186,
"confidence_moyenne": 0.81,
"lineaire_km": 8.4,
"budget_annuel": 1000000,
"budget_consomme": 284000,
"evolution_score": [
{"date": "Jan 2025", "score": 68.0},
{"date": "Avr 2025", "score": 58.3}
]
}
TR-001).| Propriété | Type | Description |
|---|---|---|
| id | string | Identifiant unique du tronçon (TR-001…) |
| nom_rue | string | Nom de la voie |
| commune | string | Commune INSEE |
| score | float 0-100 | Score d'état (0 = très dégradé, 100 = parfait) |
| urgence | enum | CRITIQUE / URGENT / PLANIFIÉ / SURVEILLÉ |
| classe_dominante | string | Classe YOLO dominante (D40, D20, D10, D00) |
| cout_estime | float | Coût de réfection estimé en € |
| longueur_m | float | Longueur en mètres |
| confidence | float 0-1 | Confiance moyenne des détections IA |
horizon : all | 12m | 6m | 3m.{"budget": 500000, "horizon": "12m"}.multipart/form-data, champ files.data/photos/ sans lancer l'analyse. Body : multipart/form-data.data/photos/. Le nom de fichier doit être URL-encodé (encodeURIComponent) — gère les espaces et caractères spéciaux. Utilisé par la galerie du dashboard en mode démo.// Exemple : charger une photo dans le navigateur
fetch('/api/v1/photos/file/' + encodeURIComponent('IMG_0142 - Copy.jpg'))
.then(r => r.blob())
.then(b => { img.src = URL.createObjectURL(b); });
// Body JSON
{
"texte": "De : contact@entreprise.fr\n... contenu email ...",
"provider": "mistral" // ou "claude"
}
// Réponse
{
"fournisseur": "Vendée TP & Voirie SARL",
"reference": "DEV-2026-0342",
"montant_ht": 21146.0,
"montant_ttc": 25375.20,
"tva_pct": 20,
"postes": [
{ "description": "Fraisage enrobé", "quantite": 800, "unite": "m²", "prix_unitaire": 4.50, "montant": 3600 }
],
"troncons_concernes": ["TR-001"],
"confidence": 0.94,
"provider": "mistral"
}
Le dashboard fonctionne sans backend grâce au mode démo intégré. Les données de démonstration représentent le réseau routier de Saint-Hilaire-de-Riez (85194).
Cliquez sur le bouton ⚡ DÉMO en bas à gauche du dashboard. Toutes les requêtes API sont interceptées et retournent des données simulées réalistes.
Le backend lit les tronçons depuis un fichier GeoJSON dans data/. Format attendu :
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": "TR-001",
"geometry": {
"type": "LineString",
"coordinates": [[-1.939, 46.703], [-1.940, 46.704]]
},
"properties": {
"id": "TR-001",
"nom_rue": "Route du Sel",
"commune": "Ma Commune",
"score": 18.5,
"urgence": "CRITIQUE",
"classe_dominante": "D40",
"cout_estime": 12800,
"longueur_m": 320,
"confidence": 0.87
}
}
]
}
Dans js/bundle.js, modifiez les coordonnées du centre de carte :
// Scan screen — ligne ~888
var center = [-1.942, 46.711]; // ← longitude, latitude de votre commune
| Variable | Requis | Description |
|---|---|---|
| MISTRAL_API_KEY | optionnel | Clé API Mistral pour le Copilote IA |
| ANTHROPIC_API_KEY | optionnel | Clé API Claude pour le Copilote IA |
| DATA_DIR | optionnel | Chemin vers le dossier de données (défaut : ./data) |
| YOLO_MODEL | optionnel | Chemin vers le modèle YOLOv8 (.pt) |
Les photos doivent être au format JPG ou PNG avec données EXIF GPS pour être géolocalisées automatiquement sur les tronçons IGN.
Le modèle actuel est ozair23/yolov8-road-damage-detector (HuggingFace, 6,2 MB). Il détecte 5 classes en anglais que le backend remapping automatiquement :
| Sortie modèle | Code ASTM | Description | Poids ASTM |
|---|---|---|---|
| longitudinal crack | D00 | Fissure longitudinale | 0.218 |
| transverse crack | D10 | Fissure transversale | 0.328 |
| alligator crack | D20 | Faïençage / alligator | 0.692 |
| other corruption | D40 | Autre dégradation | 1.000 |
| Pothole | D40 | Nid-de-poule | 1.000 |
Ce remapping est défini dans config.py (constante REMAP_VERS_DCODE) et appliqué dans core/detection.py. Tout nouveau modèle aux noms de classes différents se configure via ce dictionnaire uniquement.
score = round(exp(−S) × 100, 1)
S = Σ POIDS_ASTM[classe] × confiance
Exemples : 100 = aucun défaut · 37.9 = route très dégradée (3× D40) · 0 ≈ hors service
Le dossier train/ contient tous les outils nécessaires :
# Téléchargement automatique (HuggingFace public, pas d'auth)
python train/download_pretrained.py
# Pipeline complet : téléchargement dataset + conversion + entraînement
python train/auto_train.py
# Fine-tuning sur vos propres photos annotées
python train/prepare_dataset.py # structurer data/photos/
labelImg # annoter (format YOLO)
python train/train.py --epochs 50 --batch 4
# Évaluation du modèle
python train/evaluate.py --model models/best_last_model.pt
Le Copilote IA (onglet 05 · COPILOTE IA) extrait automatiquement les données structurées depuis les emails et devis des entreprises de voirie.
# .env
MISTRAL_API_KEY=sk-... # Mistral AI (souverain FR/EU — provider principal)
ANTHROPIC_API_KEY=sk-... # Claude API (fallback automatique si Mistral indisponible)
Le backend gère un basculement automatique entre les deux providers LLM. Si le provider principal renvoie une erreur de crédit, quota ou authentification, le second provider est tenté automatiquement :
// Comportement selon provider sélectionné
provider = "mistral" → essai Mistral → si erreur crédit/quota → bascule Claude
provider = "claude" → essai Claude → si erreur crédit/quota → bascule Mistral
Ce comportement est implémenté dans api/routers/copilote.py via la fonction _call_with_fallback().
GET /api/v1/photos/file/{filename} — sert les photos terrain avec gestion correcte des noms de fichiers (espaces, caractères spéciaux via URL-encoding)core/detection.py lit les noms de classes du modèle et les remapping vers D00/D10/D20/D40 via REMAP_VERS_DCODE dans config.py. Compatible avec tout modèle tiers (noms anglais ou codes RDD2022)ozair23/yolov8-road-damage-detector (HuggingFace public, 5 classes, téléchargé via train/download_pretrained.py)train/ avec 5 scripts : pipeline automatique, téléchargement checkpoint, fine-tuning, évaluation, préparation dataset_call_with_fallback())ui/styles.py avec 3 breakpoints (tablet 980px, mobile 640px, petit mobile 420px), navigation horizontale scrollableui/tabs/detection.py charge les photos réelles de data/photos/ en mode démo (fallback synthétique si vide)Predict W'AI · Road Maintenance OS · Documentation v1.1 · Avril 2026