#!/usr/bin/env tsx
/**
* Investigation Seed Script
*
* This script seeds the database with a sample investigation based on the
* Galileo Cloud interface example. It tests the investigation collection
* schema and validates that all fields are properly structured.
*/
import { Investigation } from "@models/investigation/investigation.model";
import { InvestigationStatus } from "@typez/investigation/enums";
import { getLogger } from "@utils/asyncLocalStorage";
import { Types } from "mongoose";
import { closeDatabaseConnection, connectToDatabase } from "../database/mongo.connection";
const logger = getLogger();
/**
* Create a sample investigation based on the Galileo Cloud interface
* @category Scripts
* @returns {IInvestigation} The sample investigation
*/
function createSampleInvestigation() {
const now = new Date();
const authorId = new Types.ObjectId(); // Mock author ID
return {
// Lifecycle & locking
status: InvestigationStatus.DRAFT_CONTRADICTORY_COMPLETE,
isLocked: false,
lockedBy: null,
lockedAt: null,
// Editable content (all wrapped with EditableField<T>)
originalRequest: {
value: "Produce an investigation corresponding to OpenSciEd 8.1 — contact forces. Set goal to 'Figure out how speed, mass, and padding affect the force during a collision and the amount of damage.' Ensure it has six steps: Predict & plan, Calibrate speeds, Run trials: speed, Run trials: mass, Run trials: padding, Analyze patterns This video provides a reference: https://www.youtube.com/watch?v=ygLbCFuqYFo Use a brick for end stop and use dynamic carts for collision objects. Lock the ramp. Make calibration variability 10% and measurement variability 20%.",
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
son: null,
updatedBy: authorId,
updatedAt: now,
},
curriculum: {
value: "OpenSciEd Grade 6 Unit 6.2 (Thermal Energy)",
aiGeneratedValue: "OpenSciEd 8.1 — contact forces",
humanEdited: true,
aiEditable: true,
isContradicting: true,
contradictionReason: "Curriculum was changed from contact forces to thermal energy",
updatedBy: authorId,
updatedAt: now,
},
gradeAndUnit: {
value: "Grade 6",
aiGeneratedValue: "Grade 8",
humanEdited: true,
aiEditable: true,
isContradicting: true,
contradictionReason: "Grade level changed from 8 to 6",
updatedBy: authorId,
updatedAt: now,
},
title: {
value: "Exploring the intricate dynamics of ecosystems reveals that the removal of key species can lead to catastrophic consequences",
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
unitTitle: {
value: "Thermal Energy and Convection Currents",
aiGeneratedValue: null,
humanEdited: true,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
calibrationVariability: {
value: 10,
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
measurementVariability: {
value: 20,
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
outcomeVariability: {
value: 15,
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
correspondence: {
value: "Unit 6.2 focuses on energy transfer, including conduction, convection, and radiation, and data. This investigation aligns specifically to Lesson Set 2, which emphasizes observing and explaining convection currents in fluids.",
aiGeneratedValue: null,
humanEdited: true,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
differences: {
value: "The investigation closely follows OpenSciEd guidance but uses food coloring for clearer visualization and omits some extended data collection to simplify for introductory use.",
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: true,
contradictionReason: "Contradicts Material #2 (Room temperature water)",
updatedBy: authorId,
updatedAt: now,
},
objectives: {
value: "Students will be able to: 1) Observe and describe convection currents in fluids, 2) Explain how temperature differences create fluid movement, 3) Connect convection to energy transfer processes, 4) Apply understanding to real-world examples of thermal energy transfer.",
aiGeneratedValue: null,
humanEdited: true,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
discussionTopics: {
value: "1) Why do warmer fluids rise? 2) How does this relate to weather patterns? 3) What other examples of convection can you think of? 4) How might this apply to heating systems in buildings?",
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
trivia: {
value: "Convection currents in the Earth's mantle drive plate tectonics, and convection in the atmosphere creates weather patterns. The same principle is used in convection ovens to cook food more evenly.",
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
analyticalFacts: {
value: "Convection occurs when warmer, less dense fluid rises and cooler, denser fluid sinks, creating a continuous cycle. This is a form of heat transfer that doesn't require direct contact between objects.",
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
introAndGoals: {
value: "Students engage in a hands-on exploration of fluid dynamics by directly observing how warmer fluids rise while cooler fluids sink. This visual demonstration allows them to identify convection currents, which serve as a foundational concept in understanding energy transfer. By witnessing these phenomena in action, students can better grasp the principles of thermal energy movement and its implications in various natural processes.",
aiGeneratedValue: null,
humanEdited: true,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
steps: [
{
title: {
value: "Predict & Plan",
aiGeneratedValue: null,
humanEdited: true,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
descriptionEn: {
value: "Students predict what will happen when hot and cold water are mixed, and plan their observation strategy.",
aiGeneratedValue: null,
humanEdited: true,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
skippable: {
value: false,
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
skippable_after_marking: {
value: false,
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
},
{
title: {
value: "Calibrate Temperatures",
aiGeneratedValue: null,
humanEdited: true,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
descriptionEn: {
value: "Students prepare water at different temperatures and add food coloring for visualization.",
aiGeneratedValue: null,
humanEdited: true,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
skippable: {
value: false,
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
skippable_after_marking: {
value: false,
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
},
{
title: {
value: "Run Trials: Temperature",
aiGeneratedValue: null,
humanEdited: true,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
descriptionEn: {
value: "Students observe and record convection currents when hot water is added to room temperature water.",
aiGeneratedValue: null,
humanEdited: true,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
skippable: {
value: false,
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
skippable_after_marking: {
value: false,
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
},
{
title: {
value: "Run Trials: Volume",
aiGeneratedValue: null,
humanEdited: true,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
descriptionEn: {
value: "Students test different volumes of hot water to see how it affects convection patterns.",
aiGeneratedValue: null,
humanEdited: true,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
skippable: {
value: true,
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
skippable_after_marking: {
value: true,
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
},
{
title: {
value: "Run Trials: Container Shape",
aiGeneratedValue: null,
humanEdited: true,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
descriptionEn: {
value: "Students test convection in different container shapes to understand how geometry affects flow.",
aiGeneratedValue: null,
humanEdited: true,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
skippable: {
value: true,
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
skippable_after_marking: {
value: true,
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
},
{
title: {
value: "Analyze Patterns",
aiGeneratedValue: null,
humanEdited: true,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
descriptionEn: {
value: "Students analyze their observations and connect them to energy transfer principles.",
aiGeneratedValue: null,
humanEdited: true,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
skippable: {
value: false,
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
skippable_after_marking: {
value: false,
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
},
],
objects: {
value: [
{
id: "container_001",
name: "Clear glass container (beaker or tank)",
position: { x: 0, y: 0, z: 0 },
rotation: { x: 0, y: 0, z: 0 },
size: 1.0,
view: {
mesh: "glass_beaker_mesh",
material: "glass_material",
texture: "clear_glass_texture",
interactions: [{ name: "fill" }, { name: "empty" }, { name: "inspect" }],
},
fill: {
maxVolume: 1000,
currentVolume: 0,
currentLiquid: null,
fillRate: 50,
drainRate: 30,
hasOverflow: true,
interactions: [{ name: "fill" }, { name: "drain" }],
},
collisionMesh: {
type: "Box",
size: { x: 10, y: 15, z: 10 },
isTrigger: false,
material: "glass_physics_material",
interactions: [{ name: "collide" }],
},
},
{
id: "water_room_temp",
name: "Room temperature water",
position: { x: 0, y: 5, z: 0 },
rotation: { x: 0, y: 0, z: 0 },
size: 0.8,
view: {
mesh: "water_mesh",
material: "water_material",
texture: "clear_water_texture",
interactions: [{ name: "stir" }, { name: "heat" }, { name: "cool" }],
},
fluidRigidBody: {
density: 1000,
viscosity: 0.001,
surfaceTension: 0.072,
particleSize: 0.1,
simulationMethod: "SPH",
maxParticles: 1000,
interactions: [{ name: "flow" }, { name: "mix" }],
},
temperature: {
value: 20,
unit: "C",
conductivity: 0.6,
specificHeat: 4180,
interactions: [{ name: "measure_temperature" }, { name: "heat" }, { name: "cool" }],
},
stateOfMatter: {
currentState: "Liquid",
meltingPoint: 0,
boilingPoint: 100,
stateChangeObjects: {
solid: "ice_cube_001",
liquid: "water_room_temp",
gas: "steam_001",
},
interactions: [{ name: "change_state" }],
},
},
{
id: "water_hot_colored",
name: "Small cup with hot water (colored with red food coloring)",
position: { x: 2, y: 8, z: 0 },
rotation: { x: 0, y: 0, z: 0 },
size: 0.3,
view: {
mesh: "small_cup_mesh",
material: "ceramic_material",
texture: "white_ceramic_texture",
interactions: [{ name: "pour" }, { name: "inspect" }],
},
fill: {
maxVolume: 100,
currentVolume: 100,
currentLiquid: "hot_red_water",
fillRate: 0,
drainRate: 20,
hasOverflow: false,
interactions: [{ name: "pour" }],
},
temperature: {
value: 80,
unit: "C",
conductivity: 0.6,
specificHeat: 4180,
interactions: [{ name: "measure_temperature" }],
},
},
],
aiGeneratedValue: null,
humanEdited: false,
aiEditable: true,
isContradicting: false,
contradictionReason: null,
updatedBy: authorId,
updatedAt: now,
},
observers: [{ name: "Edward Kowalski" }],
// Metadata (system/relations)
metadata: {
dateOfDevelopment: new Date("2025-03-01"),
dateOfDevelopmentDelivery: new Date("2025-04-30"),
dateOfPublishing: null,
author: authorId,
editors: [],
views: 0,
sourceInvestigation: null,
dateOfCreation: new Date(),
dateModified: new Date(),
},
};
}
/**
* Main seed function
* @category Scripts
* @returns {Promise<void>} A promise that resolves when the seed script is completed
*/
async function seedInvestigation() {
try {
logger.info("Starting investigation seed script...");
// Connect to database
await connectToDatabase();
logger.info("Connected to database successfully");
// Clear existing investigations (optional - remove this in production)
const existingCount = await Investigation.countDocuments();
if (existingCount > 0) {
logger.info(`Found ${existingCount} existing investigations. Clearing them...`);
await Investigation.deleteMany({});
logger.info("Cleared existing investigations");
}
// Create sample investigation
const sampleData = createSampleInvestigation();
logger.info("Created sample investigation data");
// Save to database
const investigation = new Investigation(sampleData);
const savedInvestigation = await investigation.save();
logger.info({
investigationId: savedInvestigation._id,
status: savedInvestigation.status,
title: savedInvestigation.title.value,
curriculum: savedInvestigation.curriculum.value,
contradictions: {
curriculum: savedInvestigation.curriculum.isContradicting,
},
}, "Successfully seeded investigation");
// Validate the saved data
const validation = await Investigation.findById(savedInvestigation._id);
if (!validation) {
throw new Error("Failed to retrieve saved investigation");
}
logger.info({
totalFields: Object.keys(validation.toObject()).length,
editableFields: Object.keys(validation.toObject()).filter((key) => key !== "_id" &&
key !== "__v" &&
key !== "createdAt" &&
key !== "updatedAt" &&
key !== "observers").length,
stepsCount: Array.isArray(validation.steps) ? validation.steps.length : 0,
objectsCount: 0,
}, "Investigation validation completed");
logger.info("Investigation seed script completed successfully!");
}
catch (error) {
logger.error({
error: error instanceof Error ? error.message : "Unknown error",
stack: error instanceof Error ? error.stack : undefined,
}, "Failed to seed investigation");
throw error;
}
finally {
// Close database connection
await closeDatabaseConnection();
logger.info("Database connection closed");
}
}
/**
* Run the seed script
* @category Scripts
* @returns {Promise<void>} A promise that resolves when the seed script is completed
*/
seedInvestigation()
.then(() => {
logger.info("Seed script completed successfully");
process.exit(0);
})
.catch((error) => {
logger.error({
error: error instanceof Error ? error.message : "Unknown error",
}, "Seed script failed");
process.exit(1);
});
export { seedInvestigation, createSampleInvestigation };
Source