220 lines
8.1 KiB
PHP
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("FIELD(tc.position, 'main','ta')")
|
|
->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;
|
|
}
|
|
}
|