Possibly solved part of the issue with speaking
This commit is contained in:
@@ -15,10 +15,7 @@ import useUser from "@/hooks/useUser";
|
||||
import {Exam, UserSolution, Variant} from "@/interfaces/exam";
|
||||
import {Stat} from "@/interfaces/user";
|
||||
import useExamStore from "@/stores/examStore";
|
||||
import {
|
||||
evaluateSpeakingAnswer,
|
||||
evaluateWritingAnswer,
|
||||
} from "@/utils/evaluation";
|
||||
import {evaluateSpeakingAnswer, evaluateWritingAnswer} from "@/utils/evaluation";
|
||||
import {defaultExamUserSolutions, getExam} from "@/utils/exams";
|
||||
import axios from "axios";
|
||||
import {useRouter} from "next/router";
|
||||
@@ -37,9 +34,7 @@ export default function ExamPage({ page }: Props) {
|
||||
const [hasBeenUploaded, setHasBeenUploaded] = useState(false);
|
||||
const [showAbandonPopup, setShowAbandonPopup] = useState(false);
|
||||
const [isEvaluationLoading, setIsEvaluationLoading] = useState(false);
|
||||
const [statsAwaitingEvaluation, setStatsAwaitingEvaluation] = useState<
|
||||
string[]
|
||||
>([]);
|
||||
const [statsAwaitingEvaluation, setStatsAwaitingEvaluation] = useState<string[]>([]);
|
||||
const [timeSpent, setTimeSpent] = useState(0);
|
||||
|
||||
const resetStore = useExamStore((state) => state.reset);
|
||||
@@ -57,9 +52,7 @@ export default function ExamPage({ page }: Props) {
|
||||
const {exerciseIndex, setExerciseIndex} = useExamStore((state) => state);
|
||||
const {userSolutions, setUserSolutions} = useExamStore((state) => state);
|
||||
const {showSolutions, setShowSolutions} = useExamStore((state) => state);
|
||||
const { selectedModules, setSelectedModules } = useExamStore(
|
||||
(state) => state,
|
||||
);
|
||||
const {selectedModules, setSelectedModules} = useExamStore((state) => state);
|
||||
|
||||
const {user} = useUser({redirectTo: "/login"});
|
||||
const router = useRouter();
|
||||
@@ -97,10 +90,7 @@ export default function ExamPage({ page }: Props) {
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(
|
||||
() => setTimeSpent((prev) => prev + initialTimeSpent),
|
||||
[initialTimeSpent],
|
||||
);
|
||||
useEffect(() => setTimeSpent((prev) => prev + initialTimeSpent), [initialTimeSpent]);
|
||||
|
||||
useEffect(() => {
|
||||
if (userSolutions.length === 0 && exams.length > 0) {
|
||||
@@ -122,28 +112,10 @@ export default function ExamPage({ page }: Props) {
|
||||
)
|
||||
saveSession();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
assignment,
|
||||
exam,
|
||||
exams,
|
||||
moduleIndex,
|
||||
selectedModules,
|
||||
sessionId,
|
||||
userSolutions,
|
||||
user,
|
||||
exerciseIndex,
|
||||
partIndex,
|
||||
questionIndex,
|
||||
]);
|
||||
}, [assignment, exam, exams, moduleIndex, selectedModules, sessionId, userSolutions, user, exerciseIndex, partIndex, questionIndex]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
timeSpent % 20 === 0 &&
|
||||
timeSpent > 0 &&
|
||||
moduleIndex < selectedModules.length &&
|
||||
!showSolutions
|
||||
)
|
||||
saveSession();
|
||||
if (timeSpent % 20 === 0 && timeSpent > 0 && moduleIndex < selectedModules.length && !showSolutions) saveSession();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [timeSpent]);
|
||||
|
||||
@@ -177,20 +149,11 @@ export default function ExamPage({ page }: Props) {
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (
|
||||
selectedModules.length > 0 &&
|
||||
exams.length > 0 &&
|
||||
moduleIndex < selectedModules.length
|
||||
) {
|
||||
if (selectedModules.length > 0 && exams.length > 0 && moduleIndex < selectedModules.length) {
|
||||
const nextExam = exams[moduleIndex];
|
||||
|
||||
if (partIndex === -1 && nextExam.module !== "listening")
|
||||
setPartIndex(0);
|
||||
if (
|
||||
exerciseIndex === -1 &&
|
||||
!["reading", "listening"].includes(nextExam?.module)
|
||||
)
|
||||
setExerciseIndex(0);
|
||||
if (partIndex === -1 && nextExam.module !== "listening") setPartIndex(0);
|
||||
if (exerciseIndex === -1 && !["reading", "listening"].includes(nextExam?.module)) setExerciseIndex(0);
|
||||
setExam(nextExam ? updateExamWithUserSolutions(nextExam) : undefined);
|
||||
}
|
||||
})();
|
||||
@@ -205,9 +168,7 @@ export default function ExamPage({ page }: Props) {
|
||||
module,
|
||||
avoidRepeated,
|
||||
variant,
|
||||
user?.type === "student" || user?.type === "developer"
|
||||
? user.preferredGender
|
||||
: undefined,
|
||||
user?.type === "student" || user?.type === "developer" ? user.preferredGender : undefined,
|
||||
),
|
||||
);
|
||||
Promise.all(examPromises).then((values) => {
|
||||
@@ -224,13 +185,7 @@ export default function ExamPage({ page }: Props) {
|
||||
}, [selectedModules, setExams, exams]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
selectedModules.length > 0 &&
|
||||
exams.length !== 0 &&
|
||||
moduleIndex >= selectedModules.length &&
|
||||
!hasBeenUploaded &&
|
||||
!showSolutions
|
||||
) {
|
||||
if (selectedModules.length > 0 && exams.length !== 0 && moduleIndex >= selectedModules.length && !hasBeenUploaded && !showSolutions) {
|
||||
const newStats: Stat[] = userSolutions.map((solution) => ({
|
||||
...solution,
|
||||
id: solution.id || uuidv4(),
|
||||
@@ -266,17 +221,10 @@ export default function ExamPage({ page }: Props) {
|
||||
const checkIfStatsHaveBeenEvaluated = (ids: string[]) => {
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
const awaitedStats = await Promise.all(
|
||||
ids.map(
|
||||
async (id) => (await axios.get<Stat>(`/api/stats/${id}`)).data,
|
||||
),
|
||||
);
|
||||
const solutionsEvaluated = awaitedStats.every((stat) =>
|
||||
stat.solutions.every((x) => x.evaluation !== null),
|
||||
);
|
||||
const awaitedStats = await Promise.all(ids.map(async (id) => (await axios.get<Stat>(`/api/stats/${id}`)).data));
|
||||
const solutionsEvaluated = awaitedStats.every((stat) => stat.solutions.every((x) => x.evaluation !== null));
|
||||
if (solutionsEvaluated) {
|
||||
const statsUserSolutions: UserSolution[] = awaitedStats.map(
|
||||
(stat) => ({
|
||||
const statsUserSolutions: UserSolution[] = awaitedStats.map((stat) => ({
|
||||
id: stat.id,
|
||||
exercise: stat.exercise,
|
||||
score: stat.score,
|
||||
@@ -284,20 +232,15 @@ export default function ExamPage({ page }: Props) {
|
||||
type: stat.type,
|
||||
exam: stat.exam,
|
||||
module: stat.module,
|
||||
}),
|
||||
);
|
||||
}));
|
||||
|
||||
const updatedUserSolutions = userSolutions.map((x) => {
|
||||
const respectiveSolution = statsUserSolutions.find(
|
||||
(y) => y.exercise === x.exercise,
|
||||
);
|
||||
const respectiveSolution = statsUserSolutions.find((y) => y.exercise === x.exercise);
|
||||
return respectiveSolution ? respectiveSolution : x;
|
||||
});
|
||||
|
||||
setUserSolutions(updatedUserSolutions);
|
||||
return setStatsAwaitingEvaluation((prev) =>
|
||||
prev.filter((x) => !ids.includes(x)),
|
||||
);
|
||||
return setStatsAwaitingEvaluation((prev) => prev.filter((x) => !ids.includes(x)));
|
||||
}
|
||||
|
||||
return checkIfStatsHaveBeenEvaluated(ids);
|
||||
@@ -313,8 +256,7 @@ export default function ExamPage({ page }: Props) {
|
||||
Object.assign(p, {
|
||||
exercises: p.exercises.map((x) =>
|
||||
Object.assign(x, {
|
||||
userSolutions: userSolutions.find((y) => x.id === y.exercise)
|
||||
?.solutions,
|
||||
userSolutions: userSolutions.find((y) => x.id === y.exercise)?.solutions,
|
||||
}),
|
||||
),
|
||||
}),
|
||||
@@ -324,8 +266,7 @@ export default function ExamPage({ page }: Props) {
|
||||
|
||||
const exercises = exam.exercises.map((x) =>
|
||||
Object.assign(x, {
|
||||
userSolutions: userSolutions.find((y) => x.id === y.exercise)
|
||||
?.solutions,
|
||||
userSolutions: userSolutions.find((y) => x.id === y.exercise)?.solutions,
|
||||
}),
|
||||
);
|
||||
return Object.assign(exam, {exercises});
|
||||
@@ -339,12 +280,7 @@ export default function ExamPage({ page }: Props) {
|
||||
|
||||
if (exam && !solutionExams.includes(exam.id)) return;
|
||||
|
||||
if (
|
||||
exam &&
|
||||
(exam.module === "writing" || exam.module === "speaking") &&
|
||||
solutions.length > 0 &&
|
||||
!showSolutions
|
||||
) {
|
||||
if (exam && (exam.module === "writing" || exam.module === "speaking") && solutions.length > 0 && !showSolutions) {
|
||||
setHasBeenUploaded(true);
|
||||
setIsEvaluationLoading(true);
|
||||
|
||||
@@ -353,45 +289,27 @@ export default function ExamPage({ page }: Props) {
|
||||
exam.exercises.map(async (exercise, index) => {
|
||||
const evaluationID = uuidv4();
|
||||
if (exercise.type === "writing")
|
||||
return await evaluateWritingAnswer(
|
||||
exercise,
|
||||
index + 1,
|
||||
solutions.find((x) => x.exercise === exercise.id)!,
|
||||
evaluationID,
|
||||
);
|
||||
return await evaluateWritingAnswer(exercise, index + 1, solutions.find((x) => x.exercise === exercise.id)!, evaluationID);
|
||||
|
||||
if (
|
||||
exercise.type === "interactiveSpeaking" ||
|
||||
exercise.type === "speaking"
|
||||
)
|
||||
if (exercise.type === "interactiveSpeaking" || exercise.type === "speaking")
|
||||
return await evaluateSpeakingAnswer(
|
||||
exercise,
|
||||
solutions.find((x) => x.exercise === exercise.id)!,
|
||||
evaluationID,
|
||||
index === 0 ? 1 : 2,
|
||||
);
|
||||
}),
|
||||
)
|
||||
).filter((x) => !!x) as UserSolution[];
|
||||
|
||||
newSolutions = [
|
||||
...newSolutions.filter(
|
||||
(x) => !responses.map((y) => y.exercise).includes(x.exercise),
|
||||
),
|
||||
...responses,
|
||||
];
|
||||
setStatsAwaitingEvaluation((prev) => [
|
||||
...prev,
|
||||
...responses.filter((x) => !!x).map((r) => (r as any).id),
|
||||
]);
|
||||
newSolutions = [...newSolutions.filter((x) => !responses.map((y) => y.exercise).includes(x.exercise)), ...responses];
|
||||
setStatsAwaitingEvaluation((prev) => [...prev, ...responses.filter((x) => !!x).map((r) => (r as any).id)]);
|
||||
setHasBeenUploaded(false);
|
||||
}
|
||||
|
||||
axios.get("/api/stats/update");
|
||||
|
||||
setUserSolutions([
|
||||
...userSolutions.filter((x) => !solutionIds.includes(x.exercise)),
|
||||
...newSolutions,
|
||||
]);
|
||||
setUserSolutions([...userSolutions.filter((x) => !solutionIds.includes(x.exercise)), ...newSolutions]);
|
||||
setModuleIndex(moduleIndex + 1);
|
||||
|
||||
setPartIndex(-1);
|
||||
@@ -437,12 +355,7 @@ export default function ExamPage({ page }: Props) {
|
||||
|
||||
userSolutions.forEach((x) => {
|
||||
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);
|
||||
|
||||
scores[examModule!] = {
|
||||
total: scores[examModule!].total + x.score.total,
|
||||
@@ -482,9 +395,7 @@ export default function ExamPage({ page }: Props) {
|
||||
onViewResults={(index?: number) => {
|
||||
setShowSolutions(true);
|
||||
setModuleIndex(index || 0);
|
||||
setExerciseIndex(
|
||||
["reading", "listening"].includes(exams[0].module) ? -1 : 0,
|
||||
);
|
||||
setExerciseIndex(["reading", "listening"].includes(exams[0].module) ? -1 : 0);
|
||||
setPartIndex(exams[0].module === "listening" ? -1 : 0);
|
||||
setExam(exams[0]);
|
||||
}}
|
||||
@@ -494,49 +405,23 @@ export default function ExamPage({ page }: Props) {
|
||||
}
|
||||
|
||||
if (exam && exam.module === "reading") {
|
||||
return (
|
||||
<Reading
|
||||
exam={exam}
|
||||
onFinish={onFinish}
|
||||
showSolutions={showSolutions}
|
||||
/>
|
||||
);
|
||||
return <Reading exam={exam} onFinish={onFinish} showSolutions={showSolutions} />;
|
||||
}
|
||||
|
||||
if (exam && exam.module === "listening") {
|
||||
return (
|
||||
<Listening
|
||||
exam={exam}
|
||||
onFinish={onFinish}
|
||||
showSolutions={showSolutions}
|
||||
/>
|
||||
);
|
||||
return <Listening exam={exam} onFinish={onFinish} showSolutions={showSolutions} />;
|
||||
}
|
||||
|
||||
if (exam && exam.module === "writing") {
|
||||
return (
|
||||
<Writing
|
||||
exam={exam}
|
||||
onFinish={onFinish}
|
||||
showSolutions={showSolutions}
|
||||
/>
|
||||
);
|
||||
return <Writing exam={exam} onFinish={onFinish} showSolutions={showSolutions} />;
|
||||
}
|
||||
|
||||
if (exam && exam.module === "speaking") {
|
||||
return (
|
||||
<Speaking
|
||||
exam={exam}
|
||||
onFinish={onFinish}
|
||||
showSolutions={showSolutions}
|
||||
/>
|
||||
);
|
||||
return <Speaking exam={exam} onFinish={onFinish} showSolutions={showSolutions} />;
|
||||
}
|
||||
|
||||
if (exam && exam.module === "level") {
|
||||
return (
|
||||
<Level exam={exam} onFinish={onFinish} showSolutions={showSolutions} />
|
||||
);
|
||||
return <Level exam={exam} onFinish={onFinish} showSolutions={showSolutions} />;
|
||||
}
|
||||
|
||||
return <>Loading...</>;
|
||||
@@ -549,13 +434,8 @@ export default function ExamPage({ page }: Props) {
|
||||
<Layout
|
||||
user={user}
|
||||
className="justify-between"
|
||||
focusMode={
|
||||
selectedModules.length !== 0 &&
|
||||
!showSolutions &&
|
||||
moduleIndex < selectedModules.length
|
||||
}
|
||||
onFocusLayerMouseEnter={() => setShowAbandonPopup(true)}
|
||||
>
|
||||
focusMode={selectedModules.length !== 0 && !showSolutions && moduleIndex < selectedModules.length}
|
||||
onFocusLayerMouseEnter={() => setShowAbandonPopup(true)}>
|
||||
<>
|
||||
{renderScreen()}
|
||||
{!showSolutions && moduleIndex < selectedModules.length && (
|
||||
|
||||
@@ -30,6 +30,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
|
||||
const audioFile = files.audio;
|
||||
const audioFileRef = ref(storage, `speaking_recordings/${fields.id}.wav`);
|
||||
const task = parseInt(fields.task.toString());
|
||||
|
||||
const binary = fs.readFileSync((audioFile as any).path).buffer;
|
||||
const snapshot = await uploadBytes(audioFileRef, binary);
|
||||
@@ -39,7 +40,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
res.status(200).json(null);
|
||||
|
||||
console.log("🌱 - Still processing");
|
||||
const backendRequest = await evaluate({answers: [{question: fields.question, answer: path}]});
|
||||
const backendRequest = await evaluate({answer: path, question: fields.question}, task);
|
||||
console.log("🌱 - Process complete");
|
||||
|
||||
const correspondingStat = await getCorrespondingStat(fields.id, 1);
|
||||
@@ -76,14 +77,14 @@ async function getCorrespondingStat(id: string, index: number): Promise<Stat> {
|
||||
return getCorrespondingStat(id, index + 1);
|
||||
}
|
||||
|
||||
async function evaluate(body: {answers: object[]}): Promise<AxiosResponse> {
|
||||
const backendRequest = await axios.post(`${process.env.BACKEND_URL}/speaking_task_3`, body, {
|
||||
async function evaluate(body: {answer: string; question: string}, task: number): Promise<AxiosResponse> {
|
||||
const backendRequest = await axios.post(`${process.env.BACKEND_URL}/speaking_task_${task}`, body, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${process.env.BACKEND_JWT}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (typeof backendRequest.data === "string") return evaluate(body);
|
||||
if (typeof backendRequest.data === "string") return evaluate(body, task);
|
||||
return backendRequest;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,10 +45,11 @@ export const evaluateSpeakingAnswer = async (
|
||||
exercise: SpeakingExercise | InteractiveSpeakingExercise,
|
||||
solution: UserSolution,
|
||||
id: string,
|
||||
task: number,
|
||||
): Promise<UserSolution | undefined> => {
|
||||
switch (exercise?.type) {
|
||||
case "speaking":
|
||||
return {...(await evaluateSpeakingExercise(exercise, exercise.id, solution, id)), id} as UserSolution;
|
||||
return {...(await evaluateSpeakingExercise(exercise, exercise.id, solution, id, task)), id} as UserSolution;
|
||||
case "interactiveSpeaking":
|
||||
return {...(await evaluateInteractiveSpeakingExercise(exercise.id, solution, id)), id} as UserSolution;
|
||||
default:
|
||||
@@ -66,6 +67,7 @@ const evaluateSpeakingExercise = async (
|
||||
exerciseId: string,
|
||||
solution: UserSolution,
|
||||
id: string,
|
||||
task: number,
|
||||
): Promise<UserSolution | undefined> => {
|
||||
const formData = new FormData();
|
||||
|
||||
@@ -81,6 +83,7 @@ const evaluateSpeakingExercise = async (
|
||||
`${exercise.text.replaceAll("\n", "")}` + (exercise.prompts.length > 0 ? `You should talk about: ${exercise.prompts.join(", ")}` : "");
|
||||
formData.append("question", evaluationQuestion);
|
||||
formData.append("id", id);
|
||||
formData.append("task", task.toString());
|
||||
|
||||
const config = {
|
||||
headers: {
|
||||
|
||||
Reference in New Issue
Block a user