import { drizzle } from "drizzle-orm/postgres-js"; import postgres from "postgres"; import * as schema from "../../src/server/db/schema"; import { sql } from "drizzle-orm"; import { v4 as uuidv4 } from "uuid"; // Database connection const connectionString = process.env.DATABASE_URL!; const connection = postgres(connectionString); const db = drizzle(connection, { schema }); async function main() { console.log("🌱 Seeding 'Control Flow Demo' experiment..."); try { // 1. Find Admin User & Study const user = await db.query.users.findFirst({ where: (users, { eq }) => eq(users.email, "sean@soconnor.dev") }); if (!user) throw new Error("Admin user 'sean@soconnor.dev' not found. Run seed-dev.ts first."); const study = await db.query.studies.findFirst({ where: (studies, { eq }) => eq(studies.name, "Comparative WoZ Study") }); if (!study) throw new Error("Study 'Comparative WoZ Study' not found. Run seed-dev.ts first."); // Find Robot const robot = await db.query.robots.findFirst({ where: (robots, { eq }) => eq(robots.name, "NAO6") }); if (!robot) throw new Error("Robot 'NAO6' not found. Run seed-dev.ts first."); // 2. Create Experiment const [experiment] = await db.insert(schema.experiments).values({ studyId: study.id, name: "Control Flow Demo", description: "Demonstration of enhanced control flow actions: Sequence, Parallel, Wait, Loop, Branch.", version: 1, status: "draft", robotId: robot.id, createdBy: user.id, }).returning(); if (!experiment) throw new Error("Failed to create experiment"); console.log(`✅ Created Experiment: ${experiment.id}`); // 3. Create Steps // Step 1: Sequence & Parallel const [step1] = await db.insert(schema.steps).values({ experimentId: experiment.id, name: "Complex Action Structures", description: "Demonstrating Sequence and Parallel groups", type: "robot", orderIndex: 0, required: true, durationEstimate: 30 }).returning(); if (!step1) throw new Error("Failed to create step1"); // Step 2: Loops & Waits const [step2] = await db.insert(schema.steps).values({ experimentId: experiment.id, name: "Repetition & Delays", description: "Demonstrating Loop and Wait actions", type: "robot", orderIndex: 1, required: true, durationEstimate: 45 }).returning(); if (!step2) throw new Error("Failed to create step2"); // 4. Create Actions // --- Step 1 Actions --- // Action 1: Sequence // Note: Nested children are stored in 'children' property of the action object in frontend, // but in DB 'parameters' is the JSONB field. // However, looking at ActionChip, it expects `action.children`. // The `ExperimentAction` type usually has `children` at top level. // If the DB doesn't have it, the API must be hydrating it. // BUT, for the purpose of this seed which writes to DB directly, I will put it in `parameters.children` // and assume the frontend/API handles it or I'm missing a column. // Actually, looking at schema again, `actions` table DOES NOT have children. // So it MUST be in `parameters` or it's not persisted in this table structure yet (which would be a bug, but I'm seeding what exists). // Wait, if I put it in parameters, does the UI read it? // `ActionChip` reads `action.children`. // I will try to put it in `parameters` and distinct `children` property in the JSON passed to `parameters`? // No, `parameters` is jsonb. // I will assume for now that the system expects it in parameters if it's not a column, OR it's not fully supported in DB yet. // I will stick to what the UI likely consumes. `parameters: { children: [...] }` // Sequence await db.insert(schema.actions).values({ stepId: step1.id, name: "Introduction Sequence", type: "sequence", orderIndex: 0, // Embedding children here to demonstrate. // Real implementation might vary if keys are strictly checked. parameters: { children: [ { id: uuidv4(), name: "Say Hello", type: "nao6-ros2.say_text", parameters: { text: "Hello there!" }, category: "interaction" }, { id: uuidv4(), name: "Wave Hand", type: "nao6-ros2.move_arm", parameters: { arm: "right", action: "wave" }, category: "movement" } ] }, pluginId: "hristudio-core", category: "control", sourceKind: "core" }); // Parallel await db.insert(schema.actions).values({ stepId: step1.id, name: "Parallel Actions", type: "parallel", orderIndex: 1, parameters: { children: [ { id: uuidv4(), name: "Say 'Moving'", type: "nao6-ros2.say_text", parameters: { text: "I am moving and talking." }, category: "interaction" }, { id: uuidv4(), name: "Walk Forward", type: "nao6-ros2.move_to", parameters: { x: 0.5, y: 0 }, category: "movement" } ] }, pluginId: "hristudio-core", category: "control", sourceKind: "core" }); // --- Step 2 Actions --- // Loop await db.insert(schema.actions).values({ stepId: step2.id, name: "Repeat Message", type: "loop", orderIndex: 0, parameters: { iterations: 3, children: [ { id: uuidv4(), name: "Say 'Echo'", type: "nao6-ros2.say_text", parameters: { text: "Echo" }, category: "interaction" } ] }, pluginId: "hristudio-core", category: "control", sourceKind: "core" }); // Wait await db.insert(schema.actions).values({ stepId: step2.id, name: "Wait 5 Seconds", type: "wait", orderIndex: 1, parameters: { duration: 5 }, pluginId: "hristudio-core", category: "control", sourceKind: "core" }); // Branch (Controls step routing, not nested actions) // Note: Branch configuration is stored in step.trigger.conditions, not action.parameters // The branch action itself is just a marker that this step has conditional routing await db.insert(schema.actions).values({ stepId: step2.id, name: "Conditional Routing", type: "branch", orderIndex: 2, parameters: { // Branch actions don't have nested children // Routing is configured at the step level via trigger.conditions }, pluginId: "hristudio-core", category: "control", sourceKind: "core" }); // Update step2 to have conditional routing await db.update(schema.steps) .set({ type: "conditional", conditions: { options: [ { label: "High Score Path", nextStepIndex: 2, // Would go to a hypothetical step 3 variant: "default" }, { label: "Low Score Path", nextStepIndex: 0, // Loop back to step 1 variant: "outline" } ] } }) .where(sql`id = ${step2.id}`); } catch (err) { console.error(err); process.exit(1); } finally { await connection.end(); } } main();