diff options
| author | Simon Rettberg | 2026-01-15 11:21:11 +0100 |
|---|---|---|
| committer | Simon Rettberg | 2026-01-15 11:21:11 +0100 |
| commit | 60dbee86166915bc24d70782132eb38aa5cfb6db (patch) | |
| tree | 48b4d784e0112ed70cd31d17ff0b6074f0f66396 /modules-available/exams/page.inc.php | |
| parent | [dozmod] Disable checkboxes *after* submit (diff) | |
| download | slx-admin-60dbee86166915bc24d70782132eb38aa5cfb6db.tar.gz slx-admin-60dbee86166915bc24d70782132eb38aa5cfb6db.tar.xz slx-admin-60dbee86166915bc24d70782132eb38aa5cfb6db.zip | |
[exams] Add check and warning for colliding exams
If two (or more) exams share at least one location and their start/end
times overlap, display a warning to the user.
Diffstat (limited to 'modules-available/exams/page.inc.php')
| -rw-r--r-- | modules-available/exams/page.inc.php | 52 |
1 files changed, 50 insertions, 2 deletions
diff --git a/modules-available/exams/page.inc.php b/modules-available/exams/page.inc.php index f42711a2..2bd7a215 100644 --- a/modules-available/exams/page.inc.php +++ b/modules-available/exams/page.inc.php @@ -224,8 +224,51 @@ class Page_Exams extends Page { $out = []; $now = time(); + + // Pre-compute collisions: exams overlap in time and share at least one location + $collisionFlags = []; + $parsedExams = []; if (is_array($this->exams)) { - foreach ($this->exams as $exam) { + foreach ($this->exams as $idx => $exam) { + IF ($exam['endtime'] < $now) + continue; // Don't care about past exams + // Parse location IDs: treat NULL as global (0) to be consistent with permission checks + if (empty($exam['locationids'])) { + $lids = Location::getAllLocationIds(0); + } else { + $lids = array_map(function($val) { + return Location::getAllLocationIds($val, true); + }, explode(',', (string)$exam['locationids'])); + $lids = array_unique(array_merge(...$lids)); + } + $parsedExams[$idx] = [ + 'start' => (int)$exam['starttime'], + 'end' => (int)$exam['endtime'], + 'lids' => $lids, + ]; + $collisionFlags[$idx] = false; + } + + $indices = array_keys($parsedExams); + $cnt = count($indices); + for ($i = 0; $i < $cnt; $i++) { + for ($j = $i + 1; $j < $cnt; $j++) { + $a = $parsedExams[$indices[$i]]; + $b = $parsedExams[$indices[$j]]; + // Time overlap if startA < endB and startB < endA + if ($a['start'] < $b['end'] && $b['start'] < $a['end']) { + // Location overlap + if (!empty(array_intersect($a['lids'], $b['lids']))) { + $collisionFlags[$indices[$i]] = true; + $collisionFlags[$indices[$j]] = true; + } + } + } + } + + // Build output, attaching visual classes and collision flag + $numCollisions = 0; + foreach ($this->exams as $idx => $exam) { if ($exam['endtime'] < $now) { $exam['rowClass'] = 'text-muted'; $exam['btnClass'] = 'btn-default'; @@ -238,10 +281,15 @@ class Page_Exams extends Page } $exam['starttime_s'] = date('Y-m-d H:i', $exam['starttime']); $exam['endtime_s'] = date('Y-m-d H:i', $exam['endtime']); + if ($collisionFlags[$idx] ?? false) { + $exam['rowClass'] = 'danger slx-bold'; + $exam['collision'] = true; + $numCollisions++; + } $out[] = $exam; } } - return ['exams' => $out]; + return ['exams' => $out, 'collisions' => $numCollisions]; } protected function makeLectureExamList(): array |
