sistem_rekomendasi/app/Http/Controllers/Api/Student/RecommendationApiController...

296 lines
11 KiB
PHP

<?php
namespace App\Http\Controllers\Api\Student;
use App\Http\Controllers\Controller;
use App\Models\RiwayatRekomendasi;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Schema;
class RecommendationApiController extends Controller
{
public function hitungPrediksi(Request $request)
{
$user = Auth::user();
if (!$user) {
return response()->json([
'success' => false,
'message' => 'User belum login.'
], 401);
}
$validated = $request->validate([
'agama' => 'required|numeric|min:0|max:100',
'pkn' => 'required|numeric|min:0|max:100',
'bahasa_indonesia' => 'required|numeric|min:0|max:100',
'bahasa_inggris' => 'required|numeric|min:0|max:100',
'matematika' => 'required|numeric|min:0|max:100',
'fisika' => 'required|numeric|min:0|max:100',
'kimia' => 'required|numeric|min:0|max:100',
'biologi' => 'required|numeric|min:0|max:100',
'sejarah' => 'required|numeric|min:0|max:100',
'geografi' => 'required|numeric|min:0|max:100',
'ekonomi_mapel' => 'required|numeric|min:0|max:100',
'sosiologi' => 'required|numeric|min:0|max:100',
'seni_budaya' => 'required|numeric|min:0|max:100',
'pjok' => 'required|numeric|min:0|max:100',
'informatika' => 'required|numeric|min:0|max:100',
'riasec_r' => 'required|numeric|min:0|max:100',
'riasec_i' => 'required|numeric|min:0|max:100',
'riasec_a' => 'required|numeric|min:0|max:100',
'riasec_s' => 'required|numeric|min:0|max:100',
'riasec_e' => 'required|numeric|min:0|max:100',
'riasec_c' => 'required|numeric|min:0|max:100',
'gaji_ortu' => 'required|numeric|min:0',
]);
$payload = $validated;
$payload['ekonomi_orang_tua'] = self::getLabelEkonomiSingkat($validated['gaji_ortu']);
try {
$response = Http::timeout(15)
->acceptJson()
->post('http://127.0.0.1:5000/predict', $payload);
if (!$response->successful()) {
return response()->json([
'success' => false,
'message' => 'ML Engine Error: ' . ($response->json()['message'] ?? $response->body())
], 500);
}
$responseData = $response->json();
if (!isset($responseData['success']) || !$responseData['success']) {
return response()->json([
'success' => false,
'message' => 'ML Engine Error: ' . ($responseData['message'] ?? 'Unknown Error')
], 500);
}
$teksJurusan = $responseData['prediksi_jurusan'] ?? null;
if (!$teksJurusan) {
return response()->json([
'success' => false,
'message' => 'Prediksi jurusan tidak ditemukan pada respons Flask.'
], 500);
}
/*
* Ambil nilai probabilitas dari Flask.
* Contoh respons Flask yang disarankan:
* {
* "success": true,
* "prediksi_jurusan": "Teknik Informatika",
* "probabilitas": 86.45
* }
*/
$akurasiPrediksi = self::normalisasiAkurasi($responseData['probabilitas'] ?? null);
$dataRiwayat = [
'user_id' => $user->id,
'agama' => $validated['agama'],
'pkn' => $validated['pkn'],
'bahasa_indonesia' => $validated['bahasa_indonesia'],
'bahasa_inggris' => $validated['bahasa_inggris'],
'matematika' => $validated['matematika'],
'fisika' => $validated['fisika'],
'kimia' => $validated['kimia'],
'biologi' => $validated['biologi'],
'sejarah' => $validated['sejarah'],
'geografi' => $validated['geografi'],
'ekonomi_mapel' => $validated['ekonomi_mapel'],
'sosiologi' => $validated['sosiologi'],
'seni_budaya' => $validated['seni_budaya'],
'pjok' => $validated['pjok'],
'informatika' => $validated['informatika'],
'riasec_r' => $validated['riasec_r'],
'riasec_i' => $validated['riasec_i'],
'riasec_a' => $validated['riasec_a'],
'riasec_s' => $validated['riasec_s'],
'riasec_e' => $validated['riasec_e'],
'riasec_c' => $validated['riasec_c'],
'gaji_ortu' => $validated['gaji_ortu'],
'hasil_rekomendasi_jurusan' => $teksJurusan,
];
/*
* Kolom akurasi_prediksi hanya diisi kalau migration baru sudah dijalankan.
* Ini mencegah error kalau kolom belum ada.
*/
$tableName = (new RiwayatRekomendasi())->getTable();
if (Schema::hasColumn($tableName, 'akurasi_prediksi')) {
$dataRiwayat['akurasi_prediksi'] = $akurasiPrediksi;
}
$riwayat = RiwayatRekomendasi::create($dataRiwayat);
return response()->json([
'success' => true,
'message' => 'Prediksi berhasil dibuat.',
'redirect_url' => route('student.result', ['id' => $riwayat->id]),
'prediksi_jurusan' => $teksJurusan,
'akurasi_prediksi' => $akurasiPrediksi
]);
} catch (\Throwable $e) {
return response()->json([
'success' => false,
'message' => 'Koneksi ke API Flask Python gagal: ' . $e->getMessage()
], 500);
}
}
public function getHasilRekomendasi($id)
{
$riwayat = RiwayatRekomendasi::findOrFail($id);
return response()->json([
'success' => true,
'data' => self::formatHasilData($riwayat)
]);
}
public static function formatHasilData(RiwayatRekomendasi $riwayat): array
{
return [
'id' => $riwayat->id,
'hasil_rekomendasi_jurusan' => $riwayat->hasil_rekomendasi_jurusan,
'akurasi_prediksi' => $riwayat->akurasi_prediksi,
'label_ekonomi' => self::getLabelEkonomiDetail($riwayat->gaji_ortu),
'minat_utama' => self::getMinatUtamaLabel($riwayat),
'prospek_kerja' => self::getProspekKerjaText($riwayat->hasil_rekomendasi_jurusan),
'gaji_ortu' => $riwayat->gaji_ortu,
'riasec' => [
'r' => $riwayat->riasec_r,
'i' => $riwayat->riasec_i,
'a' => $riwayat->riasec_a,
's' => $riwayat->riasec_s,
'e' => $riwayat->riasec_e,
'c' => $riwayat->riasec_c,
],
];
}
private static function normalisasiAkurasi($value): ?float
{
if ($value === null || $value === '') {
return null;
}
if (is_string($value)) {
$value = str_replace('%', '', $value);
$value = str_replace(',', '.', $value);
$value = trim($value);
}
if (!is_numeric($value)) {
return null;
}
$number = (float) $value;
/*
* Jika Flask mengirim 0.86, ubah menjadi 86.
* Jika Flask mengirim 86.45, tetap 86.45.
*/
if ($number <= 1) {
$number = $number * 100;
}
if ($number < 0) {
$number = 0;
}
if ($number > 100) {
$number = 100;
}
return round($number, 2);
}
public static function getLabelEkonomiSingkat($gaji): string
{
$gaji = (float) $gaji;
if ($gaji < 2000000) {
return 'Kurang Mampu';
} elseif ($gaji <= 5000000) {
return 'Menengah';
}
return 'Mampu';
}
public static function getLabelEkonomiDetail($gaji): string
{
$gaji = (float) $gaji;
if ($gaji < 2000000) {
return 'Kurang Mampu (< Rp 2.000.000)';
} elseif ($gaji <= 5000000) {
return 'Menengah (Rp 2.000.000 - Rp 5.000.000)';
}
return 'Mampu (> Rp 5.000.000)';
}
public static function getMinatUtamaLabel(RiwayatRekomendasi $riwayat): string
{
$riasecScores = [
'r' => (float) $riwayat->riasec_r,
'i' => (float) $riwayat->riasec_i,
'a' => (float) $riwayat->riasec_a,
's' => (float) $riwayat->riasec_s,
'e' => (float) $riwayat->riasec_e,
'c' => (float) $riwayat->riasec_c,
];
arsort($riasecScores);
$topCode = array_key_first($riasecScores);
$labels = [
'r' => 'Teknis & Lapangan',
'i' => 'Sains, Riset & Analitis',
'a' => 'Kreatif & Desain',
's' => 'Sosial & Pelayanan',
'e' => 'Bisnis & Kepemimpinan',
'c' => 'Administratif & Terstruktur',
];
return $labels[$topCode] ?? 'Belum teridentifikasi';
}
public static function getProspekKerjaText($jurusan): string
{
$jurusan = strtolower((string) $jurusan);
if (str_contains($jurusan, 'ekonomi') || str_contains($jurusan, 'manajemen') || str_contains($jurusan, 'bisnis')) {
return 'Lulusan bidang ini dapat berkarier sebagai manajer bisnis, staf keuangan, analis pasar, konsultan bisnis, wirausahawan, maupun pegawai instansi pemerintah dan BUMN pada sektor ekonomi dan administrasi.';
}
if (str_contains($jurusan, 'teknik') || str_contains($jurusan, 'informatika') || str_contains($jurusan, 'komputer')) {
return 'Lulusan bidang ini memiliki peluang karier sebagai software developer, analis sistem, teknisi, network engineer, data analyst, maupun profesional teknologi di perusahaan swasta, startup, dan instansi pemerintah.';
}
if (str_contains($jurusan, 'kesehatan') || str_contains($jurusan, 'keperawatan') || str_contains($jurusan, 'farmasi')) {
return 'Lulusan bidang ini dapat berkarier di rumah sakit, klinik, laboratorium, industri kesehatan, layanan masyarakat, maupun lembaga penelitian kesehatan.';
}
if (str_contains($jurusan, 'pendidikan') || str_contains($jurusan, 'guru')) {
return 'Lulusan bidang ini memiliki prospek sebagai guru, tenaga pendidik, instruktur pelatihan, pengembang kurikulum, maupun konsultan pendidikan.';
}
return 'Prospek karier untuk jurusan ini sangat luas, mulai dari spesialis industri, konsultan ahli, hingga pegawai instansi pemerintah/BUMN di sektor terkait.';
}
}