Commented all related to shuffle

This commit is contained in:
Carlos Mesquita
2024-08-19 16:42:14 +01:00
parent bcb1a0f914
commit 8669ef462d
4 changed files with 71 additions and 61 deletions

View File

@@ -20,7 +20,7 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
onNext, onNext,
onBack, onBack,
}) => { }) => {
const { shuffleMaps } = useExamStore((state) => state); //const { shuffleMaps } = useExamStore((state) => state);
const [answers, setAnswers] = useState<{ id: string; solution: string }[]>(userSolutions); const [answers, setAnswers] = useState<{ id: string; solution: string }[]>(userSolutions);
const hasExamEnded = useExamStore((state) => state.hasExamEnded); const hasExamEnded = useExamStore((state) => state.hasExamEnded);
@@ -62,7 +62,7 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
return solution.toLowerCase() === option.toLowerCase(); return solution.toLowerCase() === option.toLowerCase();
} else if ('letter' in option) { } else if ('letter' in option) {
return solution.toLowerCase() === option.word.toLowerCase(); return solution.toLowerCase() === option.word.toLowerCase();
} else if ('options' in option) { } /*else if ('options' in option) {
if (shuffleMaps.length !== 0) { if (shuffleMaps.length !== 0) {
const shuffleMap = shuffleMaps.find((map) => map.id == x.id) const shuffleMap = shuffleMaps.find((map) => map.id == x.id)
if (!shuffleMap) { if (!shuffleMap) {
@@ -73,7 +73,7 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
} }
return solution.toLowerCase() === (option.options[x.solution as keyof typeof option.options] || '').toLowerCase(); return solution.toLowerCase() === (option.options[x.solution as keyof typeof option.options] || '').toLowerCase();
} }*/
return false; return false;
}).length; }).length;
@@ -129,7 +129,7 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
setAnswers((prev) => [...prev.filter((x) => x.id !== id), { id: id, solution: value }]); setAnswers((prev) => [...prev.filter((x) => x.id !== id), { id: id, solution: value }]);
} }
const getShuffles = () => { /*const getShuffles = () => {
let shuffle = {}; let shuffle = {};
if (shuffleMaps.length !== 0) { if (shuffleMaps.length !== 0) {
shuffle = { shuffle = {
@@ -139,7 +139,7 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
} }
} }
return shuffle; return shuffle;
} }*/
return ( return (
<> <>
@@ -166,7 +166,7 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
<div className="bg-mti-gray-smoke rounded-xl px-5 py-6 flex flex-col gap-4"> <div className="bg-mti-gray-smoke rounded-xl px-5 py-6 flex flex-col gap-4">
<span className="font-medium text-mti-purple-dark">Options</span> <span className="font-medium text-mti-purple-dark">Options</span>
<div className="flex gap-4 flex-wrap"> <div className="flex gap-4 flex-wrap">
{currentMCSelection.selection?.options && Object.entries(currentMCSelection.selection.options).map(([key, value]) => { {currentMCSelection.selection?.options && Object.entries(currentMCSelection.selection.options).sort((a, b) => a[0].localeCompare(b[0])).map(([key, value]) => {
return <button return <button
className={clsx( className={clsx(
"border border-mti-purple-light rounded-full px-3 py-0.5 transition ease-in-out duration-300", "border border-mti-purple-light rounded-full px-3 py-0.5 transition ease-in-out duration-300",
@@ -212,14 +212,14 @@ 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, ...getShuffles() })} onClick={() => onBack({ exercise: id, solutions: answers, score: calculateScore(), type, })}//...getShuffles() })}
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, ...getShuffles() })} onClick={() => onNext({ exercise: id, solutions: answers, score: calculateScore(), type, })}//...getShuffles() })}
className="max-w-[200px] self-end w-full"> className="max-w-[200px] self-end w-full">
Next Next
</Button> </Button>

View File

@@ -76,7 +76,7 @@ function Question({
export default function MultipleChoice({ id, prompt, type, questions, userSolutions, onNext, onBack }: MultipleChoiceExercise & CommonProps) { export default function MultipleChoice({ id, prompt, type, questions, userSolutions, onNext, onBack }: MultipleChoiceExercise & CommonProps) {
const [answers, setAnswers] = useState<{ question: string; option: string }[]>(userSolutions); const [answers, setAnswers] = useState<{ question: string; option: string }[]>(userSolutions);
const { shuffleMaps } = useExamStore((state) => state); //const { shuffleMaps } = useExamStore((state) => state);
const { questionIndex, setQuestionIndex } = useExamStore((state) => state); const { questionIndex, setQuestionIndex } = useExamStore((state) => state);
const { userSolutions: storeUserSolutions, setUserSolutions } = useExamStore((state) => state); const { userSolutions: storeUserSolutions, setUserSolutions } = useExamStore((state) => state);
const hasExamEnded = useExamStore((state) => state.hasExamEnded); const hasExamEnded = useExamStore((state) => state.hasExamEnded);
@@ -109,12 +109,12 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
}); });
let isSolutionCorrect; let isSolutionCorrect;
if (shuffleMaps.length == 0) { //if (shuffleMaps.length == 0) {
isSolutionCorrect = matchingQuestion?.solution === x.option; isSolutionCorrect = matchingQuestion?.solution === x.option;
} else { //} else {
const shuffleMap = shuffleMaps.find((map) => map.id == x.question) // const shuffleMap = shuffleMaps.find((map) => map.id == x.question)
isSolutionCorrect = shuffleMap?.map[x.option] == matchingQuestion?.solution; // isSolutionCorrect = shuffleMap?.map[x.option] == matchingQuestion?.solution;
} //}
return isSolutionCorrect || false; return isSolutionCorrect || false;
}).length; }).length;
const missing = total - correct; const missing = total - correct;
@@ -122,7 +122,7 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
return { total, correct, missing }; return { total, correct, missing };
}; };
const getShuffles = () => { /*const getShuffles = () => {
let shuffle = {}; let shuffle = {};
if (shuffleMaps.length !== 0) { if (shuffleMaps.length !== 0) {
shuffle = { shuffle = {
@@ -132,11 +132,11 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
} }
} }
return shuffle; return shuffle;
} }*/
const next = () => { const next = () => {
if (questionIndex === questions.length - 1) { if (questionIndex === questions.length - 1) {
onNext({ exercise: id, solutions: answers, score: calculateScore(), type, ...getShuffles() }); onNext({ exercise: id, solutions: answers, score: calculateScore(), type, });//...getShuffles() });
} else { } else {
setQuestionIndex(questionIndex + 1); setQuestionIndex(questionIndex + 1);
} }
@@ -145,7 +145,7 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
const back = () => { const back = () => {
if (questionIndex === 0) { if (questionIndex === 0) {
onBack({ exercise: id, solutions: answers, score: calculateScore(), type, ...getShuffles() }); onBack({ exercise: id, solutions: answers, score: calculateScore(), type, });// ...getShuffles() });
} else { } else {
setQuestionIndex(questionIndex - 1); setQuestionIndex(questionIndex - 1);
} }

View File

@@ -17,26 +17,36 @@ function Question({
}: MultipleChoiceQuestion & { userSolution: string | undefined; onSelectOption?: (option: string) => void; showSolution?: boolean }) { }: MultipleChoiceQuestion & { userSolution: string | undefined; onSelectOption?: (option: string) => void; showSolution?: boolean }) {
const { userSolutions } = useExamStore((state) => state); const { userSolutions } = useExamStore((state) => state);
/*
const getShuffledOptions = (options: {id: string, text: string}[], questionShuffleMap: ShuffleMap) => {
const shuffledOptions = ['A', 'B', 'C', 'D'].map(newId => {
const originalId = questionShuffleMap.map[newId];
const originalOption = options.find(option => option.id === originalId);
return {
id: newId,
text: originalOption!.text
};
});
return shuffledOptions;
}
const getShuffledSolution = (originalSolution: string, questionShuffleMap: ShuffleMap) => {
for (const [newPosition, originalPosition] of Object.entries(questionShuffleMap.map)) {
if (originalPosition === originalSolution) {
return newPosition;
}
}
return originalSolution;
}
const questionShuffleMap = userSolutions.reduce((foundMap, userSolution) => { const questionShuffleMap = userSolutions.reduce((foundMap, userSolution) => {
if (foundMap) return foundMap; if (foundMap) return foundMap;
return userSolution.shuffleMaps?.find(map => map.id === id) || null; return userSolution.shuffleMaps?.find(map => map.id === id) || null;
}, null as ShuffleMap | null); }, null as ShuffleMap | null);
*/
const shuffledOptions = new Array(options.length);
options.forEach(option => { const questionOptions = options; // questionShuffleMap ? getShuffledOptions(options as {id: string, text: string}[], questionShuffleMap) : options;
const newId = questionShuffleMap?.map[option.id]; const newSolution = solution; //questionShuffleMap ? getShuffledSolution(solution, questionShuffleMap) : solution;
const newIndex = options.findIndex(opt => opt.id === newId);
shuffledOptions[newIndex] = option;
});
const lettersMap = ['A', 'B', 'C', 'D'];
const optionsWithLetters = shuffledOptions.map((option, index) => ({
...option,
id: lettersMap[index]
}));
const questionOptions = questionShuffleMap ? optionsWithLetters : options;
const newQuestionSolution = questionShuffleMap ? questionShuffleMap.map[solution] : solution;
const renderPrompt = (prompt: string) => { const renderPrompt = (prompt: string) => {
return reactStringReplace(prompt, /((<u>)[\w\s']+(<\/u>))/g, (match) => { return reactStringReplace(prompt, /((<u>)[\w\s']+(<\/u>))/g, (match) => {
@@ -46,11 +56,11 @@ function Question({
}; };
const optionColor = (option: string) => { const optionColor = (option: string) => {
if (option === newQuestionSolution && !userSolution) { if (option === newSolution && !userSolution) {
return "!border-mti-gray-davy !text-mti-gray-davy"; return "!border-mti-gray-davy !text-mti-gray-davy";
} }
if (option === newQuestionSolution) { if (option === newSolution) {
return "!border-mti-purple-light !text-mti-purple-light"; return "!border-mti-purple-light !text-mti-purple-light";
} }
@@ -77,8 +87,8 @@ function Question({
"flex flex-col items-center border border-mti-gray-platinum p-4 px-8 rounded-xl gap-4 cursor-pointer bg-white relative", "flex flex-col items-center border border-mti-gray-platinum p-4 px-8 rounded-xl gap-4 cursor-pointer bg-white relative",
optionColor(option!.id), optionColor(option!.id),
)}> )}>
<span className={clsx("text-sm", newQuestionSolution !== option?.id && userSolution !== option?.id && "opacity-50")}>{option?.id}</span> <span className={clsx("text-sm", newSolution !== option?.id && userSolution !== option?.id && "opacity-50")}>{option?.id}</span>
<img src={option?.src!} alt={`Option ${option?.id}`} /> {"src" in option && <img src={option?.src!} alt={`Option ${option?.id}`} />}
</div> </div>
))} ))}
{variant === "text" && {variant === "text" &&

View File

@@ -171,7 +171,7 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
const { partIndex, setPartIndex } = useExamStore((state) => state); const { partIndex, setPartIndex } = useExamStore((state) => state);
const { exerciseIndex, setExerciseIndex } = useExamStore((state) => state); const { exerciseIndex, setExerciseIndex } = useExamStore((state) => state);
const [storeQuestionIndex, setStoreQuestionIndex] = useExamStore((state) => [state.questionIndex, state.setQuestionIndex]); const [storeQuestionIndex, setStoreQuestionIndex] = useExamStore((state) => [state.questionIndex, state.setQuestionIndex]);
const [shuffleMaps, setShuffleMaps] = useExamStore((state) => [state.shuffleMaps, state.setShuffleMaps]) //const [shuffleMaps, setShuffleMaps] = useExamStore((state) => [state.shuffleMaps, state.setShuffleMaps])
const [currentExercise, setCurrentExercise] = useState<Exercise>(); const [currentExercise, setCurrentExercise] = useState<Exercise>();
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));
@@ -180,11 +180,11 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
const [contextWord, setContextWord] = useState<string | undefined>(undefined); const [contextWord, setContextWord] = useState<string | undefined>(undefined);
const [contextWordLine, setContextWordLine] = useState<number | undefined>(undefined); const [contextWordLine, setContextWordLine] = useState<number | undefined>(undefined);
useEffect(() => { /*useEffect(() => {
if (showSolutions && userSolutions[exerciseIndex].shuffleMaps) { if (showSolutions && userSolutions[exerciseIndex].shuffleMaps) {
setShuffleMaps(userSolutions[exerciseIndex].shuffleMaps as ShuffleMap[]) setShuffleMaps(userSolutions[exerciseIndex].shuffleMaps as ShuffleMap[])
} }
}, [showSolutions]) }, [showSolutions])*/
useEffect(() => { useEffect(() => {
if (hasExamEnded && exerciseIndex === -1) { if (hasExamEnded && exerciseIndex === -1) {
@@ -214,8 +214,9 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
userSolutions: userSolutions.find((x) => x.exercise === exercise.id)?.solutions || [], userSolutions: userSolutions.find((x) => x.exercise === exercise.id)?.solutions || [],
}; };
if (exam.shuffle && exercise.type === "multipleChoice") { /*if (exam.shuffle && exercise.type === "multipleChoice") {
if (shuffleMaps.length == 0 && !showSolutions) { if (shuffleMaps.length == 0 && !showSolutions) {
console.log("Shuffling answers");
const newShuffleMaps: ShuffleMap[] = []; const newShuffleMaps: ShuffleMap[] = [];
exercise.questions = exercise.questions.map(question => { exercise.questions = exercise.questions.map(question => {
@@ -242,6 +243,7 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
setShuffleMaps(newShuffleMaps); setShuffleMaps(newShuffleMaps);
} else { } else {
console.log("Retrieving shuffles");
exercise.questions = exercise.questions.map(question => { exercise.questions = exercise.questions.map(question => {
const questionShuffleMap = shuffleMaps.find(map => map.id === question.id); const questionShuffleMap = shuffleMaps.find(map => map.id === question.id);
if (questionShuffleMap) { if (questionShuffleMap) {
@@ -261,20 +263,21 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
exercise.words = exercise.words.map(word => { exercise.words = exercise.words.map(word => {
if ('options' in word) { if ('options' in word) {
const options = { ...word.options }; const options = { ...word.options };
const shuffledKeys = Object.keys(options).sort(() => Math.random() - 0.5); const originalKeys = Object.keys(options);
const shuffledKeys = [...originalKeys].sort(() => Math.random() - 0.5);
const newOptions = shuffledKeys.reduce((acc, key, index) => { const newOptions = shuffledKeys.reduce((acc, key, index) => {
acc[key as keyof typeof options] = options[shuffledKeys[index] as keyof typeof options]; acc[key as keyof typeof options] = options[originalKeys[index] as keyof typeof options];
return acc; return acc;
}, {} as { [key in keyof typeof options]: string }); }, {} as { [key in keyof typeof options]: string });
const optionMapping = shuffledKeys.reduce((acc, key, index) => { const optionMapping = originalKeys.reduce((acc, key, index) => {
acc[key as keyof typeof options] = Object.keys(options)[index] as keyof typeof options; acc[key as keyof typeof options] = shuffledKeys[index];
return acc; return acc;
}, {} as { [key in keyof typeof options]: string }); }, {} as { [key in keyof typeof options]: string });
newShuffleMaps.push({ id: word.id, map: optionMapping }); newShuffleMaps.push({ id: word.id, map: optionMapping });
return { ...word, options: newOptions }; return { ...word, options: newOptions };
} }
return word; return word;
@@ -283,20 +286,17 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
setShuffleMaps(newShuffleMaps); setShuffleMaps(newShuffleMaps);
} }
} }
*/
return exercise; return exercise;
}; };
useEffect(() => { useEffect(() => {
const newExercise = getExercise(); //console.log("Getting another exercise");
setCurrentExercise(newExercise); //setShuffleMaps([]);
setCurrentExercise(getExercise());
}, [partIndex, exerciseIndex]); }, [partIndex, exerciseIndex]);
//useShuffledMultipleChoiceOptions(currentExercise, exam.shuffle, storeQuestionIndex, shuffleMaps, setShuffleMaps, setCurrentExercise);
//useShuffledFillBlanksOptions(currentExercise, exam.shuffle, storeQuestionIndex, shuffleMaps, setShuffleMaps, setCurrentExercise);
useEffect(() => { useEffect(() => {
const regex = /.*?['"](.*?)['"] in line (\d+)\?$/; const regex = /.*?['"](.*?)['"] in line (\d+)\?$/;
if (currentExercise && currentExercise.type === "multipleChoice") { if (currentExercise && currentExercise.type === "multipleChoice") {
@@ -321,7 +321,7 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
setContextWord(undefined); setContextWord(undefined);
} }
} }
}, [storeQuestionIndex, contextWordLine, exerciseIndex, partIndex, shuffleMaps]); }, [storeQuestionIndex, contextWordLine, exerciseIndex, partIndex]); //, shuffleMaps]);
const nextExercise = (solution?: UserSolution) => { const nextExercise = (solution?: UserSolution) => {
scrollToTop(); scrollToTop();
@@ -362,9 +362,9 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
if (solution) { if (solution) {
let stat = { ...solution, module: "level" as Module, exam: exam.id } let stat = { ...solution, module: "level" as Module, exam: exam.id }
if (exam.shuffle) { /*if (exam.shuffle) {
stat.shuffleMaps = shuffleMaps stat.shuffleMaps = shuffleMaps
} }*/
onFinish([...userSolutions.filter((x) => x.exercise !== solution.exercise), { ...stat }]); onFinish([...userSolutions.filter((x) => x.exercise !== solution.exercise), { ...stat }]);
} else { } else {
onFinish(userSolutions); onFinish(userSolutions);