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:
2026-04-17 23:04:41 +02:00
parent a79ab37add
commit 2e4db53f6a
2 changed files with 150 additions and 0 deletions
+50
View File
@@ -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",