feat: Semaine 8
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
"""
|
||||
seed_db.py, Peuplement de la base de données LivrExpress
|
||||
==========================================================
|
||||
Réinitialise et peuple livrexpress.db avec des données de démonstration.
|
||||
|
||||
Usage :
|
||||
uv run python seed_db.py (depuis le dossier code/livexpress/)
|
||||
|
||||
Ce script illustre concrètement toutes les règles métier de OrderService :
|
||||
|
||||
RÈGLES (OrderService)
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ urgency_level → "normal" commande créée il y a < 30min │
|
||||
│ "light_delay" entre 30 et 45min │
|
||||
│ "severe_delay" > 45min sans livraison │
|
||||
│ "delivered" commande livrée │
|
||||
│ is_late → True si non livrée et créée il y a > 45min │
|
||||
│ free_delivery → True si total >= 25.0 € │
|
||||
│ is_premium → True si le client a >= 10 commandes au total │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
|
||||
Le middleware simule l'utilisateur connecté avec customer_id = 1 (Marie).
|
||||
"""
|
||||
|
||||
import sqlite3
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
DB_PATH = "livrexpress.db"
|
||||
NOW = datetime.now()
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Schéma
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
SCHEMA = """
|
||||
DROP TABLE IF EXISTS order_dishes;
|
||||
DROP TABLE IF EXISTS orders;
|
||||
DROP TABLE IF EXISTS dishes;
|
||||
DROP TABLE IF EXISTS restaurants;
|
||||
DROP TABLE IF EXISTS customers;
|
||||
|
||||
CREATE TABLE customers (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
address TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE restaurants (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE dishes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
price REAL NOT NULL,
|
||||
restaurant_id INTEGER NOT NULL,
|
||||
FOREIGN KEY (restaurant_id) REFERENCES restaurants(id)
|
||||
);
|
||||
|
||||
CREATE TABLE orders (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
customer_id INTEGER NOT NULL,
|
||||
restaurant_id INTEGER NOT NULL,
|
||||
delivery_address TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
delivered_at TEXT,
|
||||
FOREIGN KEY (customer_id) REFERENCES customers(id),
|
||||
FOREIGN KEY (restaurant_id) REFERENCES restaurants(id)
|
||||
);
|
||||
|
||||
CREATE TABLE order_dishes (
|
||||
order_id INTEGER NOT NULL,
|
||||
dish_id INTEGER NOT NULL,
|
||||
FOREIGN KEY (order_id) REFERENCES orders(id),
|
||||
FOREIGN KEY (dish_id) REFERENCES dishes(id)
|
||||
);
|
||||
"""
|
||||
|
||||
|
||||
def seed():
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
conn.executescript(SCHEMA)
|
||||
c = conn.cursor()
|
||||
|
||||
# ── Clients ──────────────────────────────────────────────────────────────
|
||||
# Marie : 12 commandes au total → is_premium = True (seuil = 10)
|
||||
# Lucas : 3 commandes au total → is_premium = False
|
||||
c.execute("INSERT INTO customers (name, email, address) VALUES (?, ?, ?)",
|
||||
("Marie Dupont", "marie@example.com", "12 rue de la Paix, Paris"))
|
||||
marie_id = c.lastrowid
|
||||
c.execute("INSERT INTO customers (name, email, address) VALUES (?, ?, ?)",
|
||||
("Lucas Martin", "lucas@example.com", "5 avenue Montaigne, Paris"))
|
||||
lucas_id = c.lastrowid
|
||||
|
||||
# ── Restaurants ──────────────────────────────────────────────────────────
|
||||
c.execute("INSERT INTO restaurants (name) VALUES (?)", ("Chez Mario",))
|
||||
mario_id = c.lastrowid
|
||||
c.execute("INSERT INTO restaurants (name) VALUES (?)", ("Le Wok d'Or",))
|
||||
wok_id = c.lastrowid
|
||||
c.execute("INSERT INTO restaurants (name) VALUES (?)", ("Burger Palace",))
|
||||
burger_id = c.lastrowid
|
||||
|
||||
# ── Plats ────────────────────────────────────────────────────────────────
|
||||
dishes = [
|
||||
("Margherita", 12.0, mario_id), # d1
|
||||
("Quattro Stagioni", 14.5, mario_id), # d2
|
||||
("Tiramisu", 6.0, mario_id), # d3
|
||||
("Pad Thaï", 13.5, wok_id), # d4
|
||||
("Nem croustillants", 8.0, wok_id), # d5
|
||||
("Burger Classic", 11.0, burger_id), # d6
|
||||
("Burger Double Bacon", 14.0, burger_id), # d7
|
||||
("Frites maison", 4.5, burger_id), # d8
|
||||
("Cheese Cake", 7.0, burger_id), # d9
|
||||
]
|
||||
dish_ids = []
|
||||
for name, price, rid in dishes:
|
||||
c.execute("INSERT INTO dishes (name, price, restaurant_id) VALUES (?, ?, ?)",
|
||||
(name, price, rid))
|
||||
dish_ids.append(c.lastrowid)
|
||||
d1, d2, d3, d4, d5, d6, d7, d8, d9 = dish_ids
|
||||
|
||||
# ── Commandes pour Marie (customer_id=1) ─────────────────────────────────
|
||||
# On couvre tous les cas des règles métier :
|
||||
#
|
||||
# urgency is_late free_del dishes total
|
||||
# -------- ------- -------- ---------------------- -----
|
||||
# normal False False Margherita 12.0
|
||||
# normal False True Quattro + Tiramisu 20.5
|
||||
# light_del False False Nem croustillants 8.0
|
||||
# light_del False True Pad Thaï + Nem 21.5
|
||||
# severe_del True False Burger Classic 11.0
|
||||
# severe_del True True Burger x2 + Frites + CC 40.5
|
||||
# delivered False True Margherita + Pad Thaï 25.5
|
||||
|
||||
order_specs = [
|
||||
# (label, restaurant_id, created_at_offset_min, delivered_offset_min, dish_list)
|
||||
("normal / pas chère", mario_id, -10, None, [d1]),
|
||||
("normal / livraison ok",mario_id, -20, None, [d2, d3]),
|
||||
("légère alerte", wok_id, -35, None, [d5]),
|
||||
("légère alerte + gratu",wok_id, -38, None, [d4, d5]),
|
||||
("retard / pas chère", burger_id, -60, None, [d6]),
|
||||
("retard / livraison ok",burger_id, -90, None, [d7, d7, d8, d9]),
|
||||
("livrée", mario_id, -50, -5, [d1, d4]),
|
||||
]
|
||||
|
||||
marie_orders = []
|
||||
for label, rest_id, offset_min, delivered_offset, dish_list in order_specs:
|
||||
created = NOW + timedelta(minutes=offset_min)
|
||||
delivered = (NOW + timedelta(minutes=delivered_offset)).isoformat() if delivered_offset else None
|
||||
c.execute(
|
||||
"INSERT INTO orders (customer_id, restaurant_id, delivery_address, created_at, delivered_at)"
|
||||
" VALUES (?, ?, ?, ?, ?)",
|
||||
(marie_id, rest_id, "12 rue de la Paix, Paris",
|
||||
created.isoformat(), delivered)
|
||||
)
|
||||
order_id = c.lastrowid
|
||||
for dish_id in dish_list:
|
||||
c.execute("INSERT INTO order_dishes (order_id, dish_id) VALUES (?, ?)",
|
||||
(order_id, dish_id))
|
||||
total = sum(price for name, price, rid in dishes
|
||||
for did in dish_list if did == dish_ids[dishes.index((name, price, rid))])
|
||||
marie_orders.append((label, order_id, total, created, delivered, dish_list))
|
||||
|
||||
# Commandes historiques pour atteindre order_count >= 10 (Marie est premium)
|
||||
# On en a déjà 7 ci-dessus, on en ajoute 5 dans le passé.
|
||||
for i in range(5):
|
||||
past = NOW - timedelta(days=30 + i)
|
||||
c.execute(
|
||||
"INSERT INTO orders (customer_id, restaurant_id, delivery_address, created_at, delivered_at)"
|
||||
" VALUES (?, ?, ?, ?, ?)",
|
||||
(marie_id, mario_id, "12 rue de la Paix, Paris",
|
||||
past.isoformat(), (past + timedelta(minutes=35)).isoformat())
|
||||
)
|
||||
c.execute("INSERT INTO order_dishes (order_id, dish_id) VALUES (?, ?)",
|
||||
(c.lastrowid, d1))
|
||||
|
||||
# ── Commandes pour Lucas (customer_id=2), non visibles via /my-orders ──
|
||||
for i in range(3):
|
||||
past = NOW - timedelta(days=5 + i)
|
||||
c.execute(
|
||||
"INSERT INTO orders (customer_id, restaurant_id, delivery_address, created_at, delivered_at)"
|
||||
" VALUES (?, ?, ?, ?, ?)",
|
||||
(lucas_id, burger_id, "5 avenue Montaigne, Paris",
|
||||
past.isoformat(), (past + timedelta(minutes=40)).isoformat())
|
||||
)
|
||||
c.execute("INSERT INTO order_dishes (order_id, dish_id) VALUES (?, ?)",
|
||||
(c.lastrowid, d6))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
# ── Résumé ──────────────────────────────────────────────────────────────
|
||||
print()
|
||||
print("━" * 68)
|
||||
print(" LivrExpress, Base de données peuplée")
|
||||
print("━" * 68)
|
||||
print(f"\n Heure actuelle : {NOW.strftime('%H:%M:%S')}")
|
||||
print(f"\n CLIENTS")
|
||||
print(f" #1 Marie Dupont , 12 commandes au total → 🌟 PREMIUM (seuil ≥ 10)")
|
||||
print(f" #2 Lucas Martin , 3 commandes au total → non premium")
|
||||
print(f"\n Client simulé par le middleware : customer_id = 1 (Marie)")
|
||||
print(f"\n COMMANDES VISIBLES sur /my-orders (Marie, 7 commandes récentes)")
|
||||
print(f"\n {'scénario':<26} {'total':>7} {'créée il y a':>13} {'urgency_level':<14} badges")
|
||||
print(" " + "─" * 66)
|
||||
|
||||
urgency_labels = {
|
||||
"normal": "normal ",
|
||||
"light_delay": "⚠️ light_delay ",
|
||||
"severe_delay": "🔴 severe_delay",
|
||||
"delivered": "✅ delivered ",
|
||||
}
|
||||
|
||||
for label, order_id, total, created, delivered, dish_list in marie_orders:
|
||||
elapsed = (NOW - created).total_seconds() / 60
|
||||
if delivered:
|
||||
ul = "delivered"
|
||||
elif elapsed > 45:
|
||||
ul = "severe_delay"
|
||||
elif elapsed > 30:
|
||||
ul = "light_delay"
|
||||
else:
|
||||
ul = "normal"
|
||||
|
||||
badges = []
|
||||
if not delivered and elapsed > 45:
|
||||
badges.append("⏰ is_late")
|
||||
if total >= 25.0:
|
||||
badges.append("🚚 free_delivery")
|
||||
if delivered:
|
||||
badges.append("livrée")
|
||||
|
||||
age_str = f"{int(elapsed)}min ago"
|
||||
print(f" {label:<26} {total:>6.1f}€ {age_str:>13} "
|
||||
f"{urgency_labels[ul]} {', '.join(badges) if badges else '—'}")
|
||||
|
||||
print(f"\n + 5 commandes anciennes (livrées, > 30 jours) pour atteindre order_count = 12")
|
||||
print(f"\n COMMANDES Lucas (non visibles) : 3 commandes livrées")
|
||||
print()
|
||||
print(" ✓ Lancez l'app : uv run uvicorn main:app --reload")
|
||||
print(" ✓ Commandes : http://localhost:8000/my-orders")
|
||||
print("━" * 68)
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
seed()
|
||||
Reference in New Issue
Block a user