mirror of
https://litchi.icu/ngc2207/judge4c-db.git
synced 2025-05-18 12:47:13 +00:00
575 lines
17 KiB
TypeScript
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();
|