From 9faf82ee9c0c56143b9c7a7149d721ef539321c4 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Tue, 26 Nov 2024 15:04:24 +0000 Subject: [PATCH] Fixed a typo in the records --- src/components/Medium/StatGridItem.tsx | 42 ++++++++++++++---- src/exams/Finish.tsx | 9 ++-- src/pages/record.tsx | 9 ++-- src/pages/training/[id]/index.tsx | 59 +++++++++++++------------- src/utils/stats.ts | 43 ++++++++++--------- 5 files changed, 95 insertions(+), 67 deletions(-) diff --git a/src/components/Medium/StatGridItem.tsx b/src/components/Medium/StatGridItem.tsx index 95c7b394..a5f40837 100644 --- a/src/components/Medium/StatGridItem.tsx +++ b/src/components/Medium/StatGridItem.tsx @@ -2,9 +2,9 @@ import React from "react"; import { BsClock, BsXCircle } from "react-icons/bs"; import clsx from "clsx"; import { Stat, User } from "@/interfaces/user"; -import { Module, Step } from "@/interfaces"; +import { Grading, Module, Step } from "@/interfaces"; import ai_usage from "@/utils/ai.detection"; -import { calculateBandScore } from "@/utils/score"; +import { calculateBandScore, getGradingLabel } from "@/utils/score"; import moment from "moment"; import { Assignment } from "@/interfaces/results"; import { uuidv4 } from "@firebase/util"; @@ -15,6 +15,7 @@ import { convertToUserSolutions } from "@/utils/stats"; import { getExamById } from "@/utils/exams"; import { Exam, UserSolution } from "@/interfaces/exam"; import ModuleBadge from "../ModuleBadge"; +import { findBy } from "@/utils"; const formatTimestamp = (timestamp: string | number) => { const time = typeof timestamp === "string" ? parseInt(timestamp) : timestamp; @@ -70,6 +71,7 @@ const aggregateScoresByModule = (stats: Stat[]): { module: Module; total: number interface StatsGridItemProps { width?: string | undefined; height?: string | undefined; + gradingSystems: Grading[] examNumber?: number | undefined; stats: Stat[]; timestamp: string | number; @@ -94,6 +96,7 @@ const StatsGridItem: React.FC = ({ timestamp, user, assignments, + gradingSystems, users, training, selectedTrainingExams, @@ -191,9 +194,34 @@ const StatsGridItem: React.FC = ({ return true; }; + const levelAverage = () => + aggregatedLevels.reduce((accumulator, current) => accumulator + current.level, 0) / aggregatedLevels.length + + const renderLevelScore = () => { + const defaultLevelScore = levelAverage().toFixed(1) + if (!stats.every(s => s.module === "level")) return defaultLevelScore + if (gradingSystems.length === 0) return defaultLevelScore + + const score = { + correct: stats.reduce((acc, curr) => acc + curr.score.correct, 0), + total: stats.reduce((acc, curr) => acc + curr.score.total, 0) + } + + const level: number = calculateBandScore(score.correct, score.total, "level", user.focus); + + if (!!assignment) { + const gradingSystem = findBy(gradingSystems, 'entity', assignment.entity) + if (!gradingSystem) return defaultLevelScore + + return getGradingLabel(level, gradingSystem.steps) + } + + return getGradingLabel(level, gradingSystems[0].steps) + } + const content = ( <> -
+
{formatTimestamp(timestamp)}
@@ -211,12 +239,10 @@ const StatsGridItem: React.FC = ({
- {!!assignment && (assignment.released || assignment.released === undefined) && ( + {((!!assignment && (assignment.released || assignment.released === undefined)) || !assignment) && ( - Level{" "} - {( - aggregatedLevels.reduce((accumulator, current) => accumulator + current.level, 0) / aggregatedLevels.length - ).toFixed(1)} + Level{' '} + {renderLevelScore()} )} {shouldRenderPDFIcon() && renderPdfIcon(session, textColor, textColor)} diff --git a/src/exams/Finish.tsx b/src/exams/Finish.tsx index 3fd06f87..ac06426b 100644 --- a/src/exams/Finish.tsx +++ b/src/exams/Finish.tsx @@ -6,9 +6,8 @@ import { User } from "@/interfaces/user"; import useExamStore from "@/stores/examStore"; import { calculateBandScore, getGradingLabel } from "@/utils/score"; import clsx from "clsx"; -import Link from "next/link"; import { useRouter } from "next/router"; -import { Fragment, useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { BsArrowCounterclockwise, BsBan, @@ -19,10 +18,7 @@ import { BsHeadphones, BsMegaphone, BsPen, - BsShareFill, } from "react-icons/bs"; -import { LevelScore } from "@/constants/ielts"; -import { getLevelScore } from "@/utils/score"; import { capitalize } from "lodash"; import Modal from "@/components/Modal"; import { UserSolution } from "@/interfaces/exam"; @@ -68,6 +64,7 @@ export default function Finish({ user, practiceScores, scores, modules, informat const router = useRouter() useEffect(() => setSelectedScore(scores.find((x) => x.module === selectedModule)!), [scores, selectedModule]); + useEffect(() => setSelectedPracticeScore(practiceScores.find((x) => x.module === selectedModule)!), [practiceScores, selectedModule]); const moduleColors: { [key in Module]: { progress: string; inner: string } } = { reading: { @@ -284,7 +281,7 @@ export default function Finish({ user, practiceScores, scores, modules, informat
- {selectedPracticeScore.correct} / {selectedScore.total} + {selectedPracticeScore.correct} / {selectedPracticeScore.total} Practice Questions
diff --git a/src/pages/record.tsx b/src/pages/record.tsx index 9e76e49d..e10fd6fe 100644 --- a/src/pages/record.tsx +++ b/src/pages/record.tsx @@ -27,7 +27,7 @@ import { mapBy, redirect, serialize } from "@/utils"; import { getEntitiesWithRoles } from "@/utils/entities.be"; import { checkAccess } from "@/utils/permissions"; import { getGroups, getGroupsByEntities } from "@/utils/groups.be"; -import { getGradingSystemByEntity } from "@/utils/grading.be"; +import { getGradingSystemByEntities, getGradingSystemByEntity } from "@/utils/grading.be"; import { Grading } from "@/interfaces"; import { EntityWithRoles } from "@/interfaces/entity"; import CardList from "@/components/High/CardList"; @@ -44,9 +44,10 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { const entities = await getEntitiesWithRoles(checkAccess(user, ["admin", "developer"]) ? undefined : entityIDs) const users = await (checkAccess(user, ["admin", "developer"]) ? getUsers() : getEntitiesUsers(mapBy(entities, 'id'))) const assignments = await (checkAccess(user, ["admin", "developer"]) ? getAssignments() : getEntitiesAssignments(mapBy(entities, 'id'))) + const gradingSystems = await getGradingSystemByEntities(mapBy(entities, 'id')) return { - props: serialize({ user, users, assignments, entities }), + props: serialize({ user, users, assignments, entities, gradingSystems }), }; }, sessionOptions); @@ -57,11 +58,12 @@ interface Props { users: User[]; assignments: Assignment[]; entities: EntityWithRoles[] + gradingSystems: Grading[] } const MAX_TRAINING_EXAMS = 10; -export default function History({ user, users, assignments, entities }: Props) { +export default function History({ user, users, assignments, entities, gradingSystems }: Props) { const router = useRouter(); const [statsUserId, setStatsUserId, training, setTraining] = useRecordStore((state) => [ state.selectedUser, @@ -165,6 +167,7 @@ export default function History({ user, users, assignments, entities }: Props) { { +export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { const user = await requestUser(req, res) if (!user) return redirect("/login") if (shouldRedirectHome(user)) redirect("/") return { - props: serialize({user}), + props: serialize({ user }), }; }, sessionOptions); -const TrainingContent: React.FC<{user: User}> = ({user}) => { +const TrainingContent: React.FC<{ user: User }> = ({ user }) => { // Record stuff const setExams = useExamStore((state) => state.setExams); const setShowSolutions = useExamStore((state) => state.setShowSolutions); @@ -59,11 +59,11 @@ const TrainingContent: React.FC<{user: User}> = ({user}) => { const [loading, setLoading] = useState(true); const [trainingTips, setTrainingTips] = useState([]); const [currentTipIndex, setCurrentTipIndex] = useState(0); - const {assignments} = useAssignments({}); - const {users} = useUsers(); + const { assignments } = useAssignments({}); + const { users } = useUsers(); const router = useRouter(); - const {id} = router.query; + const { id } = router.query; useEffect(() => { const fetchTrainingContent = async () => { @@ -84,14 +84,14 @@ const TrainingContent: React.FC<{user: User}> = ({user}) => { return statResponse.data; }), ); - return {...exam, stats}; + return { ...exam, stats }; }), ), }; const tips = await axios.get("/api/training/walkthrough", { - params: {ids: trainingContent.tip_ids}, - paramsSerializer: (params) => qs.stringify(params, {arrayFormat: "repeat"}), + params: { ids: trainingContent.tip_ids }, + paramsSerializer: (params) => qs.stringify(params, { arrayFormat: "repeat" }), }); const processedTips = tips.data.map(formatTip); @@ -121,7 +121,7 @@ const TrainingContent: React.FC<{user: User}> = ({user}) => { return getExamById(stat.module, stat.exam); }); - const {timeSpent, inactivity} = stats[0]; + const { timeSpent, inactivity } = stats[0]; Promise.all(examPromises).then((exams) => { if (exams.every((x) => !!x)) { @@ -177,6 +177,7 @@ const TrainingContent: React.FC<{user: User}> = ({user}) => { {trainingContent.exams.map((exam, examIndex) => ( = ({user}) => { {trainingContent.weak_areas.map((x, index) => ( + className={({ selected }) => clsx( "text-[#53B2F9] pb-2 border-b-2", "focus:outline-none", diff --git a/src/utils/stats.ts b/src/utils/stats.ts index 08ba03e8..50d273b0 100644 --- a/src/utils/stats.ts +++ b/src/utils/stats.ts @@ -1,9 +1,9 @@ -import {Stat} from "@/interfaces/user"; -import {capitalize, groupBy} from "lodash"; -import {convertCamelCaseToReadable} from "@/utils/string"; -import {UserSolution} from "@/interfaces/exam"; -import {Module} from "@/interfaces"; -import {MODULES} from "@/constants/ielts"; +import { Stat } from "@/interfaces/user"; +import { capitalize, groupBy } from "lodash"; +import { convertCamelCaseToReadable } from "@/utils/string"; +import { UserSolution } from "@/interfaces/exam"; +import { Module } from "@/interfaces"; +import { MODULES } from "@/constants/ielts"; import moment from "moment"; export const timestampToMoment = (stat: Stat): moment.Moment => { @@ -16,15 +16,15 @@ export const totalExams = (stats: Stat[]): number => { }; export const averageScore = (stats: Stat[]): number => { - const {correct, total} = stats.reduce( - (acc, current) => ({correct: acc.correct + current.score.correct, total: acc.total + current.score.total}), - {correct: 0, total: 0}, + const { correct, total } = stats.reduce( + (acc, current) => ({ correct: acc.correct + current.score.correct, total: acc.total + current.score.total }), + { correct: 0, total: 0 }, ); return parseFloat(((correct / total) * 100).toFixed(2)); }; -export const formatModuleTotalStats = (stats: Stat[]): {label: string; value: number}[] => { - const moduleSessions: {[key: string]: string[]} = {}; +export const formatModuleTotalStats = (stats: Stat[]): { label: string; value: number }[] => { + const moduleSessions: { [key: string]: string[] } = {}; stats.forEach((stat) => { if (stat.module in moduleSessions) { @@ -43,7 +43,7 @@ export const formatModuleTotalStats = (stats: Stat[]): {label: string; value: nu }; export const totalExamsByModule = (stats: Stat[], module: Module): number => { - const moduleSessions: {[key: string]: string[]} = {}; + const moduleSessions: { [key: string]: string[] } = {}; stats.forEach((stat) => { if (stat.module in moduleSessions) { @@ -58,8 +58,8 @@ export const totalExamsByModule = (stats: Stat[], module: Module): number => { return moduleSessions[module]?.length || 0; }; -export const calculateModuleAverageScoreStats = (stats: Stat[]): {module: Module; value: number}[] => { - const moduleScores: {[key: string]: {correct: number; total: number}} = {}; +export const calculateModuleAverageScoreStats = (stats: Stat[]): { module: Module; value: number }[] => { + const moduleScores: { [key: string]: { correct: number; total: number } } = {}; stats.forEach((stat) => { if (stat.module in moduleScores) { @@ -82,11 +82,11 @@ export const calculateModuleAverageScoreStats = (stats: Stat[]): {module: Module }); }; -export const formatModuleAverageScoreStats = (stats: Stat[]): {label: string; value: number}[] => { - return calculateModuleAverageScoreStats(stats).map((x) => ({label: capitalize(x.module), value: x.value})); +export const formatModuleAverageScoreStats = (stats: Stat[]): { label: string; value: number }[] => { + return calculateModuleAverageScoreStats(stats).map((x) => ({ label: capitalize(x.module), value: x.value })); }; -export const formatExerciseTotalStats = (stats: Stat[]): {label: string; value: number}[] => { +export const formatExerciseTotalStats = (stats: Stat[]): { label: string; value: number }[] => { const totalExercises = stats.map((stat) => ({ label: convertCamelCaseToReadable(stat.type), value: stats.filter((x) => x.type === stat.type).length, @@ -95,8 +95,8 @@ export const formatExerciseTotalStats = (stats: Stat[]): {label: string; value: return totalExercises.filter((ex, index) => totalExercises.findIndex((x) => x.label === ex.label) === index); }; -export const formatExerciseAverageScoreStats = (stats: Stat[]): {label: string; value: number}[] => { - const typeScores: {[key: string]: {correct: number; total: number}} = {}; +export const formatExerciseAverageScoreStats = (stats: Stat[]): { label: string; value: number }[] => { + const typeScores: { [key: string]: { correct: number; total: number } } = {}; stats.forEach((stat) => { if (stat.type in typeScores) { @@ -110,7 +110,7 @@ export const formatExerciseAverageScoreStats = (stats: Stat[]): {label: string; }); return Object.keys(typeScores).map((x) => { - const {correct, total} = typeScores[x as keyof typeof typeScores]; + const { correct, total } = typeScores[x as keyof typeof typeScores]; return { label: convertCamelCaseToReadable(x), @@ -138,6 +138,7 @@ export const convertToUserSolutions = (stats: Stat[]): UserSolution[] => { solutions: stat.solutions, type: stat.type, module: stat.module, - shuffleMaps: stat.shuffleMaps + shuffleMaps: stat.shuffleMaps, + isPractice: stat.isPractice })); };