ENCOA-233: Added the option for certain exercises to not count towards scores

This commit is contained in:
Tiago Ribeiro
2024-11-12 11:03:19 +00:00
parent 1787e3ed53
commit b2dc9b2e31
13 changed files with 165 additions and 149 deletions

View File

@@ -11,6 +11,7 @@ import MCDropdown from "./MCDropdown";
const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
id, id,
type, type,
isPractice = false,
prompt, prompt,
solutions, solutions,
text, text,
@@ -40,6 +41,11 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
const shuffleMaps = shuffles.find((x) => x.exerciseID == id)?.shuffles; const shuffleMaps = shuffles.find((x) => x.exerciseID == id)?.shuffles;
const dropdownRef = useRef<HTMLDivElement>(null); const dropdownRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (disableProgressButtons) onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [answers, disableProgressButtons])
const excludeWordMCType = (x: any) => { const excludeWordMCType = (x: any) => {
return typeof x === "string" ? x : (x as { letter: string; word: string }); return typeof x === "string" ? x : (x as { letter: string; word: string });
}; };
@@ -169,7 +175,7 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
<Button <Button
color="purple" color="purple"
variant="outline" variant="outline"
onClick={() => onBack({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps })} onClick={() => onBack({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps, isPractice })}
className="max-w-[200px] w-full" className="max-w-[200px] w-full"
disabled={exam && exam.module === "level" && partIndex === 0 && questionIndex === 0}> disabled={exam && exam.module === "level" && partIndex === 0 && questionIndex === 0}>
Previous Page Previous Page
@@ -178,7 +184,7 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
<Button <Button
color="purple" color="purple"
onClick={() => { onClick={() => {
onNext({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps }); onNext({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps, isPractice });
}} }}
className="max-w-[200px] self-end w-full"> className="max-w-[200px] self-end w-full">
Next Page Next Page

View File

@@ -24,6 +24,7 @@ export default function InteractiveSpeaking({
userSolutions, userSolutions,
onNext, onNext,
onBack, onBack,
isPractice = false,
preview = false preview = false
}: InteractiveSpeakingExercise & CommonProps) { }: InteractiveSpeakingExercise & CommonProps) {
const [recordingDuration, setRecordingDuration] = useState(0); const [recordingDuration, setRecordingDuration] = useState(0);
@@ -54,6 +55,7 @@ export default function InteractiveSpeaking({
solutions: [...answers.filter((x) => x.questionIndex !== questionIndex), answer], solutions: [...answers.filter((x) => x.questionIndex !== questionIndex), answer],
score: { correct: 100, total: 100, missing: 0 }, score: { correct: 100, total: 100, missing: 0 },
type, type,
isPractice
}); });
}; };
@@ -76,6 +78,7 @@ export default function InteractiveSpeaking({
solutions: [...answers.filter((x) => x.questionIndex !== questionIndex), answer], solutions: [...answers.filter((x) => x.questionIndex !== questionIndex), answer],
score: { correct: 100, total: 100, missing: 0 }, score: { correct: 100, total: 100, missing: 0 },
type, type,
isPractice
}); });
}; };
@@ -146,6 +149,7 @@ export default function InteractiveSpeaking({
module: "speaking", module: "speaking",
exam: examID, exam: examID,
type, type,
isPractice
}, },
]); ]);

View File

