diff --git a/src/exams/Speaking.tsx b/src/exams/Speaking.tsx
index 8dcd8709..7d6271db 100644
--- a/src/exams/Speaking.tsx
+++ b/src/exams/Speaking.tsx
@@ -2,7 +2,7 @@ import {renderExercise} from "@/components/Exercises";
import ModuleTitle from "@/components/Medium/ModuleTitle";
import {renderSolution} from "@/components/Solutions";
import {infoButtonStyle} from "@/constants/buttonStyles";
-import {UserSolution, SpeakingExam} from "@/interfaces/exam";
+import {UserSolution, SpeakingExam, SpeakingExercise, InteractiveSpeakingExercise} from "@/interfaces/exam";
import useExamStore from "@/stores/examStore";
import {defaultUserSolutions} from "@/utils/exams";
import {countExercises} from "@/utils/moduleUtils";
@@ -64,8 +64,9 @@ export default function Speaking({exam, showSolutions = false, onFinish}: Props)
const exercise = exam.exercises[exerciseIndex];
return {
...exercise,
+ variant: exerciseIndex < 2 && exercise.type === "interactiveSpeaking" ? "initial" : undefined,
userSolutions: userSolutions.find((x) => x.exercise === exercise.id)?.solutions || [],
- };
+ } as SpeakingExercise | InteractiveSpeakingExercise;
};
return (
diff --git a/src/interfaces/exam.ts b/src/interfaces/exam.ts
index 9b4b255a..50031470 100644
--- a/src/interfaces/exam.ts
+++ b/src/interfaces/exam.ts
@@ -177,6 +177,8 @@ export interface InteractiveSpeakingExercise {
id: string;
type: "interactiveSpeaking";
title: string;
+ first_title?: string;
+ second_title?: string;
text: string;
prompts: {text: string; video_url: string}[];
userSolutions: {
@@ -185,6 +187,9 @@ export interface InteractiveSpeakingExercise {
evaluation?: InteractiveSpeakingEvaluation;
}[];
topic?: string;
+ first_topic?: string;
+ second_topic?: string;
+ variant?: "initial" | "final";
}
export interface FillBlanksExercise {
diff --git a/src/pages/(exam)/ExamPage.tsx b/src/pages/(exam)/ExamPage.tsx
index 367cfa10..52a81e5e 100644
--- a/src/pages/(exam)/ExamPage.tsx
+++ b/src/pages/(exam)/ExamPage.tsx
@@ -133,14 +133,22 @@ export default function ExamPage({page}: Props) {
!!exam &&
timeSpent > 0 &&
!showSolutions &&
- moduleIndex < selectedModules.length
+ moduleIndex < selectedModules.length &&
+ selectedModules[moduleIndex] !== "speaking"
)
saveSession();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [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 &&
+ selectedModules[moduleIndex] !== "speaking" &&
+ !showSolutions
+ )
+ saveSession();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [timeSpent]);
diff --git a/src/pages/(generation)/SpeakingGeneration.tsx b/src/pages/(generation)/SpeakingGeneration.tsx
index 25d3e70d..e0d1134a 100644
--- a/src/pages/(generation)/SpeakingGeneration.tsx
+++ b/src/pages/(generation)/SpeakingGeneration.tsx
@@ -45,6 +45,7 @@ const PartTab = ({
.then((result) => {
playSound(typeof result.data === "string" ? "error" : "check");
if (typeof result.data === "string") return toast.error("Something went wrong, please try to generate again.");
+ console.log(result.data);
setPart(result.data);
})
.catch((error) => {
@@ -54,7 +55,7 @@ const PartTab = ({
.finally(() => setIsLoading(false));
};
- const generateVideo = () => {
+ const generateVideo = async () => {
if (!part) return toast.error("Please generate the first part before generating the video!");
toast.info("This will take quite a while, please do not leave this page or close the tab/window.");
@@ -64,11 +65,12 @@ const PartTab = ({
const initialTime = moment();
axios
- .post(`/api/exam/speaking/generate/speaking/generate_${index === 3 ? "interactive" : "speaking"}_video`, {...part, avatar: avatar?.id})
+ .post(`/api/exam/speaking/generate/speaking/generate_video_${index}`, {...part, avatar: avatar?.id})
.then((result) => {
const isError = typeof result.data === "string" || moment().diff(initialTime, "seconds") < 60;
playSound(isError ? "error" : "check");
+ console.log(result.data);
if (isError) return toast.error("Something went wrong, please try to generate the video again.");
setPart({...part, result: {...result.data, topic: part?.topic}, gender, avatar});
})
@@ -139,7 +141,9 @@ const PartTab = ({
)}
{part && !isLoading && (
-
{part.topic}
+
+ {!!part.first_topic && !!part.second_topic ? `${part.first_topic} & ${part.second_topic}` : part.topic}
+
{part.question &&
{part.question}}
{part.questions && (
@@ -177,6 +181,8 @@ interface SpeakingPart {
question?: string;
questions?: string[];
topic: string;
+ first_topic?: string;
+ second_topic?: string;
result?: SpeakingExercise | InteractiveSpeakingExercise;
gender?: "male" | "female";
avatar?: (typeof AVATARS)[number];
@@ -208,10 +214,18 @@ const SpeakingGeneration = () => {
const genders = [part1?.gender, part2?.gender, part3?.gender].filter((x) => !!x);
+ const exercises = [part1?.result, part2?.result, part3?.result]
+ .filter((x) => !!x)
+ .map((x) => ({
+ ...x,
+ first_title: x?.type === "interactiveSpeaking" ? x.first_topic : undefined,
+ second_title: x?.type === "interactiveSpeaking" ? x.second_topic : undefined,
+ }));
+
const exam: SpeakingExam = {
id: v4(),
isDiagnostic: false,
- exercises: [part1?.result, part2?.result, part3?.result].filter((x) => !!x) as (SpeakingExercise | InteractiveSpeakingExercise)[],
+ exercises: exercises as (SpeakingExercise | InteractiveSpeakingExercise)[],
minTimer,
variant: minTimer >= 14 ? "full" : "partial",
module: "speaking",
diff --git a/src/pages/api/evaluate/interactiveSpeaking.ts b/src/pages/api/evaluate/interactiveSpeaking.ts
index 9612d5a7..6f38eac7 100644
--- a/src/pages/api/evaluate/interactiveSpeaking.ts
+++ b/src/pages/api/evaluate/interactiveSpeaking.ts
@@ -47,7 +47,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
res.status(200).json(null);
console.log("🌱 - Still processing");
- const backendRequest = await evaluate({answers: uploadingAudios});
+ const backendRequest = await evaluate({answers: uploadingAudios}, fields.variant);
console.log("🌱 - Process complete");
const correspondingStat = await getCorrespondingStat(fields.id, 1);
@@ -79,8 +79,8 @@ async function getCorrespondingStat(id: string, index: number): Promise
{
return getCorrespondingStat(id, index + 1);
}
-async function evaluate(body: {answers: object[]}): Promise {
- const backendRequest = await axios.post(`${process.env.BACKEND_URL}/speaking_task_3`, body, {
+async function evaluate(body: {answers: object[]}, variant?: "initial" | "final"): Promise {
+ const backendRequest = await axios.post(`${process.env.BACKEND_URL}/speaking_task_${variant === "initial" ? "1" : "3"}`, body, {
headers: {
Authorization: `Bearer ${process.env.BACKEND_JWT}`,
},
diff --git a/src/pages/api/evaluate/speaking.ts b/src/pages/api/evaluate/speaking.ts
index 3f9431b9..382ddc02 100644
--- a/src/pages/api/evaluate/speaking.ts
+++ b/src/pages/api/evaluate/speaking.ts
@@ -78,7 +78,7 @@ async function getCorrespondingStat(id: string, index: number): Promise {
}
async function evaluate(body: {answer: string; question: string}, task: number): Promise {
- const backendRequest = await axios.post(`${process.env.BACKEND_URL}/speaking_task_${task}`, body, {
+ const backendRequest = await axios.post(`${process.env.BACKEND_URL}/speaking_task_2`, body, {
headers: {
Authorization: `Bearer ${process.env.BACKEND_JWT}`,
},
diff --git a/src/utils/evaluation.ts b/src/utils/evaluation.ts
index 82283a4b..c1e950b3 100644
--- a/src/utils/evaluation.ts
+++ b/src/utils/evaluation.ts
@@ -51,7 +51,7 @@ export const evaluateSpeakingAnswer = async (
case "speaking":
return {...(await evaluateSpeakingExercise(exercise, exercise.id, solution, id, task)), id} as UserSolution;
case "interactiveSpeaking":
- return {...(await evaluateInteractiveSpeakingExercise(exercise.id, solution, id)), id} as UserSolution;
+ return {...(await evaluateInteractiveSpeakingExercise(exercise.id, solution, id, exercise.variant)), id} as UserSolution;
default:
return undefined;
}
@@ -110,7 +110,12 @@ const evaluateSpeakingExercise = async (
return undefined;
};
-const evaluateInteractiveSpeakingExercise = async (exerciseId: string, solution: UserSolution, id: string): Promise => {
+const evaluateInteractiveSpeakingExercise = async (
+ exerciseId: string,
+ solution: UserSolution,
+ id: string,
+ variant?: "initial" | "final",
+): Promise => {
const promiseParts = solution.solutions.map(async (x: {prompt: string; blob: string}) => {
const blob = await downloadBlob(x.blob);
if (!x.blob.startsWith("blob")) await axios.post("/api/storage/delete", {path: x.blob});
@@ -132,6 +137,7 @@ const evaluateInteractiveSpeakingExercise = async (exerciseId: string, solution:
formData.append(`answer_${seed}`, audioFile, `${seed}.wav`);
});
formData.append("id", id);
+ formData.append("variant", variant || "final");
const config = {
headers: {