From eec1bb0c30d1a09b8f2084c54a865c045a75ca09 Mon Sep 17 00:00:00 2001 From: Joao Ramos Date: Wed, 21 Aug 2024 00:37:33 +0100 Subject: [PATCH] Added final touches --- src/exams/pdf/level.test.report.tsx | 97 ++++++--- src/pages/api/stats/[id]/[export]/pdf.tsx | 22 +- src/utils/groups.be.ts | 234 ++++++++++++++-------- 3 files changed, 234 insertions(+), 119 deletions(-) diff --git a/src/exams/pdf/level.test.report.tsx b/src/exams/pdf/level.test.report.tsx index 59c6ca1f..c09cba73 100644 --- a/src/exams/pdf/level.test.report.tsx +++ b/src/exams/pdf/level.test.report.tsx @@ -3,6 +3,7 @@ import React from "react"; import { Document, Page, View, Text, Image } from "@react-pdf/renderer"; import { ModuleScore } from "@/interfaces/module.scores"; import { styles } from "./styles"; +import TestReportFooter from "./test.report.footer"; import { StyleSheet } from "@react-pdf/renderer"; @@ -17,17 +18,16 @@ const customStyles = StyleSheet.create({ }, table: { width: "100%", - margin: "10px", }, tableRow: { flexDirection: "row", }, - tableCol50: { - width: "50%", // First column width (50%) + tableCol70: { + width: "70%", // First column width (50%) borderStyle: "solid", borderWidth: 1, borderColor: "#000", - padding: 5, + // padding: 5, }, tableCol25: { width: "16.67%", // Remaining four columns each get 1/6 of the total width (50% / 3 = 16.67%) @@ -43,11 +43,20 @@ const customStyles = StyleSheet.create({ borderColor: "#000", padding: 5, }, + tableCol10: { + width: "10%", // Width for each of the 5 sub-columns (50% / 5 = 20%) + borderStyle: "solid", + borderWidth: 1, + borderColor: "#000", + padding: 5, + }, tableCellHeader: { - backgroundColor: "#d3d3d3", - fontWeight: "bold", + fontSize: 12, textAlign: "center", }, + tableCellHeaderColor: { + backgroundColor: "#d3d3d3", + }, tableCell: { fontSize: 10, textAlign: "center", @@ -86,7 +95,7 @@ const LevelTestReport = ({ const defaultTextStyle = [styles.textFont, { fontSize: 8 }]; return ( - + Corporate Name: {corporateName} @@ -111,56 +120,97 @@ const LevelTestReport = ({ {/* Header Row */} - - Test sections + + + Test sections + - + Time spent - + Score - + {uniqueExercises.map((exercise, index) => ( - + Part {index + 1} ))} - + - + - + {uniqueExercises.map((exercise, index) => ( - + {exercise.name} ))} - + - + - + {uniqueExercises.map((exercise, index) => ( - + {exercise.result} @@ -168,14 +218,15 @@ const LevelTestReport = ({ ))} - + {timeSpent} - + {score} + ); diff --git a/src/pages/api/stats/[id]/[export]/pdf.tsx b/src/pages/api/stats/[id]/[export]/pdf.tsx index fdfbffc9..e04ffa39 100644 --- a/src/pages/api/stats/[id]/[export]/pdf.tsx +++ b/src/pages/api/stats/[id]/[export]/pdf.tsx @@ -34,7 +34,7 @@ import { streamToBuffer, } from "@/utils/pdf"; import moment from "moment-timezone"; - +import { getCorporateNameForStudent } from "@/utils/groups.be"; const db = getFirestore(app); export default withIronSessionApiRoute(handler, sessionOptions); @@ -325,14 +325,14 @@ async function post(req: NextApiRequest, res: NextApiResponse) { } const userId = stats[statIndex].user; - // if (hasPDF) { - // // if it does, return the pdf url - // const fileRef = ref(storage, hasPDF.pdf!.path); - // const url = await getDownloadURL(fileRef); + if (hasPDF) { + // if it does, return the pdf url + const fileRef = ref(storage, hasPDF.pdf!.path); + const url = await getDownloadURL(fileRef); - // res.status(200).end(url); - // return; - // } + res.status(200).end(url); + return; + } try { // generate the pdf report @@ -355,7 +355,7 @@ async function post(req: NextApiRequest, res: NextApiResponse) { stats.reduce((accm, s: Stat) => accm + (s.timeSpent || 0), 0) / 60 } minutes`; const score = stats.reduce((accm, s) => accm + s.score.correct, 0); - + const corporateName = await getCorporateNameForStudent(userId); const pdfStream = await ReactPDF.renderToStream( { - const corporateRef = await getDoc(doc(db, "users", corporateID)); - const participantRef = await getDoc(doc(db, "users", participantID)); +export const updateExpiryDateOnGroup = async ( + participantID: string, + corporateID: string +) => { + const corporateRef = await getDoc(doc(db, "users", corporateID)); + const participantRef = await getDoc(doc(db, "users", participantID)); - if (!corporateRef.exists() || !participantRef.exists()) return; + if (!corporateRef.exists() || !participantRef.exists()) return; - const corporate = { - ...corporateRef.data(), - id: corporateRef.id, - } as CorporateUser; - const participant = {...participantRef.data(), id: participantRef.id} as StudentUser | TeacherUser; + const corporate = { + ...corporateRef.data(), + id: corporateRef.id, + } as CorporateUser; + const participant = { ...participantRef.data(), id: participantRef.id } as + | StudentUser + | TeacherUser; - if (corporate.type !== "corporate" || (participant.type !== "student" && participant.type !== "teacher")) return; + if ( + corporate.type !== "corporate" || + (participant.type !== "student" && participant.type !== "teacher") + ) + return; - if (!corporate.subscriptionExpirationDate || !participant.subscriptionExpirationDate) { - return await setDoc(doc(db, "users", participant.id), {subscriptionExpirationDate: null}, {merge: true}); - } + if ( + !corporate.subscriptionExpirationDate || + !participant.subscriptionExpirationDate + ) { + return await setDoc( + doc(db, "users", participant.id), + { subscriptionExpirationDate: null }, + { merge: true } + ); + } - const corporateDate = moment(corporate.subscriptionExpirationDate); - const participantDate = moment(participant.subscriptionExpirationDate); + const corporateDate = moment(corporate.subscriptionExpirationDate); + const participantDate = moment(participant.subscriptionExpirationDate); - if (corporateDate.isAfter(participantDate)) - return await setDoc(doc(db, "users", participant.id), {subscriptionExpirationDate: corporateDate.toISOString()}, {merge: true}); + if (corporateDate.isAfter(participantDate)) + return await setDoc( + doc(db, "users", participant.id), + { subscriptionExpirationDate: corporateDate.toISOString() }, + { merge: true } + ); - return; + return; }; export const getGroups = async () => { - const groupDocs = await getDocs(collection(db, "groups")); - return groupDocs.docs.map((x) => ({...x.data(), id: x.id})) as Group[]; + const groupDocs = await getDocs(collection(db, "groups")); + return groupDocs.docs.map((x) => ({ ...x.data(), id: x.id })) as Group[]; }; export const getUserGroups = async (id: string): Promise => { - const groupDocs = await getDocs(query(collection(db, "groups"), where("admin", "==", id))); - return groupDocs.docs.map((x) => ({...x.data(), id})) as Group[]; + const groupDocs = await getDocs( + query(collection(db, "groups"), where("admin", "==", id)) + ); + return groupDocs.docs.map((x) => ({ ...x.data(), id })) as Group[]; }; -export const getAllAssignersByCorporate = async (corporateID: string): Promise => { - const groups = await getUserGroups(corporateID); - const groupUsers = (await Promise.all(groups.map(async (g) => await Promise.all(g.participants.map(getUser))))).flat(); - const teacherPromises = await Promise.all( - groupUsers.map(async (u) => - u.type === "teacher" ? u.id : u.type === "corporate" ? [...(await getAllAssignersByCorporate(u.id)), u.id] : undefined, - ), - ); +export const getAllAssignersByCorporate = async ( + corporateID: string +): Promise => { + const groups = await getUserGroups(corporateID); + const groupUsers = ( + await Promise.all( + groups.map(async (g) => await Promise.all(g.participants.map(getUser))) + ) + ).flat(); + const teacherPromises = await Promise.all( + groupUsers.map(async (u) => + u.type === "teacher" + ? u.id + : u.type === "corporate" + ? [...(await getAllAssignersByCorporate(u.id)), u.id] + : undefined + ) + ); - return teacherPromises.filter((x) => !!x).flat() as string[]; + return teacherPromises.filter((x) => !!x).flat() as string[]; }; export const getGroupsForUser = async (admin: string, participant: string) => { - try { - const queryConstraints = [ - ...(admin ? [where("admin", "==", admin)] : []), - ...(participant - ? [where("participants", "array-contains", participant)] - : []), - ]; - const snapshot = await getDocs( - queryConstraints.length > 0 - ? query(collection(db, "groups"), ...queryConstraints) - : collection(db, "groups") - ); - const groups = snapshot.docs.map((doc) => ({ - id: doc.id, - ...doc.data(), - })) as Group[]; - - return groups; - } catch (e) { - console.error(e); - return []; - } - }; + try { + const queryConstraints = [ + ...(admin ? [where("admin", "==", admin)] : []), + ...(participant + ? [where("participants", "array-contains", participant)] + : []), + ]; + const snapshot = await getDocs( + queryConstraints.length > 0 + ? query(collection(db, "groups"), ...queryConstraints) + : collection(db, "groups") + ); + const groups = snapshot.docs.map((doc) => ({ + id: doc.id, + ...doc.data(), + })) as Group[]; - export const getStudentGroupsForUsersWithoutAdmin = async (admin: string, participants: string[]) => { - try { - const queryConstraints = [ - ...(admin ? [where("admin", "!=", admin)] : []), - ...(participants - ? [where("participants", "array-contains-any", participants)] - : []), - where("name", "==", "Students"), - ]; - const snapshot = await getDocs( - queryConstraints.length > 0 - ? query(collection(db, "groups"), ...queryConstraints) - : collection(db, "groups") - ); - const groups = snapshot.docs.map((doc) => ({ - id: doc.id, - ...doc.data(), - })) as Group[]; - - return groups; - } catch (e) { - console.error(e); - return []; - } - }; \ No newline at end of file + return groups; + } catch (e) { + console.error(e); + return []; + } +}; + +export const getStudentGroupsForUsersWithoutAdmin = async ( + admin: string, + participants: string[] +) => { + try { + const queryConstraints = [ + ...(admin ? [where("admin", "!=", admin)] : []), + ...(participants + ? [where("participants", "array-contains-any", participants)] + : []), + where("name", "==", "Students"), + ]; + const snapshot = await getDocs( + queryConstraints.length > 0 + ? query(collection(db, "groups"), ...queryConstraints) + : collection(db, "groups") + ); + const groups = snapshot.docs.map((doc) => ({ + id: doc.id, + ...doc.data(), + })) as Group[]; + + return groups; + } catch (e) { + console.error(e); + return []; + } +}; + +export const getCorporateNameForStudent = async (studentID: string) => { + const groups = await getStudentGroupsForUsersWithoutAdmin("", [studentID]); + if (groups.length === 0) return ''; + + const adminUserIds = [...new Set(groups.map((g) => g.admin))]; + const adminUsersData = await getSpecificUsers(adminUserIds); + + if(adminUsersData.length === 0) return ''; + const admins = adminUsersData.filter((x) => x.type === 'corporate'); + + if(admins.length > 0) { + return (admins[0] as CorporateUser).corporateInformation.companyInformation.name; + } + + return ''; +};