// Next.js API route support: https://nextjs.org/docs/api-routes/introduction import type {NextApiRequest, NextApiResponse} from "next"; import {app} from "@/firebase"; import {getFirestore, collection, getDocs, query, where, setDoc, doc, getDoc} from "firebase/firestore"; import {withIronSessionApiRoute} from "iron-session/next"; import {sessionOptions} from "@/lib/session"; import {uuidv4} from "@firebase/util"; import {Module} from "@/interfaces"; import {getExams} from "@/utils/exams.be"; import {Exam, Variant} from "@/interfaces/exam"; import {capitalize, flatten} from "lodash"; import {User} from "@/interfaces/user"; import moment from "moment"; import {sendEmail} from "@/email"; const db = getFirestore(app); export default withIronSessionApiRoute(handler, sessionOptions); async function handler(req: NextApiRequest, res: NextApiResponse) { if (!req.session.user) { res.status(401).json({ok: false}); return; } if (req.method === "GET") return GET(req, res); if (req.method === "POST") return POST(req, res); res.status(404).json({ok: false}); } async function GET(req: NextApiRequest, res: NextApiResponse) { const q = query(collection(db, "assignments")); const snapshot = await getDocs(q); const docs = snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data(), })); res.status(200).json(docs); } interface ExamWithUser { module: Module; id: string; assignee: string; } function getRandomIndex(arr: any[]): number { const randomIndex = Math.floor(Math.random() * arr.length); return randomIndex; } const generateExams = async ( generateMultiple: Boolean, selectedModules: Module[], assignees: string[], variant?: Variant, ): Promise => { if (generateMultiple) { // for optimization purposes, it would be better to create a new endpoint that returned the answers for all users at once const allExams = await assignees.map(async (assignee) => { const selectedModulePromises = await selectedModules.map(async (module: Module) => { try { const exams: Exam[] = await getExams(db, module, "true", assignee, variant); const exam = exams[getRandomIndex(exams)]; if (exam) { return {module: exam.module, id: exam.id, assignee}; } return null; } catch (e) { console.error(e); return null; } }, []); const newModules = await Promise.all(selectedModulePromises); return newModules; }, []); const exams = flatten(await Promise.all(allExams)).filter((x) => x !== null) as ExamWithUser[]; return exams; } const selectedModulePromises = await selectedModules.map(async (module: Module) => { const exams: Exam[] = await getExams(db, module, "false", undefined); const exam = exams[getRandomIndex(exams)]; if (exam) { return {module: exam.module, id: exam.id}; } return null; }); const exams = await Promise.all(selectedModulePromises); const examesFiltered = exams.filter((x) => x !== null) as ExamWithUser[]; return flatten(assignees.map((assignee) => examesFiltered.map((exam) => ({...exam, assignee})))); }; async function POST(req: NextApiRequest, res: NextApiResponse) { const { selectedModules, assignees, // Generate multiple true would generate an unique exam for each user // false would generate the same exam for all users generateMultiple = false, variant, ...body } = req.body as { selectedModules: Module[]; assignees: string[]; generateMultiple: Boolean; name: string; startDate: string; endDate: string; variant?: Variant; }; const exams: ExamWithUser[] = await generateExams(generateMultiple, selectedModules, assignees, variant); if (exams.length === 0) { res.status(400).json({ok: false, error: "No exams found for the selected modules"}); return; } await setDoc(doc(db, "assignments", uuidv4()), { assigner: req.session.user?.id, assignees, results: [], exams, ...body, }); res.status(200).json({ok: true}); for (const assigneeID of assignees) { const assigneeSnapshot = await getDoc(doc(db, "users", assigneeID)); if (!assigneeSnapshot.exists()) continue; const assignee = {id: assigneeID, ...assigneeSnapshot.data()} as User; const name = body.name; const teacher = req.session.user!; const examModulesLabel = exams.map((x) => capitalize(x.module)).join(", "); const startDate = moment(body.startDate).format("DD/MM/YYYY - HH:mm"); const endDate = moment(body.endDate).format("DD/MM/YYYY - HH:mm"); await sendEmail( "assignment", {user: {name: assignee.name}, assignment: {name, startDate, endDate, modules: examModulesLabel, assigner: teacher.name}}, [assignee.email], "EnCoach - New Assignment!", ); } }