diff --git a/src/dashboards/MasterCorporate/MasterStatistical.tsx b/src/dashboards/MasterCorporate/MasterStatistical.tsx index 8978395a..8d3fb70b 100644 --- a/src/dashboards/MasterCorporate/MasterStatistical.tsx +++ b/src/dashboards/MasterCorporate/MasterStatistical.tsx @@ -343,7 +343,7 @@ const MasterStatistical = (props: Props) => { {renderSearch()}
-
diff --git a/src/pages/api/assignments/statistical/excel.ts b/src/pages/api/assignments/statistical/excel.ts index 7eed838d..43345e88 100644 --- a/src/pages/api/assignments/statistical/excel.ts +++ b/src/pages/api/assignments/statistical/excel.ts @@ -1,254 +1,229 @@ -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 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"; +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; + 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); + // 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 - ); + // 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 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 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, - }; - } - } + 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 || "academic"); + return { + label: getGradingLabel(bandScore, gradingSystem?.steps || []), + score: bandScore, + }; + } + } - return { score: -1, label: "N/A" }; - }; + 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 - ); + 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(), - }; - } + 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}`, - }; - }, {}) - : {}; + 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 { + ...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); + 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"); + // 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 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; + 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))); - }); + 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(); + // 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", - }); + // 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; - } + const url = await getDownloadURL(fileRef); + res.status(200).end(url); + return; + } - return res.status(401).json({ error: "Unauthorized" }); + return res.status(401).json({error: "Unauthorized"}); }