@@ -72,6 +72,7 @@ export default function MatchSentences({
userSolutions, userSolutions,
onNext, onNext,
onBack, onBack,
isPractice = false,
disableProgressButtons = false disableProgressButtons = false
}: MatchSentencesExercise & CommonProps) { }: MatchSentencesExercise & CommonProps) {
const [answers, setAnswers] = useState<{ question: string; option: string }[]>(userSolutions); const [answers, setAnswers] = useState<{ question: string; option: string }[]>(userSolutions);
@@ -81,7 +82,7 @@ export default function MatchSentences({
const setCurrentSolution = useExamStore((state) => state.setCurrentSolution); const setCurrentSolution = useExamStore((state) => state.setCurrentSolution);
useEffect(() => { useEffect(() => {
setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type }); setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [answers, setAnswers]); }, [answers, setAnswers]);
@@ -105,12 +106,12 @@ export default function MatchSentences({
}; };
useEffect(() => { useEffect(() => {
if (disableProgressButtons) onNext({ exercise: id, solutions: answers, score: calculateScore(), type }); if (disableProgressButtons) onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [answers, disableProgressButtons]) }, [answers, disableProgressButtons])
useEffect(() => { useEffect(() => {
if (hasExamEnded) onNext({ exercise: id, solutions: answers, score: calculateScore(), type }); if (hasExamEnded) onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [hasExamEnded]); }, [hasExamEnded]);
@@ -119,14 +120,14 @@ export default function MatchSentences({
<Button <Button
color="purple" color="purple"
variant="outline" variant="outline"
onClick={() => onBack({ exercise: id, solutions: answers, score: calculateScore(), type })} onClick={() => onBack({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice })}
className="max-w-[200px] w-full"> className="max-w-[200px] w-full">
Back Back
</Button> </Button>
<Button <Button
color="purple" color="purple"
onClick={() => onNext({ exercise: id, solutions: answers, score: calculateScore(), type })} onClick={() => onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice })}
className="max-w-[200px] self-end w-full"> className="max-w-[200px] self-end w-full">
Next Next
</Button> </Button>

View File

@@ -78,6 +78,7 @@ export default function MultipleChoice({
type, type,
questions, questions,
userSolutions, userSolutions,
isPractice = false,
onNext, onNext,
onBack, onBack,
disableProgressButtons = false disableProgressButtons = false
@@ -93,7 +94,7 @@ export default function MultipleChoice({
const scrollToTop = () => Array.from(document.getElementsByTagName("body")).forEach((body) => body.scrollTo(0, 0)); const scrollToTop = () => Array.from(document.getElementsByTagName("body")).forEach((body) => body.scrollTo(0, 0));
useEffect(() => { useEffect(() => {
if (hasExamEnded) onNext({ exercise: id, solutions: answers, score: calculateScore(), type }); if (hasExamEnded) onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [hasExamEnded]); }, [hasExamEnded]);
@@ -102,7 +103,7 @@ export default function MultipleChoice({
}; };
useEffect(() => { useEffect(() => {
setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps }); setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps, isPractice });
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [answers, setAnswers]); }, [answers, setAnswers]);
@@ -140,13 +141,13 @@ export default function MultipleChoice({
}; };
useEffect(() => { useEffect(() => {
if (disableProgressButtons) onNext({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps }); if (disableProgressButtons) onNext({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps, isPractice });
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [answers, disableProgressButtons]) }, [answers, disableProgressButtons])
const next = () => { const next = () => {
if (questionIndex + 1 >= questions.length - 1) { if (questionIndex + 1 >= questions.length - 1) {
onNext({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps }); onNext({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps, isPractice });
} else { } else {
setQuestionIndex(questionIndex + 2); setQuestionIndex(questionIndex + 2);
} }
@@ -155,7 +156,7 @@ export default function MultipleChoice({
const back = () => { const back = () => {
if (questionIndex === 0) { if (questionIndex === 0) {
onBack({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps }); onBack({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps, isPractice });
} else { } else {
if (exam?.module === "level" && typeof exam.parts[0].intro !== "undefined" && questionIndex === 0) return; if (exam?.module === "level" && typeof exam.parts[0].intro !== "undefined" && questionIndex === 0) return;
setQuestionIndex(questionIndex - 2); setQuestionIndex(questionIndex - 2);

View File

@@ -14,7 +14,7 @@ const ReactMediaRecorder = dynamic(() => import("react-media-recorder").then((mo
ssr: false, ssr: false,
}); });
export default function Speaking({ id, title, text, video_url, type, prompts, suffix, userSolutions, onNext, onBack, preview = false }: SpeakingExercise & CommonProps) { export default function Speaking({ id, title, text, video_url, type, prompts, suffix, userSolutions, isPractice = false, onNext, onBack, preview = false }: SpeakingExercise & CommonProps) {
const [recordingDuration, setRecordingDuration] = useState(0); const [recordingDuration, setRecordingDuration] = useState(0);
const [isRecording, setIsRecording] = useState(false); const [isRecording, setIsRecording] = useState(false);
const [mediaBlob, setMediaBlob] = useState<string>(); const [mediaBlob, setMediaBlob] = useState<string>();
@@ -81,7 +81,7 @@ export default function Speaking({ id, title, text, video_url, type, prompts, su
exercise: id, exercise: id,
solutions: mediaBlob ? [{ id, solution: mediaBlob }] : [], solutions: mediaBlob ? [{ id, solution: mediaBlob }] : [],
score: { correct: 0, total: 100, missing: 0 }, score: { correct: 0, total: 100, missing: 0 },
type, type, isPractice
}); });
}; };
@@ -90,7 +90,7 @@ export default function Speaking({ id, title, text, video_url, type, prompts, su
exercise: id, exercise: id,
solutions: mediaBlob ? [{ id, solution: mediaBlob }] : [], solutions: mediaBlob ? [{ id, solution: mediaBlob }] : [],
score: { correct: 0, total: 100, missing: 0 }, score: { correct: 0, total: 100, missing: 0 },
type, type, isPractice
}); });
}; };

View File

@@ -11,6 +11,7 @@ export default function TrueFalse({
prompt, prompt,
questions, questions,
userSolutions, userSolutions,
isPractice = false,
onNext, onNext,
onBack, onBack,
disableProgressButtons = false disableProgressButtons = false
@@ -21,7 +22,7 @@ export default function TrueFalse({
const setCurrentSolution = useExamStore((state) => state.setCurrentSolution); const setCurrentSolution = useExamStore((state) => state.setCurrentSolution);
useEffect(() => { useEffect(() => {
if (hasExamEnded) onNext({ exercise: id, solutions: answers, score: calculateScore(), type }); if (hasExamEnded) onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [hasExamEnded]); }, [hasExamEnded]);
@@ -40,7 +41,7 @@ export default function TrueFalse({
}; };
useEffect(() => { useEffect(() => {
setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type }); setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [answers, setAnswers]); }, [answers, setAnswers]);
@@ -55,7 +56,7 @@ export default function TrueFalse({
}; };
useEffect(() => { useEffect(() => {
if (disableProgressButtons) onNext({ exercise: id, solutions: answers, score: calculateScore(), type }); if (disableProgressButtons) onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [answers, disableProgressButtons]) }, [answers, disableProgressButtons])
@@ -64,14 +65,14 @@ export default function TrueFalse({
<Button <Button
color="purple" color="purple"
variant="outline" variant="outline"
onClick={() => onBack({ exercise: id, solutions: answers, score: calculateScore(), type })} onClick={() => onBack({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice })}
className="max-w-[200px] w-full"> className="max-w-[200px] w-full">
Back Back
</Button> </Button>
<Button <Button
color="purple" color="purple"
onClick={() => onNext({ exercise: id, solutions: answers, score: calculateScore(), type })} onClick={() => onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice })}
className="max-w-[200px] self-end w-full"> className="max-w-[200px] self-end w-full">
Next Next
</Button> </Button>

View File

@@ -53,6 +53,7 @@ export default function WriteBlanks({
maxWords, maxWords,
solutions, solutions,
userSolutions, userSolutions,
isPractice = false,
text, text,
onNext, onNext,
onBack, onBack,
@@ -63,7 +64,7 @@ export default function WriteBlanks({
const { hasExamEnded, setCurrentSolution } = useExamStore((state) => state); const { hasExamEnded, setCurrentSolution } = useExamStore((state) => state);
useEffect(() => { useEffect(() => {
if (hasExamEnded) onNext({ exercise: id, solutions: answers, score: calculateScore(), type }); if (hasExamEnded) onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [hasExamEnded]); }, [hasExamEnded]);
@@ -82,12 +83,12 @@ export default function WriteBlanks({
}; };
useEffect(() => { useEffect(() => {
setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type }); setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [answers, setAnswers]); }, [answers, setAnswers]);
useEffect(() => { useEffect(() => {
if (disableProgressButtons) onNext({ exercise: id, solutions: answers, score: calculateScore(), type }); if (disableProgressButtons) onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice });
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [answers, disableProgressButtons]) }, [answers, disableProgressButtons])
@@ -112,14 +113,14 @@ export default function WriteBlanks({
<Button <Button
color="purple" color="purple"
variant="outline" variant="outline"
onClick={() => onBack({ exercise: id, solutions: answers, score: calculateScore(), type })} onClick={() => onBack({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice })}
className="max-w-[200px] w-full"> className="max-w-[200px] w-full">
Back Back
</Button> </Button>
<Button <Button
color="purple" color="purple"
onClick={() => onNext({ exercise: id, solutions: answers, score: calculateScore(), type })} onClick={() => onNext({ exercise: id, solutions: answers, score: calculateScore(), type, isPractice })}
className="max-w-[200px] self-end w-full"> className="max-w-[200px] self-end w-full">
Next Next
</Button> </Button>

View File

@@ -16,6 +16,7 @@ export default function Writing({
wordCounter, wordCounter,
attachment, attachment,
userSolutions, userSolutions,
isPractice = false,
onNext, onNext,
onBack, onBack,
enableNavigation = false enableNavigation = false
@@ -66,7 +67,7 @@ export default function Writing({
useEffect(() => { useEffect(() => {
if (hasExamEnded) if (hasExamEnded)
onNext({exercise: id, solutions: [{id, solution: inputText}], score: {correct: 100, total: 100, missing: 0}, type, module: "writing"}); onNext({ exercise: id, solutions: [{ id, solution: inputText }], score: { correct: 100, total: 100, missing: 0 }, type, module: "writing", isPractice });
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [hasExamEnded]); }, [hasExamEnded]);
@@ -91,7 +92,7 @@ export default function Writing({
color="purple" color="purple"
variant="outline" variant="outline"
onClick={() => onClick={() =>
onBack({exercise: id, solutions: [{id, solution: inputText}], score: {correct: 100, total: 100, missing: 0}, type}) onBack({ exercise: id, solutions: [{ id, solution: inputText }], score: { correct: 100, total: 100, missing: 0 }, type, isPractice })
} }
className="max-w-[200px] self-end w-full"> className="max-w-[200px] self-end w-full">
Back Back
@@ -105,7 +106,7 @@ export default function Writing({
solutions: [{ id, solution: inputText.replaceAll(/\s{2,}/g, " ") }], solutions: [{ id, solution: inputText.replaceAll(/\s{2,}/g, " ") }],
score: { correct: 100, total: 100, missing: 0 }, score: { correct: 100, total: 100, missing: 0 },
type, type,
module: "writing", module: "writing", isPractice
}) })
} }
className="max-w-[200px] self-end w-full"> className="max-w-[200px] self-end w-full">
@@ -177,7 +178,7 @@ export default function Writing({
color="purple" color="purple"
variant="outline" variant="outline"
onClick={() => onClick={() =>
onBack({exercise: id, solutions: [{id, solution: inputText}], score: {correct: 100, total: 100, missing: 0}, type}) onBack({ exercise: id, solutions: [{ id, solution: inputText }], score: { correct: 100, total: 100, missing: 0 }, type, isPractice })
} }
className="max-w-[200px] self-end w-full"> className="max-w-[200px] self-end w-full">
Back Back
@@ -191,7 +192,7 @@ export default function Writing({
solutions: [{ id, solution: inputText.replaceAll(/\s{2,}/g, " ") }], solutions: [{ id, solution: inputText.replaceAll(/\s{2,}/g, " ") }],
score: { correct: 100, total: 100, missing: 0 }, score: { correct: 100, total: 100, missing: 0 },
type, type,
module: "writing", module: "writing", isPractice
}) })
} }
className="max-w-[200px] self-end w-full"> className="max-w-[200px] self-end w-full">

View File

@@ -54,7 +54,7 @@ const aggregateScoresByModule = (stats: Stat[]): {module: Module; total: number;
}, },
}; };
stats.forEach((x) => { stats.filter(x => !x.isPractice).forEach((x) => {
scores[x.module!] = { scores[x.module!] = {
total: scores[x.module!].total + x.score.total, total: scores[x.module!].total + x.score.total,
correct: scores[x.module!].correct + x.score.correct, correct: scores[x.module!].correct + x.score.correct,

View File

@@ -88,6 +88,7 @@ export interface UserSolution {
exercise: string; exercise: string;
isDisabled?: boolean; isDisabled?: boolean;
shuffleMaps?: ShuffleMap[]; shuffleMaps?: ShuffleMap[];
isPractice?: boolean
} }
export interface WritingExam extends ExamBase { export interface WritingExam extends ExamBase {
@@ -162,6 +163,7 @@ export interface WritingExercise extends Section {
evaluation?: WritingEvaluation; evaluation?: WritingEvaluation;
}[]; }[];
topic?: string; topic?: string;
isPractice?: boolean
} }
export interface AIDetectionAttributes { export interface AIDetectionAttributes {
@@ -196,6 +198,7 @@ export interface SpeakingExercise extends Section {
evaluation?: SpeakingEvaluation; evaluation?: SpeakingEvaluation;
}[]; }[];
topic?: string; topic?: string;
isPractice?: boolean
} }
export interface InteractiveSpeakingExercise extends Section { export interface InteractiveSpeakingExercise extends Section {
@@ -215,6 +218,7 @@ export interface InteractiveSpeakingExercise extends Section {
first_topic?: string; first_topic?: string;
second_topic?: string; second_topic?: string;
variant?: "initial" | "final"; variant?: "initial" | "final";
isPractice?: boolean
} }
export interface FillBlanksMCOption { export interface FillBlanksMCOption {
@@ -243,6 +247,7 @@ export interface FillBlanksExercise {
solution: string; // *EXAMPLE: "preserve" solution: string; // *EXAMPLE: "preserve"
}[]; }[];
variant?: string; variant?: string;
isPractice?: boolean
} }
export interface TrueFalseExercise { export interface TrueFalseExercise {
@@ -251,6 +256,7 @@ export interface TrueFalseExercise {
prompt: string; // *EXAMPLE: "Select the appropriate option." prompt: string; // *EXAMPLE: "Select the appropriate option."
questions: TrueFalseQuestion[]; questions: TrueFalseQuestion[];
userSolutions: { id: string; solution: "true" | "false" | "not_given" }[]; userSolutions: { id: string; solution: "true" | "false" | "not_given" }[];
isPractice?: boolean
} }
export interface TrueFalseQuestion { export interface TrueFalseQuestion {
@@ -274,6 +280,7 @@ export interface WriteBlanksExercise {
solution: string; solution: string;
}[]; }[];
variant?: string; variant?: string;
isPractice?: boolean
} }
export interface MatchSentencesExercise { export interface MatchSentencesExercise {
@@ -285,6 +292,7 @@ export interface MatchSentencesExercise {
allowRepetition: boolean; allowRepetition: boolean;
options: MatchSentenceExerciseOption[]; options: MatchSentenceExerciseOption[];
variant?: string; variant?: string;
isPractice?: boolean
} }
export interface MatchSentenceExerciseSentence { export interface MatchSentenceExerciseSentence {
@@ -309,6 +317,7 @@ export interface MultipleChoiceExercise {
title: string; title: string;
content: string; content: string;
} }
isPractice?: boolean
} }
export interface MultipleChoiceQuestion { export interface MultipleChoiceQuestion {

View File

@@ -142,6 +142,7 @@ export interface Stat {
path: string; path: string;
version: string; version: string;
}; };
isPractice?: boolean
} }
export interface Group { export interface Group {

View File

@@ -11,7 +11,6 @@ import Reading from "@/exams/Reading";
import Selection from "@/exams/Selection"; import Selection from "@/exams/Selection";
import Speaking from "@/exams/Speaking"; import Speaking from "@/exams/Speaking";
import Writing from "@/exams/Writing"; import Writing from "@/exams/Writing";
import useUser from "@/hooks/useUser";
import { Exam, LevelExam, UserSolution, Variant } from "@/interfaces/exam"; import { Exam, LevelExam, UserSolution, Variant } from "@/interfaces/exam";
import { Stat, User } from "@/interfaces/user"; import { Stat, User } from "@/interfaces/user";
import useExamStore from "@/stores/examStore"; import useExamStore from "@/stores/examStore";
@@ -21,12 +20,7 @@ import axios from "axios";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { toast, ToastContainer } from "react-toastify"; import { toast, ToastContainer } from "react-toastify";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import useSessions from "@/hooks/useSessions";
import ShortUniqueId from "short-unique-id"; import ShortUniqueId from "short-unique-id";
import clsx from "clsx";
import useGradingSystem from "@/hooks/useGrading";
import { Assignment } from "@/interfaces/results";
import { mapBy } from "@/utils";
interface Props { interface Props {
page: "exams" | "exercises"; page: "exams" | "exercises";
@@ -214,7 +208,6 @@ export default function ExamPage({ page, user, destination = "/exam", hideSideba
}, [setModuleIndex, showSolutions]); }, [setModuleIndex, showSolutions]);
useEffect(() => { useEffect(() => {
console.log(selectedModules)
if (selectedModules.length > 0 && exams.length > 0 && moduleIndex < selectedModules.length) { if (selectedModules.length > 0 && exams.length > 0 && moduleIndex < selectedModules.length) {
const nextExam = exams[moduleIndex]; const nextExam = exams[moduleIndex];
@@ -264,6 +257,7 @@ export default function ExamPage({ page, user, destination = "/exam", hideSideba
isDisabled: solution.isDisabled, isDisabled: solution.isDisabled,
shuffleMaps: solution.shuffleMaps, shuffleMaps: solution.shuffleMaps,
...(assignment ? { assignment: assignment.id } : {}), ...(assignment ? { assignment: assignment.id } : {}),
isPractice: solution.isPractice
})); }));
axios axios
@@ -422,7 +416,7 @@ export default function ExamPage({ page, user, destination = "/exam", hideSideba
}, },
}; };
userSolutions.forEach((x) => { userSolutions.filter(x => !x.isPractice).forEach((x) => {
const examModule = const examModule =
x.module || (x.type === "writing" ? "writing" : x.type === "speaking" || x.type === "interactiveSpeaking" ? "speaking" : undefined); x.module || (x.type === "writing" ? "writing" : x.type === "speaking" || x.type === "interactiveSpeaking" ? "speaking" : undefined);

View File

@@ -43,12 +43,10 @@ export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
const entities = await getEntitiesWithRoles(checkAccess(user, ["admin", "developer"]) ? undefined : entityIDs) const entities = await getEntitiesWithRoles(checkAccess(user, ["admin", "developer"]) ? undefined : entityIDs)
const users = await (checkAccess(user, ["admin", "developer"]) ? getUsers() : getEntitiesUsers(mapBy(entities, 'id'))) const users = await (checkAccess(user, ["admin", "developer"]) ? getUsers() : getEntitiesUsers(mapBy(entities, 'id')))
const groups = await (checkAccess(user, ["admin", "developer"]) ? getGroups() : getGroupsByEntities(mapBy(entities, 'id')))
const assignments = await (checkAccess(user, ["admin", "developer"]) ? getAssignments() : getEntitiesAssignments(mapBy(entities, 'id'))) const assignments = await (checkAccess(user, ["admin", "developer"]) ? getAssignments() : getEntitiesAssignments(mapBy(entities, 'id')))
const gradingSystems = await Promise.all(entityIDs.map(getGradingSystemByEntity))
return { return {
props: serialize({user, users, assignments, entities, gradingSystems}), props: serialize({ user, users, assignments, entities }),
}; };
}, sessionOptions); }, sessionOptions);
@@ -58,13 +56,12 @@ interface Props {
user: User; user: User;
users: User[]; users: User[];
assignments: Assignment[]; assignments: Assignment[];
gradingSystems: Grading[]
entities: EntityWithRoles[] entities: EntityWithRoles[]
} }
const MAX_TRAINING_EXAMS = 10; const MAX_TRAINING_EXAMS = 10;
export default function History({user, users, assignments, entities, gradingSystems}: Props) { export default function History({ user, users, assignments, entities }: Props) {
const router = useRouter(); const router = useRouter();
const [statsUserId, setStatsUserId, training, setTraining] = useRecordStore((state) => [ const [statsUserId, setStatsUserId, training, setTraining] = useRecordStore((state) => [
state.selectedUser, state.selectedUser,