177 lines
5.0 KiB
TypeScript
177 lines
5.0 KiB
TypeScript
// 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, InstructorGender, Variant} from "@/interfaces/exam";
|
|
import {capitalize, flatten, uniqBy} 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,
|
|
instructorGender?: InstructorGender,
|
|
): Promise<ExamWithUser[]> => {
|
|
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 = assignees.map(async (assignee) => {
|
|
const selectedModulePromises = selectedModules.map(async (module: Module) => {
|
|
try {
|
|
const exams: Exam[] = await getExams(db, module, "true", assignee, variant, instructorGender);
|
|
|
|
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 = selectedModules.map(async (module: Module) => {
|
|
const exams: Exam[] = await getExams(db, module, "false", undefined, variant, instructorGender);
|
|
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 {
|
|
examIDs,
|
|
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,
|
|
instructorGender,
|
|
...body
|
|
} = req.body as {
|
|
examIDs?: {id: string; module: Module}[];
|
|
selectedModules: Module[];
|
|
assignees: string[];
|
|
generateMultiple: Boolean;
|
|
name: string;
|
|
startDate: string;
|
|
endDate: string;
|
|
variant?: Variant;
|
|
instructorGender?: InstructorGender;
|
|
};
|
|
|
|
const exams: ExamWithUser[] = !!examIDs
|
|
? examIDs.flatMap((e) => assignees.map((a) => ({...e, assignee: a})))
|
|
: await generateExams(generateMultiple, selectedModules, assignees, variant, instructorGender);
|
|
|
|
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,
|
|
instructorGender,
|
|
...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 = uniqBy(exams, (x) => x.module)
|
|
.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,
|
|
},
|
|
environment: process.env.ENVIRONMENT,
|
|
},
|
|
[assignee.email],
|
|
"EnCoach - New Assignment!",
|
|
);
|
|
}
|
|
}
|