mirror of
https://litchi.icu/ngc2207/judge4c-db.git
synced 2025-05-18 16:17:16 +00:00
feat: add Prisma integration and initial data seeding
This commit is contained in:
parent
2fc607accd
commit
3e68af8f7a
5
.gitignore
vendored
5
.gitignore
vendored
@ -41,4 +41,7 @@ next-env.d.ts
|
||||
cache
|
||||
|
||||
# bun
|
||||
bun.lockb
|
||||
bun.lockb
|
||||
|
||||
# prisma
|
||||
/prisma/migrations
|
11
package.json
11
package.json
@ -9,6 +9,7 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "^5.22.0",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.456.0",
|
||||
@ -19,13 +20,15 @@
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5",
|
||||
"@faker-js/faker": "^9.2.0",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "15.0.3"
|
||||
"eslint-config-next": "15.0.3",
|
||||
"postcss": "^8",
|
||||
"prisma": "^5.22.0",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
|
165
prisma/schema.prisma
Normal file
165
prisma/schema.prisma
Normal file
@ -0,0 +1,165 @@
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
enum UserRole {
|
||||
ADMIN
|
||||
TEACHER
|
||||
STUDENT
|
||||
GUEST
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
email String @unique @db.VarChar(255)
|
||||
displayName String? @db.VarChar(255)
|
||||
hashedPassword String @default("")
|
||||
passwordSalt String @default("")
|
||||
role UserRole @default(GUEST)
|
||||
createdAt DateTime @default(now()) @db.Timestamptz
|
||||
updatedAt DateTime @updatedAt @db.Timestamptz
|
||||
organizedEvents Event[] @relation("OrganizedEvents")
|
||||
participatedEvents Event[] @relation("ParticipatedEvents")
|
||||
createdQuestions Question[] @relation("CreatedQuestions")
|
||||
submissions Submission[]
|
||||
}
|
||||
|
||||
enum EventType {
|
||||
COMPETITION
|
||||
EXAM
|
||||
HOMEWORK
|
||||
}
|
||||
|
||||
model Event {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
startTime DateTime @db.Timestamptz
|
||||
endTime DateTime @db.Timestamptz
|
||||
isVisible Boolean @default(true)
|
||||
type EventType @default(COMPETITION)
|
||||
createdAt DateTime @default(now()) @db.Timestamptz
|
||||
updatedAt DateTime @updatedAt @db.Timestamptz
|
||||
organizer User[] @relation("OrganizedEvents")
|
||||
participants User[] @relation("ParticipatedEvents")
|
||||
eventQuestions EventQuestion[]
|
||||
}
|
||||
|
||||
enum QuestionDifficulty {
|
||||
EASY
|
||||
MEDIUM
|
||||
HARD
|
||||
}
|
||||
|
||||
model Question {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
statement String @db.Text
|
||||
difficulty QuestionDifficulty @default(EASY)
|
||||
createdAt DateTime @default(now()) @db.Timestamptz
|
||||
updatedAt DateTime @updatedAt @db.Timestamptz
|
||||
creators User[] @relation("CreatedQuestions")
|
||||
codeTemplates CodeTemplate[]
|
||||
eventQuestions EventQuestion[]
|
||||
tags QuestionTag[] @relation("QuestionTags")
|
||||
}
|
||||
|
||||
model QuestionTag {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
createdAt DateTime @default(now()) @db.Timestamptz
|
||||
updatedAt DateTime @updatedAt @db.Timestamptz
|
||||
questions Question[] @relation("QuestionTags")
|
||||
}
|
||||
|
||||
enum CodeLanguage {
|
||||
C
|
||||
CPP
|
||||
JAVA
|
||||
}
|
||||
|
||||
model CodeTemplate {
|
||||
id String @id @default(cuid())
|
||||
language CodeLanguage
|
||||
starterCode String @db.Text
|
||||
correctSolution String @default("") @db.Text
|
||||
createdAt DateTime @default(now()) @db.Timestamptz
|
||||
updatedAt DateTime @updatedAt @db.Timestamptz
|
||||
question Question @relation(fields: [questionId], references: [id])
|
||||
questionId String
|
||||
}
|
||||
|
||||
model EventQuestion {
|
||||
id String @id @default(cuid())
|
||||
event Event @relation(fields: [eventId], references: [id])
|
||||
eventId String
|
||||
question Question @relation(fields: [questionId], references: [id])
|
||||
questionId String
|
||||
displayOrder Int @default(0)
|
||||
hidden Boolean @default(false)
|
||||
createdAt DateTime @default(now()) @db.Timestamptz
|
||||
updatedAt DateTime @updatedAt @db.Timestamptz
|
||||
submissions Submission[]
|
||||
|
||||
@@unique([eventId, questionId])
|
||||
}
|
||||
|
||||
enum SubmissionStatus {
|
||||
PENDING
|
||||
ACCEPTED
|
||||
REJECTED
|
||||
COMPILATION_ERROR
|
||||
RUNTIME_ERROR
|
||||
INTERNAL_ERROR
|
||||
PARTIAL_ACCEPTED
|
||||
SKIPPED
|
||||
}
|
||||
|
||||
model Submission {
|
||||
id String @id @default(cuid())
|
||||
submitter User @relation(fields: [submitterId], references: [id])
|
||||
submitterId String @db.VarChar(255)
|
||||
eventQuestion EventQuestion @relation(fields: [eventQuestionId], references: [id])
|
||||
eventQuestionId String @db.VarChar(255)
|
||||
language CodeLanguage
|
||||
code String @db.Text
|
||||
status SubmissionStatus
|
||||
statusMessage String?
|
||||
memoryUsage Int?
|
||||
executionTime Int?
|
||||
score Int? @default(0)
|
||||
createdAt DateTime @default(now()) @db.Timestamptz
|
||||
updatedAt DateTime @updatedAt @db.Timestamptz
|
||||
testResults TestResult[]
|
||||
}
|
||||
|
||||
enum TestResultStatus {
|
||||
ACCEPTED
|
||||
REJECTED
|
||||
TIMEOUT
|
||||
MEMORY_LIMIT_EXCEEDED
|
||||
COMPILATION_ERROR
|
||||
RUNTIME_ERROR
|
||||
INTERNAL_ERROR
|
||||
OUTPUT_MISMATCH
|
||||
SKIPPED
|
||||
}
|
||||
|
||||
model TestResult {
|
||||
id String @id @default(cuid())
|
||||
submission Submission @relation(fields: [submissionId], references: [id])
|
||||
submissionId String
|
||||
input String @db.Text
|
||||
output String @db.Text
|
||||
status TestResultStatus
|
||||
statusMessage String?
|
||||
memoryUsage Int?
|
||||
executionTime Int?
|
||||
score Int? @default(0)
|
||||
createdAt DateTime @default(now()) @db.Timestamptz
|
||||
updatedAt DateTime @updatedAt @db.Timestamptz
|
||||
}
|
574
prisma/seed.ts
Normal file
574
prisma/seed.ts
Normal file
@ -0,0 +1,574 @@
|
||||
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();
|
5
src/lib/prisma.ts
Normal file
5
src/lib/prisma.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
const prisma: PrismaClient = new PrismaClient();
|
||||
|
||||
export default prisma;
|
Loading…
Reference in New Issue
Block a user