Exercices — Séance 1
Les exercices sont progressifs : conception d'abord, puis implémentation.
Contexte commun
Vous travaillez sur une API pour une chaîne de magasins. La base de données SQLite contient les tables suivantes :
CREATE TABLE produits (
id INTEGER PRIMARY KEY,
nom TEXT NOT NULL,
categorie TEXT NOT NULL,
prix REAL NOT NULL
);
CREATE TABLE ventes (
id INTEGER PRIMARY KEY,
produit_id INTEGER REFERENCES produits(id),
magasin TEXT NOT NULL,
region TEXT NOT NULL,
quantite INTEGER NOT NULL,
montant REAL NOT NULL,
date TEXT NOT NULL
);
CREATE TABLE clients (
id INTEGER PRIMARY KEY,
nom TEXT NOT NULL,
email TEXT NOT NULL,
region TEXT NOT NULL,
date_inscription TEXT NOT NULL
);
Exercice : Exercice 1 — Conception des routes
Sans écrire de code, concevez la table de routes de l'API pour les trois ressources (produits, ventes, clients).
Pour chaque ressource, listez :
- Les endpoints CRUD (lister, détail, créer, modifier, supprimer)
- Les endpoints d'agrégation pertinents (au moins 2 par ressource)
- Les query parameters disponibles (filtres, pagination, tri)
Présentez le résultat sous forme de tableau :
| Méthode | URI | Description | Query params |
|---|---|---|---|
GET | /api/v1/produits | Liste paginée des produits | limit, offset, categorie, sort_by |
| ... | ... | Complétez pour les 3 ressources | ... |
Indices :
- Pour les ventes, pensez à des agrégations comme "ventes par région", "CA mensuel", "top produits"
- Pour les clients, pensez à "inscriptions par mois", "répartition par région"
- Pour les produits, pensez à "répartition par catégorie", "prix moyen par catégorie"
Exercice : Exercice 2 — Pagination et filtrage
Implémentez un endpoint Flask GET /api/v1/ventes avec :
- Pagination :
offset(défaut 0) etlimit(défaut 20, max 100) - Filtrage par
region,magasinetannee(tous optionnels) - Tri par
montantoudate(défaut :dateDESC) - Enveloppe de réponse avec
data,total,limit,offset
Structure du projet :
exercice2/
├── app.py
├── routes/
│ └── ventes.py
├── controllers/
│ └── ventes_controller.py
├── repositories/
│ └── ventes_repository.py
└── init_db.py # Script pour créer et peupler la base
Script d'initialisation (init_db.py) :
import sqlite3
import random
from datetime import datetime, timedelta
def init_db():
conn = sqlite3.connect('data.db')
conn.execute("""
CREATE TABLE IF NOT EXISTS ventes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
produit TEXT NOT NULL,
magasin TEXT NOT NULL,
region TEXT NOT NULL,
quantite INTEGER NOT NULL,
montant REAL NOT NULL,
date TEXT NOT NULL
)
""")
regions = ['IDF', 'PACA', 'ARA', 'NAQ', 'OCC']
magasins = ['Paris-Centre', 'Lyon-Part-Dieu', 'Marseille-Prado',
'Toulouse-Capitole', 'Bordeaux-Lac']
produits = ['Laptop', 'Smartphone', 'Tablette', 'Écran', 'Clavier']
for i in range(5000):
region_idx = random.randint(0, 4)
date = datetime(2024, 1, 1) + timedelta(days=random.randint(0, 364))
montant = round(random.uniform(10, 2500), 2)
conn.execute(
"INSERT INTO ventes (produit, magasin, region, quantite, montant, date) VALUES (?, ?, ?, ?, ?, ?)",
(random.choice(produits), magasins[region_idx], regions[region_idx],
random.randint(1, 10), montant, date.strftime('%Y-%m-%d'))
)
conn.commit()
conn.close()
print("Base initialisée avec 5000 ventes.")
if __name__ == '__main__':
init_db()
Vérification : testez votre endpoint avec les requêtes suivantes :
# Page 1, 20 résultats
curl "http://localhost:5000/api/v1/ventes"
# Filtrer par région IDF, trier par montant décroissant
curl "http://localhost:5000/api/v1/ventes?region=IDF&sort_by=montant&order=desc"
# Page 3 (offset 40), 10 résultats, année 2024
curl "http://localhost:5000/api/v1/ventes?offset=40&limit=10&annee=2024"
Exercice : Exercice 3 — Endpoints d'agrégation et export
En réutilisant le projet de l'exercice 2, ajoutez :
1. Statistiques globales — GET /api/v1/ventes/stats
Retourne le nombre de ventes, le CA total, le montant moyen, min et max. Accepte les filtres region et annee.
{
"nombre_ventes": 5000,
"ca_total": 6250000.00,
"montant_moyen": 1250.00,
"montant_min": 10.50,
"montant_max": 2499.99
}
2. Ventes par région — GET /api/v1/ventes/par-region
Retourne le CA et le nombre de ventes par région, trié par CA décroissant. Accepte le filtre annee.
{
"data": [
{"region": "IDF", "nombre_ventes": 1050, "ca": 1320000.00, "panier_moyen": 1257.14},
{"region": "PACA", "nombre_ventes": 980, "ca": 1180000.00, "panier_moyen": 1204.08}
]
}
3. Évolution mensuelle — GET /api/v1/ventes/evolution-mensuelle
Retourne le CA par mois. Accepte les filtres region et annee.
4. Export CSV — GET /api/v1/ventes?format=csv
Modifiez l'endpoint principal pour supporter le paramètre format=csv. Quand format=csv, retournez un fichier CSV téléchargeable avec toutes les ventes filtrées (sans pagination).
Vérification :
# Statistiques globales
curl "http://localhost:5000/api/v1/ventes/stats"
# Ventes par région en 2024
curl "http://localhost:5000/api/v1/ventes/par-region?annee=2024"
# Export CSV des ventes IDF
curl -o ventes_idf.csv "http://localhost:5000/api/v1/ventes?region=IDF&format=csv"
À retenir
Progression
- Exercice 1 : concevoir les routes avant de coder
- Exercice 2 : implémenter pagination, filtrage et tri avec l'architecture Blueprint/Controller/Repository
- Exercice 3 : ajouter les endpoints d'agrégation et d'export