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, id: string): Promise => { const response = await axios.post("/api/evaluate/writing", { question: `${exercise.prompt} ${exercise.attachment ? exercise.attachment.description : ""}`.replaceAll("\n", ""), answer: solution.solutions[0].solution.trim().replaceAll("\n", " "), id, }); if (response.status === 200) { return { ...solution, id, score: { correct: response.data ? 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, id: string) => { switch (exercise?.type) { case "speaking": return {...(await evaluateSpeakingExercise(exercise, exercise.id, solution, id)), id}; case "interactiveSpeaking": return {...(await evaluateInteractiveSpeakingExercise(exercise.id, solution, id)), id}; default: return undefined; } }; const downloadBlob = async (url: string): Promise => { const blobResponse = await axios.get(url, {responseType: "arraybuffer"}); return Buffer.from(blobResponse.data, "binary"); }; const evaluateSpeakingExercise = async (exercise: SpeakingExercise, exerciseId: string, solution: UserSolution, id: string) => { 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); formData.append("id", id); 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: response.data ? speakingReverseMarking[response.data.overall] : 0, missing: 0, total: 100, }, solutions: [{id: exerciseId, solution: response.data ? response.data.fullPath : null, evaluation: response.data}], }; } return undefined; }; const evaluateInteractiveSpeakingExercise = async (exerciseId: string, solution: UserSolution, id: string) => { 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`); }); formData.append("id", id); 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: response.data ? speakingReverseMarking[response.data.overall] : 0, missing: 0, total: 100, }, solutions: [{id: exerciseId, solution: response.data ? response.data.answer : null, evaluation: response.data}], }; } return undefined; };