ENCOA-315

This commit is contained in:
Carlos-Mesquita
2025-01-22 04:46:24 +00:00
parent 4724e98993
commit e36b24ea3f
12 changed files with 425 additions and 231 deletions

View File

@@ -1,95 +1,114 @@
import { UserSolution } from '@/interfaces/exam';
import useExamStore from '@/stores/exam';
import { StateFlags } from '@/stores/exam/types';
import axios from 'axios';
import { SetStateAction, useEffect, useRef } from 'react';
import { UserSolution } from "@/interfaces/exam";
import useExamStore from "@/stores/exam";
import axios from "axios";
import { useEffect, useRef } from "react";
import { useRouter } from "next/router";
type UseEvaluationPolling = (props: {
pendingExercises: string[],
setPendingExercises: React.Dispatch<SetStateAction<string[]>>,
}) => void;
const useEvaluationPolling = (sessionIds: string[], mode: "exam" | "records", userId: string) => {
const { setUserSolutions, userSolutions } = useExamStore();
const pollingTimeoutsRef = useRef<Map<string, NodeJS.Timeout>>(new Map());
const router = useRouter();
const useEvaluationPolling: UseEvaluationPolling = ({
pendingExercises,
setPendingExercises,
}) => {
const {
flags, sessionId, user,
userSolutions, evaluated,
setEvaluated, setFlags
} = useExamStore();
const poll = async (sessionId: string) => {
try {
const { data: statusData } = await axios.get('/api/evaluate/status', {
params: { op: 'pending', userId, sessionId }
});
const pollingTimeoutRef = useRef<NodeJS.Timeout>();
if (!statusData.hasPendingEvaluation) {
useEffect(() => {
return () => {
if (pollingTimeoutRef.current) {
clearTimeout(pollingTimeoutRef.current);
}
};
}, []);
let solutionsOrStats = userSolutions;
useEffect(() => {
if (!flags.pendingEvaluation || pendingExercises.length === 0) {
if (pollingTimeoutRef.current) {
clearTimeout(pollingTimeoutRef.current);
}
return;
if (mode === "records") {
const res = await axios.get(`/api/stats/session/${sessionId}`)
solutionsOrStats = res.data;
}
const { data: completedSolutions } = await axios.post('/api/evaluate/fetchSolutions?op=session', {
sessionId,
userId,
stats: solutionsOrStats,
});
const pollStatus = async () => {
try {
const { data } = await axios.get('/api/evaluate/status', {
params: {
sessionId,
userId: user,
exerciseIds: pendingExercises.join(',')
}
});
await axios.post('/api/stats/disabled', {
sessionId,
userId,
solutions: completedSolutions,
});
if (data.finishedExerciseIds.length > 0) {
const remainingExercises = pendingExercises.filter(
id => !data.finishedExerciseIds.includes(id)
);
const timeout = pollingTimeoutsRef.current.get(sessionId);
if (timeout) clearTimeout(timeout);
pollingTimeoutsRef.current.delete(sessionId);
setPendingExercises(remainingExercises);
if (mode === "exam") {
const updatedSolutions = userSolutions.map(solution => {
const completed = completedSolutions.find(
(c: UserSolution) => c.exercise === solution.exercise
);
return completed || solution;
});
if (remainingExercises.length === 0) {
const evaluatedData = await axios.post('/api/evaluate/fetchSolutions', {
sessionId,
userId: user,
userSolutions
});
setUserSolutions(updatedSolutions);
} else {
router.reload();
}
} else {
if (pollingTimeoutsRef.current.has(sessionId)) {
clearTimeout(pollingTimeoutsRef.current.get(sessionId));
}
pollingTimeoutsRef.current.set(
sessionId,
setTimeout(() => poll(sessionId), 5000)
);
}
} catch (error) {
if (pollingTimeoutsRef.current.has(sessionId)) {
clearTimeout(pollingTimeoutsRef.current.get(sessionId));
}
pollingTimeoutsRef.current.set(
sessionId,
setTimeout(() => poll(sessionId), 5000)
);
}
};
const newEvaluations = evaluatedData.data.filter(
(newEval: UserSolution) =>
!evaluated.some(existingEval => existingEval.exercise === newEval.exercise)
);
useEffect(() => {
if (mode === "exam") {
const hasDisabledSolutions = userSolutions.some(s => s.isDisabled);
setEvaluated([...evaluated, ...newEvaluations]);
setFlags({ pendingEvaluation: false });
return;
}
}
if (hasDisabledSolutions && sessionIds.length > 0) {
poll(sessionIds[0]);
} else {
pollingTimeoutsRef.current.forEach((timeout) => {
clearTimeout(timeout);
});
pollingTimeoutsRef.current.clear();
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [mode, sessionIds, userSolutions]);
if (pendingExercises.length > 0) {
pollingTimeoutRef.current = setTimeout(pollStatus, 5000);
}
} catch (error) {
console.error('Evaluation polling error:', error);
pollingTimeoutRef.current = setTimeout(pollStatus, 5000);
}
};
useEffect(() => {
if (mode === "records" && sessionIds.length > 0) {
sessionIds.forEach(sessionId => {
poll(sessionId);
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [mode, sessionIds]);
pollStatus();
useEffect(() => {
const timeouts = pollingTimeoutsRef.current;
return () => {
timeouts.forEach((timeout) => {
clearTimeout(timeout);
});
timeouts.clear();
};
}, []);
return () => {
if (pollingTimeoutRef.current) {
clearTimeout(pollingTimeoutRef.current);
}
};
});
return {
isPolling: pollingTimeoutsRef.current.size > 0
};
};
export default useEvaluationPolling;