feat(setup): seed ST-DEFAULT station and assign existing recipes
Add _seed_default_station() helper to /api/setup/seed endpoint that creates the ST-DEFAULT station and assigns all active recipes to it, preserving existing behaviour after migration 002 runs on live DBs. Helper is idempotent and is called on both the normal seed path and the early-return path (when demo data already exists). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,12 +13,14 @@ from fastapi.responses import HTMLResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import inspect as sa_inspect, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from config import settings
|
||||
from database import Base, engine, async_session_factory
|
||||
from models import (
|
||||
User, Recipe, RecipeVersion, RecipeTask, RecipeSubtask, Measurement,
|
||||
)
|
||||
from models.station import Station, StationRecipeAssignment
|
||||
from services.auth_service import hash_password
|
||||
from services.measurement_service import calculate_pass_fail
|
||||
|
||||
@@ -161,6 +163,50 @@ def _generate_measurements_for_subtask(
|
||||
return measurements
|
||||
|
||||
|
||||
async def _seed_default_station(session: AsyncSession, admin_user: User) -> None:
|
||||
"""Ensure ST-DEFAULT station exists and all active recipes are assigned to it.
|
||||
|
||||
Idempotent: safe to call multiple times. Skips creation if the station
|
||||
already exists and only adds assignments for recipes not yet assigned.
|
||||
"""
|
||||
existing = await session.execute(
|
||||
select(Station).where(Station.code == "ST-DEFAULT")
|
||||
)
|
||||
default_station = existing.scalar_one_or_none()
|
||||
|
||||
if default_station is None:
|
||||
default_station = Station(
|
||||
code="ST-DEFAULT",
|
||||
name="Default Station",
|
||||
location="Initial seed - change me",
|
||||
created_by=admin_user.id,
|
||||
)
|
||||
session.add(default_station)
|
||||
await session.flush()
|
||||
await session.refresh(default_station)
|
||||
|
||||
# Collect already-assigned recipe IDs to avoid unique-constraint violations.
|
||||
existing_assignments = await session.execute(
|
||||
select(StationRecipeAssignment.recipe_id).where(
|
||||
StationRecipeAssignment.station_id == default_station.id
|
||||
)
|
||||
)
|
||||
existing_ids = {row[0] for row in existing_assignments}
|
||||
|
||||
recipes_result = await session.execute(
|
||||
select(Recipe).where(Recipe.active == True)
|
||||
)
|
||||
for r in recipes_result.scalars().all():
|
||||
if r.id not in existing_ids:
|
||||
session.add(StationRecipeAssignment(
|
||||
station_id=default_station.id,
|
||||
recipe_id=r.id,
|
||||
assigned_by=admin_user.id,
|
||||
))
|
||||
|
||||
await session.flush()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Endpoints
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -299,6 +345,7 @@ async def seed_demo_data(body: PasswordBody):
|
||||
select(Recipe).where(Recipe.code == "DEMO-001")
|
||||
)
|
||||
if existing_recipe.scalar_one_or_none():
|
||||
await _seed_default_station(session, admin_user)
|
||||
return {
|
||||
"status": "ok",
|
||||
"message": "Demo data already exists, skipped",
|
||||
@@ -453,6 +500,9 @@ async def seed_demo_data(body: PasswordBody):
|
||||
session.add_all(ms)
|
||||
measurements_created += len(ms)
|
||||
|
||||
# ---- Default Station ----------------------------------------------
|
||||
await _seed_default_station(session, admin_user)
|
||||
|
||||
return {
|
||||
"status": "ok",
|
||||
"message": "Demo data seeded successfully",
|
||||
|
||||
Reference in New Issue
Block a user