Files
alrahma_sunday_school_api/app/Services/Grading/HomeworkTrackingService.php
T
2026-06-09 02:32:58 -04:00

220 lines
8.1 KiB
PHP

<?php
namespace App\Services\Grading;
use App\Models\Configuration;
use Illuminate\Support\Facades\DB;
class HomeworkTrackingService
{
private array $teacherAssignmentCache = [];
public function __construct(private HomeworkTrackingCalendarService $calendarService)
{
}
public function report(?string $semester = null, ?string $schoolYear = null, int $page = 1): array
{
$semester = $semester ?: (string) (Configuration::getConfig('semester') ?? '');
$schoolYear = $schoolYear ?: (string) (Configuration::getConfig('school_year') ?? '');
[$start, $end] = $this->calendarService->buildDateRange($schoolYear, $semester);
$sundays = $this->calendarService->buildSundays($start, $end);
$eventDays = $this->calendarService->eventDays($schoolYear);
$dateToIndex = $this->calendarService->dateToIndex($sundays, $eventDays);
$limitToSemester = $this->hasTeacherAssignments($schoolYear);
$homeworkByIndex = $this->loadHomeworkByIndex($schoolYear, $semester, $limitToSemester);
$homeworkByDate = $this->loadHomeworkByDate($schoolYear, $semester, $limitToSemester, $sundays, $eventDays);
$teachers = $this->loadTeachers($schoolYear);
$perPage = 8;
$totalRows = count($teachers);
$totalPages = max(1, (int) ceil($totalRows / $perPage));
$page = max(1, min($page, $totalPages));
$offset = ($page - 1) * $perPage;
$teachersPage = array_slice($teachers, $offset, $perPage);
return [
'semester' => $semester,
'school_year' => $schoolYear,
'sundays' => $sundays,
'event_days' => $eventDays,
'date_to_index' => $dateToIndex,
'teachers' => $teachersPage,
'has_homework' => $homeworkByIndex['has_homework'],
'hw_entered_at' => $homeworkByIndex['entered_at'],
'has_homework_by_date' => $homeworkByDate['has_homework'],
'hw_entered_at_by_date' => $homeworkByDate['entered_at'],
'page' => $page,
'total_pages' => $totalPages,
'per_page' => $perPage,
'total_rows' => $totalRows,
];
}
private function hasTeacherAssignments(string $schoolYear): bool
{
$year = trim((string) $schoolYear);
if ($year === '') {
return false;
}
if (array_key_exists($year, $this->teacherAssignmentCache)) {
return $this->teacherAssignmentCache[$year];
}
$cnt = DB::table('teacher_class')->where('school_year', $year)->count();
$this->teacherAssignmentCache[$year] = $cnt > 0;
return $this->teacherAssignmentCache[$year];
}
private function loadHomeworkByIndex(string $schoolYear, string $semester, bool $limitToSemester): array
{
$query = DB::table('homework')
->select('class_section_id', 'homework_index', DB::raw('MIN(created_at) as first_created'), DB::raw('COUNT(*) as cnt'))
->where('school_year', $schoolYear);
if ($limitToSemester && $semester !== '') {
$query->where('semester', $semester);
}
$rows = $query->groupBy('class_section_id', 'homework_index')->get();
$hasHomework = [];
$enteredAt = [];
foreach ($rows as $row) {
$csid = (int) $row->class_section_id;
$hi = (int) $row->homework_index;
$cnt = (int) $row->cnt;
if ($csid > 0 && $hi > 0 && $cnt > 0) {
$hasHomework[$csid][$hi] = true;
$dateStr = substr((string) ($row->first_created ?? ''), 0, 10);
$enteredAt[$csid][$hi] = $dateStr ?: null;
}
}
return ['has_homework' => $hasHomework, 'entered_at' => $enteredAt];
}
private function loadHomeworkByDate(string $schoolYear, string $semester, bool $limitToSemester, array $sundays, array $eventDays): array
{
$query = DB::table('homework')
->select(DB::raw('class_section_id, DATE(created_at) as hw_date, MIN(created_at) as first_created, COUNT(*) as cnt'))
->where('school_year', $schoolYear);
if ($limitToSemester && $semester !== '') {
$query->where('semester', $semester);
}
$rows = $query
->groupBy('class_section_id', DB::raw('DATE(created_at)'))
->orderBy('hw_date', 'ASC')
->get();
$hasHomework = [];
$enteredAt = [];
foreach ($rows as $row) {
$csid = (int) $row->class_section_id;
$date = substr((string) ($row->hw_date ?? ''), 0, 10);
$cnt = (int) $row->cnt;
$firstCreated = substr((string) ($row->first_created ?? ''), 0, 10);
if ($csid <= 0 || !$date || $cnt <= 0) {
continue;
}
$baseIndex = -1;
for ($i = count($sundays) - 1; $i >= 0; $i--) {
if ($sundays[$i] <= $date) {
$baseIndex = $i;
break;
}
}
if ($baseIndex < 0) {
continue;
}
$j = $baseIndex;
while ($j >= 0 && !empty($eventDays[$sundays[$j]])) {
$j--;
}
if ($j < 0) {
continue;
}
$sunday = $sundays[$j];
$hasHomework[$csid][$sunday] = true;
$candidate = $firstCreated ?: $date;
if (empty($enteredAt[$csid][$sunday]) || ($candidate && $candidate < $enteredAt[$csid][$sunday])) {
$enteredAt[$csid][$sunday] = $candidate;
}
}
return ['has_homework' => $hasHomework, 'entered_at' => $enteredAt];
}
private function loadTeachers(string $schoolYear): array
{
$rows = DB::table('teacher_class as tc')
->select('tc.class_section_id', 'tc.position', 'cs.class_section_name', 'cs.class_id', 'u.firstname', 'u.lastname')
->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'tc.class_section_id')
->leftJoin('users as u', 'u.id', '=', 'tc.teacher_id')
->where('tc.school_year', $schoolYear)
->whereNotNull('tc.class_section_id')
->whereIn('tc.position', ['main', 'ta'])
->orderBy('cs.class_id', 'ASC')
->orderBy('cs.class_section_name', 'ASC')
->orderByRaw("CASE tc.position WHEN 'main' THEN 0 WHEN 'ta' THEN 1 ELSE 2 END")
->orderBy('u.firstname', 'ASC')
->get();
$bySection = [];
foreach ($rows as $row) {
$sid = (int) $row->class_section_id;
if ($sid <= 0) {
continue;
}
if (!isset($bySection[$sid])) {
$bySection[$sid] = [
'class_section_id' => $sid,
'class_id' => (int) ($row->class_id ?? 0),
'class_section_name' => (string) ($row->class_section_name ?? ''),
'teachers' => [],
'tas' => [],
];
}
$name = trim((string) ($row->firstname ?? '') . ' ' . (string) ($row->lastname ?? ''));
$pos = strtolower((string) ($row->position ?? ''));
if ($name !== '') {
if ($pos === 'main') {
$bySection[$sid]['teachers'][] = $name;
} elseif ($pos === 'ta') {
$bySection[$sid]['tas'][] = $name;
}
}
}
$teachers = array_values($bySection);
usort($teachers, function ($a, $b) {
$ai = (int) ($a['class_id'] ?? 0);
$bi = (int) ($b['class_id'] ?? 0);
$order = function (int $cid): int {
if ($cid === 13) return 0;
if ($cid >= 1 && $cid <= 11) return $cid;
if ($cid === 12) return 99;
return 200 + $cid;
};
$cmp = $order($ai) <=> $order($bi);
if ($cmp !== 0) {
return $cmp;
}
return strnatcasecmp($a['class_section_name'] ?? '', $b['class_section_name'] ?? '');
});
return $teachers;
}
}