Added some code comments

This commit is contained in:
Joao Ramos
2024-01-08 18:58:54 +00:00
parent e6c82412bf
commit 12d608879d
2 changed files with 162 additions and 119 deletions

View File

@@ -94,31 +94,6 @@ const getPerformanceSummary = (module: Module, score: number) => {
if (module === "level") return getLevelSummary(score);
return getExamSummary(score);
};
const getListeningFeedback = () =>
"Your listening skills are exceptional. You display a high level of attentiveness, accurately understanding spoken information across various contexts. Your ability to follow instructions and discern details from spoken content reflects a strong foundation in auditory comprehension. To further refine this skill, continue exposing yourself to diverse listening materials, including podcasts, interviews, and authentic conversations.";
const getReadingFeedback = () =>
"Your reading skills are advanced, demonstrating a keen ability to comprehend and analyse written texts. You not only grasp the main ideas effectively but also excel in identifying supporting details and drawing inferences from context. Your enthusiasm for reading is evident, and I encourage you to explore more diverse and challenging materials to further expand your vocabulary and enhance your critical thinking skills.";
const getWritingFeedback = () =>
"In the realm of writing, you showcase a commendable command of language. Your ability to construct well-organized and coherent sentences is notable. You exhibit a strong grasp of grammar and punctuation, contributing to the overall clarity of your written expression. Continue refining your writing style, and consider experimenting with different genres to unleash your creative potential.";
const getSpeakingFeedback = () =>
"Your oral communication skills are a standout feature of your language proficiency. You articulate ideas with clarity and confidence, actively participating in discussions. Your ability to express yourself verbally is a valuable asset. To enhance your speaking skills even further, consider taking on leadership roles in group activities and engaging in more challenging speaking tasks, such as presentations and debates.";
const getFeedback = (module: Module) => {
switch (module) {
case "listening":
return getListeningFeedback();
case "reading":
return getReadingFeedback();
case "writing":
return getWritingFeedback();
case "speaking":
return getSpeakingFeedback();
default:
return "";
}
};
interface SkillsFeedbackRequest {
code: Module;
name: string;
@@ -144,7 +119,23 @@ const getSkillsFeedback = async (sections: SkillsFeedbackRequest[]) => {
return backendRequest.data?.sections;
} catch (err) {
console.log(err);
return err;
}
};
const handleSkillsFeedbackRequest = async (
sections: SkillsFeedbackRequest[]
): Promise<SkillsFeedbackResponse[] | null> => {
let i = 0;
try {
const data = await getSkillsFeedback(sections);
return data;
} catch (err) {
if (i < 3) {
i++;
return handleSkillsFeedbackRequest(sections);
}
return null;
}
};
@@ -173,15 +164,10 @@ const getRadialProgressPNG = (
};
async function post(req: NextApiRequest, res: NextApiResponse) {
// verify if it's a logged user that is trying to export
if (req.session.user) {
const { id } = req.query as { id: string };
// const codeCheckerRef = await getDocs(
// query(collection(db, "codes"), where("checkout", "==", checkout))
// );
// const docRef = doc(db, "stats", id).where;
// const docSnap = await getDoc(docRef);
// fetch stats entries for this particular user with the requested exam session
const docsSnap = await getDocs(
query(
collection(db, "stats"),
@@ -195,106 +181,150 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
return;
}
const docUser = await getDoc(doc(db, "users", req.session.user.id));
const stats = docsSnap.docs.map((d) => d.data());
// verify if the stats already have a pdf generated
const hasPDF = stats.find((s) => s.pdf);
if (docUser.exists()) {
const user = docUser.data() as User;
if (hasPDF) {
// if it does, return the pdf url
res.status(200).end(hasPDF.pdf);
return;
}
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,
};
}
try {
// generate the pdf report
const docUser = await getDoc(doc(db, "users", req.session.user.id));
return e;
});
}
if (docUser.exists()) {
// we'll need the user in order to get the user data (name, email, focus, etc);
const user = docUser.data() as User;
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
// generate the QR code for the report
const qrcode = await generateQRCode(
(req.headers.origin || "") + req.url
);
return {
...moduleScore,
png: getRadialProgressPNG("azul", score, total),
bandScore,
};
});
if (!qrcode) {
res.status(500).json({ ok: false });
return;
}
const skillsFeedback =
(await getSkillsFeedback(
// stats may contain multiple exams of the same type so we need to aggregate them
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 [
...accm,
{
module: fixedModuleStr,
score: score.correct,
total: score.total,
code: module,
},
];
}, []) as ModuleScore[]
).map((moduleScore) => {
const { score, total } = moduleScore;
// with all the scores aggreated we can calculate the band score for each module
const bandScore = calculateBandScore(
score,
total,
moduleScore.code as Module,
user.focus
);
return {
...moduleScore,
// generate the closest radial progress png for the score
png: getRadialProgressPNG("azul", score, total),
bandScore,
};
});
// get the skills feedback from the backend based on the module grade
const skillsFeedback = (await handleSkillsFeedbackRequest(
results.map(({ code, bandScore }) => ({
code,
name: moduleLabels[code],
grade: bandScore,
}))
)) || ([] as SkillsFeedbackResponse[]);
)) as SkillsFeedbackResponse[];
const finalResults = results.map((result) => {
const feedback = skillsFeedback.find(
(f: SkillsFeedbackResponse) => f.code === result.module
if (!skillsFeedback) {
res.status(500).json({ ok: false });
return;
}
// assign the feedback to the results
const finalResults = results.map((result) => {
const feedback = skillsFeedback.find(
(f: SkillsFeedbackResponse) => f.code === result.code
);
if (feedback) {
return {
...result,
feedback: feedback?.evaluation + " " + feedback?.suggestions,
};
}
return result;
});
// generate the file ref for storage
const fileName = `${Date.now().toString()}.pdf`;
const fileRef = ref(storage, `exam_report/${fileName}`);
// calculate the overall score out of all the aggregated results
const overallScore = results.reduce(
(accm, { score }) => accm + score,
0
);
const overallTotal = results.reduce(
(accm, { total }) => accm + total,
0
);
const overallResult = overallScore / overallTotal;
// generate the performance summary based on the overall result
const performanceSummary = getPerformanceSummary(
"level",
overallResult
);
if (feedback) {
return {
...result,
feedback: feedback?.evaluation + " " + feedback?.suggestions,
};
}
// generate the overall detail report
const overallDetail = {
module: "Overall",
score: overallScore,
total: overallTotal,
png: getRadialProgressPNG("laranja", overallScore, overallTotal),
} as ModuleScore;
const testDetails = [overallDetail, ...finalResults];
return result;
});
const [stat] = stats;
// level exams have a different report structure than the skill exams
const renderDetails = () => {
if (stat.module === "level") {
return <LevelExamDetails detail={overallDetail} />;
}
const [stat] = stats as Stat[];
const fileName = `${Date.now().toString()}.pdf`;
const fileRef = ref(storage, `exam_report/${fileName}`);
const overallScore = results.reduce((accm, { score }) => accm + score, 0);
const overallTotal = results.reduce((accm, { total }) => accm + total, 0);
const overallResult = overallScore / overallTotal;
const performanceSummary = getPerformanceSummary("level", overallResult);
const qrcode = await generateQRCode((req.headers.origin || "") + req.url);
const overallDetail = {
module: "Overall",
score: overallScore,
total: overallTotal,
png: getRadialProgressPNG("laranja", overallScore, overallTotal),
} as ModuleScore;
const testDetails = [overallDetail, ...finalResults];
const renderDetails = () => {
if (stats[0].module === "level") {
return <LevelExamDetails detail={overallDetail} />;
}
return <SkillExamDetails testDetails={testDetails} />;
};
if (qrcode) {
return <SkillExamDetails testDetails={testDetails} />;
};
const pdfStream = await ReactPDF.renderToStream(
<PDFReport
date={new Date(stat.date).toLocaleString()}
@@ -310,10 +340,13 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
/>
);
// upload the pdf to storage
const pdfBuffer = await streamToBuffer(pdfStream);
const snapshot = await uploadBytes(fileRef, pdfBuffer, {
contentType: "application/pdf",
});
// update the stats entries with the pdf url to prevent duplication
docsSnap.docs.forEach(async (doc) => {
await updateDoc(doc.ref, {
pdf: snapshot.ref.fullPath,
@@ -322,6 +355,12 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
res.status(200).end(snapshot.ref.fullPath);
return;
}
res.status(401).json({ ok: false });
return;
} catch (err) {
res.status(500).json({ ok: false });
return;
}
}