Implemented the Writing exercise's solution display

This commit is contained in:
Tiago Ribeiro
2023-06-22 16:59:13 +01:00
parent b194a9183e
commit fe4a97ec85
7 changed files with 334 additions and 110 deletions

View File

@@ -0,0 +1,36 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type {NextApiRequest, NextApiResponse} from "next";
import {getFirestore, doc, getDoc} from "firebase/firestore";
import {withIronSessionApiRoute} from "iron-session/next";
import {sessionOptions} from "@/lib/session";
import axios from "axios";
interface Body {
question: string;
answer: string;
}
export default withIronSessionApiRoute(handler, sessionOptions);
async function handler(req: NextApiRequest, res: NextApiResponse) {
if (!req.session.user) {
res.status(401).json({ok: false});
return;
}
const {module} = req.query as {module: string};
if (module === "writing") {
const backendRequest = await axios.post(`${process.env.BACKEND_URL}/writing_task2`, req.body as Body, {
headers: {
Authorization: `Bearer ${process.env.BACKEND_JWT}`,
},
});
res.status(backendRequest.status).json(backendRequest.data);
return;
}
res.status(404).json({ok: false});
return;
}

View File

@@ -6,7 +6,7 @@ import {Module} from "@/interfaces";
import Selection from "@/exams/Selection";
import Reading from "@/exams/Reading";
import {Exam, ListeningExam, ReadingExam, SpeakingExam, UserSolution, WritingExam} from "@/interfaces/exam";
import {Exam, ListeningExam, ReadingExam, SpeakingExam, UserSolution, WritingExam, WritingExercise} from "@/interfaces/exam";
import Listening from "@/exams/Listening";
import Writing from "@/exams/Writing";
import {ToastContainer, toast} from "react-toastify";
@@ -21,6 +21,7 @@ import useUser from "@/hooks/useUser";
import useExamStore from "@/stores/examStore";
import Sidebar from "@/components/Sidebar";
import Layout from "@/components/High/Layout";
import {sortByModule} from "@/utils/moduleUtils";
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
const user = req.session.user;
@@ -46,7 +47,7 @@ export default function Page() {
const [moduleIndex, setModuleIndex] = useState(0);
const [sessionId, setSessionId] = useState("");
const [exam, setExam] = useState<Exam>();
const [timer, setTimer] = useState(-1);
const [isEvaluationLoading, setIsEvaluationLoading] = useState(false);
const [exams, setExams] = useExamStore((state) => [state.exams, state.setExams]);
const [userSolutions, setUserSolutions] = useExamStore((state) => [state.userSolutions, state.setUserSolutions]);
@@ -101,17 +102,6 @@ export default function Page() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedModules, moduleIndex, hasBeenUploaded]);
useEffect(() => {
if (exam) {
setTimer(exam.minTimer * 60);
const timerInterval = setInterval(() => setTimer((prev) => (prev && prev > 0 ? prev - 1 : 0)), 1000);
return () => {
clearInterval(timerInterval);
};
}
}, [exam]);
const getExam = async (module: Module): Promise<Exam | undefined> => {
const examRequest = await axios<Exam[]>(`/api/exam/${module}`);
if (examRequest.status !== 200) {
@@ -133,6 +123,24 @@ export default function Page() {
}
};
const evaluateWritingAnswer = async (examId: string, exerciseId: string, answer: string) => {
const writingExam = exams.find((x) => x.id === examId)!;
const exercise = writingExam.exercises.find((x) => x.id === exerciseId)! as WritingExercise;
const response = await axios.post("/api/exam/writing/evaluate", {
question: `${exercise.prompt} ${exercise.attachment ? exercise.attachment.description : ""}`.replaceAll("\n", ""),
answer: answer.trim().replaceAll("\n", " "),
});
if (response.status === 200) {
writingExam.exercises = [
...writingExam.exercises.filter((x) => x.id !== exerciseId),
Object.assign(exercise, {evaluation: response.data}),
];
setExams([...exams.filter((x) => x.id !== examId), writingExam].sort(sortByModule));
}
};
const updateExamWithUserSolutions = (exam: Exam): Exam => {
const exercises = exam.exercises.map((x) =>
Object.assign(x, !x.userSolutions ? {userSolutions: userSolutions.find((y) => x.id === y.exercise)?.solutions} : x.userSolutions),
@@ -144,6 +152,17 @@ export default function Page() {
const onFinish = (solutions: UserSolution[]) => {
const solutionIds = solutions.map((x) => x.exercise);
if (exam && exam.module === "writing" && solutions.length > 0 && !showSolutions) {
setIsEvaluationLoading(true);
Promise.all(
exam.exercises.map((exercise) =>
evaluateWritingAnswer(exam.id, exercise.id, solutions.find((x) => x.exercise === exercise.id)!.solutions[0]),
),
).finally(() => {
setIsEvaluationLoading(false);
});
}
setUserSolutions([...userSolutions.filter((x) => !solutionIds.includes(x.exercise)), ...solutions]);
setModuleIndex((prev) => prev + 1);
};
@@ -193,6 +212,7 @@ export default function Page() {
if (moduleIndex >= selectedModules.length) {
return (
<Finish
isLoading={isEvaluationLoading}
user={user!}
modules={selectedModules}
onViewResults={() => {
@@ -213,11 +233,6 @@ export default function Page() {
return <Listening exam={exam} onFinish={onFinish} showSolutions={showSolutions} />;
}
if (exam && exam.module === "writing" && showSolutions) {
setModuleIndex((prev) => prev + 1);
return <></>;
}
if (exam && exam.module === "writing") {
return <Writing exam={exam} onFinish={onFinish} showSolutions={showSolutions} />;
}