249 lines
11 KiB
Python
249 lines
11 KiB
Python
"""
|
|
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() |