333 lines
13 KiB
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());
|
|
}
|
|
}
|
|
}
|