middleware(function ($request, $next) { if (Auth::user()->role !== 'student') { abort(403, 'Unauthorized action.'); } return $next($request); }); } /** * Menampilkan daftar asesmen yang sedang berjalan atau sudah selesai untuk student yang login. */ public function index() { $assessments = LiteracyAssessment::where('user_id', Auth::id()) ->orderBy('created_at', 'desc') ->get(); return view('literacy.student.assessments.index', compact('assessments')); } /** * Memulai asesmen dan mengubah status menjadi "in_progress". */ public function start($id) { $assessment = LiteracyAssessment::where('id', $id) ->where('user_id', Auth::id()) ->firstOrFail(); // Ubah status asesmen menjadi in_progress $assessment->update(['status' => 'in_progress']); // Ambil semua pertanyaan terkait asesmen ini $questions = LiteracyQuestion::all(); // Sesuaikan jika ada filter // Insert jawaban kosong jika belum ada foreach ($questions as $question) { LiteracyAnswer::updateOrInsert( [ 'assessment_id' => $assessment->id, 'question_id' => $question->id, ], [ 'option_id' => null, 'answer_text' => null, 'created_at' => now(), 'updated_at' => now(), ] ); } return redirect()->route('literacy_assessments_show', $id); } /** * Menampilkan halaman asesmen dengan pertanyaan yang diurutkan. */ public function show($id) { $assessment = LiteracyAssessment::where('id', $id) ->where('user_id', Auth::id()) ->firstOrFail(); // Ambil semua pertanyaan, urutkan pilihan ganda dulu baru essay $questions = LiteracyQuestion::orderByRaw("FIELD(type, 'multiple_choice', 'essay')") ->with([ 'answers' => function ($query) use ($id) { $query->where('assessment_id', $id); } ]) ->get(); return view('literacy.student.assessments.assessment', compact('assessment', 'questions')); } /** * Melanjutkan asesmen yang belum selesai. */ public function continue($id) { $assessment = LiteracyAssessment::where('id', $id) ->where('user_id', Auth::id()) ->firstOrFail(); return redirect()->route('literacy_assessments_show', $id); } /** * Menyimpan jawaban siswa ke database secara otomatis saat memilih atau mengisi jawaban. */ public function storeAnswer(Request $request, $assessmentId): JsonResponse { Log::info('Request Data:', $request->all()); $request->validate([ 'question_id' => 'required|exists:literacy_questions,id', 'answer' => 'nullable|string', ]); // Pastikan pengguna memiliki asesmen ini $assessment = LiteracyAssessment::where('id', $assessmentId) ->where('user_id', Auth::id()) ->first(); if (!$assessment) { return response()->json(['error' => 'Asesmen tidak ditemukan'], 404); } $questionId = $request->question_id; $answerValue = $request->answer; $question = LiteracyQuestion::find($questionId); if (!$question) { return response()->json(['error' => 'Pertanyaan tidak ditemukan'], 404); } $answerData = ['option_id' => null, 'answer_text' => $answerValue]; if ($question->type === 'multiple_choice') { $option = LiteracyOption::where('id', $answerValue) ->where('question_id', $questionId) ->first(); if (!$option) { return response()->json(['error' => 'Jawaban tidak valid'], 400); } $answerData = ['option_id' => $option->id, 'answer_text' => null]; } LiteracyAnswer::updateOrCreate( [ 'assessment_id' => $assessment->id, 'question_id' => $questionId, ], $answerData ); // Hitung jumlah pertanyaan dalam asesmen ini $totalQuestions = $assessment->questions()->count(); // Hitung jumlah pertanyaan yang sudah dijawab $answeredQuestions = LiteracyAnswer::where('assessment_id', $assessment->id) ->where(function ($query) { $query->whereNotNull('option_id') ->orWhere('answer_text', '!=', ''); }) ->count(); Log::info('Answered Questions:', ['answered' => $answeredQuestions, 'total' => $totalQuestions]); // Jika semua pertanyaan sudah dijawab, update status ke `completed` if ($answeredQuestions >= $totalQuestions) { $assessment->update(['status' => 'completed']); } return response()->json(['success' => true]); } // public function submitAssessment($id) // { // try { // $user = Auth::user(); // $assessment = LiteracyAssessment::where('id', $id) // ->where('user_id', $user->id) // ->firstOrFail(); // if ($assessment->status !== 'in_progress') { // return response()->json(['error' => 'Asesmen tidak sedang berjalan.'], 400); // } // $answers = LiteracyAnswer::where('assessment_id', $id)->get(); // $correctWeight = 0; // $totalWeight = 0; // $essayThreshold = 60; // $normalizeText = function ($text) { // $text = strtolower($text); // $text = preg_replace('/[^\p{L}\p{N}\s]/u', '', $text); // $text = preg_replace('/\s+/', ' ', $text); // return trim($text); // }; // $feedback = []; // foreach ($answers as $answer) { // $question = LiteracyQuestion::find($answer->question_id); // if (!$question) // continue; // $weight = $question->weight ?? ($question->type === 'essay' ? 5 : 1); // $totalWeight += $weight; // if ($question->type === 'multiple_choice') { // if ( // $answer->option_id && // LiteracyOption::where('id', $answer->option_id) // ->where('is_correct', true) // ->exists() // ) { // $correctWeight += $weight; // } // } elseif ($question->type === 'essay') { // $userAnswer = $normalizeText($answer->answer_text ?? ''); // $correctAnswerRaw = $question->essay_answer ?? ''; // $correctAnswers = array_map($normalizeText, preg_split('/\r\n|\r|\n/', $correctAnswerRaw)); // $maxMatchPercent = 0; // foreach ($correctAnswers as $correctAnswer) { // similar_text($userAnswer, $correctAnswer, $percent); // $maxMatchPercent = max($maxMatchPercent, $percent); // } // if ($maxMatchPercent >= $essayThreshold) { // $correctWeight += $weight; // } // } // } // // Hitung skor akhir berdasarkan bobot // $score = $totalWeight > 0 ? ($correctWeight / $totalWeight) * 100 : 0; // // Update asesmen yang sedang berjalan jadi completed // $assessment->update([ // 'score' => round($score, 2), // 'status' => 'completed', // 'assessed_at' => now(), // 'feedback' => json_encode($feedback), // Menyimpan feedback ke database // ]); // // Buat asesmen baru dengan status 'pending' untuk percakapan berikutnya // LiteracyAssessment::create([ // 'user_id' => $user->id, // 'status' => 'pending', // 'score' => null, // 'feedback' => '', // 'assessed_at' => null, // 'created_at' => now(), // 'updated_at' => now(), // ]); // return response()->json([ // 'message' => 'Asesmen berhasil diselesaikan.', // 'score' => round($score, 2), // 'feedback' => $feedback, // Mengembalikan feedback dalam respons // ], 200); // } catch (\Exception $e) { // return response()->json(['error' => 'Terjadi kesalahan: ' . $e->getMessage()], 500); // } // } public function submitAssessment($id) { try { $user = Auth::user(); $assessment = LiteracyAssessment::where('id', $id) ->where('user_id', $user->id) ->firstOrFail(); if ($assessment->status !== 'in_progress') { return response()->json(['error' => 'Asesmen tidak sedang berjalan.'], 400); } $answers = LiteracyAnswer::where('assessment_id', $id)->get(); $correctWeight = 0; $totalWeight = 0; $essayThreshold = 60; $normalizeText = function ($text) { $text = strtolower($text); $text = preg_replace('/[^\p{L}\p{N}\s]/u', '', $text); $text = preg_replace('/\s+/', ' ', $text); return trim($text); }; $feedback = []; foreach ($answers as $answer) { $question = LiteracyQuestion::find($answer->question_id); if (!$question) continue; $weight = $question->weight ?? ($question->type === 'essay' ? 5 : 1); $totalWeight += $weight; if ($question->type === 'multiple_choice') { if ( $answer->option_id && LiteracyOption::where('id', $answer->option_id) ->where('is_correct', true) ->exists() ) { $correctWeight += $weight; } } elseif ($question->type === 'essay') { $userAnswer = $normalizeText($answer->answer_text ?? ''); $correctAnswerRaw = $question->essay_answer ?? ''; $correctAnswers = array_map($normalizeText, preg_split('/\r\n|\r|\n/', $correctAnswerRaw)); $maxMatchPercent = 0; $bestMatch = ''; foreach ($correctAnswers as $correctAnswer) { similar_text($userAnswer, $correctAnswer, $percent); if ($percent > $maxMatchPercent) { $maxMatchPercent = $percent; $bestMatch = $correctAnswer; } } if ($maxMatchPercent >= $essayThreshold) { $correctWeight += $weight; } else { try { $apiResponse = Http::timeout(10)->post('http://127.0.0.1:8001/generate-feedback/', [ 'user_answer' => $answer->answer_text, 'expected_answer' => $bestMatch, ]); if ($apiResponse->ok()) { $aiFeedback = $apiResponse->json()['feedback']; } else { $aiFeedback = 'Feedback tidak tersedia saat ini.'; } } catch (\Exception $e) { $aiFeedback = 'Terjadi kesalahan saat menghubungi AI.'; } // SIMPAN ke kolom feedback $answer->feedback = $aiFeedback; $answer->save(); $feedback[] = [ 'question_id' => $question->id, 'feedback' => $aiFeedback, ]; } } } // Hitung skor akhir berdasarkan bobot $score = $totalWeight > 0 ? ($correctWeight / $totalWeight) * 100 : 0; // Update asesmen yang sedang berjalan jadi completed $assessment->update([ 'score' => round($score, 2), 'status' => 'completed', 'assessed_at' => now(), 'feedback' => json_encode($feedback), ]); // Buat asesmen baru dengan status 'pending' untuk percakapan berikutnya LiteracyAssessment::create([ 'user_id' => $user->id, 'status' => 'pending', 'score' => null, 'feedback' => '', 'assessed_at' => null, 'created_at' => now(), 'updated_at' => now(), ]); return response()->json([ 'message' => 'Asesmen berhasil diselesaikan.', 'score' => round($score, 2), 'feedback' => $feedback, ], 200); } catch (\Exception $e) { return response()->json(['error' => 'Terjadi kesalahan: ' . $e->getMessage()], 500); } } public function viewResult($id) { // Ambil asesmen berdasarkan ID, pastikan asesmen milik user dan statusnya sudah 'completed' $assessment = LiteracyAssessment::where('id', $id) ->where('user_id', Auth::id()) ->where('status', 'completed') ->firstOrFail(); // Ambil semua pertanyaan beserta jawaban untuk asesmen ini $questions = LiteracyQuestion::with([ 'answers' => function ($query) use ($id) { $query->where('assessment_id', $id); }, 'options' ])->get(); return view('literacy.student.assessments.result', compact('assessment', 'questions')); } public function result($id) { $assessment = LiteracyAssessment::where('id', $id) ->where('user_id', Auth::id()) ->where('status', 'completed') ->firstOrFail(); $questions = LiteracyQuestion::with([ 'answers' => function ($query) use ($id) { $query->where('assessment_id', $id); }, 'options' ])->get(); return view('literacy.student.assessments.result', compact('assessment', 'questions')); } }