Added level export to excel

This commit is contained in:
Joao Ramos
2024-09-05 23:30:03 +01:00
parent a61ad2cc7e
commit e433a150a9
4 changed files with 104 additions and 43 deletions

View File

@@ -18,6 +18,7 @@ import Checkbox from "@/components/Low/Checkbox";
import { useListSearch } from "@/hooks/useListSearch"; import { useListSearch } from "@/hooks/useListSearch";
import axios from "axios"; import axios from "axios";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import Button from "@/components/Low/Button";
interface Props { interface Props {
corporateUsers: User[]; corporateUsers: User[];
users: User[]; users: User[];
@@ -207,10 +208,11 @@ const MasterStatistical = (props: Props) => {
}), }),
]; ];
const { rows: filteredRows, renderSearch, text: searchText } = useListSearch( const {
searchFilters, rows: filteredRows,
tableResults renderSearch,
); text: searchText,
} = useListSearch(searchFilters, tableResults);
const table = useReactTable({ const table = useReactTable({
data: filteredRows, data: filteredRows,
@@ -240,7 +242,6 @@ const MasterStatistical = (props: Props) => {
); );
}; };
const triggerDownload = async () => { const triggerDownload = async () => {
try { try {
setDownloading(true); setDownloading(true);
@@ -267,28 +268,9 @@ const MasterStatistical = (props: Props) => {
} }
}; };
const renderIcon = () => {
if (downloading) {
return (
<span className={`text-mti-gray-dim loading loading-infinity w-6`} />
);
}
return (
<BsFileExcel
className={`text-mti-gray-dim text-2xl cursor-pointer`}
onClick={(e) => {
e.stopPropagation();
triggerDownload();
}}
/>
);
};
const consolidateResults = getStudentsConsolidateScore(); const consolidateResults = getStudentsConsolidateScore();
return ( return (
<> <>
{renderIcon()}
<div className="flex flex-wrap gap-2 items-center text-center"> <div className="flex flex-wrap gap-2 items-center text-center">
<IconCard <IconCard
Icon={BsBank} Icon={BsBank}
@@ -353,6 +335,15 @@ const MasterStatistical = (props: Props) => {
/> />
</div> </div>
{renderSearch()} {renderSearch()}
<div className="flex flex-col gap-3 justify-end">
<Button
className="max-w-[200px] h-[70px]"
variant="outline"
onClick={triggerDownload}
>
Download
</Button>
</div>
</div> </div>
<div> <div>

View File

@@ -11,7 +11,11 @@ import { getSpecificUsers } from "@/utils/users.be";
import { checkAccess } from "@/utils/permissions"; import { checkAccess } from "@/utils/permissions";
import { getAssignmentsForCorporates } from "@/utils/assignments.be"; import { getAssignmentsForCorporates } from "@/utils/assignments.be";
import { search } from "@/utils/search"; import { search } from "@/utils/search";
import { getGradingSystem } from "@/utils/grading.be";
import { Exam } from "@/interfaces/exam";
import { User } from "@/interfaces/user";
import { calculateBandScore, getGradingLabel } from "@/utils/score";
import { Module } from "@/interfaces";
const db = getFirestore(app); const db = getFirestore(app);
export default withIronSessionApiRoute(handler, sessionOptions); export default withIronSessionApiRoute(handler, sessionOptions);
@@ -25,6 +29,7 @@ interface TableData {
date: moment.Moment; date: moment.Moment;
assignment: string; assignment: string;
corporateId: string; corporateId: string;
level: string;
} }
async function handler(req: NextApiRequest, res: NextApiResponse) { async function handler(req: NextApiRequest, res: NextApiResponse) {
@@ -59,7 +64,42 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
const assignmentUsers = [ const assignmentUsers = [
...new Set(assignments.flatMap((a) => a.assignees)), ...new Set(assignments.flatMap((a) => a.assignees)),
]; ];
const assigners = [...new Set(assignments.map((a) => a.assigner))];
const users = await getSpecificUsers(assignmentUsers); 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 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 getGradingLabel(bandScore, gradingSystem?.steps || []);
}
}
return "N/A";
};
const tableResults = assignments.reduce( const tableResults = assignments.reduce(
(accmA: TableData[], a: AssignmentWithCorporateId) => { (accmA: TableData[], a: AssignmentWithCorporateId) => {
@@ -67,14 +107,25 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
const userStats = const userStats =
a.results.find((r) => r.user === assignee)?.stats || []; a.results.find((r) => r.user === assignee)?.stats || [];
const userData = users.find((u) => u.id === assignee); const userData = users.find((u) => u.id === assignee);
const corporate = users.find((u) => u.id === a.assigner)?.name || ""; 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 level = getGradingSystemHelper(
a.exams,
a.assigner,
userData!,
correct,
total
);
console.log("Level", level);
const commonData = { const commonData = {
user: userData?.name || "", user: userData?.name || "",
email: userData?.email || "", email: userData?.email || "",
userId: assignee, userId: assignee,
corporateId: a.corporateId, corporateId: a.corporateId,
corporate, corporate: corporateUser?.name || "",
assignment: a.name, assignment: a.name,
level,
}; };
if (userStats.length === 0) { if (userStats.length === 0) {
return { return {
@@ -87,7 +138,7 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
return { return {
...commonData, ...commonData,
correct: userStats.reduce((n, e) => n + e.score.correct, 0), correct,
submitted: true, submitted: true,
date: moment.max(userStats.map((e) => moment(e.date))), date: moment.max(userStats.map((e) => moment(e.date))),
}; };
@@ -129,11 +180,17 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
}, },
{ {
label: "Date", label: "Date",
value: (entry: TableData) => entry.date?.format("YYYY/MM/DD") || '', value: (entry: TableData) => entry.date?.format("YYYY/MM/DD") || "",
}, },
{
label: "Level",
value: (entry: TableData) => entry.level,
}
]; ];
const filteredSearch = searchText ? search(searchText, searchFilters, tableResults) : tableResults; const filteredSearch = searchText
? search(searchText, searchFilters, tableResults)
: tableResults;
worksheet.addRow(headers.map((h) => h.label)); worksheet.addRow(headers.map((h) => h.label));
(filteredSearch as TableData[]).forEach((entry) => { (filteredSearch as TableData[]).forEach((entry) => {

View File

@@ -15,6 +15,7 @@ import {Grading} from "@/interfaces";
import {getGroupsForUser} from "@/utils/groups.be"; import {getGroupsForUser} from "@/utils/groups.be";
import {uniq} from "lodash"; import {uniq} from "lodash";
import {getUser} from "@/utils/users.be"; import {getUser} from "@/utils/users.be";
import { getGradingSystem } from "@/utils/grading.be";
const db = getFirestore(app); const db = getFirestore(app);
@@ -31,19 +32,8 @@ async function get(req: NextApiRequest, res: NextApiResponse) {
return; return;
} }
const snapshot = await getDoc(doc(db, "grading", req.session.user.id)); const gradingSystem = await getGradingSystem(req.session.user);
if (snapshot.exists()) return res.status(200).json(snapshot.data()); return res.status(200).json(gradingSystem);
if (req.session.user.type !== "teacher" && req.session.user.type !== "student")
return res.status(200).json({steps: CEFR_STEPS, user: req.session.user.id});
const corporate = await getUserCorporate(req.session.user.id);
if (!corporate) return res.status(200).json(CEFR_STEPS);
const corporateSnapshot = await getDoc(doc(db, "grading", corporate.id));
if (corporateSnapshot.exists()) return res.status(200).json(snapshot.data());
return res.status(200).json({steps: CEFR_STEPS, user: req.session.user.id});
} }
async function post(req: NextApiRequest, res: NextApiResponse) { async function post(req: NextApiRequest, res: NextApiResponse) {

23
src/utils/grading.be.ts Normal file
View File

@@ -0,0 +1,23 @@
import { app } from "@/firebase";
import { getFirestore, doc, getDoc } from "firebase/firestore";
import { CEFR_STEPS } from "@/resources/grading";
import { getUserCorporate } from "@/utils/groups.be";
import { User } from "@/interfaces/user";
import { Grading } from "@/interfaces";
const db = getFirestore(app);
export const getGradingSystem = async (user: User): Promise<Grading> => {
const snapshot = await getDoc(doc(db, "grading", user.id));
if (snapshot.exists()) return snapshot.data() as Grading;
if (user.type !== "teacher" && user.type !== "student")
return { steps: CEFR_STEPS, user: user.id };
const corporate = await getUserCorporate(user.id);
if (!corporate) return { steps: CEFR_STEPS, user: user.id };
const corporateSnapshot = await getDoc(doc(db, "grading", corporate.id));
if (corporateSnapshot.exists()) return corporateSnapshot.data() as Grading;
return { steps: CEFR_STEPS, user: user.id };
};