diff --git a/utils/seed.ts b/utils/seed.ts new file mode 100644 index 0000000..5416dc8 --- /dev/null +++ b/utils/seed.ts @@ -0,0 +1,99 @@ +import { db } from "../src/lib/server/db/db"; +import { tasks, taskTypes } from "../src/lib/server/db/schema/tasks"; +import { faker } from "@faker-js/faker"; +import logger from "../src/lib/server/logger"; + +async function resetDatabase() { + logger.info("Resetting database..."); + // Delete in reverse order of creation to respect foreign key constraints + await db.delete(tasks); + await db.delete(taskTypes); + logger.info("Database reset complete."); +} + +async function seedDatabase(count: number) { + logger.info(`Seeding database with ${count} tasks...`); + + // 1. Seed Task Types + const insertedTaskTypes = await db.insert(taskTypes).values([ + { name: "Product Change", prefix: "PC" }, + { name: "Task", prefix: "TA" }, + ]).returning(); + logger.info(`Seeded ${insertedTaskTypes.length} task types.`); + + if (insertedTaskTypes.length === 0) { + throw new Error("Task type seeding failed. Cannot seed tasks."); + } + + // 2. Seed Tasks + const newTasks = []; + const usedTaskIds = new Set(); + for (let i = 0; i < count; i++) { + let randomId: number; + do { + randomId = Math.floor(100000 + Math.random() * 900000); + } while (usedTaskIds.has(randomId)); + usedTaskIds.add(randomId); + + newTasks.push({ + // task_id with a guaranteed unique 6 digit number + taskId: randomId.toString(), + // a random phrase in each description + description: faker.lorem.sentence(), + // open_date with a valid date + openDate: faker.date.recent({ days: 30 }).toISOString(), + // a random paragraph in body + body: faker.lorem.paragraphs(3), + // other fields as needed + type: faker.helpers.arrayElement(insertedTaskTypes).id, + status: faker.helpers.arrayElement(["Todo", "In Progress", "Done", "Canceled"]), + priority: faker.helpers.arrayElement(["Low", "Medium", "High", "Urgent"]), + }); + } + + // 3. Insert the new tasks in chunks to avoid exceeding driver limits + logger.info(`Inserting ${newTasks.length} tasks in chunks...`); + const chunkSize = 500; + let totalInserted = 0; + for (let i = 0; i < newTasks.length; i += chunkSize) { + const chunk = newTasks.slice(i, i + chunkSize); + const inserted = await db.insert(tasks).values(chunk).returning(); + totalInserted += inserted.length; + logger.info(` ... inserted chunk ${Math.floor(i / chunkSize) + 1}, ${totalInserted}/${newTasks.length} tasks`); + } + logger.info(`Seeding complete. Total tasks inserted: ${totalInserted}.`); +} + +async function main() { + const mode = process.argv[2]; + + // Default count if no flag is provided + let count = 20; + + // Find and parse the --count flag + const countArg = process.argv.find((arg) => arg.startsWith("--count=")); + if (countArg) { + const countValue = parseInt(countArg.split("=")[1], 10); + if (!isNaN(countValue) && countValue > 0) { + count = countValue; + } + } + + if (mode === "reset") { + await resetDatabase(); + } else if (mode === "seed") { + // Seeding implies a reset first for a clean slate + await resetDatabase(); + await seedDatabase(count); + } else { + logger.error('Invalid mode. Use "seed" or "reset".'); + process.exit(1); + } + + logger.info("Script finished."); +} + +main().catch((e) => { + logger.error(e, "An error occurred during the script execution."); + process.exit(1); +}); \ No newline at end of file