From e6c82412bf97982931e6a130e27668314b5e94d6 Mon Sep 17 00:00:00 2001 From: Joao Ramos Date: Mon, 8 Jan 2024 01:01:17 +0000 Subject: [PATCH] Added integration with backend to fetch skills feedback --- src/interfaces/module.scores.ts | 1 + src/pages/api/stats/[id]/export.tsx | 133 +++++++++++++++++++++------- 2 files changed, 104 insertions(+), 30 deletions(-) diff --git a/src/interfaces/module.scores.ts b/src/interfaces/module.scores.ts index 622f9ba4..53ee07fe 100644 --- a/src/interfaces/module.scores.ts +++ b/src/interfaces/module.scores.ts @@ -3,6 +3,7 @@ import {Module} from "@/interfaces"; export interface ModuleScore { score: number; total: number; + code: Module; module: Module | 'Overall'; feedback?: string, png?: string, diff --git a/src/pages/api/stats/[id]/export.tsx b/src/pages/api/stats/[id]/export.tsx index 7c345516..4f889ab7 100644 --- a/src/pages/api/stats/[id]/export.tsx +++ b/src/pages/api/stats/[id]/export.tsx @@ -22,6 +22,10 @@ import { ModuleScore } from "@/interfaces/module.scores"; import qrcode from "qrcode"; import { SkillExamDetails } from "@/exams/pdf/details/skill.exam"; import { LevelExamDetails } from "@/exams/pdf/details/level.exam"; +import { calculateBandScore } from "@/utils/score"; +import axios from "axios"; +import { moduleLabels } from "@/utils/moduleUtils"; + const db = getFirestore(app); export default withIronSessionApiRoute(handler, sessionOptions); @@ -115,6 +119,36 @@ const getFeedback = (module: Module) => { } }; +interface SkillsFeedbackRequest { + code: Module; + name: string; + grade: number; +} + +interface SkillsFeedbackResponse extends SkillsFeedbackRequest { + evaluation: string; + suggestions: string; +} + +const getSkillsFeedback = async (sections: SkillsFeedbackRequest[]) => { + try { + const backendRequest = await axios.post( + `${process.env.BACKEND_URL}/grading_summary`, + { sections }, + { + headers: { + Authorization: `Bearer ${process.env.BACKEND_JWT}`, + }, + } + ); + + return backendRequest.data?.sections; + } catch (err) { + console.log(err); + return null; + } +}; + const generateQRCode = async (link: string) => { try { const qrCodeDataURL = await qrcode.toDataURL(link); @@ -125,14 +159,18 @@ const generateQRCode = async (link: string) => { } }; -type RADIAL_PROGRESS_COLOR = 'laranja' | 'azul'; +type RADIAL_PROGRESS_COLOR = "laranja" | "azul"; -const getRadialProgressPNG = (color: RADIAL_PROGRESS_COLOR, score: number, total: number) => { +const getRadialProgressPNG = ( + color: RADIAL_PROGRESS_COLOR, + score: number, + total: number +) => { const percent = (score / total) * 100; const remainder = percent % 10; const roundedPercent = percent - remainder; return `public/radial_progress/${color}_${roundedPercent}.png`; -} +}; async function post(req: NextApiRequest, res: NextApiResponse) { if (req.session.user) { @@ -163,37 +201,72 @@ async function post(req: NextApiRequest, res: NextApiResponse) { const user = docUser.data() as User; const stats = docsSnap.docs.map((d) => d.data()); - const results = (stats.reduce((accm: ModuleScore[], { module, score }) => { - const fixedModuleStr = module[0].toUpperCase() + module.substring(1); - if (accm.find((e: ModuleScore) => e.module === fixedModuleStr)) { - return accm.map((e: ModuleScore) => { - if (e.module === fixedModuleStr) { - return { - ...e, - score: e.score + score.correct, - total: e.total + score.total, - }; - } + const results = ( + stats.reduce((accm: ModuleScore[], { module, score }) => { + const fixedModuleStr = module[0].toUpperCase() + module.substring(1); + if (accm.find((e: ModuleScore) => e.module === fixedModuleStr)) { + return accm.map((e: ModuleScore) => { + if (e.module === fixedModuleStr) { + return { + ...e, + score: e.score + score.correct, + total: e.total + score.total, + }; + } - return e; - }); - } + return e; + }); + } - return [ - ...accm, - { - module: fixedModuleStr, - score: score.correct, - total: score.total, - feedback: getFeedback(module), - }, - ]; - }, []) as ModuleScore[]).map((moduleScore) => { + return [ + ...accm, + { + module: fixedModuleStr, + score: score.correct, + total: score.total, + feedback: getFeedback(module), + code: module, + }, + ]; + }, []) as ModuleScore[] + ).map((moduleScore) => { const { score, total } = moduleScore; + const bandScore = calculateBandScore( + score, + total, + moduleScore.code as Module, + user.focus + ); + return { ...moduleScore, - png: getRadialProgressPNG('azul', score, total), + png: getRadialProgressPNG("azul", score, total), + bandScore, + }; + }); + + const skillsFeedback = + (await getSkillsFeedback( + results.map(({ code, bandScore }) => ({ + code, + name: moduleLabels[code], + grade: bandScore, + })) + )) || ([] as SkillsFeedbackResponse[]); + + const finalResults = results.map((result) => { + const feedback = skillsFeedback.find( + (f: SkillsFeedbackResponse) => f.code === result.module + ); + + if (feedback) { + return { + ...result, + feedback: feedback?.evaluation + " " + feedback?.suggestions, + }; } + + return result; }); const [stat] = stats as Stat[]; @@ -211,9 +284,9 @@ async function post(req: NextApiRequest, res: NextApiResponse) { module: "Overall", score: overallScore, total: overallTotal, - png: getRadialProgressPNG('laranja', overallScore, overallTotal), + png: getRadialProgressPNG("laranja", overallScore, overallTotal), } as ModuleScore; - const testDetails = [overallDetail, ...results]; + const testDetails = [overallDetail, ...finalResults]; const renderDetails = () => { if (stats[0].module === "level") { return ;