judge4c-db/prisma/seed.ts

575 lines
17 KiB
TypeScript

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<User> {
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<User[]> {
const users = await Promise.all(
rolesWithCount.flatMap(({ role, count }) =>
Array.from({ length: count }, () => createUser(role))
)
);
return users;
}
async function createQuestionTags(): Promise<QuestionTag[]> {
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<Question[]> {
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<T>(
check: () => Promise<T[]>,
create: () => Promise<T[]>,
entityName: string
): Promise<void> {
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<CodeTemplate[]> {
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<int> twoSum(vector<int>& nums, int target) {
}
};`,
correctSolution: `class Solution {
public:
vector<int> twoSum(vector<int> &nums, int target) {
unordered_map<int, int> 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<Integer, Integer> 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<char> 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<Character> 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<Event[]> {
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<EventQuestion[]> {
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();