124 lines
3.6 KiB
TypeScript
124 lines
3.6 KiB
TypeScript
import {
|
|
Evaluation,
|
|
Exam,
|
|
InteractiveSpeakingExercise,
|
|
SpeakingExam,
|
|
SpeakingExercise,
|
|
UserSolution,
|
|
WritingExam,
|
|
WritingExercise,
|
|
} from "@/interfaces/exam";
|
|
import axios from "axios";
|
|
import {speakingReverseMarking, writingReverseMarking} from "./score";
|
|
|
|
export const evaluateWritingAnswer = async (exercise: WritingExercise, solution: UserSolution) => {
|
|
const response = await axios.post<Evaluation>("/api/evaluate/writing", {
|
|
question: `${exercise.prompt} ${exercise.attachment ? exercise.attachment.description : ""}`.replaceAll("\n", ""),
|
|
answer: solution.solutions[0].solution.trim().replaceAll("\n", " "),
|
|
});
|
|
|
|
if (response.status === 200) {
|
|
return {
|
|
...solution,
|
|
score: {
|
|
correct: writingReverseMarking[response.data.overall] || 0,
|
|
missing: 0,
|
|
total: 100,
|
|
},
|
|
solutions: [{id: exercise.id, solution: solution.solutions[0].solution, evaluation: response.data}],
|
|
};
|
|
}
|
|
|
|
return undefined;
|
|
};
|
|
|
|
export const evaluateSpeakingAnswer = async (exercise: SpeakingExercise | InteractiveSpeakingExercise, solution: UserSolution) => {
|
|
switch (exercise?.type) {
|
|
case "speaking":
|
|
return await evaluateSpeakingExercise(exercise, exercise.id, solution);
|
|
case "interactiveSpeaking":
|
|
return await evaluateInteractiveSpeakingExercise(exercise.id, solution);
|
|
default:
|
|
return undefined;
|
|
}
|
|
};
|
|
|
|
const downloadBlob = async (url: string): Promise<Buffer> => {
|
|
const blobResponse = await axios.get(url, {responseType: "arraybuffer"});
|
|
return Buffer.from(blobResponse.data, "binary");
|
|
};
|
|
|
|
const evaluateSpeakingExercise = async (exercise: SpeakingExercise, exerciseId: string, solution: UserSolution) => {
|
|
const audioBlob = await downloadBlob(solution.solutions[0].solution.trim());
|
|
const audioFile = new File([audioBlob], "audio.wav", {type: "audio/wav"});
|
|
|
|
const formData = new FormData();
|
|
formData.append("audio", audioFile, "audio.wav");
|
|
|
|
const evaluationQuestion =
|
|
`${exercise.text.replaceAll("\n", "")}` + (exercise.prompts.length > 0 ? `You should talk about: ${exercise.prompts.join(", ")}` : "");
|
|
formData.append("question", evaluationQuestion);
|
|
|
|
const config = {
|
|
headers: {
|
|
"Content-Type": "audio/mp3",
|
|
},
|
|
};
|
|
|
|
const response = await axios.post("/api/evaluate/speaking", formData, config);
|
|
|
|
if (response.status === 200) {
|
|
return {
|
|
...solution,
|
|
score: {
|
|
correct: speakingReverseMarking[response.data.overall] || 0,
|
|
missing: 0,
|
|
total: 100,
|
|
},
|
|
solutions: [{id: exerciseId, solution: response.data.fullPath, evaluation: response.data}],
|
|
};
|
|
}
|
|
|
|
return undefined;
|
|
};
|
|
|
|
const evaluateInteractiveSpeakingExercise = async (exerciseId: string, solution: UserSolution) => {
|
|
const promiseParts = solution.solutions.map(async (x: {prompt: string; blob: string}) => ({
|
|
question: x.prompt,
|
|
answer: await downloadBlob(x.blob),
|
|
}));
|
|
const body = await Promise.all(promiseParts);
|
|
|
|
const formData = new FormData();
|
|
body.forEach(({question, answer}) => {
|
|
const seed = Math.random().toString().replace("0.", "");
|
|
|
|
const audioFile = new File([answer], `${seed}.wav`, {type: "audio/wav"});
|
|
|
|
formData.append(`question_${seed}`, question);
|
|
formData.append(`answer_${seed}`, audioFile, `${seed}.wav`);
|
|
});
|
|
|
|
const config = {
|
|
headers: {
|
|
"Content-Type": "audio/mp3",
|
|
},
|
|
};
|
|
|
|
const response = await axios.post("/api/evaluate/interactiveSpeaking", formData, config);
|
|
|
|
if (response.status === 200) {
|
|
return {
|
|
...solution,
|
|
score: {
|
|
correct: speakingReverseMarking[response.data.overall] || 0,
|
|
missing: 0,
|
|
total: 100,
|
|
},
|
|
solutions: [{id: exerciseId, solution: response.data.answer, evaluation: response.data}],
|
|
};
|
|
}
|
|
|
|
return undefined;
|
|
};
|