import {collection, getDocs, query, where, setDoc, doc, Firestore, getDoc, and} from "firebase/firestore"; import {shuffle} from "lodash"; import {Difficulty, Exam, InstructorGender, SpeakingExam, Variant, WritingExam} from "@/interfaces/exam"; import {DeveloperUser, Stat, StudentUser, User} from "@/interfaces/user"; import {Module} from "@/interfaces"; import {getCorporateUser} from "@/resources/user"; import {getUserCorporate} from "./groups.be"; import { Db, ObjectId } from "mongodb"; export const getExams = async ( db: Db, module: Module, avoidRepeated: string, // added userId as due to assignments being set from the teacher to the student // we need to make sure we are serving exams not executed by the user and not // by the teacher that performed the request userId: string | undefined, variant?: Variant, instructorGender?: InstructorGender, ): Promise => { const allExams = await db.collection(module).find({ isDiagnostic: false }).toArray(); const shuffledPublicExams = ( shuffle( allExams.map((doc) => ({ ...doc, module, })) as Exam[], ) ).filter((x) => !x.private); let exams: Exam[] = await filterByOwners(shuffledPublicExams, userId); exams = filterByVariant(exams, variant); exams = filterByInstructorGender(exams, instructorGender); exams = await filterByDifficulty(db, exams, module, userId); exams = await filterByPreference(db, exams, module, userId); if (avoidRepeated === "true") { const stats = await db.collection("stats").find({ user: userId }).toArray(); const filteredExams = exams.filter((x) => !stats.map((s) => s.exam).includes(x.id)); return filteredExams.length > 0 ? filteredExams : exams; } return exams; }; const filterByInstructorGender = (exams: Exam[], instructorGender?: InstructorGender) => { if (!instructorGender || instructorGender === "varied") return exams; return exams.filter((e) => (e.module === "speaking" ? e.instructorGender === instructorGender : true)); }; const filterByVariant = (exams: Exam[], variant?: Variant) => { const filtered = variant && variant === "partial" ? exams.filter((x) => x.variant === "partial") : exams.filter((x) => x.variant !== "partial"); return filtered.length > 0 ? filtered : exams; }; const filterByOwners = async (exams: Exam[], userID?: string) => { if (!userID) return exams.filter((x) => !x.owners || x.owners.length === 0); return await Promise.all( exams.filter(async (x) => { if (!x.owners) return true; if (x.owners.length === 0) return true; if (x.owners.includes(userID)) return true; const corporate = await getUserCorporate(userID); return !corporate ? false : x.owners.includes(corporate.id); }), ); }; const filterByDifficulty = async (db: Db, exams: Exam[], module: Module, userID?: string) => { if (!userID) return exams; const user = await db.collection("users").findOne({ _id: new ObjectId(userID) }); if (!user) return exams; const difficulty = user.levels[module] <= 3 ? "easy" : user.levels[module] <= 6 ? "medium" : "hard"; const filteredExams = exams.filter((exam) => exam.difficulty === difficulty); return filteredExams.length === 0 ? exams : filteredExams; }; const filterByPreference = async (db: Db, exams: Exam[], module: Module, userID?: string) => { if (!["speaking", "writing"].includes(module)) return exams; if (!userID) return exams; const user = await db.collection("users").findOne({ _id: new ObjectId(userID) }); if (!user) return exams; if (!["developer", "student"].includes(user.type)) return exams; if (!user.preferredTopics || user.preferredTopics.length === 0) return exams; const userTopics = user.preferredTopics; const topicalExams = exams.filter((e) => { const exam = e as WritingExam | SpeakingExam; const topics = exam.exercises.map((x) => x.topic).filter((x) => !!x) as string[]; return topics.some((topic) => userTopics.map((x) => x.toLowerCase()).includes(topic.toLowerCase())); }); return topicalExams.length > 0 ? shuffle(topicalExams) : exams; };