import type { NextApiRequest, NextApiResponse } from "next"; import { storage } from "@/firebase"; import { withIronSessionApiRoute } from "iron-session/next"; import { sessionOptions } from "@/lib/session"; import { ref, uploadBytes, getDownloadURL } from "firebase/storage"; import { AssignmentWithCorporateId } from "@/interfaces/results"; import moment from "moment-timezone"; import ExcelJS from "exceljs"; import { getSpecificUsers } from "@/utils/users.be"; import { checkAccess } from "@/utils/permissions"; import { getAssignmentsForCorporates } from "@/utils/assignments.be"; import { search } from "@/utils/search"; import { getGradingSystem } from "@/utils/grading.be"; import { User } from "@/interfaces/user"; import { calculateBandScore, getGradingLabel } from "@/utils/score"; import { Module } from "@/interfaces"; export default withIronSessionApiRoute(handler, sessionOptions); interface TableData { user: string; email: string; correct: number; corporate: string; submitted: boolean; date: moment.Moment; assignment: string; corporateId: string; score: number; level: string; part1?: string; part2?: string; part3?: string; part4?: string; part5?: string; } async function handler(req: NextApiRequest, res: NextApiResponse) { // if (req.method === "GET") return get(req, res); if (req.method === "POST") return await post(req, res); } const searchFilters = [["email"], ["user"], ["userId"]]; async function post(req: NextApiRequest, res: NextApiResponse) { // verify if it's a logged user that is trying to export if (req.session.user) { if ( !checkAccess(req.session.user, ["mastercorporate", "corporate", "developer", "admin"]) ) { return res.status(403).json({ error: "Unauthorized" }); } const { ids, startDate, endDate, searchText, displaySelection = true, } = req.body as { ids: string[]; startDate?: string; endDate?: string; searchText: string; displaySelection?: boolean; }; const startDateParsed = startDate ? new Date(startDate) : undefined; const endDateParsed = endDate ? new Date(endDate) : undefined; const assignments = await getAssignmentsForCorporates( ids, startDateParsed, endDateParsed ); const assignmentUsers = [ ...new Set(assignments.flatMap((a) => a.assignees)), ]; const assigners = [...new Set(assignments.map((a) => a.assigner))]; const users = await getSpecificUsers(assignmentUsers); const assignerUsers = await getSpecificUsers(assigners); const assignerUsersGradingSystems = await Promise.all( assignerUsers.map(async (user: User) => { const data = await getGradingSystem(user); // in this context I need to override as I'll have to match to the assigner return { ...data, user: user.id }; }) ); const getGradingSystemHelper = ( exams: { id: string; module: Module; assignee: string }[], assigner: string, user: User, correct: number, total: number ) => { if (exams.some((e) => e.module === "level")) { const gradingSystem = assignerUsersGradingSystems.find( (gs) => gs.user === assigner ); if (gradingSystem) { const bandScore = calculateBandScore( correct, total, "level", user.focus ); return { label: getGradingLabel(bandScore, gradingSystem?.steps || []), score: bandScore, }; } } return { score: -1, label: "N/A" }; }; const tableResults = assignments .reduce((accmA: TableData[], a: AssignmentWithCorporateId) => { const userResults = a.assignees.map((assignee) => { const userStats = a.results.find((r) => r.user === assignee)?.stats || []; const userData = users.find((u) => u.id === assignee); const corporateUser = users.find((u) => u.id === a.assigner); const correct = userStats.reduce((n, e) => n + e.score.correct, 0); const total = userStats.reduce((n, e) => n + e.score.total, 0); const { label: level, score } = getGradingSystemHelper( a.exams, a.assigner, userData!, correct, total ); console.log("Level", level); const commonData = { user: userData?.name || "", email: userData?.email || "", userId: assignee, corporateId: a.corporateId, corporate: corporateUser?.name || "", assignment: a.name, level, score, }; if (userStats.length === 0) { return { ...commonData, correct: 0, submitted: false, // date: moment(), }; } const partsData = userStats.every((e) => e.module === "level") ? userStats.reduce((acc, e, index) => { return { ...acc, [`part${index}`]: `${e.score.correct}/${e.score.total}`, }; }, {}) : {}; return { ...commonData, correct, submitted: true, date: moment.max(userStats.map((e) => moment(e.date))), ...partsData, }; }) as TableData[]; return [...accmA, ...userResults]; }, []) .sort((a, b) => b.score - a.score); // Create a new workbook and add a worksheet const workbook = new ExcelJS.Workbook(); const worksheet = workbook.addWorksheet("Master Statistical"); const headers = [ { label: "User", value: (entry: TableData) => entry.user, }, { label: "Email", value: (entry: TableData) => entry.email, }, ...(displaySelection ? [ { label: "Corporate", value: (entry: TableData) => entry.corporate, }, ] : []), { label: "Assignment", value: (entry: TableData) => entry.assignment, }, { label: "Submitted", value: (entry: TableData) => (entry.submitted ? "Yes" : "No"), }, { label: "Correct", value: (entry: TableData) => entry.correct, }, { label: "Date", value: (entry: TableData) => entry.date?.format("YYYY/MM/DD") || "", }, { label: "Level", value: (entry: TableData) => entry.level, }, ...new Array(5).fill(0).map((_, index) => ({ label: `Part ${index + 1}`, value: (entry: TableData) => { const key = `part${index}` as keyof TableData; return entry[key] || ""; }, })), ]; const filteredSearch = searchText ? search(searchText, searchFilters, tableResults) : tableResults; worksheet.addRow(headers.map((h) => h.label)); (filteredSearch as TableData[]).forEach((entry) => { worksheet.addRow(headers.map((h) => h.value(entry))); }); // Convert workbook to Buffer (Node.js) or Blob (Browser) const buffer = await workbook.xlsx.writeBuffer(); // generate the file ref for storage const fileName = `${Date.now().toString()}.xlsx`; const refName = `statistical/${fileName}`; const fileRef = ref(storage, refName); // upload the pdf to storage await uploadBytes(fileRef, buffer, { contentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", }); const url = await getDownloadURL(fileRef); res.status(200).end(url); return; } return res.status(401).json({ error: "Unauthorized" }); }