import { UserRole, User, EventType, Event, QuestionDifficulty, Question, QuestionTag, CodeLanguage, CodeTemplate, EventQuestion, } from "@prisma/client"; import prisma from "@/lib/prisma"; import { faker } from "@faker-js/faker"; async function createUser(role: UserRole): Promise { return prisma.user.create({ data: { email: faker.internet.email(), displayName: faker.person.fullName(), hashedPassword: faker.internet.password(), passwordSalt: faker.string.alphanumeric(16), role, }, }); } async function createUsers( rolesWithCount: { role: UserRole; count: number }[] ): Promise { const users = await Promise.all( rolesWithCount.flatMap(({ role, count }) => Array.from({ length: count }, () => createUser(role)) ) ); return users; } async function createQuestionTags(): Promise { const questionTags = [ "Array", "String", "Hash Table", "Dynamic Programming", "Math", "Sorting", "Greedy", "Depth-First Search", "Database", "Binary Search", "Matrix", "Tree", "Breadth-First Search", ]; await prisma.questionTag.createMany({ data: questionTags.map((name) => ({ name })), }); return prisma.questionTag.findMany(); } async function createQuestions(): Promise { const questions = [ { name: "Two Sum", statement: `Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.`, difficulty: QuestionDifficulty.EASY, tags: ["Array", "Hash Table"], }, { name: "Add Two Numbers", statement: `You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list.`, difficulty: QuestionDifficulty.MEDIUM, tags: ["Linked List", "Math"], }, { name: "Longest Substring Without Repeating Characters", statement: `Given a string s, find the length of the longest substring without repeating characters.`, difficulty: QuestionDifficulty.MEDIUM, tags: ["Hash Table", "String", "Sliding Window"], }, ]; const creators = await prisma.user.findMany({ where: { role: UserRole.TEACHER }, }); if (creators.length === 0) { throw new Error("No creators found for questions."); } const tags = await prisma.questionTag.findMany(); const tagMap = new Map(tags.map((tag) => [tag.name, tag])); const missingTags = questions.flatMap((question) => question.tags.filter((tag) => !tagMap.has(tag)) ); if (missingTags.length > 0) { await prisma.questionTag.createMany({ data: missingTags.map((name) => ({ name })), }); const createdTags = await prisma.questionTag.findMany({ where: { name: { in: missingTags } }, }); createdTags.forEach((tag) => tagMap.set(tag.name, tag)); } return Promise.all( questions.map(({ name, statement, difficulty, tags: questionTags }) => prisma.question.create({ data: { name, statement, difficulty, tags: { connect: questionTags.map((tag) => ({ id: tagMap.get(tag)!.id })), }, creators: { connect: creators .sort(() => Math.random() - 0.5) .slice( 0, Math.max(1, Math.floor(Math.random() * creators.length)) ) .map((creator) => ({ id: creator.id })), }, }, }) ) ); } async function checkAndSeed( check: () => Promise, create: () => Promise, entityName: string ): Promise { const existingEntities = await check(); if (existingEntities.length > 0) { console.log(`${entityName} already exist, skipping seeding ${entityName}.`); } else { console.log(`Seeding ${entityName} ...`); const entities = await create(); console.log(`Created ${entityName}:`); console.log(entities); } } async function createCodeTemplates(): Promise { const codeTemplates = [ [ { language: CodeLanguage.C, starterCode: `/** * Note: The returned array must be malloced, assume caller calls free(). */ int* twoSum(int* nums, int numsSize, int target, int* returnSize) { }`, correctSolution: `int* twoSum(int* nums, int numsSize, int target, int* returnSize) { struct hashTable { int key; int value; UT_hash_handle hh; } *hashTable = NULL, *item, *tmpItem; for (int i = 0; i < numsSize; i++) { HASH_FIND_INT(hashTable, &nums[i], item); if (item) { int* result = malloc(sizeof(int) * 2); result[0] = item->value; result[1] = i; *returnSize = 2; HASH_ITER(hh, hashTable, item, tmpItem) { HASH_DEL(hashTable, item); free(item); } return result; } item = malloc(sizeof(struct hashTable)); item->key = target - nums[i]; item->value = i; HASH_ADD_INT(hashTable, key, item); } HASH_ITER(hh, hashTable, item, tmpItem) { HASH_DEL(hashTable, item); free(item); } *returnSize = 0; // If no valid pair is found, return an empty array return malloc(sizeof(int) * 0); }`, }, { language: CodeLanguage.CPP, starterCode: `class Solution { public: vector twoSum(vector& nums, int target) { } };`, correctSolution: `class Solution { public: vector twoSum(vector &nums, int target) { unordered_map hash; for (int i = 0; i < nums.size(); i++) { hash[nums[i]] = i; } for (int i = 0; i < nums.size(); i++) { int complement = target - nums[i]; if (hash.find(complement) != hash.end() && hash[complement] != i) { return {i, hash[complement]}; } } // If no valid pair is found, return an empty vector return {}; } };`, }, { language: CodeLanguage.JAVA, starterCode: `class Solution { public int[] twoSum(int[] nums, int target) { } }`, correctSolution: `class Solution { public int[] twoSum(int[] nums, int target) { Map map = new HashMap<>(); for (int i = 0; i < nums.length; i++) { map.put(nums[i], i); } for (int i = 0; i < nums.length; i++) { int complement = target - nums[i]; if (map.containsKey(complement) && map.get(complement) != i) { return new int[] { i, map.get(complement) }; } } // In case there is no solution, return an empty array return new int[] {}; } }`, }, ], [ { language: CodeLanguage.C, starterCode: `/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2) { }`, correctSolution: `struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2) { struct ListNode* dummyHead = malloc(sizeof(struct ListNode)); dummyHead->val = 0; dummyHead->next = NULL; struct ListNode* curr = dummyHead; int carry = 0; while (l1 != NULL || l2 != NULL || carry != 0) { int x = (l1 != NULL) ? l1->val : 0; int y = (l2 != NULL) ? l2->val : 0; int sum = carry + x + y; carry = sum / 10; curr->next = malloc(sizeof(struct ListNode)); curr->next->val = sum % 10; curr->next->next = NULL; curr = curr->next; if (l1 != NULL) l1 = l1->next; if (l2 != NULL) l2 = l2->next; } struct ListNode* result = dummyHead->next; free(dummyHead); // Free the memory allocated for dummyHead return result; }`, }, { language: CodeLanguage.CPP, starterCode: `/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { } };`, correctSolution: `class Solution { public: ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { ListNode* dummyHead = new ListNode(0); ListNode* curr = dummyHead; int carry = 0; while (l1 != NULL || l2 != NULL || carry != 0) { int x = l1 ? l1->val : 0; int y = l2 ? l2->val : 0; int sum = carry + x + y; carry = sum / 10; curr->next = new ListNode(sum % 10); curr = curr->next; l1 = l1 ? l1->next : nullptr; l2 = l2 ? l2->next : nullptr; } ListNode* result = dummyHead->next; delete dummyHead; // Freeing the memory allocated for dummyHead return result; } };`, }, { language: CodeLanguage.JAVA, starterCode: `/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */ class Solution { public ListNode addTwoNumbers(ListNode l1, ListNode l2) { } }`, correctSolution: `class Solution { // Add Two Numbers (Java improved) public ListNode addTwoNumbers(ListNode l1, ListNode l2) { ListNode dummyHead = new ListNode(0); ListNode curr = dummyHead; int carry = 0; while (l1 != null || l2 != null || carry != 0) { int x = (l1 != null) ? l1.val : 0; int y = (l2 != null) ? l2.val : 0; int sum = carry + x + y; carry = sum / 10; curr.next = new ListNode(sum % 10); curr = curr.next; if (l1 != null) l1 = l1.next; if (l2 != null) l2 = l2.next; } return dummyHead.next; } }`, }, ], [ { language: CodeLanguage.C, starterCode: `int lengthOfLongestSubstring(char* s) { }`, correctSolution: `int lengthOfLongestSubstring(char * s){ int arr[256] = {0}; int start = 0; int len = 0; for (int i = 0; s[i]; i++) { char c = s[i]; arr[c] != 0 ? (arr[c] > start ? start = arr[c] : start) : arr[c]; (i - start + 1) > len ? len = (i - start + 1) : len; arr[c] = i + 1; } return (len); return(len); }`, }, { language: CodeLanguage.CPP, starterCode: `class Solution { public: int lengthOfLongestSubstring(string s) { } };`, correctSolution: `class Solution { public: int lengthOfLongestSubstring(std::string_view sv) { std::vector vec; std::size_t size = 0; for (auto c : sv) { if (const auto& it = std::find(std::cbegin(vec), std::cend(vec), c); it != std::cend(vec)) { size = std::max(size, vec.size()); vec.erase(std::cbegin(vec), std::next(it)); } vec.emplace_back(c); } return std::max(size, vec.size()); } };`, }, { language: CodeLanguage.JAVA, starterCode: `class Solution { public int lengthOfLongestSubstring(String s) { } }`, correctSolution: `class Solution { public int lengthOfLongestSubstring(String s) { Queue queue = new LinkedList<>(); int res = 0; for (char c : s.toCharArray()) { while (queue.contains(c)) { queue.poll(); } queue.offer(c); res = Math.max(res, queue.size()); } return res; } }`, }, ], ]; const questions = await prisma.question.findMany(); const newCodeTemplates = questions.flatMap((question, index) => codeTemplates[index].map((template) => ({ ...template, questionId: question.id, })) ); await prisma.codeTemplate.createMany({ data: newCodeTemplates, }); return prisma.codeTemplate.findMany(); } async function createEvents(): Promise { const users = await prisma.user.findMany(); if (users.length === 0) { throw new Error("No users found fororganizers and participants."); } const eventTypes = Object.values(EventType); const events = await Promise.all( Array.from({ length: 5 }, () => { const startTime = faker.date.future(); const endTime = faker.date.future({ refDate: startTime }); const type = faker.helpers.arrayElement(eventTypes); const isVisible = faker.datatype.boolean(); return prisma.event.create({ data: { name: faker.lorem.words(3), startTime, endTime, type, isVisible, organizer: { connect: { id: faker.helpers.arrayElement(users).id, }, }, participants: { connect: faker.helpers .arrayElements(users, { min: 1, max: 5 }) .map((user) => ({ id: user.id })), }, }, }); }) ); return events; } async function createEventQuestions( event: Event, questions: Question[] ): Promise { const selectedQuestions = faker.helpers.arrayElements( questions, faker.number.int({ min: 1, max: questions.length }) ); const shuffledQuestions = faker.helpers.shuffle(selectedQuestions); return Promise.all( shuffledQuestions.map((question, index) => prisma.eventQuestion.create({ data: { event: { connect: { id: event.id, }, }, question: { connect: { id: question.id, }, }, displayOrder: index, }, }) ) ); } async function main() { const rolesWithCount = [ { role: UserRole.ADMIN, count: 2 }, { role: UserRole.TEACHER, count: 3 }, { role: UserRole.STUDENT, count: 5 }, { role: UserRole.GUEST, count: 2 }, ]; try { await checkAndSeed( () => prisma.user.findMany(), () => createUsers(rolesWithCount), "users" ); await checkAndSeed( () => prisma.questionTag.findMany(), createQuestionTags, "question tags" ); await checkAndSeed( () => prisma.question.findMany(), createQuestions, "questions" ); await checkAndSeed( () => prisma.codeTemplate.findMany(), createCodeTemplates, "code templates" ); await checkAndSeed(() => prisma.event.findMany(), createEvents, "events"); const questions = await prisma.question.findMany(); const events = await prisma.event.findMany(); await Promise.all( events.map((event) => createEventQuestions(event, questions)) ); } catch (e) { console.error(e); process.exit(1); } finally { await prisma.$disconnect(); } } main();