get(); \Log::info('Rekomendasi pakar count: ' . $rekomendasiPakar->count()); // Ambil hasil AHP terbaru untuk setiap waktu makan dan komponen $hasilAHP = $this->ambilHasilAHP($topN); \Log::info('Hasil AHP count: ' . $hasilAHP->count()); \Log::info('Hasil AHP structure:', $hasilAHP->toArray()); $hasilValidasi = []; $totalLebihBaik = 0; $totalSetara = 0; $totalLebihBuruk = 0; foreach ($rekomendasiPakar as $pakar) { // Cari hasil AHP yang sesuai dengan waktu makan dan komponen $hasilAHPUntukKomponen = $hasilAHP ->where('waktu_makan_id', $pakar->waktu_makan_id) ->where('komponen_id', $pakar->komponen_id) ->first(); if (!$hasilAHPUntukKomponen) { \Log::info('Tidak ada hasil AHP untuk waktu_makan_id: ' . $pakar->waktu_makan_id . ', komponen_id: ' . $pakar->komponen_id); continue; } $makananPakar = $pakar->makanan; $makananSistem = collect($hasilAHPUntukKomponen['makanans']); $makananSistemIds = $makananSistem->pluck('id')->toArray(); \Log::info('Processing pakar:', [ 'pakar_id' => $pakar->id, 'makanan_pakar_id' => $pakar->makanan_id, 'makanan_sistem_ids' => $makananSistemIds ]); // Cek kecocokan $status = $this->tentukanStatusKecocokan($makananPakar, $makananSistem); // Update counter switch ($status) { case 'lebih_baik': $totalLebihBaik++; break; case 'setara': $totalSetara++; break; case 'lebih_buruk': $totalLebihBuruk++; break; } // Simpan hasil validasi $hasilValidasi[] = [ 'hari' => $pakar->hari, 'waktu_makan_id' => $pakar->waktu_makan_id, 'komponen_id' => $pakar->komponen_id, 'makanan_pakar_id' => $pakar->makanan_id, 'makanan_sistem_ids' => $makananSistemIds, 'status_kecocokan' => $status ]; } // Hitung persentase kecocokan // $total = count($hasilValidasi); $total = $totalLebihBaik + $totalSetara + $totalLebihBuruk; $persentaseCocok = $total > 0 ? (($totalLebihBaik + $totalSetara) / $total) * 100 : 0; // Simpan hasil ke database $this->simpanHasilValidasi($hasilValidasi); return [ 'hasil_validasi' => $hasilValidasi, 'statistik' => [ 'total' => $total, 'lebih_baik' => $totalLebihBaik, 'setara' => $totalSetara, 'lebih_buruk' => $totalLebihBuruk, 'persentase_cocok' => round($persentaseCocok, 2) ] ]; } catch (\Exception $e) { \Log::error('Error in bandingkanRekomendasi: ' . $e->getMessage(), [ 'trace' => $e->getTraceAsString() ]); throw $e; } } /** * Ambil hasil AHP terbaru untuk setiap waktu makan dan komponen */ private function ambilHasilAHP(int $topN): Collection { try { // Ambil data rekomendasi dengan eager loading $rekomendasis = Rekomendasi::with(['makanan', 'waktuMakan', 'komponen']) ->orderBy('waktu_makan_id') ->orderBy('komponen_id') ->orderBy('nilai_akhir', 'desc') ->get(); \Log::info('Total rekomendasi found: ' . $rekomendasis->count()); // Group by waktu makan dan komponen $groupedData = $rekomendasis->groupBy(['waktu_makan_id', 'komponen_id']); // Transform data structure $transformedData = collect(); foreach ($groupedData as $waktuMakanId => $komponenGroups) { foreach ($komponenGroups as $komponenId => $items) { // Ambil top N makanan berdasarkan nilai_akhir $topMakanans = $items->take($topN) ->map(function($item) { return [ 'id' => $item->makanan_id, 'nama' => $item->makanan->nama, 'nilai_akhir' => $item->nilai_akhir, 'energi' => $item->makanan->energi, 'lemak' => $item->makanan->lemak, 'karbohidrat' => $item->makanan->karbohidrat, 'natrium' => $item->makanan->natrium ]; }); $transformedData->push([ 'waktu_makan_id' => $waktuMakanId, 'komponen_id' => $komponenId, 'makanans' => $topMakanans ]); \Log::info("Added data for waktu_makan_id: $waktuMakanId, komponen_id: $komponenId, makanan_count: " . $topMakanans->count()); } } \Log::info('Transformed data count: ' . $transformedData->count()); return $transformedData; } catch (\Exception $e) { \Log::error('Error in ambilHasilAHP: ' . $e->getMessage(), [ 'trace' => $e->getTraceAsString() ]); throw $e; } } /** * Tentukan status kecocokan antara makanan pakar dan sistem */ private function tentukanStatusKecocokan($makananPakar, Collection $makananSistem): string { $toleransi = 0.10; // 10% $lebihBaik = 0; $setara = 0; $lebihBuruk = 0; foreach ($makananSistem as $makanan) { $lemakPakar = $makananPakar->lemak; $natriumPakar = $makananPakar->natrium; $lemakAHP = $makanan['lemak']; $natriumAHP = $makanan['natrium']; $lemakSelisih = $this->hitungSelisihPersen($lemakPakar, $lemakAHP); $natriumSelisih = $this->hitungSelisihPersen($natriumPakar, $natriumAHP); if ($lemakAHP < $lemakPakar && $natriumAHP < $natriumPakar) { $lebihBaik++; } elseif ($lemakSelisih <= $toleransi && $natriumSelisih <= $toleransi) { $setara++; } else { $lebihBuruk++; } } // Tentukan status dominan // Logika khusus: Jika lebih_baik = 1 dan setara = 1, maka dianggap lebih_baik if ($lebihBaik === 1 && $setara === 1) { return 'lebih_baik'; } // Tentukan status dominan (logika umum) if ($lebihBaik >= max($setara, $lebihBuruk)) { return 'lebih_baik'; } elseif ($setara >= max($lebihBaik, $lebihBuruk)) { return 'setara'; } else { return 'lebih_buruk'; } } private function hitungSelisihPersen($nilai1, $nilai2): float { if ($nilai1 == 0 && $nilai2 == 0) return 0; if ($nilai1 == 0 || $nilai2 == 0) return 1; // 100% return abs($nilai1 - $nilai2) / max($nilai1, $nilai2); } /** * Cek apakah kandungan gizi dua makanan mirip (±10%) */ private function isGiziMirip($makanan1, $makanan2): bool { $toleransi = 0.10; // 10% $giziMirip = true; $giziMirip &= $this->isDalamToleransi($makanan1->energi, $makanan2['energi'], $toleransi); $giziMirip &= $this->isDalamToleransi($makanan1->lemak, $makanan2['lemak'], $toleransi); $giziMirip &= $this->isDalamToleransi($makanan1->karbohidrat, $makanan2['karbohidrat'], $toleransi); $giziMirip &= $this->isDalamToleransi($makanan1->natrium, $makanan2['natrium'], $toleransi); return $giziMirip; } /** * Cek apakah dua nilai dalam toleransi yang ditentukan */ private function isDalamToleransi($nilai1, $nilai2, $toleransi): bool { if ($nilai1 == 0 && $nilai2 == 0) return true; if ($nilai1 == 0 || $nilai2 == 0) return false; $selisih = abs($nilai1 - $nilai2) / max($nilai1, $nilai2); return $selisih <= $toleransi; } /** * Simpan hasil validasi ke database */ private function simpanHasilValidasi(array $hasilValidasi): void { // Hapus data validasi lama ValidasiRekomendasi::truncate(); // Simpan data baru foreach ($hasilValidasi as $validasi) { ValidasiRekomendasi::create($validasi); } } }