Added some code comments
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user