/* eslint-disable @next/next/no-img-element */ import Layout from "@/components/High/Layout"; import Table from "@/components/High/Table"; import Checkbox from "@/components/Low/Checkbox"; import Separator from "@/components/Low/Separator"; import { Session } from "@/hooks/useSessions"; import { Entity, EntityWithRoles } from "@/interfaces/entity"; import { Exam } from "@/interfaces/exam"; import { Assignment, AssignmentResult } from "@/interfaces/results"; import { Group, Stat, StudentUser, User } from "@/interfaces/user"; import { sessionOptions } from "@/lib/session"; import { filterBy, findBy, mapBy, redirect, serialize } from "@/utils"; import { requestUser } from "@/utils/api"; import { getEntitiesAssignments } from "@/utils/assignments.be"; import { getEntitiesWithRoles } from "@/utils/entities.be"; import { getExamsByIds } from "@/utils/exams.be"; import { checkAccess, findAllowedEntities } from "@/utils/permissions"; import { getSessionsByAssignments, getSessionsByUser } from "@/utils/sessions.be"; import { getStatsByUsers } from "@/utils/stats.be"; import { isAdmin } from "@/utils/users"; import { getEntitiesUsers } from "@/utils/users.be"; import { createColumnHelper } from "@tanstack/react-table"; import axios from "axios"; import { clsx } from "clsx"; import { withIronSessionSsr } from "iron-session/next"; import { capitalize, orderBy } from "lodash"; import moment from "moment"; import Head from "next/head"; import Link from "next/link"; import { useEffect, useMemo, useState } from "react"; import ReactDatePicker from "react-datepicker"; import { BsBank, BsChevronLeft, BsX, } from "react-icons/bs"; interface Props { user: User; students: StudentUser[]; entities: EntityWithRoles[]; assignments: Assignment[]; sessions: Session[] exams: Exam[] } export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { const user = await requestUser(req, res) if (!user) return redirect("/login") if (!checkAccess(user, ["admin", "developer", "mastercorporate"])) return redirect("/") const entityIDS = mapBy(user.entities, "id") || []; const entities = await getEntitiesWithRoles(isAdmin(user) ? undefined : entityIDS); const studentsAllowedEntities = findAllowedEntities(user, entities, 'view_students') const students = await getEntitiesUsers(mapBy(studentsAllowedEntities, 'id'), { type: "student" }) const assignments = await getEntitiesAssignments(mapBy(entities, "id")); const sessions = await getSessionsByAssignments(mapBy(assignments, 'id')) const exams = await getExamsByIds(assignments.flatMap(a => a.exams)) return { props: serialize({ user, students, entities, assignments, sessions, exams }) }; }, sessionOptions); interface Item { student: StudentUser result?: AssignmentResult assignment: Assignment exams: Exam[] session?: Session } const columnHelper = createColumnHelper(); export default function Statistical({ user, students, entities, assignments, sessions, exams }: Props) { const [startDate, setStartDate] = useState(new Date()); const [endDate, setEndDate] = useState(moment().add(1, 'month').toDate()); const [selectedEntities, setSelectedEntities] = useState([]) const [isDownloading, setIsDownloading] = useState(false) const resetDateRange = () => { const orderedAssignments = orderBy(assignments, ['startDate'], ['asc']) setStartDate(moment(orderedAssignments.shift()?.startDate || "2024-01-01T00:00:01.986Z").toDate()) setEndDate(moment().add(1, 'month').toDate()) } useEffect(resetDateRange, [assignments]) const updateDateRange = (dates: [Date, Date | null]) => { const [start, end] = dates; setStartDate(start!); setEndDate(end); }; const toggleEntity = (id: string) => setSelectedEntities(prev => prev.includes(id) ? prev.filter(x => x !== id) : [...prev, id]) const renderAssignmentResolution = (entityID: string) => { const entityAssignments = filterBy(assignments, 'entity', entityID) const total = entityAssignments.reduce((acc, curr) => acc + curr.assignees.length, 0) const results = entityAssignments.reduce((acc, curr) => acc + curr.results.length, 0) return `${results}/${total}` } const totalAssignmentResolution = useMemo(() => { const total = assignments.reduce((acc, curr) => acc + curr.assignees.length, 0) const results = assignments.reduce((acc, curr) => acc + curr.results.length, 0) return { results, total } }, [assignments]) const filteredAssignments = useMemo(() => { if (!startDate && !endDate) return assignments const startDateFiltered = startDate ? assignments.filter(a => moment(a.startDate).isSameOrAfter(moment(startDate))) : assignments return endDate ? startDateFiltered.filter(a => moment(a.endDate).isSameOrBefore(moment(endDate))) : startDateFiltered }, [startDate, endDate, assignments]) const data: Item[] = useMemo(() => filteredAssignments.filter(a => selectedEntities.includes(a.entity || "")).flatMap(a => a.assignees.map(x => { const result = findBy(a.results, 'user', x) const student = findBy(students, 'id', x) const assignmentExams = exams.filter(e => a.exams.map(x => `${x.id}_${x.module}`).includes(`${e.id}_${e.module}`)) const session = sessions.find(s => s.assignment?.id === a.id && s.user === x) if (!student) return undefined return { student, result, assignment: a, exams: assignmentExams, session } })).filter(x => !!x) as Item[], [students, selectedEntities, filteredAssignments, exams, sessions] ) const sortedData: Item[] = useMemo(() => data.sort((a, b) => { const aTotalScore = a.result?.stats.filter(x => !x.isPractice).reduce((acc, curr) => acc + curr.score.correct, 0) || 0 const bTotalScore = b.result?.stats.filter(x => !x.isPractice).reduce((acc, curr) => acc + curr.score.correct, 0) || 0 return bTotalScore - aTotalScore }), [data]) const downloadExcel = async () => { setIsDownloading(true) const request = await axios.post("/api/statistical", { entities: entities.filter(e => selectedEntities.includes(e.id)), items: data, assignments: filteredAssignments, startDate, endDate }, { responseType: 'blob' }) const href = URL.createObjectURL(request.data) const link = document.createElement('a'); link.href = href; link.setAttribute('download', `statistical_${new Date().toISOString()}.xlsx`); document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(href); setIsDownloading(false) } const columns = [ columnHelper.accessor("student.name", { header: "Student", cell: (info) => info.getValue(), }), columnHelper.accessor("student.studentID", { header: "Student ID", cell: (info) => info.getValue(), }), columnHelper.accessor("student.email", { header: "E-mail", cell: (info) => info.getValue(), }), columnHelper.accessor("assignment.name", { header: "Assignment", cell: (info) => info.getValue(), }), columnHelper.accessor("result", { header: "Progress", cell: (info) => { const student = info.row.original.student const session = info.row.original.session if (!student.lastLogin) return Never logged in if (info.getValue()) return Submitted if (!session) return Not started return {capitalize(session.exam?.module || "")} Module, Part {session.partIndex + 1}, Exercise {session.exerciseIndex + 1} }, }) ] return ( <> Statistical | EnCoach

Statistical

setSelectedEntities(value ? mapBy(entities, 'id') : [])} isChecked={selectedEntities.length === entities.length} > Select All
{entities.map(entity => ( ))}
{startDate !== null && endDate !== null && ( )}
Total: {totalAssignmentResolution.results} / {totalAssignmentResolution.total}
{selectedEntities.length > 0 && ( )} ) }