chore: Phase 1 complète — socle technique HomeHub opérationnel

- Corrige le seed pour vérifier l'existence des tables avant insertion
  (évite l'échec au démarrage si les migrations n'ont pas encore été appliquées)
- Ajuste le port frontend de 3000 à 3001 (port 3000 occupé sur l'hôte)
- Migrations Alembic : schémas notes, shopping, todos créés avec succès
- Seed : 114 produits et 9 magasins chargés
- Endpoint /api/health : {"status":"ok"}
- Tests : 6/6 passent (health + media)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-24 05:30:53 +02:00
co-authored by Claude Sonnet 4.6
parent 99b9f1641e
commit 9041b24384
2 changed files with 40 additions and 20 deletions
+39 -19
View File
@@ -6,7 +6,8 @@ import asyncio
import json import json
from pathlib import Path from pathlib import Path
from sqlalchemy import select from sqlalchemy import select, text
from sqlalchemy.exc import ProgrammingError
from app.core.database import AsyncSessionLocal from app.core.database import AsyncSessionLocal
from app.models.shopping import Product, Store from app.models.shopping import Product, Store
@@ -15,27 +16,46 @@ DATA_DIR = Path(__file__).parent
async def run_seed() -> None: async def run_seed() -> None:
async with AsyncSessionLocal() as session: try:
count = await session.scalar(select(Store).limit(1)) async with AsyncSessionLocal() as session:
if count is not None: # Vérifier que la table existe avant de faire le seed
print("Seed déjà chargé — rien à faire.") try:
return result = await session.execute(
text(
"SELECT COUNT(*) FROM information_schema.tables "
"WHERE table_schema = 'shopping' AND table_name = 'stores'"
)
)
table_exists = result.scalar() > 0
except Exception:
table_exists = False
stores_raw = json.loads((DATA_DIR / "seed_stores.json").read_text()) if not table_exists:
for s in stores_raw: print("Seed ignoré — les migrations Alembic n'ont pas encore été appliquées.")
session.add(Store(name=s["name"], location=s.get("location"))) return
products_raw = json.loads((DATA_DIR / "seed_products.json").read_text()) count = await session.scalar(select(Store).limit(1))
for p in products_raw: if count is not None:
session.add(Product( print("Seed déjà chargé — rien à faire.")
name=p["name"], return
category=p.get("category"),
default_unit=p.get("default_unit"),
frequency_score=p.get("frequency_score", 0),
))
await session.commit() stores_raw = json.loads((DATA_DIR / "seed_stores.json").read_text())
print(f"Seed : {len(stores_raw)} magasins, {len(products_raw)} produits chargés.") for s in stores_raw:
session.add(Store(name=s["name"], location=s.get("location")))
products_raw = json.loads((DATA_DIR / "seed_products.json").read_text())
for p in products_raw:
session.add(Product(
name=p["name"],
category=p.get("category"),
default_unit=p.get("default_unit"),
frequency_score=p.get("frequency_score", 0),
))
await session.commit()
print(f"Seed : {len(stores_raw)} magasins, {len(products_raw)} produits chargés.")
except ProgrammingError as e:
print(f"Seed ignoré — tables non disponibles (migrations requises) : {e}")
if __name__ == "__main__": if __name__ == "__main__":
+1 -1
View File
@@ -30,7 +30,7 @@ services:
frontend: frontend:
build: ./frontend build: ./frontend
ports: ports:
- "3000:80" - "3001:80"
depends_on: depends_on:
- backend - backend