penjadwalan-ga/app/Services/LogbookGeneratorService.php

162 lines
6.4 KiB
PHP

<?php
namespace App\Services;
use App\Models\JadwalKelas;
use App\Models\Logbooks;
use Illuminate\Support\Carbon;
class LogbookGeneratorService
{
/**
* Generate entri logbook berdasarkan Master Jadwal GA (jadwal_kelas)
* untuk rentang tanggal tertentu, dibatasi oleh total sesi/pertemuan per kelas (jenis_kelas).
*
* @param string $startDate (Format: YYYY-MM-DD)
* @param string $endDate (Format: YYYY-MM-DD)
* @param array $options
* @return array
*/
public function generateFromJadwal(string $startDate, string $endDate, array $options = []): array
{
$start = Carbon::parse($startDate)->startOfDay();
$end = Carbon::parse($endDate)->endOfDay();
// 1. Ambil Master Jadwal Kelas beserta relasinya
$masterJadwal = JadwalKelas::with(['kelas', 'guru', 'ruangan', 'sesi'])->get();
if ($masterJadwal->isEmpty()) {
throw new \Exception('Belum ada data Master Jadwal Mingguan (`jadwal_kelas`). Silakan jalankan generate GA terlebih dahulu.');
}
$createdCount = 0;
$updatedCount = 0;
$skippedCount = 0;
$deletedExcessCount = 0;
// Peta maksimal pertemuan per kelas dari jenis_kelas (ex: "12 Sesi" => 12)
$maxMeetingsMap = [];
foreach ($masterJadwal as $j) {
if ($j->kelas && !isset($maxMeetingsMap[$j->kelas_id])) {
$maxM = null;
if (!empty($j->kelas->jenis_kelas)) {
if (preg_match('/\d+/', $j->kelas->jenis_kelas, $matches)) {
$maxM = (int) $matches[0];
}
}
$maxMeetingsMap[$j->kelas_id] = $maxM;
}
}
// Track per-kelas pertemuan counter
$pertemuanCounter = [];
// 2. Iterasi per hari dari startDate hingga endDate
$currentDate = $start->copy();
while ($currentDate->lte($end)) {
$dayIso = $currentDate->dayOfWeekIso; // 1 = Senin, ..., 7 = Minggu
$dateString = $currentDate->format('Y-m-d');
// Cari jadwal yang sesuai dengan hari pada tanggal ini
$schedulesForDay = $masterJadwal->where('hari', $dayIso);
foreach ($schedulesForDay as $jadwal) {
$kelasId = $jadwal->kelas_id;
$maxMeetings = $maxMeetingsMap[$kelasId] ?? null;
if (!isset($pertemuanCounter[$kelasId])) {
// Hitung berapa entri logbook yang sudah ada untuk kelas ini secara keseluruhan
$totalExisting = Logbooks::where('kelas_id', $kelasId)->count();
$pertemuanCounter[$kelasId] = $totalExisting;
}
// Cek apakah logbook untuk kelas, tanggal, dan sesi ini sudah ada
$existingLogbook = Logbooks::where('kelas_id', $kelasId)
->where('tanggal', $dateString)
->where('sesi_id', $jadwal->sesi_id)
->first();
// Ekstrak jam start & end dari Sesi (ex: "08:00 - 10:00")
$jamStart = null;
$jamEnd = null;
if ($jadwal->sesi && $jadwal->sesi->jam) {
$jamParts = explode('-', $jadwal->sesi->jam);
$jamStart = isset($jamParts[0]) ? trim($jamParts[0]) : null;
$jamEnd = isset($jamParts[1]) ? trim($jamParts[1]) : null;
}
if ($existingLogbook) {
// Jika logbook sudah completed, lewati agar data presensi guru tidak hilang
if ($existingLogbook->status === 'completed') {
$skippedCount++;
continue;
}
// Update logbook pending dengan info master jadwal terbaru
$existingLogbook->update([
'guru_id' => $jadwal->guru_id,
'ruangan_id' => $jadwal->ruangan_id,
'hari_id' => $dayIso,
'kode_kelas' => $jadwal->kelas->kode_kelas ?? $existingLogbook->kode_kelas,
'start' => $jamStart ?? $existingLogbook->start,
'end' => $jamEnd ?? $existingLogbook->end,
]);
$updatedCount++;
} else {
// Cek apakah kelas sudah mencapai batas maksimal pertemuan (misal 12 sesi)
if ($maxMeetings !== null && $pertemuanCounter[$kelasId] >= $maxMeetings) {
$skippedCount++;
continue;
}
// Inkremen nomor pertemuan
$pertemuanCounter[$kelasId]++;
$pertemuanKe = (string) $pertemuanCounter[$kelasId];
Logbooks::create([
'kelas_id' => $kelasId,
'kode_kelas' => $jadwal->kelas->kode_kelas ?? null,
'guru_id' => $jadwal->guru_id,
'ruangan_id' => $jadwal->ruangan_id,
'sesi_id' => $jadwal->sesi_id,
'hari_id' => $dayIso,
'tanggal' => $dateString,
'start' => $jamStart,
'end' => $jamEnd,
'pertemuan' => $pertemuanKe,
'status' => 'pending',
]);
$createdCount++;
}
}
$currentDate->addDay();
}
// 3. Bersihkan entri logbook berstatus 'pending' yang melebihi maxMeetings per kelas
foreach ($maxMeetingsMap as $kelasId => $maxM) {
if ($maxM !== null) {
$excessLogbooks = Logbooks::where('kelas_id', $kelasId)
->where('status', 'pending')
->get()
->filter(function ($lb) use ($maxM) {
return is_numeric($lb->pertemuan) && (int)$lb->pertemuan > $maxM;
});
foreach ($excessLogbooks as $excess) {
$excess->delete();
$deletedExcessCount++;
}
}
}
return [
'created' => $createdCount,
'updated' => $updatedCount,
'skipped' => $skippedCount,
'deleted_excess' => $deletedExcessCount,
'total' => $createdCount + $updatedCount + $skippedCount
];
}
}