penjadwalan-ga/app/Http/Controllers/Admin/JadwalController.php

333 lines
13 KiB
PHP

<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Guru;
use App\Models\Logbooks;
use Carbon\Carbon;
use Illuminate\Http\Request;
class JadwalController extends Controller
{
public function getDataHariIni(){
$jharini = Logbooks::whereDate('tanggal', Carbon::today())
->orderByRaw("CASE WHEN start IS NULL OR start = '' THEN 1 ELSE 0 END, start ASC")
->get();
return $jharini->toJson();
}
public function index(){
$data = [];
$peserta = [];
$guru = Guru::where('status', 'Aktif')->select('id', 'nama')->get();
return view('admin.pages.jadwal', compact('data', 'peserta', 'guru'));
}
public function calendarData(){
$jharini = Logbooks::orderByRaw("CASE WHEN start IS NULL OR start = '' THEN 1 ELSE 0 END, start ASC")
->with('Kelas.Guru', 'FotoLogbook')
->get();
return json_encode($jharini);
}
public function calendarCountData($id){
if ($id != 0){
$jharini = Logbooks::whereHas('kelas', function($query) use ($id) {
$query->where('guru_id', $id);
})
->orderByRaw("CASE WHEN start IS NULL OR start = '' THEN 1 ELSE 0 END, start ASC")
->with('Kelas', 'FotoLogbook')
->get();
}else{
$jharini = Logbooks::orderByRaw("CASE WHEN start IS NULL OR start = '' THEN 1 ELSE 0 END, start ASC")
->with('Kelas', 'FotoLogbook')
->get();
}
$total = $jharini->count();
$completed = $jharini->where('status', 'completed')->count();
$kelas = $jharini->pluck('kelas_id')->unique()->count();
$response = [
'kelas' => $kelas,
'total' => $total,
'completed' => $completed,
// 'data' => $jharini
];
return response()->json($response);
}
public function calendar(){
$guru = Guru::where('status', 'Aktif')->get();
return view('admin.pages.jadwal-calendar', compact('guru'));
}
// AJAX Endpoints untuk menampilkan data tanpa timeout
public function getJadwalHariIni(){
$jharini = Logbooks::whereDate('tanggal', Carbon::today())
->orderByRaw("CASE WHEN start IS NULL OR start = '' THEN 1 ELSE 0 END, start ASC")
->with(['Kelas.guru', 'Guru', 'FotoLogbook'])
->get();
return response()->json([
'success' => true,
'data' => $jharini,
'count' => count($jharini)
]);
}
public function getJadwalBesok(){
$jbesok = Logbooks::whereDate('tanggal', Carbon::tomorrow())
->orderByRaw("CASE WHEN start IS NULL OR start = '' THEN 1 ELSE 0 END, start ASC")
->with(['Kelas.guru', 'Guru', 'FotoLogbook'])
->get();
return response()->json([
'success' => true,
'data' => $jbesok,
'count' => count($jbesok)
]);
}
public function getLogbookData(){
Carbon::setLocale('id');
$groupedLogbooks = Logbooks::orderBy('tanggal', 'asc')
->with(['Kelas.guru', 'Guru', 'FotoLogbook'])
->get()
->groupBy(function($item) {
return \Carbon\Carbon::parse($item->tanggal)->translatedFormat('F Y');
});
$now = \Carbon\Carbon::now();
$sortedLogbooks = collect([
'upcoming' => collect(),
'past' => collect(),
]);
$logbookSummary = collect();
foreach ($groupedLogbooks as $bulanTahun => $items) {
$firstItemDate = \Carbon\Carbon::parse($items->first()->tanggal);
$category = $firstItemDate->greaterThanOrEqualTo($now->startOfMonth()) ? 'upcoming' : 'past';
$sortedLogbooks[$category][$bulanTahun] = $items;
// Menyimpan jumlah logbook tiap bulan
$logbookSummary[$bulanTahun] = [
'jumlah_sesi' => $items->count(),
'jumlah_sesi_completed' => $items->where('status', 'completed')->count(),
'jumlah_siswa' => $items->pluck('Kelas')->filter()->flatMap->siswa->unique('id')->count(),
'jumlah_kelas' => $items->pluck('kelas_id')->unique()->count(),
];
}
$logbooksUpcoming = $sortedLogbooks['upcoming'];
$logbooksPast = $sortedLogbooks['past']->sortByDesc(function($items, $bulanTahun) {
return \Carbon\Carbon::parse($items->first()->tanggal);
});
return response()->json([
'success' => true,
'upcoming' => $logbooksUpcoming,
'past' => $logbooksPast,
'summary' => $logbookSummary
]);
}
public function generateGAIndex()
{
$semesters = \App\Models\Kelas::whereNotNull('semester')->distinct()->pluck('semester');
$years = \App\Models\Kelas::whereNotNull('tahun_ajaran')->distinct()->pluck('tahun_ajaran');
return view('admin.pages.generate-ga', compact('semesters', 'years'));
}
public function hasilGAIndex()
{
return view('admin.pages.hasil-ga');
}
public function jadwalMingguan()
{
$sesiList = \App\Models\Sesi::orderBy('id', 'asc')->get();
return view('admin.pages.jadwal-mingguan', compact('sesiList'));
}
public function jadwalKromosom()
{
$jadwalMaster = \App\Models\JadwalKelas::with(['kelas', 'guru', 'ruangan', 'sesi'])
->orderBy('hari', 'asc')
->orderBy('sesi_id', 'asc')
->get();
return view('admin.pages.jadwal-kromosom', compact('jadwalMaster'));
}
public function getJadwalMingguanData(Request $request)
{
$masterJadwal = \App\Models\JadwalKelas::with(['kelas', 'guru', 'ruangan', 'sesi'])->get();
$data = $masterJadwal->map(function ($j) {
return [
'id' => $j->id,
'hari' => $j->hari,
'sesi_id' => $j->sesi_id,
'kelas' => $j->kelas,
'guru' => $j->guru,
'ruangan' => $j->ruangan,
'sesi' => $j->sesi,
'is_master' => true
];
});
return response()->json([
'success' => true,
'is_master' => true,
'data' => $data
]);
}
public function generateGA(Request $request)
{
$request->validate([
'semester' => 'nullable|string',
'tahun_ajaran' => 'nullable|string',
'population_size' => 'nullable|numeric|min:10|max:200',
'crossover_rate' => 'nullable|numeric|min:0.1|max:1.0',
'mutation_rate' => 'nullable|numeric|min:0.01|max:1.0',
'max_generations' => 'nullable|numeric|min:10|max:2000',
'random_seed' => 'nullable|numeric',
]);
try {
$semester = $request->input('semester');
$tahunAjaran = $request->input('tahun_ajaran');
$popSize = (int) $request->input('population_size', 50);
$crossRate = (float) $request->input('crossover_rate', 0.8);
$mutRate = (float) $request->input('mutation_rate', 0.1);
$maxGen = (int) $request->input('max_generations', 500);
// Solusi A: Pool 10 Controlled Seeds Teruji (Bervariasi Terkontrol & Reproducible)
$presetSeeds = [202, 101, 303, 404, 505, 606, 707, 808, 909, 1001];
$paramSignature = "ga_{$semester}_{$tahunAjaran}_{$popSize}_{$crossRate}_{$mutRate}";
$lastSignature = session('ga_last_param_signature');
$attemptIndex = session()->has('ga_attempt_index') ? (int) session('ga_attempt_index') : -1;
if ($lastSignature !== $paramSignature || $attemptIndex < 0) {
// Reset ke percobaan ke-1 (index 0) jika parameter diubah atau session baru
$attemptIndex = 0;
} else {
// Lanjutkan ke percobaan berikutnya (berputar di 0..9)
$attemptIndex = ($attemptIndex + 1) % count($presetSeeds);
}
session()->put('ga_last_param_signature', $paramSignature);
session()->put('ga_attempt_index', $attemptIndex);
$seed = $request->filled('random_seed')
? (int) $request->input('random_seed')
: $presetSeeds[$attemptIndex];
$service = new \App\Services\Scheduling\ScheduleGeneratorService();
$result = $service->generate(
semester: $semester,
tahunAjaran: $tahunAjaran,
populationSize: $popSize,
crossoverRate: $crossRate,
mutationRate: $mutRate,
maxGenerations: $maxGen,
seed: $seed
);
$result['attemptNumber'] = $attemptIndex + 1;
$result['seedUsed'] = $seed;
$result['semester'] = $semester ?? 'Genap';
$result['tahunAjaran'] = $tahunAjaran ?? '2025/2026';
$result['populationSize'] = $popSize;
$result['crossoverRate'] = $crossRate;
$result['mutationRate'] = $mutRate;
$result['maxGenerations'] = $maxGen;
$statusMsg = $result['isOptimal']
? "Penjadwalan GA Berhasil Optimal (Fitness: 1.00 pada Generasi ke-{$result['generations']}, Waktu: {$result['executionTime']}s)"
: "Penjadwalan GA Selesai pada Generasi ke-{$result['generations']} (Fitness: " . round($result['bestChromosome']->fitness, 4) . ", Waktu: {$result['executionTime']}s)";
session()->put('ga_log_result', $result);
return redirect('/admin/hasil-ga')->with('success', $statusMsg);
} catch (\Exception $e) {
return redirect('/admin/jadwal-mingguan')->with('error', 'Gagal memproses GA: ' . $e->getMessage());
}
}
public function exportExcelGA()
{
$result = session('ga_log_result');
if (!$result) {
return redirect('/admin/generate-ga')->with('error', 'Belum ada hasil eksekusi GA. Silakan jalankan eksekusi terlebih dahulu.');
}
$fileName = 'Simulasi_Algoritma_Genetika_Robotku_' . date('Ymd_His') . '.xlsx';
return \Maatwebsite\Excel\Facades\Excel::download(new \App\Exports\GaSimulationExport($result), $fileName);
}
public function exportExcelMingguan()
{
$jadwalMaster = \App\Models\JadwalKelas::with(['kelas', 'guru', 'ruangan', 'sesi'])
->orderBy('hari', 'asc')
->orderBy('sesi_id', 'asc')
->get();
if ($jadwalMaster->isEmpty()) {
return redirect()->back()->with('error', 'Belum ada data jadwal kelas untuk di-export.');
}
$fileName = 'Master_Jadwal_Kelas_Mingguan_' . date('Ymd_His') . '.xlsx';
return \Maatwebsite\Excel\Facades\Excel::download(new \App\Exports\JadwalMingguanExport($jadwalMaster), $fileName);
}
public function exportExcelGaPresentation()
{
$service = new \App\Services\Scheduling\GaPresentationSimulationService();
$simData = $service->generateSimulationData();
$fileName = 'Simulasi_GA_Presentasi_Robotku_' . date('Ymd_His') . '.xlsx';
return \Maatwebsite\Excel\Facades\Excel::download(new \App\Exports\GaPresentationExport($simData), $fileName);
}
public function generateLogbookFromJadwal(Request $request)
{
$request->validate([
'tanggal_mulai' => 'required|date',
'tanggal_selesai' => 'required|date|after_or_equal:tanggal_mulai',
], [
'tanggal_mulai.required' => 'Tanggal mulai harus diisi.',
'tanggal_selesai.required' => 'Tanggal selesai harus diisi.',
'tanggal_selesai.after_or_equal' => 'Tanggal selesai harus sama atau setelah tanggal mulai.',
]);
try {
$service = new \App\Services\LogbookGeneratorService();
$result = $service->generateFromJadwal(
startDate: $request->input('tanggal_mulai'),
endDate: $request->input('tanggal_selesai')
);
$msg = "Berhasil memproses logbook dari Master Jadwal: {$result['created']} entri baru dibuat";
if ($result['updated'] > 0) {
$msg .= ", {$result['updated']} logbook pending diperbarui";
}
if ($result['skipped'] > 0) {
$msg .= ", {$result['skipped']} logbook completed dilewati";
}
$msg .= ".";
return redirect()->back()->with('success', $msg);
} catch (\Exception $e) {
return redirect()->back()->with('error', 'Gagal generate logbook: ' . $e->getMessage());
}
}
}