mirror of
https://github.com/soconnor0919/hristudio.git
synced 2025-12-11 14:44:44 -05:00
chore: commit full workspace changes (designer modularization, diagnostics fixes, docs updates, seed script cleanup)
This commit is contained in:
@@ -1,589 +0,0 @@
|
||||
import { drizzle } from "drizzle-orm/postgres-js";
|
||||
import { eq } from "drizzle-orm";
|
||||
import postgres from "postgres";
|
||||
import * as schema from "../src/server/db/schema";
|
||||
|
||||
const connectionString =
|
||||
process.env.DATABASE_URL ??
|
||||
"postgresql://postgres:password@localhost:5140/hristudio";
|
||||
const client = postgres(connectionString);
|
||||
const db = drizzle(client, { schema });
|
||||
|
||||
async function seedCoreRepository() {
|
||||
console.log("🏗️ Seeding core system repository...");
|
||||
|
||||
// Check if core repository already exists
|
||||
const existingCoreRepo = await db
|
||||
.select()
|
||||
.from(schema.pluginRepositories)
|
||||
.where(eq(schema.pluginRepositories.url, "https://core.hristudio.com"));
|
||||
|
||||
if (existingCoreRepo.length > 0) {
|
||||
console.log("⚠️ Core repository already exists, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the first user to use as creator
|
||||
const users = await db.select().from(schema.users);
|
||||
const adminUser =
|
||||
users.find((u) => u.email?.includes("sarah.chen")) ?? users[0];
|
||||
|
||||
if (!adminUser) {
|
||||
console.log("⚠️ No users found. Please run basic seeding first.");
|
||||
return;
|
||||
}
|
||||
|
||||
const coreRepository = {
|
||||
id: "00000000-0000-0000-0000-000000000001",
|
||||
name: "HRIStudio Core System Blocks",
|
||||
url: "https://core.hristudio.com",
|
||||
description:
|
||||
"Essential system blocks for experiment design including events, control flow, wizard actions, and logic operations",
|
||||
trustLevel: "official" as const,
|
||||
isEnabled: true,
|
||||
isOfficial: true,
|
||||
lastSyncAt: new Date(),
|
||||
syncStatus: "completed" as const,
|
||||
syncError: null,
|
||||
metadata: {
|
||||
apiVersion: "1.0",
|
||||
pluginApiVersion: "1.0",
|
||||
categories: ["core", "wizard", "control", "logic", "events"],
|
||||
compatibility: {
|
||||
hristudio: { min: "0.1.0", recommended: "0.1.0" },
|
||||
},
|
||||
isCore: true,
|
||||
},
|
||||
createdAt: new Date("2024-01-01T00:00:00"),
|
||||
updatedAt: new Date(),
|
||||
createdBy: adminUser.id,
|
||||
};
|
||||
|
||||
await db.insert(schema.pluginRepositories).values([coreRepository]);
|
||||
console.log("✅ Created core system repository");
|
||||
}
|
||||
|
||||
async function seedCorePlugin() {
|
||||
console.log("🧱 Seeding core system plugin...");
|
||||
|
||||
// Check if core plugin already exists
|
||||
const existingCorePlugin = await db
|
||||
.select()
|
||||
.from(schema.plugins)
|
||||
.where(eq(schema.plugins.name, "HRIStudio Core System"));
|
||||
|
||||
if (existingCorePlugin.length > 0) {
|
||||
console.log("⚠️ Core plugin already exists, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
const corePlugin = {
|
||||
id: "00000000-0000-0000-0000-000000000001",
|
||||
robotId: null, // Core plugin doesn't need a specific robot
|
||||
name: "HRIStudio Core System",
|
||||
version: "1.0.0",
|
||||
description:
|
||||
"Essential system blocks for experiment design including events, control flow, wizard actions, and logic operations",
|
||||
author: "HRIStudio Team",
|
||||
repositoryUrl: "https://core.hristudio.com",
|
||||
trustLevel: "official" as const,
|
||||
status: "active" as const,
|
||||
configurationSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
enableAdvancedBlocks: {
|
||||
type: "boolean",
|
||||
default: true,
|
||||
description: "Enable advanced control flow blocks",
|
||||
},
|
||||
wizardInterface: {
|
||||
type: "string",
|
||||
enum: ["basic", "advanced"],
|
||||
default: "basic",
|
||||
description: "Wizard interface complexity level",
|
||||
},
|
||||
},
|
||||
},
|
||||
actionDefinitions: [
|
||||
// Event Blocks
|
||||
{
|
||||
id: "when_trial_starts",
|
||||
name: "when trial starts",
|
||||
description: "Triggered when the trial begins",
|
||||
category: "logic",
|
||||
icon: "Play",
|
||||
timeout: 0,
|
||||
retryable: false,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {},
|
||||
required: [],
|
||||
},
|
||||
blockType: "hat",
|
||||
color: "#22c55e",
|
||||
nestable: false,
|
||||
},
|
||||
{
|
||||
id: "when_participant_speaks",
|
||||
name: "when participant speaks",
|
||||
description: "Triggered when participant says something",
|
||||
category: "logic",
|
||||
icon: "Mic",
|
||||
timeout: 0,
|
||||
retryable: false,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
keywords: {
|
||||
type: "array",
|
||||
items: { type: "string" },
|
||||
default: [],
|
||||
description: "Optional keywords to listen for",
|
||||
},
|
||||
},
|
||||
required: [],
|
||||
},
|
||||
blockType: "hat",
|
||||
color: "#22c55e",
|
||||
nestable: false,
|
||||
},
|
||||
|
||||
// Wizard Actions
|
||||
{
|
||||
id: "wizard_say",
|
||||
name: "say",
|
||||
description: "Wizard speaks to participant",
|
||||
category: "interaction",
|
||||
icon: "Users",
|
||||
timeout: 30000,
|
||||
retryable: true,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
message: {
|
||||
type: "string",
|
||||
default: "",
|
||||
description: "What should the wizard say?",
|
||||
},
|
||||
},
|
||||
required: ["message"],
|
||||
},
|
||||
blockType: "action",
|
||||
color: "#a855f7",
|
||||
nestable: false,
|
||||
},
|
||||
{
|
||||
id: "wizard_gesture",
|
||||
name: "gesture",
|
||||
description: "Wizard performs a gesture",
|
||||
category: "interaction",
|
||||
icon: "Users",
|
||||
timeout: 10000,
|
||||
retryable: true,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
type: {
|
||||
type: "string",
|
||||
enum: ["wave", "point", "nod", "thumbs_up", "clap"],
|
||||
default: "wave",
|
||||
description: "Type of gesture to perform",
|
||||
},
|
||||
},
|
||||
required: ["type"],
|
||||
},
|
||||
blockType: "action",
|
||||
color: "#a855f7",
|
||||
nestable: false,
|
||||
},
|
||||
{
|
||||
id: "wizard_note",
|
||||
name: "take note",
|
||||
description: "Wizard records an observation",
|
||||
category: "sensors",
|
||||
icon: "FileText",
|
||||
timeout: 15000,
|
||||
retryable: true,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
category: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"behavior",
|
||||
"performance",
|
||||
"engagement",
|
||||
"technical",
|
||||
"other",
|
||||
],
|
||||
default: "behavior",
|
||||
description: "Category of observation",
|
||||
},
|
||||
note: {
|
||||
type: "string",
|
||||
default: "",
|
||||
description: "Observation details",
|
||||
},
|
||||
},
|
||||
required: ["note"],
|
||||
},
|
||||
blockType: "action",
|
||||
color: "#f59e0b",
|
||||
nestable: false,
|
||||
},
|
||||
|
||||
// Control Flow
|
||||
{
|
||||
id: "wait",
|
||||
name: "wait",
|
||||
description: "Pause execution for specified time",
|
||||
category: "logic",
|
||||
icon: "Clock",
|
||||
timeout: 0,
|
||||
retryable: false,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
seconds: {
|
||||
type: "number",
|
||||
minimum: 0.1,
|
||||
maximum: 300,
|
||||
default: 1,
|
||||
description: "Time to wait in seconds",
|
||||
},
|
||||
},
|
||||
required: ["seconds"],
|
||||
},
|
||||
blockType: "action",
|
||||
color: "#f97316",
|
||||
nestable: false,
|
||||
},
|
||||
{
|
||||
id: "repeat",
|
||||
name: "repeat",
|
||||
description: "Execute contained blocks multiple times",
|
||||
category: "logic",
|
||||
icon: "GitBranch",
|
||||
timeout: 0,
|
||||
retryable: false,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
times: {
|
||||
type: "number",
|
||||
minimum: 1,
|
||||
maximum: 50,
|
||||
default: 3,
|
||||
description: "Number of times to repeat",
|
||||
},
|
||||
},
|
||||
required: ["times"],
|
||||
},
|
||||
blockType: "control",
|
||||
color: "#f97316",
|
||||
nestable: true,
|
||||
},
|
||||
{
|
||||
id: "if_condition",
|
||||
name: "if",
|
||||
description: "Conditional execution based on conditions",
|
||||
category: "logic",
|
||||
icon: "GitBranch",
|
||||
timeout: 0,
|
||||
retryable: false,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
condition: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"participant_speaks",
|
||||
"time_elapsed",
|
||||
"wizard_signal",
|
||||
"custom_condition",
|
||||
],
|
||||
default: "participant_speaks",
|
||||
description: "Condition to evaluate",
|
||||
},
|
||||
value: {
|
||||
type: "string",
|
||||
default: "",
|
||||
description: "Value to compare against (if applicable)",
|
||||
},
|
||||
},
|
||||
required: ["condition"],
|
||||
},
|
||||
blockType: "control",
|
||||
color: "#f97316",
|
||||
nestable: true,
|
||||
},
|
||||
{
|
||||
id: "parallel",
|
||||
name: "do together",
|
||||
description: "Execute multiple blocks simultaneously",
|
||||
category: "logic",
|
||||
icon: "Layers",
|
||||
timeout: 0,
|
||||
retryable: false,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {},
|
||||
required: [],
|
||||
},
|
||||
blockType: "control",
|
||||
color: "#f97316",
|
||||
nestable: true,
|
||||
},
|
||||
|
||||
// Data Collection
|
||||
{
|
||||
id: "start_recording",
|
||||
name: "start recording",
|
||||
description: "Begin recording specified data streams",
|
||||
category: "sensors",
|
||||
icon: "Circle",
|
||||
timeout: 5000,
|
||||
retryable: true,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
streams: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"video",
|
||||
"audio",
|
||||
"screen",
|
||||
"robot_data",
|
||||
"wizard_actions",
|
||||
],
|
||||
},
|
||||
default: ["video", "audio"],
|
||||
description: "Data streams to record",
|
||||
},
|
||||
quality: {
|
||||
type: "string",
|
||||
enum: ["low", "medium", "high"],
|
||||
default: "medium",
|
||||
description: "Recording quality",
|
||||
},
|
||||
},
|
||||
required: ["streams"],
|
||||
},
|
||||
blockType: "action",
|
||||
color: "#dc2626",
|
||||
nestable: false,
|
||||
},
|
||||
{
|
||||
id: "stop_recording",
|
||||
name: "stop recording",
|
||||
description: "Stop recording and save data",
|
||||
category: "sensors",
|
||||
icon: "Square",
|
||||
timeout: 10000,
|
||||
retryable: true,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
save_location: {
|
||||
type: "string",
|
||||
default: "default",
|
||||
description: "Where to save the recording",
|
||||
},
|
||||
},
|
||||
required: [],
|
||||
},
|
||||
blockType: "action",
|
||||
color: "#dc2626",
|
||||
nestable: false,
|
||||
},
|
||||
{
|
||||
id: "mark_event",
|
||||
name: "mark event",
|
||||
description: "Add a timestamped marker to the data",
|
||||
category: "sensors",
|
||||
icon: "MapPin",
|
||||
timeout: 1000,
|
||||
retryable: true,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
event_name: {
|
||||
type: "string",
|
||||
default: "",
|
||||
description: "Name of the event to mark",
|
||||
},
|
||||
description: {
|
||||
type: "string",
|
||||
default: "",
|
||||
description: "Optional event description",
|
||||
},
|
||||
},
|
||||
required: ["event_name"],
|
||||
},
|
||||
blockType: "action",
|
||||
color: "#f59e0b",
|
||||
nestable: false,
|
||||
},
|
||||
|
||||
// Study Flow Control
|
||||
{
|
||||
id: "show_instructions",
|
||||
name: "show instructions",
|
||||
description: "Display instructions to the participant",
|
||||
category: "interaction",
|
||||
icon: "FileText",
|
||||
timeout: 60000,
|
||||
retryable: true,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
title: {
|
||||
type: "string",
|
||||
default: "Instructions",
|
||||
description: "Instruction title",
|
||||
},
|
||||
content: {
|
||||
type: "string",
|
||||
default: "",
|
||||
description: "Instruction content (supports markdown)",
|
||||
},
|
||||
require_acknowledgment: {
|
||||
type: "boolean",
|
||||
default: true,
|
||||
description: "Require participant to acknowledge reading",
|
||||
},
|
||||
},
|
||||
required: ["content"],
|
||||
},
|
||||
blockType: "action",
|
||||
color: "#3b82f6",
|
||||
nestable: false,
|
||||
},
|
||||
{
|
||||
id: "collect_response",
|
||||
name: "collect response",
|
||||
description: "Collect a response from the participant",
|
||||
category: "sensors",
|
||||
icon: "MessageCircle",
|
||||
timeout: 120000,
|
||||
retryable: true,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
question: {
|
||||
type: "string",
|
||||
default: "",
|
||||
description: "Question to ask the participant",
|
||||
},
|
||||
response_type: {
|
||||
type: "string",
|
||||
enum: ["text", "scale", "choice", "voice"],
|
||||
default: "text",
|
||||
description: "Type of response to collect",
|
||||
},
|
||||
options: {
|
||||
type: "array",
|
||||
items: { type: "string" },
|
||||
default: [],
|
||||
description: "Options for choice responses",
|
||||
},
|
||||
},
|
||||
required: ["question"],
|
||||
},
|
||||
blockType: "action",
|
||||
color: "#8b5cf6",
|
||||
nestable: false,
|
||||
},
|
||||
],
|
||||
createdAt: new Date("2024-01-01T00:00:00"),
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
await db.insert(schema.plugins).values([corePlugin]);
|
||||
console.log("✅ Created core system plugin");
|
||||
}
|
||||
|
||||
async function seedCoreStudyPlugins() {
|
||||
console.log("🔗 Installing core plugin in all studies...");
|
||||
|
||||
// Get all studies
|
||||
const studies = await db.select().from(schema.studies);
|
||||
|
||||
if (studies.length === 0) {
|
||||
console.log("⚠️ No studies found. Please run basic seeding first.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if core plugin installations already exist
|
||||
const existingInstallation = await db
|
||||
.select()
|
||||
.from(schema.studyPlugins)
|
||||
.where(
|
||||
eq(schema.studyPlugins.pluginId, "00000000-0000-0000-0000-000000000001"),
|
||||
);
|
||||
|
||||
if (existingInstallation.length > 0) {
|
||||
console.log("⚠️ Core plugin already installed in studies, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
const coreInstallations = studies.map((study, index) => ({
|
||||
id: `00000000-0000-0000-0000-00000000000${index + 2}`, // Start from 2 to avoid conflicts
|
||||
studyId: study.id,
|
||||
pluginId: "00000000-0000-0000-0000-000000000001",
|
||||
configuration: {
|
||||
enableAdvancedBlocks: true,
|
||||
wizardInterface: "advanced",
|
||||
recordingDefaults: {
|
||||
video: true,
|
||||
audio: true,
|
||||
quality: "high",
|
||||
},
|
||||
},
|
||||
installedAt: new Date("2024-01-01T00:00:00"),
|
||||
installedBy: study.createdBy,
|
||||
}));
|
||||
|
||||
await db.insert(schema.studyPlugins).values(coreInstallations);
|
||||
console.log(`✅ Installed core plugin in ${studies.length} studies`);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
console.log("🏗️ HRIStudio Core System Seeding Started");
|
||||
console.log("📍 Database:", connectionString.replace(/:[^:]*@/, ":***@"));
|
||||
|
||||
await seedCoreRepository();
|
||||
await seedCorePlugin();
|
||||
await seedCoreStudyPlugins();
|
||||
|
||||
console.log("✅ Core system seeding completed successfully!");
|
||||
console.log("\n📋 Core System Summary:");
|
||||
console.log(" 🏗️ Core Repository: 1 (HRIStudio Core System)");
|
||||
console.log(" 🧱 Core Plugin: 1 (with 15 essential blocks)");
|
||||
console.log(" 🔗 Study Installations: Installed in all studies");
|
||||
console.log("\n🧱 Core Blocks Available:");
|
||||
console.log(" 🎯 Events: when trial starts, when participant speaks");
|
||||
console.log(" 🧙 Wizard: say, gesture, take note");
|
||||
console.log(" ⏳ Control: wait, repeat, if condition, do together");
|
||||
console.log(" 📊 Data: start/stop recording, mark event");
|
||||
console.log(" 📋 Study: show instructions, collect response");
|
||||
console.log("\n🎨 Block Designer Integration:");
|
||||
console.log(" • All core blocks now come from the plugin system");
|
||||
console.log(" • Consistent with robot plugin architecture");
|
||||
console.log(" • Easy to extend and version core functionality");
|
||||
console.log(" • Unified block management across all categories");
|
||||
console.log("\n🚀 Ready to test unified block system!");
|
||||
} catch (error) {
|
||||
console.error("❌ Core system seeding failed:", error);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
await client.end();
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
void main();
|
||||
}
|
||||
1231
scripts/seed-dev.ts
1231
scripts/seed-dev.ts
File diff suppressed because it is too large
Load Diff
@@ -1,690 +0,0 @@
|
||||
import { drizzle } from "drizzle-orm/postgres-js";
|
||||
import postgres from "postgres";
|
||||
import * as schema from "../src/server/db/schema";
|
||||
|
||||
const connectionString =
|
||||
process.env.DATABASE_URL ??
|
||||
"postgresql://postgres:password@localhost:5140/hristudio";
|
||||
const client = postgres(connectionString);
|
||||
const db = drizzle(client, { schema });
|
||||
|
||||
async function seedRobots() {
|
||||
console.log("🤖 Seeding robots...");
|
||||
|
||||
// Check if robots already exist
|
||||
const existingRobots = await db.select().from(schema.robots);
|
||||
if (existingRobots.length > 0) {
|
||||
console.log(
|
||||
`⚠️ ${existingRobots.length} robots already exist, skipping robot seeding`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const robots = [
|
||||
{
|
||||
id: "31234567-89ab-cdef-0123-456789abcde1",
|
||||
name: "TurtleBot3 Burger",
|
||||
manufacturer: "ROBOTIS",
|
||||
model: "TurtleBot3 Burger",
|
||||
description:
|
||||
"A compact, affordable, programmable, ROS2-based mobile robot for education and research",
|
||||
capabilities: [
|
||||
"differential_drive",
|
||||
"lidar",
|
||||
"imu",
|
||||
"odometry",
|
||||
"autonomous_navigation",
|
||||
],
|
||||
communicationProtocol: "ros2" as const,
|
||||
createdAt: new Date("2024-01-01T00:00:00"),
|
||||
updatedAt: new Date("2024-01-01T00:00:00"),
|
||||
},
|
||||
{
|
||||
id: "31234567-89ab-cdef-0123-456789abcde2",
|
||||
name: "NAO Humanoid Robot",
|
||||
manufacturer: "SoftBank Robotics",
|
||||
model: "NAO v6",
|
||||
description:
|
||||
"Autonomous, programmable humanoid robot designed for education, research, and human-robot interaction studies",
|
||||
capabilities: [
|
||||
"bipedal_walking",
|
||||
"speech_synthesis",
|
||||
"speech_recognition",
|
||||
"computer_vision",
|
||||
"gestures",
|
||||
"led_control",
|
||||
"touch_sensors",
|
||||
],
|
||||
communicationProtocol: "custom" as const,
|
||||
createdAt: new Date("2024-01-01T00:00:00"),
|
||||
updatedAt: new Date("2024-01-01T00:00:00"),
|
||||
},
|
||||
{
|
||||
id: "31234567-89ab-cdef-0123-456789abcde3",
|
||||
name: "TurtleBot3 Waffle Pi",
|
||||
manufacturer: "ROBOTIS",
|
||||
model: "TurtleBot3 Waffle Pi",
|
||||
description:
|
||||
"Extended TurtleBot3 platform with additional sensors and computing power for advanced research applications",
|
||||
capabilities: [
|
||||
"differential_drive",
|
||||
"lidar",
|
||||
"imu",
|
||||
"odometry",
|
||||
"camera",
|
||||
"manipulation",
|
||||
"autonomous_navigation",
|
||||
],
|
||||
communicationProtocol: "ros2" as const,
|
||||
createdAt: new Date("2024-01-01T00:00:00"),
|
||||
updatedAt: new Date("2024-01-01T00:00:00"),
|
||||
},
|
||||
];
|
||||
|
||||
await db.insert(schema.robots).values(robots);
|
||||
console.log(`✅ Created ${robots.length} robots`);
|
||||
}
|
||||
|
||||
async function seedPluginRepositories() {
|
||||
console.log("📦 Seeding plugin repositories...");
|
||||
|
||||
// Check if repositories already exist
|
||||
const existingRepos = await db.select().from(schema.pluginRepositories);
|
||||
if (existingRepos.length > 0) {
|
||||
console.log(
|
||||
`⚠️ ${existingRepos.length} plugin repositories already exist, skipping`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the first user to use as creator
|
||||
const users = await db.select().from(schema.users);
|
||||
const adminUser =
|
||||
users.find((u) => u.email?.includes("sean@soconnor.dev")) ?? users[0];
|
||||
|
||||
if (!adminUser) {
|
||||
console.log("⚠️ No users found. Please run basic seeding first.");
|
||||
return;
|
||||
}
|
||||
|
||||
const repositories = [
|
||||
{
|
||||
id: "41234567-89ab-cdef-0123-456789abcde1",
|
||||
name: "HRIStudio Official Robot Plugins",
|
||||
url: "https://repo.hristudio.com",
|
||||
description:
|
||||
"Official collection of robot plugins maintained by the HRIStudio team",
|
||||
trustLevel: "official" as const,
|
||||
isEnabled: true,
|
||||
isOfficial: true,
|
||||
lastSyncAt: new Date("2024-01-10T12:00:00"),
|
||||
syncStatus: "completed" as const,
|
||||
syncError: null,
|
||||
metadata: {
|
||||
apiVersion: "1.0",
|
||||
pluginApiVersion: "1.0",
|
||||
categories: [
|
||||
"mobile-robots",
|
||||
"humanoid-robots",
|
||||
"manipulators",
|
||||
"drones",
|
||||
],
|
||||
compatibility: {
|
||||
hristudio: { min: "0.1.0", recommended: "0.1.0" },
|
||||
ros2: { distributions: ["humble", "iron"], recommended: "iron" },
|
||||
},
|
||||
},
|
||||
createdAt: new Date("2024-01-01T00:00:00"),
|
||||
updatedAt: new Date("2024-01-10T12:00:00"),
|
||||
createdBy: adminUser.id,
|
||||
},
|
||||
];
|
||||
|
||||
await db.insert(schema.pluginRepositories).values(repositories);
|
||||
console.log(`✅ Created ${repositories.length} plugin repositories`);
|
||||
}
|
||||
|
||||
async function seedPlugins() {
|
||||
console.log("🔌 Seeding robot plugins...");
|
||||
|
||||
// Check if plugins already exist
|
||||
const existingPlugins = await db.select().from(schema.plugins);
|
||||
if (existingPlugins.length > 0) {
|
||||
console.log(
|
||||
`⚠️ ${existingPlugins.length} plugins already exist, skipping plugin seeding`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const plugins = [
|
||||
{
|
||||
id: "51234567-89ab-cdef-0123-456789abcde1",
|
||||
robotId: "31234567-89ab-cdef-0123-456789abcde1",
|
||||
name: "TurtleBot3 Burger",
|
||||
version: "2.0.0",
|
||||
description:
|
||||
"A compact, affordable, programmable, ROS2-based mobile robot for education and research",
|
||||
author: "ROBOTIS",
|
||||
repositoryUrl: "https://repo.hristudio.com",
|
||||
trustLevel: "official" as const,
|
||||
status: "active" as const,
|
||||
configurationSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
namespace: { type: "string", default: "turtlebot3" },
|
||||
topics: {
|
||||
type: "object",
|
||||
properties: {
|
||||
cmd_vel: { type: "string", default: "/cmd_vel" },
|
||||
odom: { type: "string", default: "/odom" },
|
||||
scan: { type: "string", default: "/scan" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
actionDefinitions: [
|
||||
{
|
||||
id: "move_velocity",
|
||||
name: "Set Velocity",
|
||||
description: "Control the robot's linear and angular velocity",
|
||||
category: "movement",
|
||||
icon: "navigation",
|
||||
timeout: 30000,
|
||||
retryable: true,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
linear: {
|
||||
type: "number",
|
||||
minimum: -0.22,
|
||||
maximum: 0.22,
|
||||
default: 0,
|
||||
description: "Forward/backward velocity in m/s",
|
||||
},
|
||||
angular: {
|
||||
type: "number",
|
||||
minimum: -2.84,
|
||||
maximum: 2.84,
|
||||
default: 0,
|
||||
description: "Rotational velocity in rad/s",
|
||||
},
|
||||
},
|
||||
required: ["linear", "angular"],
|
||||
},
|
||||
ros2: {
|
||||
messageType: "geometry_msgs/msg/Twist",
|
||||
topic: "/cmd_vel",
|
||||
payloadMapping: {
|
||||
type: "transform",
|
||||
transformFn: "transformToTwist",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "move_to_pose",
|
||||
name: "Navigate to Position",
|
||||
description:
|
||||
"Navigate to a specific position on the map using autonomous navigation",
|
||||
category: "movement",
|
||||
icon: "target",
|
||||
timeout: 120000,
|
||||
retryable: true,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
x: {
|
||||
type: "number",
|
||||
default: 0,
|
||||
description: "X coordinate in meters",
|
||||
},
|
||||
y: {
|
||||
type: "number",
|
||||
default: 0,
|
||||
description: "Y coordinate in meters",
|
||||
},
|
||||
theta: {
|
||||
type: "number",
|
||||
default: 0,
|
||||
description: "Final orientation in radians",
|
||||
},
|
||||
},
|
||||
required: ["x", "y", "theta"],
|
||||
},
|
||||
ros2: {
|
||||
messageType: "geometry_msgs/msg/PoseStamped",
|
||||
action: "/navigate_to_pose",
|
||||
payloadMapping: {
|
||||
type: "transform",
|
||||
transformFn: "transformToPoseStamped",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "stop_robot",
|
||||
name: "Stop Robot",
|
||||
description: "Immediately stop all robot movement",
|
||||
category: "movement",
|
||||
icon: "square",
|
||||
timeout: 5000,
|
||||
retryable: false,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {},
|
||||
required: [],
|
||||
},
|
||||
ros2: {
|
||||
messageType: "geometry_msgs/msg/Twist",
|
||||
topic: "/cmd_vel",
|
||||
payloadMapping: {
|
||||
type: "static",
|
||||
payload: {
|
||||
linear: { x: 0.0, y: 0.0, z: 0.0 },
|
||||
angular: { x: 0.0, y: 0.0, z: 0.0 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
createdAt: new Date("2024-01-01T00:00:00"),
|
||||
updatedAt: new Date("2024-01-10T12:00:00"),
|
||||
},
|
||||
{
|
||||
id: "51234567-89ab-cdef-0123-456789abcde2",
|
||||
robotId: "31234567-89ab-cdef-0123-456789abcde2",
|
||||
name: "NAO Humanoid Robot",
|
||||
version: "1.0.0",
|
||||
description:
|
||||
"Autonomous, programmable humanoid robot designed for education, research, and human-robot interaction studies",
|
||||
author: "SoftBank Robotics",
|
||||
repositoryUrl: "https://repo.hristudio.com",
|
||||
trustLevel: "verified" as const,
|
||||
status: "active" as const,
|
||||
configurationSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
ip: { type: "string", default: "nao.local" },
|
||||
port: { type: "number", default: 9559 },
|
||||
modules: {
|
||||
type: "array",
|
||||
default: [
|
||||
"ALMotion",
|
||||
"ALTextToSpeech",
|
||||
"ALAnimationPlayer",
|
||||
"ALLeds",
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
actionDefinitions: [
|
||||
{
|
||||
id: "say_text",
|
||||
name: "Say Text",
|
||||
description: "Make the robot speak using text-to-speech",
|
||||
category: "interaction",
|
||||
icon: "volume-2",
|
||||
timeout: 15000,
|
||||
retryable: true,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
text: {
|
||||
type: "string",
|
||||
default: "Hello, I am NAO!",
|
||||
description: "Text to speak",
|
||||
},
|
||||
volume: {
|
||||
type: "number",
|
||||
minimum: 0.1,
|
||||
maximum: 1.0,
|
||||
default: 0.7,
|
||||
description: "Speech volume (0.1 to 1.0)",
|
||||
},
|
||||
},
|
||||
required: ["text"],
|
||||
},
|
||||
naoqi: {
|
||||
module: "ALTextToSpeech",
|
||||
method: "say",
|
||||
parameters: ["text"],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "play_animation",
|
||||
name: "Play Animation",
|
||||
description: "Play a predefined animation or gesture",
|
||||
category: "interaction",
|
||||
icon: "zap",
|
||||
timeout: 20000,
|
||||
retryable: true,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
animation: {
|
||||
type: "string",
|
||||
enum: ["Hello", "Goodbye", "Excited", "Thinking"],
|
||||
default: "Hello",
|
||||
description: "Animation to play",
|
||||
},
|
||||
},
|
||||
required: ["animation"],
|
||||
},
|
||||
naoqi: {
|
||||
module: "ALAnimationPlayer",
|
||||
method: "run",
|
||||
parameters: ["animations/Stand/Gestures/{animation}"],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "walk_to_position",
|
||||
name: "Walk to Position",
|
||||
description:
|
||||
"Walk to a specific position relative to current location",
|
||||
category: "movement",
|
||||
icon: "footprints",
|
||||
timeout: 30000,
|
||||
retryable: true,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
x: {
|
||||
type: "number",
|
||||
minimum: -2.0,
|
||||
maximum: 2.0,
|
||||
default: 0.5,
|
||||
description: "Forward distance in meters",
|
||||
},
|
||||
y: {
|
||||
type: "number",
|
||||
minimum: -1.0,
|
||||
maximum: 1.0,
|
||||
default: 0.0,
|
||||
description: "Sideways distance in meters (left is positive)",
|
||||
},
|
||||
theta: {
|
||||
type: "number",
|
||||
minimum: -3.14159,
|
||||
maximum: 3.14159,
|
||||
default: 0.0,
|
||||
description: "Turn angle in radians",
|
||||
},
|
||||
},
|
||||
required: ["x", "y", "theta"],
|
||||
},
|
||||
naoqi: {
|
||||
module: "ALMotion",
|
||||
method: "walkTo",
|
||||
parameters: ["x", "y", "theta"],
|
||||
},
|
||||
},
|
||||
],
|
||||
createdAt: new Date("2024-01-01T00:00:00"),
|
||||
updatedAt: new Date("2024-01-10T12:00:00"),
|
||||
},
|
||||
{
|
||||
id: "51234567-89ab-cdef-0123-456789abcde3",
|
||||
robotId: "31234567-89ab-cdef-0123-456789abcde3",
|
||||
name: "TurtleBot3 Waffle Pi",
|
||||
version: "2.0.0",
|
||||
description:
|
||||
"Extended TurtleBot3 platform with additional sensors and computing power for advanced research applications",
|
||||
author: "ROBOTIS",
|
||||
repositoryUrl: "https://repo.hristudio.com",
|
||||
trustLevel: "official" as const,
|
||||
status: "active" as const,
|
||||
configurationSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
namespace: { type: "string", default: "turtlebot3" },
|
||||
topics: {
|
||||
type: "object",
|
||||
properties: {
|
||||
cmd_vel: { type: "string", default: "/cmd_vel" },
|
||||
odom: { type: "string", default: "/odom" },
|
||||
scan: { type: "string", default: "/scan" },
|
||||
camera: { type: "string", default: "/camera/image_raw" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
actionDefinitions: [
|
||||
{
|
||||
id: "move_velocity",
|
||||
name: "Set Velocity",
|
||||
description: "Control the robot's linear and angular velocity",
|
||||
category: "movement",
|
||||
icon: "navigation",
|
||||
timeout: 30000,
|
||||
retryable: true,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
linear: {
|
||||
type: "number",
|
||||
minimum: -0.26,
|
||||
maximum: 0.26,
|
||||
default: 0,
|
||||
description: "Forward/backward velocity in m/s",
|
||||
},
|
||||
angular: {
|
||||
type: "number",
|
||||
minimum: -1.82,
|
||||
maximum: 1.82,
|
||||
default: 0,
|
||||
description: "Rotational velocity in rad/s",
|
||||
},
|
||||
},
|
||||
required: ["linear", "angular"],
|
||||
},
|
||||
ros2: {
|
||||
messageType: "geometry_msgs/msg/Twist",
|
||||
topic: "/cmd_vel",
|
||||
payloadMapping: {
|
||||
type: "transform",
|
||||
transformFn: "transformToTwist",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "capture_image",
|
||||
name: "Capture Image",
|
||||
description: "Capture an image from the robot's camera",
|
||||
category: "sensors",
|
||||
icon: "camera",
|
||||
timeout: 10000,
|
||||
retryable: true,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
filename: {
|
||||
type: "string",
|
||||
default: "image_{timestamp}.jpg",
|
||||
description: "Filename for the captured image",
|
||||
},
|
||||
quality: {
|
||||
type: "integer",
|
||||
minimum: 1,
|
||||
maximum: 100,
|
||||
default: 85,
|
||||
description: "JPEG quality (1-100)",
|
||||
},
|
||||
},
|
||||
required: ["filename"],
|
||||
},
|
||||
ros2: {
|
||||
messageType: "sensor_msgs/msg/Image",
|
||||
topic: "/camera/image_raw",
|
||||
payloadMapping: {
|
||||
type: "transform",
|
||||
transformFn: "captureAndSaveImage",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "scan_environment",
|
||||
name: "Scan Environment",
|
||||
description:
|
||||
"Perform a 360-degree scan of the environment using LIDAR",
|
||||
category: "sensors",
|
||||
icon: "radar",
|
||||
timeout: 15000,
|
||||
retryable: true,
|
||||
parameterSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
duration: {
|
||||
type: "number",
|
||||
minimum: 1.0,
|
||||
maximum: 10.0,
|
||||
default: 3.0,
|
||||
description: "Scan duration in seconds",
|
||||
},
|
||||
save_data: {
|
||||
type: "boolean",
|
||||
default: true,
|
||||
description: "Save scan data to file",
|
||||
},
|
||||
},
|
||||
required: ["duration"],
|
||||
},
|
||||
ros2: {
|
||||
messageType: "sensor_msgs/msg/LaserScan",
|
||||
topic: "/scan",
|
||||
payloadMapping: {
|
||||
type: "transform",
|
||||
transformFn: "collectLaserScan",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
createdAt: new Date("2024-01-01T00:00:00"),
|
||||
updatedAt: new Date("2024-01-10T12:00:00"),
|
||||
},
|
||||
];
|
||||
|
||||
await db.insert(schema.plugins).values(plugins);
|
||||
console.log(`✅ Created ${plugins.length} robot plugins`);
|
||||
}
|
||||
|
||||
async function seedStudyPlugins() {
|
||||
console.log("🔌 Seeding study plugin installations...");
|
||||
|
||||
// Check if study plugins already exist
|
||||
const existingStudyPlugins = await db.select().from(schema.studyPlugins);
|
||||
if (existingStudyPlugins.length > 0) {
|
||||
console.log(
|
||||
`⚠️ ${existingStudyPlugins.length} study plugin installations already exist, skipping`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get study IDs from the existing studies
|
||||
const studies = await db.select().from(schema.studies);
|
||||
|
||||
if (studies.length === 0) {
|
||||
console.log("⚠️ No studies found. Please run basic seeding first.");
|
||||
return;
|
||||
}
|
||||
|
||||
const studyPlugins = [
|
||||
{
|
||||
id: "61234567-89ab-cdef-0123-456789abcde1",
|
||||
studyId: studies[0]!.id, // First study (navigation)
|
||||
pluginId: "51234567-89ab-cdef-0123-456789abcde1", // TurtleBot3 Burger
|
||||
configuration: {
|
||||
namespace: "navigation_study",
|
||||
topics: {
|
||||
cmd_vel: "/navigation_study/cmd_vel",
|
||||
odom: "/navigation_study/odom",
|
||||
scan: "/navigation_study/scan",
|
||||
},
|
||||
max_speed: 0.15,
|
||||
safety_distance: 0.3,
|
||||
},
|
||||
installedAt: new Date("2024-01-05T10:00:00"),
|
||||
installedBy: studies[0]!.createdBy,
|
||||
},
|
||||
{
|
||||
id: "61234567-89ab-cdef-0123-456789abcde2",
|
||||
studyId: studies[1]?.id ?? studies[0]!.id, // Second study (social robots) or fallback
|
||||
pluginId: "51234567-89ab-cdef-0123-456789abcde2", // NAO Humanoid
|
||||
configuration: {
|
||||
ip: "192.168.1.100",
|
||||
port: 9559,
|
||||
modules: [
|
||||
"ALMotion",
|
||||
"ALTextToSpeech",
|
||||
"ALAnimationPlayer",
|
||||
"ALLeds",
|
||||
"ALSpeechRecognition",
|
||||
],
|
||||
language: "English",
|
||||
speech_speed: 100,
|
||||
volume: 0.8,
|
||||
},
|
||||
installedAt: new Date("2024-01-05T11:00:00"),
|
||||
installedBy: studies[1]?.createdBy ?? studies[0]!.createdBy,
|
||||
},
|
||||
{
|
||||
id: "61234567-89ab-cdef-0123-456789abcde3",
|
||||
studyId: studies[0]!.id, // First study also gets Waffle for advanced tasks
|
||||
pluginId: "51234567-89ab-cdef-0123-456789abcde3", // TurtleBot3 Waffle
|
||||
configuration: {
|
||||
namespace: "advanced_navigation",
|
||||
topics: {
|
||||
cmd_vel: "/advanced_navigation/cmd_vel",
|
||||
odom: "/advanced_navigation/odom",
|
||||
scan: "/advanced_navigation/scan",
|
||||
camera: "/advanced_navigation/camera/image_raw",
|
||||
},
|
||||
max_speed: 0.2,
|
||||
camera_enabled: true,
|
||||
lidar_enabled: true,
|
||||
},
|
||||
installedAt: new Date("2024-01-05T12:00:00"),
|
||||
installedBy: studies[0]!.createdBy,
|
||||
},
|
||||
];
|
||||
|
||||
await db.insert(schema.studyPlugins).values(studyPlugins);
|
||||
console.log(`✅ Created ${studyPlugins.length} study plugin installations`);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
console.log("🔌 HRIStudio Plugin System Seeding Started");
|
||||
console.log("📍 Database:", connectionString.replace(/:[^:]*@/, ":***@"));
|
||||
|
||||
await seedRobots();
|
||||
await seedPluginRepositories();
|
||||
await seedPlugins();
|
||||
await seedStudyPlugins();
|
||||
|
||||
console.log("✅ Plugin system seeding completed successfully!");
|
||||
console.log("\n📋 Plugin System Summary:");
|
||||
console.log(" 🤖 Robots: 3 (TurtleBot3 Burger, NAO, TurtleBot3 Waffle)");
|
||||
console.log(" 📦 Plugin Repositories: 1 (official HRIStudio repo)");
|
||||
console.log(" 🔌 Robot Plugins: 3 (with complete action definitions)");
|
||||
console.log(" 📱 Study Plugin Installations: 3 (active configurations)");
|
||||
console.log("\n🎯 Plugin Actions Available:");
|
||||
console.log(
|
||||
" 📍 TurtleBot3 Burger: 3 actions (movement, navigation, stop)",
|
||||
);
|
||||
console.log(" 🤖 NAO Humanoid: 3 actions (speech, animations, walking)");
|
||||
console.log(" 📊 TurtleBot3 Waffle: 3 actions (movement, camera, LIDAR)");
|
||||
console.log("\n🧪 Test Plugin Integration:");
|
||||
console.log(" 1. Navigate to any experiment designer");
|
||||
console.log(" 2. Check 'Robot' category in block library");
|
||||
console.log(" 3. Plugin actions should appear alongside core blocks");
|
||||
console.log(" 4. Actions are configured per study installation");
|
||||
console.log("\n🚀 Ready to test robot plugin integration!");
|
||||
} catch (error) {
|
||||
console.error("❌ Plugin seeding failed:", error);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
await client.end();
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
void main();
|
||||
}
|
||||
@@ -1,729 +0,0 @@
|
||||
#!/usr/bin/env tsx
|
||||
|
||||
/**
|
||||
* HRIStudio Database Seed Script (Simplified)
|
||||
*
|
||||
* This script seeds the database with comprehensive test data for the experiment designer,
|
||||
* using raw SQL to avoid NextAuth import issues.
|
||||
*/
|
||||
|
||||
import bcrypt from "bcryptjs";
|
||||
import postgres from "postgres";
|
||||
|
||||
// Database connection
|
||||
const connectionString =
|
||||
process.env.DATABASE_URL ??
|
||||
"postgresql://postgres:postgres@localhost:5140/hristudio";
|
||||
const sql = postgres(connectionString);
|
||||
|
||||
console.log("🌱 Starting HRIStudio database seeding...");
|
||||
|
||||
async function clearDatabase() {
|
||||
console.log("🧹 Clearing existing data...");
|
||||
|
||||
// Delete in reverse dependency order
|
||||
await sql`DELETE FROM hs_trial_event`;
|
||||
await sql`DELETE FROM hs_action`;
|
||||
await sql`DELETE FROM hs_step`;
|
||||
await sql`DELETE FROM hs_trial`;
|
||||
await sql`DELETE FROM hs_participant`;
|
||||
await sql`DELETE FROM hs_experiment`;
|
||||
await sql`DELETE FROM hs_study_member`;
|
||||
await sql`DELETE FROM hs_study`;
|
||||
await sql`DELETE FROM hs_user_system_role`;
|
||||
await sql`DELETE FROM hs_user`;
|
||||
|
||||
console.log("✅ Database cleared");
|
||||
}
|
||||
|
||||
async function seedUsers() {
|
||||
console.log("👥 Seeding users...");
|
||||
|
||||
// Hash password "password123" for all test users
|
||||
const hashedPassword = await bcrypt.hash("password123", 12);
|
||||
|
||||
const users = [
|
||||
{
|
||||
id: "550e8400-e29b-41d4-a716-446655440001",
|
||||
name: "Dr. Sarah Chen",
|
||||
email: "sarah.chen@university.edu",
|
||||
emailVerified: new Date(),
|
||||
password: hashedPassword,
|
||||
},
|
||||
{
|
||||
id: "550e8400-e29b-41d4-a716-446655440002",
|
||||
name: "Dr. Michael Rodriguez",
|
||||
email: "m.rodriguez@research.org",
|
||||
emailVerified: new Date(),
|
||||
password: hashedPassword,
|
||||
},
|
||||
{
|
||||
id: "550e8400-e29b-41d4-a716-446655440003",
|
||||
name: "Emma Thompson",
|
||||
email: "emma.thompson@university.edu",
|
||||
emailVerified: new Date(),
|
||||
password: hashedPassword,
|
||||
},
|
||||
{
|
||||
id: "550e8400-e29b-41d4-a716-446655440004",
|
||||
name: "Dr. James Wilson",
|
||||
email: "james.wilson@university.edu",
|
||||
emailVerified: new Date(),
|
||||
password: hashedPassword,
|
||||
},
|
||||
];
|
||||
|
||||
for (const user of users) {
|
||||
await sql`
|
||||
INSERT INTO hs_user (id, name, email, email_verified, password, created_at, updated_at)
|
||||
VALUES (${user.id}, ${user.name}, ${user.email}, ${user.emailVerified}, ${user.password}, NOW(), NOW())
|
||||
`;
|
||||
}
|
||||
|
||||
// Add user roles
|
||||
const userRoles = [
|
||||
{
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
role: "administrator",
|
||||
},
|
||||
{
|
||||
userId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
role: "researcher",
|
||||
},
|
||||
{
|
||||
userId: "550e8400-e29b-41d4-a716-446655440003",
|
||||
role: "wizard",
|
||||
},
|
||||
{
|
||||
userId: "550e8400-e29b-41d4-a716-446655440004",
|
||||
role: "observer",
|
||||
},
|
||||
];
|
||||
|
||||
for (const userRole of userRoles) {
|
||||
await sql`
|
||||
INSERT INTO hs_user_system_role (user_id, role, granted_at)
|
||||
VALUES (${userRole.userId}, ${userRole.role}, NOW())
|
||||
`;
|
||||
}
|
||||
|
||||
console.log(`✅ Created ${users.length} users with roles`);
|
||||
}
|
||||
|
||||
async function seedStudies() {
|
||||
console.log("📚 Seeding studies...");
|
||||
|
||||
const studies = [
|
||||
{
|
||||
id: "650e8400-e29b-41d4-a716-446655440001",
|
||||
name: "Robot Navigation Assistance Study",
|
||||
description:
|
||||
"Investigating how robots can effectively assist humans with indoor navigation tasks using multimodal interaction.",
|
||||
institution: "MIT Computer Science",
|
||||
irbProtocolNumber: "IRB-2024-001",
|
||||
status: "active",
|
||||
createdBy: "550e8400-e29b-41d4-a716-446655440002",
|
||||
metadata: {
|
||||
duration: "6 months",
|
||||
targetParticipants: 50,
|
||||
robotPlatform: "TurtleBot3",
|
||||
environment: "Indoor office building",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "650e8400-e29b-41d4-a716-446655440002",
|
||||
name: "Social Robot Interaction Patterns",
|
||||
description:
|
||||
"Exploring how different personality traits in robots affect human-robot collaboration in workplace settings.",
|
||||
institution: "Stanford HCI Lab",
|
||||
irbProtocolNumber: "IRB-2024-002",
|
||||
status: "draft",
|
||||
createdBy: "550e8400-e29b-41d4-a716-446655440002",
|
||||
metadata: {
|
||||
duration: "4 months",
|
||||
targetParticipants: 30,
|
||||
robotPlatform: "Pepper",
|
||||
environment: "Office collaboration space",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "650e8400-e29b-41d4-a716-446655440003",
|
||||
name: "Elderly Care Assistant Robot Study",
|
||||
description:
|
||||
"Evaluating the effectiveness of companion robots in assisted living facilities for elderly residents.",
|
||||
institution: "MIT Computer Science",
|
||||
irbProtocolNumber: "IRB-2024-003",
|
||||
status: "completed",
|
||||
createdBy: "550e8400-e29b-41d4-a716-446655440001",
|
||||
metadata: {
|
||||
duration: "8 months",
|
||||
targetParticipants: 25,
|
||||
robotPlatform: "NAO",
|
||||
environment: "Assisted living facility",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const study of studies) {
|
||||
await sql`
|
||||
INSERT INTO hs_study (id, name, description, institution, irb_protocol, status, created_by, metadata, created_at, updated_at)
|
||||
VALUES (${study.id}, ${study.name}, ${study.description}, ${study.institution}, ${study.irbProtocolNumber}, ${study.status}, ${study.createdBy}, ${JSON.stringify(study.metadata)}, NOW(), NOW())
|
||||
`;
|
||||
}
|
||||
|
||||
// Add study members
|
||||
const studyMembers = [
|
||||
// Navigation Study Team
|
||||
{
|
||||
studyId: "650e8400-e29b-41d4-a716-446655440001",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
role: "owner",
|
||||
},
|
||||
{
|
||||
studyId: "650e8400-e29b-41d4-a716-446655440001",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440003",
|
||||
role: "wizard",
|
||||
},
|
||||
{
|
||||
studyId: "650e8400-e29b-41d4-a716-446655440001",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440004",
|
||||
role: "observer",
|
||||
},
|
||||
// Social Robots Study Team
|
||||
{
|
||||
studyId: "650e8400-e29b-41d4-a716-446655440002",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440002",
|
||||
role: "owner",
|
||||
},
|
||||
{
|
||||
studyId: "650e8400-e29b-41d4-a716-446655440002",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
role: "researcher",
|
||||
},
|
||||
// Elderly Care Study Team
|
||||
{
|
||||
studyId: "650e8400-e29b-41d4-a716-446655440003",
|
||||
userId: "550e8400-e29b-41d4-a716-446655440001",
|
||||
role: "owner",
|
||||
},
|
||||
];
|
||||
|
||||
for (const member of studyMembers) {
|
||||
await sql`
|
||||
INSERT INTO hs_study_member (study_id, user_id, role, joined_at)
|
||||
VALUES (${member.studyId}, ${member.userId}, ${member.role}, NOW())
|
||||
`;
|
||||
}
|
||||
|
||||
console.log(`✅ Created ${studies.length} studies with team members`);
|
||||
}
|
||||
|
||||
async function seedExperiments() {
|
||||
console.log("🧪 Seeding experiments...");
|
||||
|
||||
const experiments = [
|
||||
{
|
||||
id: "750e8400-e29b-41d4-a716-446655440001",
|
||||
studyId: "650e8400-e29b-41d4-a716-446655440001",
|
||||
name: "Baseline Navigation Task",
|
||||
description:
|
||||
"Participants navigate independently without robot assistance to establish baseline performance metrics.",
|
||||
version: 1,
|
||||
status: "ready",
|
||||
estimatedDuration: 15,
|
||||
createdBy: "550e8400-e29b-41d4-a716-446655440002",
|
||||
metadata: {
|
||||
condition: "control",
|
||||
environment: "Building A, Floor 2",
|
||||
equipment: ["motion capture", "eye tracker"],
|
||||
instructions: "Find the conference room using only building signs",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "750e8400-e29b-41d4-a716-446655440002",
|
||||
studyId: "650e8400-e29b-41d4-a716-446655440001",
|
||||
name: "Robot-Assisted Navigation",
|
||||
description:
|
||||
"Participants navigate with robot providing verbal and gestural guidance to test effectiveness of robot assistance.",
|
||||
version: 2,
|
||||
status: "testing",
|
||||
estimatedDuration: 20,
|
||||
createdBy: "550e8400-e29b-41d4-a716-446655440002",
|
||||
metadata: {
|
||||
condition: "robot_assistance",
|
||||
environment: "Building A, Floor 2",
|
||||
equipment: ["motion capture", "eye tracker", "TurtleBot3"],
|
||||
instructions: "Follow robot guidance to find the conference room",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "750e8400-e29b-41d4-a716-446655440003",
|
||||
studyId: "650e8400-e29b-41d4-a716-446655440002",
|
||||
name: "Robot Personality Variants",
|
||||
description:
|
||||
"Testing different robot personality types (friendly, professional, neutral) in collaborative tasks.",
|
||||
version: 1,
|
||||
status: "draft",
|
||||
estimatedDuration: 30,
|
||||
createdBy: "550e8400-e29b-41d4-a716-446655440002",
|
||||
metadata: {
|
||||
condition: "personality_comparison",
|
||||
personalities: ["friendly", "professional", "neutral"],
|
||||
tasks: ["document review", "scheduling", "problem solving"],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "750e8400-e29b-41d4-a716-446655440004",
|
||||
studyId: "650e8400-e29b-41d4-a716-446655440003",
|
||||
name: "Daily Companion Interaction",
|
||||
description:
|
||||
"Evaluating robot as daily companion for elderly residents including conversation and activity reminders.",
|
||||
version: 3,
|
||||
status: "ready",
|
||||
estimatedDuration: 45,
|
||||
createdBy: "550e8400-e29b-41d4-a716-446655440001",
|
||||
metadata: {
|
||||
condition: "companion_interaction",
|
||||
activities: ["conversation", "medication reminder", "exercise prompts"],
|
||||
duration_days: 14,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const experiment of experiments) {
|
||||
await sql`
|
||||
INSERT INTO hs_experiment (id, study_id, name, description, version, status, estimated_duration, created_by, metadata, created_at, updated_at)
|
||||
VALUES (${experiment.id}, ${experiment.studyId}, ${experiment.name}, ${experiment.description}, ${experiment.version}, ${experiment.status}, ${experiment.estimatedDuration}, ${experiment.createdBy}, ${JSON.stringify(experiment.metadata)}, NOW(), NOW())
|
||||
`;
|
||||
}
|
||||
|
||||
console.log(`✅ Created ${experiments.length} experiments`);
|
||||
}
|
||||
|
||||
async function seedStepsAndActions() {
|
||||
console.log("📋 Seeding experiment steps and actions...");
|
||||
|
||||
// Baseline Navigation Experiment Steps
|
||||
const steps = [
|
||||
{
|
||||
id: "850e8400-e29b-41d4-a716-446655440001",
|
||||
experimentId: "750e8400-e29b-41d4-a716-446655440001",
|
||||
name: "Welcome & Consent",
|
||||
description:
|
||||
"Greet participant, explain study, and obtain informed consent",
|
||||
type: "wizard",
|
||||
orderIndex: 0,
|
||||
durationEstimate: 300,
|
||||
required: true,
|
||||
conditions: {
|
||||
environment: "lab_room",
|
||||
setup: "consent_forms_ready",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "850e8400-e29b-41d4-a716-446655440002",
|
||||
experimentId: "750e8400-e29b-41d4-a716-446655440001",
|
||||
name: "Equipment Setup",
|
||||
description: "Attach motion capture markers and calibrate eye tracker",
|
||||
type: "wizard",
|
||||
orderIndex: 1,
|
||||
durationEstimate: 180,
|
||||
required: true,
|
||||
conditions: {
|
||||
equipment: ["motion_capture", "eye_tracker"],
|
||||
calibration_required: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "850e8400-e29b-41d4-a716-446655440003",
|
||||
experimentId: "750e8400-e29b-41d4-a716-446655440001",
|
||||
name: "Task Instructions",
|
||||
description: "Explain navigation task and destination to participant",
|
||||
type: "wizard",
|
||||
orderIndex: 2,
|
||||
durationEstimate: 120,
|
||||
required: true,
|
||||
conditions: {
|
||||
destination: "Conference Room B-201",
|
||||
starting_point: "Building A Lobby",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "850e8400-e29b-41d4-a716-446655440004",
|
||||
experimentId: "750e8400-e29b-41d4-a716-446655440001",
|
||||
name: "Independent Navigation",
|
||||
description:
|
||||
"Participant navigates independently while data is collected",
|
||||
type: "parallel",
|
||||
orderIndex: 3,
|
||||
durationEstimate: 600,
|
||||
required: true,
|
||||
conditions: {
|
||||
data_collection: ["position", "gaze", "time"],
|
||||
assistance: "none",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "850e8400-e29b-41d4-a716-446655440005",
|
||||
experimentId: "750e8400-e29b-41d4-a716-446655440002",
|
||||
name: "Robot Introduction",
|
||||
description:
|
||||
"Robot introduces itself and explains its role as navigation assistant",
|
||||
type: "robot",
|
||||
orderIndex: 0,
|
||||
durationEstimate: 180,
|
||||
required: true,
|
||||
conditions: {
|
||||
robot_behavior: "friendly_introduction",
|
||||
voice_enabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "850e8400-e29b-41d4-a716-446655440006",
|
||||
experimentId: "750e8400-e29b-41d4-a716-446655440002",
|
||||
name: "Guided Navigation",
|
||||
description:
|
||||
"Robot provides turn-by-turn navigation guidance with gestures and speech",
|
||||
type: "robot",
|
||||
orderIndex: 1,
|
||||
durationEstimate: 480,
|
||||
required: true,
|
||||
conditions: {
|
||||
guidance_type: "multimodal",
|
||||
gestures: true,
|
||||
speech: true,
|
||||
adaptation: "user_pace",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "850e8400-e29b-41d4-a716-446655440007",
|
||||
experimentId: "750e8400-e29b-41d4-a716-446655440003",
|
||||
name: "Personality Calibration",
|
||||
description:
|
||||
"Robot adjusts behavior based on assigned personality condition",
|
||||
type: "conditional",
|
||||
orderIndex: 0,
|
||||
durationEstimate: 60,
|
||||
required: true,
|
||||
conditions: {
|
||||
personality_variants: ["friendly", "professional", "neutral"],
|
||||
behavior_parameters: {
|
||||
friendly: { warmth: 0.8, formality: 0.3 },
|
||||
professional: { warmth: 0.4, formality: 0.9 },
|
||||
neutral: { warmth: 0.5, formality: 0.5 },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "850e8400-e29b-41d4-a716-446655440008",
|
||||
experimentId: "750e8400-e29b-41d4-a716-446655440003",
|
||||
name: "Collaborative Task",
|
||||
description: "Human and robot work together on document review task",
|
||||
type: "parallel",
|
||||
orderIndex: 1,
|
||||
durationEstimate: 1200,
|
||||
required: true,
|
||||
conditions: {
|
||||
task_type: "document_review",
|
||||
collaboration_level: "equal_partners",
|
||||
performance_metrics: ["accuracy", "efficiency", "satisfaction"],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const step of steps) {
|
||||
await sql`
|
||||
INSERT INTO hs_step (id, experiment_id, name, description, type, order_index, duration_estimate, required, conditions, created_at, updated_at)
|
||||
VALUES (${step.id}, ${step.experimentId}, ${step.name}, ${step.description}, ${step.type}, ${step.orderIndex}, ${step.durationEstimate}, ${step.required}, ${JSON.stringify(step.conditions)}, NOW(), NOW())
|
||||
`;
|
||||
}
|
||||
|
||||
console.log("✅ Created experiment steps");
|
||||
|
||||
// Create actions for each step
|
||||
const actions = [
|
||||
{
|
||||
id: "950e8400-e29b-41d4-a716-446655440001",
|
||||
stepId: "850e8400-e29b-41d4-a716-446655440001",
|
||||
name: "Greet Participant",
|
||||
description: "Welcome participant and introduce research team",
|
||||
type: "wizard_speech",
|
||||
orderIndex: 0,
|
||||
parameters: {
|
||||
script:
|
||||
"Hello! Welcome to our navigation study. I'm [NAME] and I'll be guiding you through today's session.",
|
||||
tone: "friendly_professional",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "950e8400-e29b-41d4-a716-446655440002",
|
||||
stepId: "850e8400-e29b-41d4-a716-446655440001",
|
||||
name: "Explain Study",
|
||||
description: "Provide overview of study purpose and procedures",
|
||||
type: "wizard_speech",
|
||||
orderIndex: 1,
|
||||
parameters: {
|
||||
script:
|
||||
"Today we're studying how people navigate indoor environments. You'll be asked to find a specific location in the building.",
|
||||
documentation_required: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "950e8400-e29b-41d4-a716-446655440003",
|
||||
stepId: "850e8400-e29b-41d4-a716-446655440005",
|
||||
name: "Robot Self-Introduction",
|
||||
description: "Robot introduces itself with friendly demeanor",
|
||||
type: "robot_speech",
|
||||
orderIndex: 0,
|
||||
parameters: {
|
||||
text: "Hello! I'm your navigation assistant. My name is Robi and I'm here to help you find your destination.",
|
||||
gesture: "wave",
|
||||
eye_contact: true,
|
||||
voice_parameters: {
|
||||
pitch: 0.7,
|
||||
speed: 0.8,
|
||||
emotion: "friendly",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "950e8400-e29b-41d4-a716-446655440004",
|
||||
stepId: "850e8400-e29b-41d4-a716-446655440006",
|
||||
name: "Start Navigation",
|
||||
description: "Robot begins guiding participant toward destination",
|
||||
type: "robot_movement",
|
||||
orderIndex: 0,
|
||||
parameters: {
|
||||
movement_type: "lead",
|
||||
speed: "slow_human_pace",
|
||||
path_planning: "optimal_with_explanations",
|
||||
safety_distance: 1.5,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "950e8400-e29b-41d4-a716-446655440005",
|
||||
stepId: "850e8400-e29b-41d4-a716-446655440007",
|
||||
name: "Load Personality Profile",
|
||||
description: "Configure robot behavior based on personality condition",
|
||||
type: "robot_config",
|
||||
orderIndex: 0,
|
||||
parameters: {
|
||||
config_type: "personality_parameters",
|
||||
profiles: {
|
||||
friendly: {
|
||||
greeting_style: "warm",
|
||||
speech_patterns: "casual",
|
||||
gesture_frequency: "high",
|
||||
},
|
||||
professional: {
|
||||
greeting_style: "formal",
|
||||
speech_patterns: "business",
|
||||
gesture_frequency: "moderate",
|
||||
},
|
||||
neutral: {
|
||||
greeting_style: "standard",
|
||||
speech_patterns: "neutral",
|
||||
gesture_frequency: "low",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const action of actions) {
|
||||
await sql`
|
||||
INSERT INTO hs_action (id, step_id, name, description, type, order_index, parameters, created_at, updated_at)
|
||||
VALUES (${action.id}, ${action.stepId}, ${action.name}, ${action.description}, ${action.type}, ${action.orderIndex}, ${JSON.stringify(action.parameters)}, NOW(), NOW())
|
||||
`;
|
||||
}
|
||||
|
||||
console.log(`✅ Created ${actions.length} actions for steps`);
|
||||
}
|
||||
|
||||
async function seedParticipants() {
|
||||
console.log("👤 Seeding participants...");
|
||||
|
||||
const participants = [
|
||||
{
|
||||
id: "a50e8400-e29b-41d4-a716-446655440001",
|
||||
studyId: "650e8400-e29b-41d4-a716-446655440001",
|
||||
participantCode: "NAV001",
|
||||
name: "Alex Johnson",
|
||||
email: "alex.johnson@email.com",
|
||||
demographics: {
|
||||
age: 28,
|
||||
gender: "non-binary",
|
||||
education: "bachelor",
|
||||
tech_experience: "high",
|
||||
robot_experience: "medium",
|
||||
mobility: "none",
|
||||
},
|
||||
consentGiven: true,
|
||||
consentDate: new Date("2024-01-15"),
|
||||
notes: "Interested in robotics, works in tech industry",
|
||||
},
|
||||
{
|
||||
id: "a50e8400-e29b-41d4-a716-446655440002",
|
||||
studyId: "650e8400-e29b-41d4-a716-446655440001",
|
||||
participantCode: "NAV002",
|
||||
name: "Maria Santos",
|
||||
email: "maria.santos@email.com",
|
||||
demographics: {
|
||||
age: 34,
|
||||
gender: "female",
|
||||
education: "master",
|
||||
tech_experience: "medium",
|
||||
robot_experience: "low",
|
||||
mobility: "none",
|
||||
},
|
||||
consentGiven: true,
|
||||
consentDate: new Date("2024-01-16"),
|
||||
notes: "Architecture background, good spatial reasoning",
|
||||
},
|
||||
{
|
||||
id: "a50e8400-e29b-41d4-a716-446655440003",
|
||||
studyId: "650e8400-e29b-41d4-a716-446655440002",
|
||||
participantCode: "SOC001",
|
||||
name: "Jennifer Liu",
|
||||
email: "jennifer.liu@email.com",
|
||||
demographics: {
|
||||
age: 29,
|
||||
gender: "female",
|
||||
education: "bachelor",
|
||||
tech_experience: "medium",
|
||||
robot_experience: "low",
|
||||
work_environment: "office",
|
||||
},
|
||||
consentGiven: true,
|
||||
consentDate: new Date("2024-01-20"),
|
||||
notes: "Project manager, interested in workplace automation",
|
||||
},
|
||||
];
|
||||
|
||||
for (const participant of participants) {
|
||||
await sql`
|
||||
INSERT INTO hs_participant (id, study_id, participant_code, name, email, demographics, consent_given, consent_date, notes, created_at, updated_at)
|
||||
VALUES (${participant.id}, ${participant.studyId}, ${participant.participantCode}, ${participant.name}, ${participant.email}, ${JSON.stringify(participant.demographics)}, ${participant.consentGiven}, ${participant.consentDate}, ${participant.notes}, NOW(), NOW())
|
||||
`;
|
||||
}
|
||||
|
||||
console.log(`✅ Created ${participants.length} participants`);
|
||||
}
|
||||
|
||||
async function seedTrials() {
|
||||
console.log("🎯 Seeding trials...");
|
||||
|
||||
const trials = [
|
||||
{
|
||||
id: "b50e8400-e29b-41d4-a716-446655440001",
|
||||
experimentId: "750e8400-e29b-41d4-a716-446655440001",
|
||||
participantId: "a50e8400-e29b-41d4-a716-446655440001",
|
||||
wizardId: "550e8400-e29b-41d4-a716-446655440003",
|
||||
sessionNumber: 1,
|
||||
status: "completed",
|
||||
scheduledAt: new Date("2024-01-15T10:00:00"),
|
||||
startedAt: new Date("2024-01-15T10:05:00"),
|
||||
completedAt: new Date("2024-01-15T10:20:00"),
|
||||
notes: "Participant completed successfully, good baseline performance",
|
||||
metadata: {
|
||||
condition: "control",
|
||||
completion_time: 893,
|
||||
errors: 1,
|
||||
assistance_requests: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "b50e8400-e29b-41d4-a716-446655440002",
|
||||
experimentId: "750e8400-e29b-41d4-a716-446655440002",
|
||||
participantId: "a50e8400-e29b-41d4-a716-446655440001",
|
||||
wizardId: "550e8400-e29b-41d4-a716-446655440003",
|
||||
sessionNumber: 2,
|
||||
status: "completed",
|
||||
scheduledAt: new Date("2024-01-15T10:30:00"),
|
||||
startedAt: new Date("2024-01-15T10:35:00"),
|
||||
completedAt: new Date("2024-01-15T10:58:00"),
|
||||
notes: "Robot assistance worked well, participant very satisfied",
|
||||
metadata: {
|
||||
condition: "robot_assistance",
|
||||
completion_time: 654,
|
||||
errors: 0,
|
||||
assistance_requests: 2,
|
||||
robot_performance: "excellent",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "b50e8400-e29b-41d4-a716-446655440003",
|
||||
experimentId: "750e8400-e29b-41d4-a716-446655440003",
|
||||
participantId: "a50e8400-e29b-41d4-a716-446655440003",
|
||||
wizardId: "550e8400-e29b-41d4-a716-446655440003",
|
||||
sessionNumber: 1,
|
||||
status: "scheduled",
|
||||
scheduledAt: new Date("2024-01-25T11:00:00"),
|
||||
startedAt: null,
|
||||
completedAt: null,
|
||||
notes: "Personality condition: friendly",
|
||||
metadata: {
|
||||
condition: "friendly_personality",
|
||||
personality_type: "friendly",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const trial of trials) {
|
||||
await sql`
|
||||
INSERT INTO hs_trial (id, experiment_id, participant_id, wizard_id, session_number, status, scheduled_at, started_at, completed_at, notes, metadata, created_at, updated_at)
|
||||
VALUES (${trial.id}, ${trial.experimentId}, ${trial.participantId}, ${trial.wizardId}, ${trial.sessionNumber}, ${trial.status}, ${trial.scheduledAt}, ${trial.startedAt}, ${trial.completedAt}, ${trial.notes}, ${JSON.stringify(trial.metadata)}, NOW(), NOW())
|
||||
`;
|
||||
}
|
||||
|
||||
console.log(`✅ Created ${trials.length} trials`);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
console.log("🚀 HRIStudio Database Seeding Started");
|
||||
console.log("📍 Database:", connectionString.replace(/:[^:]*@/, ":***@"));
|
||||
|
||||
await clearDatabase();
|
||||
await seedUsers();
|
||||
await seedStudies();
|
||||
await seedExperiments();
|
||||
await seedStepsAndActions();
|
||||
await seedParticipants();
|
||||
await seedTrials();
|
||||
|
||||
console.log("✅ Database seeding completed successfully!");
|
||||
console.log("\n📋 Summary:");
|
||||
console.log(" 👥 Users: 4 (admin, researcher, wizard, observer)");
|
||||
console.log(" 📚 Studies: 3 (navigation, social robots, elderly care)");
|
||||
console.log(" 🧪 Experiments: 4 (with comprehensive test scenarios)");
|
||||
console.log(" 📋 Steps: 8 (covering all experiment types)");
|
||||
console.log(" ⚡ Actions: 5 (detailed robot and wizard actions)");
|
||||
console.log(" 👤 Participants: 3 (diverse demographics)");
|
||||
console.log(" 🎯 Trials: 3 (completed, scheduled)");
|
||||
console.log("🔑 Test Login Credentials:");
|
||||
console.log(" Admin: sarah.chen@university.edu / password123");
|
||||
console.log(" Researcher: m.rodriguez@research.org / password123");
|
||||
console.log(" Wizard: emma.thompson@university.edu / password123");
|
||||
console.log(" Observer: james.wilson@university.edu / password123");
|
||||
console.log("\n🧪 Test Experiment Designer with:");
|
||||
console.log(
|
||||
" 📍 /experiments/750e8400-e29b-41d4-a716-446655440001/designer",
|
||||
);
|
||||
console.log(
|
||||
" 📍 /experiments/750e8400-e29b-41d4-a716-446655440002/designer",
|
||||
);
|
||||
console.log(
|
||||
" 📍 /experiments/750e8400-e29b-41d4-a716-446655440003/designer",
|
||||
);
|
||||
console.log("\n🚀 Ready to test the experiment designer!");
|
||||
} catch (error) {
|
||||
console.error("❌ Seeding failed:", error);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
await sql.end();
|
||||
}
|
||||
}
|
||||
|
||||
// Run the seeding
|
||||
main().catch(console.error);
|
||||
1077
scripts/seed.ts
1077
scripts/seed.ts
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user