Various improvements: study forms, participant management, PDF generator, robot integration

This commit is contained in:
2026-03-21 20:21:18 -04:00
parent bbc34921b5
commit bbbe397ba8
15 changed files with 936 additions and 675 deletions

View File

@@ -35,7 +35,8 @@ async function main() {
.values({
studyId: study.id,
name: "Story: Red Rock",
description: "A story about a red rock on Mars with comprehension check and branching.",
description:
"A story about a red rock on Mars with comprehension check and branching.",
version: 1,
status: "draft",
robotId: robot.id,
@@ -53,102 +54,212 @@ async function main() {
const checkId = uuidv4();
// Step 1: The Hook
const [step1] = await db.insert(schema.steps).values({
experimentId: experiment.id,
name: "The Hook",
type: "wizard",
orderIndex: 0,
}).returning();
const [step1] = await db
.insert(schema.steps)
.values({
experimentId: experiment.id,
name: "The Hook",
type: "wizard",
orderIndex: 0,
})
.returning();
// Step 2: The Narrative
const [step2] = await db.insert(schema.steps).values({
experimentId: experiment.id,
name: "The Narrative",
type: "wizard",
orderIndex: 1,
}).returning();
const [step2] = await db
.insert(schema.steps)
.values({
experimentId: experiment.id,
name: "The Narrative",
type: "wizard",
orderIndex: 1,
})
.returning();
// Step 3: Comprehension Check (Conditional)
const [step3] = await db.insert(schema.steps).values({
id: checkId,
experimentId: experiment.id,
name: "Comprehension Check",
type: "conditional",
orderIndex: 2,
conditions: {
variable: "last_wizard_response",
options: [
{ label: "Answer: Red (Correct)", value: "Red", variant: "default", nextStepId: branchAId },
{ label: "Answer: Other (Incorrect)", value: "Incorrect", variant: "destructive", nextStepId: branchBId }
]
}
}).returning();
const [step3] = await db
.insert(schema.steps)
.values({
id: checkId,
experimentId: experiment.id,
name: "Comprehension Check",
type: "conditional",
orderIndex: 2,
conditions: {
variable: "last_wizard_response",
options: [
{
label: "Answer: Red (Correct)",
value: "Red",
variant: "default",
nextStepId: branchAId,
},
{
label: "Answer: Other (Incorrect)",
value: "Incorrect",
variant: "destructive",
nextStepId: branchBId,
},
],
},
})
.returning();
// Step 4: Branch A (Correct)
const [step4] = await db.insert(schema.steps).values({
id: branchAId,
experimentId: experiment.id,
name: "Branch A: Correct Response",
type: "wizard",
orderIndex: 3,
conditions: { nextStepId: conclusionId } // SKIP BRANCH B
}).returning();
const [step4] = await db
.insert(schema.steps)
.values({
id: branchAId,
experimentId: experiment.id,
name: "Branch A: Correct Response",
type: "wizard",
orderIndex: 3,
conditions: { nextStepId: conclusionId }, // SKIP BRANCH B
})
.returning();
// Step 5: Branch B (Incorrect)
const [step5] = await db.insert(schema.steps).values({
id: branchBId,
experimentId: experiment.id,
name: "Branch B: Incorrect Response",
type: "wizard",
orderIndex: 4,
conditions: { nextStepId: conclusionId }
}).returning();
const [step5] = await db
.insert(schema.steps)
.values({
id: branchBId,
experimentId: experiment.id,
name: "Branch B: Incorrect Response",
type: "wizard",
orderIndex: 4,
conditions: { nextStepId: conclusionId },
})
.returning();
// Step 6: Conclusion
const [step6] = await db.insert(schema.steps).values({
id: conclusionId,
experimentId: experiment.id,
name: "Conclusion",
type: "wizard",
orderIndex: 5,
}).returning();
const [step6] = await db
.insert(schema.steps)
.values({
id: conclusionId,
experimentId: experiment.id,
name: "Conclusion",
type: "wizard",
orderIndex: 5,
})
.returning();
// 4. Create Actions
// The Hook
await db.insert(schema.actions).values([
{ stepId: step1!.id, name: "Say Hello", type: "nao6-ros2.say_text", orderIndex: 0, parameters: { text: "Hello! Are you ready for a story?" } },
{ stepId: step1!.id, name: "Wave", type: "nao6-ros2.move_arm", orderIndex: 1, parameters: { arm: "right", shoulder_pitch: 0.5 } }
{
stepId: step1!.id,
name: "Say Hello",
type: "nao6-ros2.say_text",
orderIndex: 0,
parameters: { text: "Hello! Are you ready for a story?" },
},
{
stepId: step1!.id,
name: "Wave",
type: "nao6-ros2.move_arm",
orderIndex: 1,
parameters: { arm: "right", shoulder_pitch: 0.5 },
},
]);
// The Narrative
await db.insert(schema.actions).values([
{ stepId: step2!.id, name: "The Story", type: "nao6-ros2.say_text", orderIndex: 0, parameters: { text: "Once, a traveler went to Mars. He found a bright red rock that glowed." } },
{ stepId: step2!.id, name: "Look Left", type: "nao6-ros2.turn_head", orderIndex: 1, parameters: { yaw: 0.5, speed: 0.3 } },
{ stepId: step2!.id, name: "Look Right", type: "nao6-ros2.turn_head", orderIndex: 2, parameters: { yaw: -0.5, speed: 0.3 } }
{
stepId: step2!.id,
name: "The Story",
type: "nao6-ros2.say_text",
orderIndex: 0,
parameters: {
text: "Once, a traveler went to Mars. He found a bright red rock that glowed.",
},
},
{
stepId: step2!.id,
name: "Look Left",
type: "nao6-ros2.turn_head",
orderIndex: 1,
parameters: { yaw: 0.5, speed: 0.3 },
},
{
stepId: step2!.id,
name: "Look Right",
type: "nao6-ros2.turn_head",
orderIndex: 2,
parameters: { yaw: -0.5, speed: 0.3 },
},
]);
// Comprehension Check
await db.insert(schema.actions).values([
{ stepId: step3!.id, name: "Ask Color", type: "nao6-ros2.say_text", orderIndex: 0, parameters: { text: "What color was the rock I found on Mars?" } },
{ stepId: step3!.id, name: "Wait for Color", type: "wizard_wait_for_response", orderIndex: 1, parameters: { options: ["Red", "Blue", "Green", "Incorrect"], prompt_text: "What color did the participant say?" } }
{
stepId: step3!.id,
name: "Ask Color",
type: "nao6-ros2.say_text",
orderIndex: 0,
parameters: { text: "What color was the rock I found on Mars?" },
},
{
stepId: step3!.id,
name: "Wait for Color",
type: "wizard_wait_for_response",
orderIndex: 1,
parameters: {
options: ["Red", "Blue", "Green", "Incorrect"],
prompt_text: "What color did the participant say?",
},
},
]);
// Branch A (Using say_with_emotion)
await db.insert(schema.actions).values([
{ stepId: step4!.id, name: "Happy Response", type: "nao6-ros2.say_with_emotion", orderIndex: 0, parameters: { text: "Exacty! It was a glowing red rock.", emotion: "happy" } }
]);
await db
.insert(schema.actions)
.values([
{
stepId: step4!.id,
name: "Happy Response",
type: "nao6-ros2.say_with_emotion",
orderIndex: 0,
parameters: {
text: "Exacty! It was a glowing red rock.",
emotion: "happy",
},
},
]);
// Branch B
await db.insert(schema.actions).values([
{ stepId: step5!.id, name: "Correct them", type: "nao6-ros2.say_text", orderIndex: 0, parameters: { text: "Actually, it was red." } },
{ stepId: step5!.id, name: "Shake Head", type: "nao6-ros2.turn_head", orderIndex: 1, parameters: { yaw: 0.3, speed: 0.5 } }
{
stepId: step5!.id,
name: "Correct them",
type: "nao6-ros2.say_text",
orderIndex: 0,
parameters: { text: "Actually, it was red." },
},
{
stepId: step5!.id,
name: "Shake Head",
type: "nao6-ros2.turn_head",
orderIndex: 1,
parameters: { yaw: 0.3, speed: 0.5 },
},
]);
// Conclusion
await db.insert(schema.actions).values([
{ stepId: step6!.id, name: "Final Goodbye", type: "nao6-ros2.say_text", orderIndex: 0, parameters: { text: "That is all for today. Goodbye!" } },
{ stepId: step6!.id, name: "Rest", type: "nao6-ros2.move_arm", orderIndex: 1, parameters: { shoulder_pitch: 1.5 } }
{
stepId: step6!.id,
name: "Final Goodbye",
type: "nao6-ros2.say_text",
orderIndex: 0,
parameters: { text: "That is all for today. Goodbye!" },
},
{
stepId: step6!.id,
name: "Rest",
type: "nao6-ros2.move_arm",
orderIndex: 1,
parameters: { shoulder_pitch: 1.5 },
},
]);
console.log("✅ Seed completed successfully!");

View File

@@ -5,21 +5,27 @@ async function migrate() {
console.log("Adding identifier column to hs_plugin...");
try {
await db.execute(sql`ALTER TABLE hs_plugin ADD COLUMN identifier varchar(100)`);
await db.execute(
sql`ALTER TABLE hs_plugin ADD COLUMN identifier varchar(100)`,
);
console.log("✓ Added identifier column");
} catch (e: any) {
console.log("Column may already exist:", e.message);
}
try {
await db.execute(sql`UPDATE hs_plugin SET identifier = name WHERE identifier IS NULL`);
await db.execute(
sql`UPDATE hs_plugin SET identifier = name WHERE identifier IS NULL`,
);
console.log("✓ Copied name to identifier");
} catch (e: any) {
console.log("Error copying:", e.message);
}
try {
await db.execute(sql`ALTER TABLE hs_plugin ADD CONSTRAINT hs_plugin_identifier_unique UNIQUE (identifier)`);
await db.execute(
sql`ALTER TABLE hs_plugin ADD CONSTRAINT hs_plugin_identifier_unique UNIQUE (identifier)`,
);
console.log("✓ Added unique constraint");
} catch (e: any) {
console.log("Constraint may already exist:", e.message);