diff --git a/src/IsaacApiTypes.tsx b/src/IsaacApiTypes.tsx index 17a5c28bc5..e0a6c9cacd 100644 --- a/src/IsaacApiTypes.tsx +++ b/src/IsaacApiTypes.tsx @@ -46,8 +46,9 @@ export interface AssignmentStatusDTO { export interface AssignmentProgressDTO { user?: UserSummaryDTO; - correctPartResults?: number[]; - incorrectPartResults?: number[]; + correctMarkResults?: number[][]; + incorrectMarkResults?: number[][]; + markTotals?: number[][]; questionResults?: CompletionState[]; questionPartResults?: QuestionPartState[][]; } diff --git a/src/IsaacAppTypes.tsx b/src/IsaacAppTypes.tsx index 85310b3fd3..223968c74c 100644 --- a/src/IsaacAppTypes.tsx +++ b/src/IsaacAppTypes.tsx @@ -338,7 +338,7 @@ export interface ActiveModalWithoutState { bodyContainerClassName?: string; } -export type ProgressSortOrder = number | "name" | "totalPartPercentage" | "totalAttemptedPartPercentage" | "totalQuestionPercentage" | "totalAttemptedQuestionPercentage"; +export type ProgressSortOrder = number | "name" | "totalMarkPercentage" | "totalAttemptedMarkPercentage" | "totalQuestionPercentage" | "totalAttemptedQuestionPercentage"; export enum QuizzesBoardOrder { "title" = "title", @@ -438,6 +438,8 @@ export interface AssignmentProgressPageSettings { setFormatAsPercentage: (formatAsPercentage: boolean) => void; attemptedOrCorrect: "ATTEMPTED" | "CORRECT"; setAttemptedOrCorrect: (attemptedOrCorrect: "ATTEMPTED" | "CORRECT") => void; + displayIndividualMarks: boolean; + setDisplayIndividualMarks: (displayIndividualMarks: boolean) => void; assignmentOrder: AssignmentOrderSpec; setAssignmentOrder: (assignmentOrder: AssignmentOrderSpec) => void; groupSortOrder: GroupSortOrder; @@ -497,14 +499,14 @@ export const AssignmentScheduleContext = React.createContext<{ collapsed: boolean; setCollapsed: (b: boolean) => void; viewBy: "startDate" | "dueDate"; - }>({boardsById: {}, groupsById: {}, groupFilter: {}, boardIdsByGroupId: {}, groups: [], gameboards: [], openAssignmentModal: () => {}, collapsed: false, setCollapsed: () => {}, viewBy: "startDate"}); +}>({boardsById: {}, groupsById: {}, groupFilter: {}, boardIdsByGroupId: {}, groups: [], gameboards: [], openAssignmentModal: () => {}, collapsed: false, setCollapsed: () => {}, viewBy: "startDate"}); export const ContentSidebarContext = React.createContext<{ toggle: () => void; close: () => void; } | undefined>(undefined); export interface AuthorisedAssignmentProgress extends ApiTypes.AssignmentProgressDTO { completed?: boolean; correctQuestionPagesCount: number; - correctQuestionPartsCount: number; - incorrectQuestionPartsCount: number; + correctQuestionMarksCount: number; + incorrectQuestionMarksCount: number; notAttemptedPartResults: number[]; } @@ -741,6 +743,8 @@ export interface PageSettings { assignmentOrder?: AssignmentOrderSpec; attemptedOrCorrect?: "ATTEMPTED" | "CORRECT"; setAttemptedOrCorrect?: (newValue: "ATTEMPTED" | "CORRECT") => void; + displayIndividualMarks: boolean; + setDisplayIndividualMarks: (displayIndividualMarks: boolean) => void; } export interface GameboardBuilderQuestions { diff --git a/src/app/components/elements/quiz/QuizProgressCommon.tsx b/src/app/components/elements/quiz/QuizProgressCommon.tsx index 00e6f34a96..14cbb44c19 100644 --- a/src/app/components/elements/quiz/QuizProgressCommon.tsx +++ b/src/app/components/elements/quiz/QuizProgressCommon.tsx @@ -30,18 +30,6 @@ export function formatMark(numerator: number, denominator: number, formatAsPerce return result; } -export const generateCorrectnessIcon = (correct: number, incorrect: number, notAttempted: number, totalParts: number) => { - if (correct === totalParts) { - return ICON.correct; - } else if (notAttempted === totalParts) { - return ICON.notAttempted; - } else if (correct === 0) { - return ICON.incorrect; - } else { - return ICON.partial; - } -}; - const getAssignmentQuestionCorrectnessIcon = (state: CompletionState, attemptedOrCorrect: "ATTEMPTED" | "CORRECT") => { if (attemptedOrCorrect === "CORRECT") { switch (state) { @@ -70,17 +58,27 @@ const getAssignmentQuestionCorrectnessIcon = (state: CompletionState, attemptedO }; +export function markResultsToPartResults(markResults: number[][] = [], markTotals: number[][] = []): number[] { + return markResults.map((marks, i) => { + const markTotal = markTotals[i].reduce((acc, curr) => acc + curr, 0); + const markObtained = marks.reduce((acc, curr) => acc + curr, 0); + return Math.floor(markObtained / markTotal); + }); +}; + const getQuizQuestionCorrectnessIcon = (attemptedOrCorrect: "ATTEMPTED" | "CORRECT", studentProgress: AssignmentProgressDTO, questionIndex: number) => { + const questionCorrect = (markResultsToPartResults(studentProgress.correctMarkResults, studentProgress.markTotals))[questionIndex] === 1; + const questionIncorrect = (markResultsToPartResults(studentProgress.incorrectMarkResults, studentProgress.markTotals))[questionIndex] === 1; if (attemptedOrCorrect === "CORRECT") { - if ((studentProgress.correctPartResults || [])[questionIndex] === 1) { + if (questionCorrect) { return ICON.correct; } - else if ((studentProgress.incorrectPartResults || [])[questionIndex] === 1) { + else if (questionIncorrect) { return ICON.incorrect; } return ICON.notAttempted; } else { - if ((studentProgress.correctPartResults || [])[questionIndex] === 1 || (studentProgress.incorrectPartResults || [])[questionIndex] === 1) { + if (questionCorrect || questionIncorrect) { return ICON.correct; } return ICON.notAttempted; @@ -195,10 +193,10 @@ export function ResultsTable({ switch (sortOrder) { case "name": return sortByName(item); - case "totalPartPercentage": - return -item.correctQuestionPartsCount; - case "totalAttemptedPartPercentage": - return -(item.correctQuestionPartsCount + item.incorrectQuestionPartsCount); + case "totalMarkPercentage": + return -item.correctQuestionMarksCount; + case "totalAttemptedMarkPercentage": + return -(item.correctQuestionMarksCount + item.incorrectQuestionMarksCount); case "totalQuestionPercentage": return -item.correctQuestionPagesCount; case "totalAttemptedQuestionPercentage": @@ -206,7 +204,7 @@ export function ResultsTable({ return item.notAttemptedPartResults?.reduce((acc, curr) => acc + curr, 0) || 0; default: if (pageSettings?.attemptedOrCorrect === "CORRECT") { - return -(item.correctPartResults || [])[sortOrder]; + return -(item.correctMarkResults || [])[sortOrder]; } else { return (item.notAttemptedPartResults || [])[sortOrder]; } @@ -273,35 +271,29 @@ export function ResultsTable({ pageSettings?.attemptedOrCorrect === "CORRECT" ? className={classNames("pointer-cursor correct-attempted-header", {"sticky-ca-col": isPhy})} - defaultOrder={"totalPartPercentage"} - reverseOrder={"totalPartPercentage"} + defaultOrder={"totalMarkPercentage"} + reverseOrder={"totalMarkPercentage"} currentOrder={sortOrder} setOrder={toggleSort} reversed={reverseOrder} onClick={() => setSelectedQuestionIndex(undefined)} - label={"Total correct parts"} + label={pageSettings?.displayIndividualMarks ? "Total marks awarded" : "Total correct parts"} > - {siteSpecific( -
- Parts - (total) -
, - "Correct" - )} +
+ {pageSettings?.displayIndividualMarks ? "Marks" : "Parts"} + (total) +
: className={classNames("pointer-cursor correct-attempted-header", {"sticky-ca-col": isPhy})} - defaultOrder={"totalAttemptedPartPercentage"} - reverseOrder={"totalAttemptedPartPercentage"} + defaultOrder={"totalAttemptedMarkPercentage"} + reverseOrder={"totalAttemptedMarkPercentage"} currentOrder={sortOrder} setOrder={toggleSort} reversed={reverseOrder} onClick={() => setSelectedQuestionIndex(undefined)} - label={"Total attempted parts"} + label={pageSettings?.displayIndividualMarks ? "Total marks awarded" : "Total correct parts"} > - {siteSpecific( -
- Parts - (total) -
, - "Attempted" - )} +
+ {pageSettings?.displayIndividualMarks ? "Marks" : "Parts"} + (total) +
)} {questions.map((_, index) => @@ -341,7 +333,7 @@ export function ResultsTable({ const studentsWithAllAttempted = progress.reduce((acc, p) => acc + (isAuthorisedFullAccess(p) && !p.notAttemptedPartResults?.[index] ? 1 : 0), 0); return [studentsWithAllAttempted, progress.length]; } else { - const studentsWithAllCorrect = progress.reduce((acc, p) => acc + (p.correctPartResults?.[index] ? 1 : 0), 0); + const studentsWithAllCorrect = progress.reduce((acc, p) => acc + (markResultsToPartResults(p.correctMarkResults, p.markTotals)?.[index] ? 1 : 0), 0); return [studentsWithAllCorrect, progress.length]; } } @@ -375,6 +367,12 @@ export function ResultsTable({ {sortedProgress.map((studentProgress, index) => { const fullAccess = isAuthorisedFullAccess(studentProgress); const internalCellSpacing = isPhy && isAssignment ? "py-1" : "py-3"; + + const correctPartResults = studentProgress.questionPartResults?.map(statuses => statuses.reduce((acc, status) => acc + (status === "CORRECT" ? 1 : 0), 0)) || []; + const incorrectPartResults = studentProgress.questionPartResults?.map(statuses => statuses.reduce((acc, status) => acc + (status === "INCORRECT" ? 1 : 0), 0)) || []; + const correctPartOrMarkCount = (fullAccess && pageSettings?.displayIndividualMarks) ? studentProgress.correctQuestionMarksCount : correctPartResults.reduce((acc, curr) => acc + curr, 0); + const incorrectPartOrMarkCount = (fullAccess && pageSettings?.displayIndividualMarks) ? studentProgress.incorrectQuestionMarksCount : incorrectPartResults.reduce((acc, curr) => acc + curr, 0); + return {fullAccess && pageSettings?.isTeacher ? @@ -423,22 +421,24 @@ export function ResultsTable({ ? studentProgress.correctQuestionPagesCount : studentProgress.questionResults?.filter(r => r !== CompletionState.NOT_ATTEMPTED).length ?? 0 : pageSettings?.attemptedOrCorrect === "CORRECT" - ? studentProgress.correctQuestionPartsCount - : studentProgress.correctQuestionPartsCount + studentProgress.incorrectQuestionPartsCount, + ? correctPartOrMarkCount + : correctPartOrMarkCount + incorrectPartOrMarkCount, questions.length, !!pageSettings?.formatAsPercentage ) : "" } - {/* total parts */} + {/* total parts/marks */} {isPhy && isAssignment && {fullAccess ? formatMark( pageSettings?.attemptedOrCorrect === "CORRECT" - ? studentProgress.correctQuestionPartsCount - : studentProgress.correctQuestionPartsCount + studentProgress.incorrectQuestionPartsCount, - assignmentTotalQuestionParts, + ? correctPartOrMarkCount + : correctPartOrMarkCount + incorrectPartOrMarkCount, + pageSettings?.displayIndividualMarks + ? studentProgress.markTotals?.reduce((acc, arr) => acc + arr.reduce((a, b) => a + b, 0), 0) ?? 0 + : assignmentTotalQuestionParts, !!pageSettings?.formatAsPercentage ) : "" @@ -451,15 +451,21 @@ export function ResultsTable({ )}> {isAssignment ? (fullAccess - ? isPhy - ? formatMark( + ? siteSpecific( + formatMark( pageSettings?.attemptedOrCorrect === "CORRECT" - ? (studentProgress.correctPartResults || [])[index] - : (studentProgress.correctPartResults || [])[index] + (studentProgress.incorrectPartResults || [])[index], - questions[index].questionPartsTotal as number, + ? pageSettings?.displayIndividualMarks + ? studentProgress.correctMarkResults![index].reduce((a, b) => a + b, 0) ?? 0 + : correctPartResults[index] + : pageSettings?.displayIndividualMarks + ? studentProgress.correctMarkResults![index].concat(studentProgress.incorrectMarkResults![index]).reduce((a, b) => a + b, 0) ?? 0 + : correctPartResults[index] + incorrectPartResults[index], + pageSettings?.displayIndividualMarks + ? studentProgress.markTotals![index].reduce((a, b) => a + b, 0) ?? 0 + : studentProgress.markTotals![index].length, !!pageSettings?.formatAsPercentage - ) - : getAssignmentQuestionCorrectnessIcon((studentProgress.questionResults || [])[index], pageSettings?.attemptedOrCorrect || "CORRECT") + ), + getAssignmentQuestionCorrectnessIcon((studentProgress.questionResults || [])[index], pageSettings?.attemptedOrCorrect || "CORRECT")) : "" ) : getQuizQuestionCorrectnessIcon(pageSettings?.attemptedOrCorrect || "CORRECT", studentProgress, index) @@ -525,8 +531,8 @@ export function ResultsTablePartBreakdown({ switch (sortOrder) { case "name": return (item.user?.familyName + ", " + item.user?.givenName).toLowerCase(); - case "totalPartPercentage": - case "totalAttemptedPartPercentage": + case "totalMarkPercentage": + case "totalAttemptedMarkPercentage": case "totalQuestionPercentage": case "totalAttemptedQuestionPercentage": return 0; // These sorts are not applicable for part breakdown @@ -569,11 +575,11 @@ export function ResultsTablePartBreakdown({ defaultOrder={"totalQuestionPercentage"} reverseOrder={"totalQuestionPercentage"} currentOrder={sortOrder} setOrder={toggleSort} reversed={reverseOrder} - label={"Total correct"} + label={pageSettings?.displayIndividualMarks ? "Total marks awarded" : "Total correct"} > {siteSpecific(
- Parts + {pageSettings?.displayIndividualMarks ? "Marks" : "Parts"} (total)
, "Correct" @@ -584,11 +590,11 @@ export function ResultsTablePartBreakdown({ defaultOrder={"totalAttemptedQuestionPercentage"} reverseOrder={"totalAttemptedQuestionPercentage"} currentOrder={sortOrder} setOrder={toggleSort} reversed={reverseOrder} - label={"Total attempted"} + label={pageSettings?.displayIndividualMarks ? "Total marks attempted" : "Total correct"} > {siteSpecific(
- Parts + {pageSettings?.displayIndividualMarks ? "Marks" : "Parts"} (total)
, "Attempted" @@ -611,8 +617,23 @@ export function ResultsTablePartBreakdown({ )} - {sortedProgress.map((studentProgress, studentIndex) => ( - + {sortedProgress.map((studentProgress, studentIndex) => { + const markPartNumerator: number[] = pageSettings?.displayIndividualMarks + ? pageSettings?.attemptedOrCorrect === "CORRECT" + ? studentProgress.correctMarkResults![questionIndex] + : studentProgress.correctMarkResults![questionIndex].map((mark, partIndex) => + mark + studentProgress.incorrectMarkResults![questionIndex][partIndex] + ) + : studentProgress.questionPartResults![questionIndex].map(part => + (pageSettings?.attemptedOrCorrect === "CORRECT" + ? part === "CORRECT" + : part !== "NOT_ATTEMPTED" + ) ? 1 : 0 + ); + const markPartDenominator: number[] = studentProgress.markTotals && pageSettings?.displayIndividualMarks + ? studentProgress.markTotals[questionIndex] + : Array(studentProgress.markTotals![questionIndex]?.length).fill(1); + return {/* student name */}
@@ -628,14 +649,8 @@ export function ResultsTablePartBreakdown({ {isPhy && studentProgress.questionPartResults && {formatMark( - studentProgress.questionPartResults[questionIndex].reduce((acc, questionPartResult) => { - if (pageSettings?.attemptedOrCorrect === "CORRECT") { - return acc + (questionPartResult === "CORRECT" ? 1 : 0); - } else { - return acc + (questionPartResult !== "NOT_ATTEMPTED" ? 1 : 0); - } - }, 0), - studentProgress.questionPartResults[questionIndex].length, + markPartNumerator.reduce((a, b) => a + b, 0), + markPartDenominator.reduce((a, b) => a + b, 0), !!pageSettings?.formatAsPercentage )} @@ -643,12 +658,18 @@ export function ResultsTablePartBreakdown({ {/* main data */} {studentProgress.questionPartResults && - studentProgress.questionPartResults[questionIndex].map((questionPartResult, questionPartIndex) => ( - {getQuizQuestionPartCorrectnessIcon(questionPartResult)} - )) + studentProgress.questionPartResults[questionIndex].map((questionPartResult, questionPartIndex) => { + if (pageSettings?.displayIndividualMarks) { + const correctMarkResult = markPartNumerator[questionPartIndex] ?? 0; + const markTotal = markPartDenominator[questionPartIndex] ?? 0; + return {formatMark(correctMarkResult, markTotal, !!pageSettings?.formatAsPercentage)}; + } else { + return {getQuizQuestionPartCorrectnessIcon(questionPartResult)}; + } + }) } - - ))} + ; + })} diff --git a/src/app/components/pages/AssignmentProgressIndividual.tsx b/src/app/components/pages/AssignmentProgressIndividual.tsx index 8841f2cb05..70f5704208 100644 --- a/src/app/components/pages/AssignmentProgressIndividual.tsx +++ b/src/app/components/pages/AssignmentProgressIndividual.tsx @@ -3,7 +3,7 @@ import { Link } from "react-router-dom"; import { AssignmentProgressDTO, GameboardItem, CompletionState } from "../../../IsaacApiTypes"; import { EnhancedAssignmentWithProgress, AssignmentProgressPageSettingsContext, AuthorisedAssignmentProgress } from "../../../IsaacAppTypes"; import { getAssignmentProgressCSVDownloadLink, isAda, isAuthorisedFullAccess, isPhy, PATHS, siteSpecific } from "../../services"; -import { ICON, passMark, ResultsTable, ResultsTablePartBreakdown } from "../elements/quiz/QuizProgressCommon"; +import { ICON, markResultsToPartResults, passMark, ResultsTable, ResultsTablePartBreakdown } from "../elements/quiz/QuizProgressCommon"; import { Badge, Button, Card, CardBody } from "reactstrap"; import { formatDate } from "../elements/DateString"; import { StyledCheckbox } from "../elements/inputs/StyledCheckbox"; @@ -47,6 +47,15 @@ export const AssignmentProgressSettings = () => { onChange={(e) => assignmentProgressContext?.setAttemptedOrCorrect?.(e.currentTarget.checked ? "CORRECT" : "ATTEMPTED")} />
} + + {isPhy &&
+ Marks display mode + + assignmentProgressContext?.setDisplayIndividualMarks?.(e.currentTarget.checked)} + /> +
} ; }; @@ -102,8 +111,8 @@ const GroupAssignmentTab = ({assignment, progress}: GroupAssignmentTabProps) => return "revoked"; } - const correctParts = studentProgress.correctQuestionPartsCount; - const incorrectParts = studentProgress.incorrectQuestionPartsCount; + const correctParts = studentProgress.correctQuestionMarksCount; + const incorrectParts = studentProgress.incorrectQuestionMarksCount; const status = null; return markClassesInternal(assignmentProgressContext?.attemptedOrCorrect ?? "CORRECT", studentProgress, status, correctParts, incorrectParts, totalParts); @@ -114,12 +123,11 @@ const GroupAssignmentTab = ({assignment, progress}: GroupAssignmentTabProps) => return "revoked"; } - const question = questions[index]; const totalParts = question.questionPartsTotal; - const correctParts = (studentProgress.correctPartResults || [])[index]; - const incorrectParts = (studentProgress.incorrectPartResults || [])[index]; + const correctParts = markResultsToPartResults(studentProgress.correctMarkResults, studentProgress.markTotals)[index]; + const incorrectParts = markResultsToPartResults(studentProgress.incorrectMarkResults, studentProgress.markTotals)[index]; const status = (studentProgress.questionResults || [])[index]; return markClassesInternal(assignmentProgressContext?.attemptedOrCorrect ?? "CORRECT", studentProgress, status, correctParts, incorrectParts, totalParts); @@ -333,27 +341,29 @@ export const ProgressDetails = ({assignment}: { assignment: EnhancedAssignmentWi const initialState = { ...p, correctQuestionPagesCount: 0, - correctQuestionPartsCount: 0, - incorrectQuestionPartsCount: 0, + correctQuestionMarksCount: 0, + incorrectQuestionMarksCount: 0, notAttemptedPartResults: [] }; const ret = (p.questionResults || []).reduce((oldP, results, i) => { const correctQuestionsCount = [CompletionState.ALL_CORRECT].includes(results) ? oldP.correctQuestionPagesCount + 1 : oldP.correctQuestionPagesCount; - const questions = assignment.gameboard.contents; + const correctMarkTotal = (p.correctMarkResults || [])[i].reduce((a, b) => a + b, 0); + const incorrectMarkTotal = (p.incorrectMarkResults || [])[i].reduce((a, b) => a + b, 0); + const markTotal = (p.markTotals || [])[i].reduce((a, b) => a + b, 0); return { ...oldP, correctQuestionPagesCount: correctQuestionsCount, - correctQuestionPartsCount: oldP.correctQuestionPartsCount + (p.correctPartResults || [])[i], - incorrectQuestionPartsCount: oldP.incorrectQuestionPartsCount + (p.incorrectPartResults || [])[i], + correctQuestionMarksCount: oldP.correctQuestionMarksCount + correctMarkTotal, + incorrectQuestionMarksCount: oldP.incorrectQuestionMarksCount + incorrectMarkTotal, notAttemptedPartResults: [ ...oldP.notAttemptedPartResults, - (questions[i].questionPartsTotal - (p.correctPartResults || [])[i] - (p.incorrectPartResults || [])[i]) + (markTotal - correctMarkTotal - incorrectMarkTotal) ] }; }, initialState); return [ret, questions.length === ret.correctQuestionPagesCount]; - }), [assignment.gameboard.contents, assignment.progress, questions.length]); + }), [assignment.progress, questions.length]); const progress = progressData.map(pd => pd[0]); diff --git a/src/app/components/pages/quizzes/QuizTeacherFeedback.tsx b/src/app/components/pages/quizzes/QuizTeacherFeedback.tsx index 7d6d40e7be..9f6341ea0a 100644 --- a/src/app/components/pages/quizzes/QuizTeacherFeedback.tsx +++ b/src/app/components/pages/quizzes/QuizTeacherFeedback.tsx @@ -34,7 +34,7 @@ import { } from "../../../../IsaacAppTypes"; import {teacherQuizzesCrumbs} from "../../elements/quiz/QuizContentsComponent"; import {formatDate} from "../../elements/DateString"; -import {ResultsTable} from "../../elements/quiz/QuizProgressCommon"; +import {markResultsToPartResults, ResultsTable} from "../../elements/quiz/QuizProgressCommon"; import { Alert, Button, @@ -197,8 +197,8 @@ export const QuizProgressDetails = ({assignment}: {assignment: QuizAssignmentDTO return "revoked"; } - const correctParts = studentProgress.correctQuestionPartsCount; - const incorrectParts = studentProgress.incorrectQuestionPartsCount; + const correctParts = studentProgress.correctQuestionMarksCount; + const incorrectParts = studentProgress.incorrectQuestionMarksCount; const total = questions.reduce((acc, q) => acc + (q.questionPartsTotal ?? 0), 0); return markClassesInternal(assignmentProgressContext?.attemptedOrCorrect ?? "CORRECT", studentProgress, null, correctParts, incorrectParts, total); @@ -209,8 +209,8 @@ export const QuizProgressDetails = ({assignment}: {assignment: QuizAssignmentDTO return "revoked"; } - const correctParts = (studentProgress.correctPartResults || [])[index]; - const incorrectParts = (studentProgress.incorrectPartResults || [])[index]; + const correctParts = markResultsToPartResults(studentProgress.correctMarkResults, studentProgress.markTotals)[index]; + const incorrectParts = markResultsToPartResults(studentProgress.incorrectMarkResults, studentProgress.markTotals)[index]; const totalParts = questions[index].questionPartsTotal ?? 0; return markClassesInternal(assignmentProgressContext?.attemptedOrCorrect ?? "CORRECT", studentProgress, null, correctParts, incorrectParts, totalParts); @@ -218,22 +218,22 @@ export const QuizProgressDetails = ({assignment}: {assignment: QuizAssignmentDTO const totalParts = questions.length; - const progress : AuthorisedAssignmentProgress[] = !assignment.userFeedback ? [] : assignment.userFeedback.map(user => { + const progress: AuthorisedAssignmentProgress[] = !assignment.userFeedback ? [] : assignment.userFeedback.map(user => { const partsCorrect = questions.reduce((acc, q) => acc + (user.feedback?.questionMarks?.[q?.id ?? -1]?.correct ?? 0), 0); return { user: user.user as UserSummaryDTO, completed: user.feedback?.complete ?? false, // a list of the correct parts of an answer, one list for each question - correctPartResults: questions.map(q => user.feedback?.questionMarks?.[q?.id ?? -1]?.correct ?? 0), - incorrectPartResults: questions.map(q => user.feedback?.questionMarks?.[q?.id ?? -1]?.incorrect ?? 0), + correctMarkResults: [questions.map(q => user.feedback?.questionMarks?.[q?.id ?? -1]?.correct ?? 0)], + incorrectMarkResults: [questions.map(q => user.feedback?.questionMarks?.[q?.id ?? -1]?.incorrect ?? 0)], notAttemptedPartResults: user.feedback?.complete || user.feedback?.questionMarks !== undefined ? questions.map(q => user.feedback?.questionMarks?.[q?.id ?? -1]?.notAttempted ?? 0) // if the quiz has not been completed (i.e. submitted), then all parts are not attempted : questions.map(q => q.questionPartsTotal ?? 0), questionResults: [], correctQuestionPagesCount: partsCorrect, // quizzes don't have pages, but QuizProgressCommon expects this key to be the "Correct" column value for sorting - correctQuestionPartsCount: partsCorrect, - incorrectQuestionPartsCount: questions.reduce((acc, q) => acc + (user.feedback?.questionMarks?.[q?.id ?? -1]?.incorrect ?? 0), 0), + correctQuestionMarksCount: partsCorrect, + incorrectQuestionMarksCount: questions.reduce((acc, q) => acc + (user.feedback?.questionMarks?.[q?.id ?? -1]?.incorrect ?? 0), 0), }; }); diff --git a/src/app/services/progress.ts b/src/app/services/progress.ts index 238db64535..62dee759de 100644 --- a/src/app/services/progress.ts +++ b/src/app/services/progress.ts @@ -28,6 +28,7 @@ export function useAssignmentProgressAccessibilitySettings({user}: {user: Regist const [colourBlind, setColourBlind] = useState(false); const [formatAsPercentage, setFormatAsPercentage] = useState(false); const [attemptedOrCorrect, setAttemptedOrCorrect] = useState<"ATTEMPTED" | "CORRECT">("CORRECT"); + const [displayIndividualMarks, setDisplayIndividualMarks] = useState(false); const [assignmentOrder, setAssignmentOrder] = useState(AssignmentOrder.startDateDescending); const [groupSortOrder, setGroupSortOrder] = useState(GroupSortOrder.Alphabetical); @@ -35,6 +36,7 @@ export function useAssignmentProgressAccessibilitySettings({user}: {user: Regist colourBlind, setColourBlind, formatAsPercentage, setFormatAsPercentage, attemptedOrCorrect, setAttemptedOrCorrect, + displayIndividualMarks, setDisplayIndividualMarks, assignmentOrder, setAssignmentOrder, groupSortOrder, setGroupSortOrder, isTeacher: isTeacherOrAbove(user), diff --git a/src/mocks/data.ts b/src/mocks/data.ts index ff75256957..28f9a5f992 100644 --- a/src/mocks/data.ts +++ b/src/mocks/data.ts @@ -6207,8 +6207,8 @@ export const mockProgress = { "registeredContexts": [], "id": 19 }, - "correctPartResults": null, - "incorrectPartResults": null, + "correctMarkResults": null, + "incorrectMarkResults": null, "questionResults": null, "questionPartResults": null, }, @@ -6226,13 +6226,13 @@ export const mockProgress = { ], "id": 9 }, - "correctPartResults": [ - 6, - 1 + "correctMarkResults": [ + [1,1,1,1,1,1], + [1,0,0] ], - "incorrectPartResults": [ - 0, - 2 + "incorrectMarkResults": [ + [0,0,0,0,0,0], + [0,1,1] ], "questionResults": [ "ALL_CORRECT", @@ -6268,13 +6268,13 @@ export const mockProgress = { ], "id": 8 }, - "correctPartResults": [ - 0, - 0 + "correctMarkResults": [ + [0,0,0,0,0,0], + [0,0,0] ], - "incorrectPartResults": [ - 6, - 0 + "incorrectMarkResults": [ + [1,1,1,1,1,1], + [0,0,0] ], "questionResults": [ "ALL_INCORRECT",