198 lines
7.5 KiB
PHP
Executable File
198 lines
7.5 KiB
PHP
Executable File
<?php
|
|
|
|
namespace App\Commands;
|
|
|
|
use App\Controllers\View\EmailController;
|
|
use App\Models\ClassSectionModel;
|
|
use App\Models\ConfigurationModel;
|
|
use App\Models\TeacherSubmissionNotificationHistoryModel;
|
|
use App\Models\UserModel;
|
|
use CodeIgniter\CLI\BaseCommand;
|
|
use CodeIgniter\CLI\CLI;
|
|
use Config\Database;
|
|
|
|
class SendExamDraftDeadlineReminders extends BaseCommand
|
|
{
|
|
protected $group = 'Exam Drafts';
|
|
protected $name = 'exam-drafts:deadline-reminders';
|
|
protected $description = 'Sends exam draft deadline reminders to teachers who have not submitted drafts.';
|
|
|
|
public function run(array $params)
|
|
{
|
|
helper('date');
|
|
|
|
$configModel = new ConfigurationModel();
|
|
$schoolYear = (string) ($configModel->getConfig('school_year') ?? '');
|
|
$semester = (string) ($configModel->getConfig('semester') ?? '');
|
|
$deadlineValue = trim((string) ($configModel->getConfig('exam_draft_deadline') ?? ''));
|
|
if ($deadlineValue === '') {
|
|
CLI::write('exam_draft_deadline is not configured.', 'yellow');
|
|
return;
|
|
}
|
|
|
|
$tz = new \DateTimeZone(config('App')->appTimezone ?? 'UTC');
|
|
try {
|
|
$deadline = new \DateTimeImmutable($deadlineValue, $tz);
|
|
} catch (\Throwable $e) {
|
|
CLI::write('Invalid exam_draft_deadline value.', 'red');
|
|
return;
|
|
}
|
|
$deadline = $deadline->setTime(0, 0, 0);
|
|
$today = new \DateTimeImmutable('now', $tz);
|
|
$today = $today->setTime(0, 0, 0);
|
|
|
|
if ($today > $deadline) {
|
|
CLI::write('Deadline has passed. No reminders sent.', 'yellow');
|
|
return;
|
|
}
|
|
|
|
$daysToDeadline = (int) $today->diff($deadline)->format('%r%a');
|
|
if (!$this->shouldSendOnDay($daysToDeadline)) {
|
|
CLI::write('No reminder scheduled for today.', 'yellow');
|
|
return;
|
|
}
|
|
|
|
$db = Database::connect();
|
|
$teacherClassRows = $db->table('teacher_class')
|
|
->select('teacher_id, class_section_id, school_year, semester')
|
|
->when($schoolYear !== '', static function ($builder) use ($schoolYear) {
|
|
return $builder->where('school_year', $schoolYear);
|
|
})
|
|
->when($semester !== '', static function ($builder) use ($semester) {
|
|
return $builder->where('semester', $semester);
|
|
})
|
|
->get()
|
|
->getResultArray();
|
|
|
|
if (empty($teacherClassRows)) {
|
|
CLI::write('No teacher assignments found.', 'yellow');
|
|
return;
|
|
}
|
|
|
|
$draftTable = 'exam_drafts';
|
|
$fields = $db->getFieldNames($draftTable);
|
|
$teacherColumn = in_array('teacher_id', $fields, true) ? 'teacher_id' : 'author_id';
|
|
$hasStatusColumn = in_array('status', $fields, true);
|
|
$hasIsLegacyColumn = in_array('is_legacy', $fields, true);
|
|
|
|
$draftsQuery = $db->table($draftTable)
|
|
->select("{$teacherColumn} AS teacher_id, class_section_id")
|
|
->when($schoolYear !== '', static function ($builder) use ($schoolYear) {
|
|
return $builder->where('school_year', $schoolYear);
|
|
})
|
|
->when($semester !== '', static function ($builder) use ($semester) {
|
|
return $builder->where('semester', $semester);
|
|
});
|
|
|
|
if ($hasStatusColumn) {
|
|
$draftsQuery = $draftsQuery->where('status !=', 'legacy');
|
|
}
|
|
if ($hasIsLegacyColumn) {
|
|
$draftsQuery = $draftsQuery->where('is_legacy', 0);
|
|
}
|
|
|
|
$draftRows = $draftsQuery->get()->getResultArray();
|
|
$submittedMap = [];
|
|
foreach ($draftRows as $row) {
|
|
$key = (int) ($row['teacher_id'] ?? 0) . '|' . (int) ($row['class_section_id'] ?? 0);
|
|
$submittedMap[$key] = true;
|
|
}
|
|
|
|
$missingByTeacher = [];
|
|
foreach ($teacherClassRows as $row) {
|
|
$teacherId = (int) ($row['teacher_id'] ?? 0);
|
|
$classSectionId = (int) ($row['class_section_id'] ?? 0);
|
|
if ($teacherId <= 0 || $classSectionId <= 0) {
|
|
continue;
|
|
}
|
|
$key = $teacherId . '|' . $classSectionId;
|
|
if (!isset($submittedMap[$key])) {
|
|
$missingByTeacher[$teacherId][] = $classSectionId;
|
|
}
|
|
}
|
|
|
|
if (empty($missingByTeacher)) {
|
|
CLI::write('All teachers have submitted drafts.', 'green');
|
|
return;
|
|
}
|
|
|
|
$userModel = new UserModel();
|
|
$classSectionModel = new ClassSectionModel();
|
|
$historyModel = new TeacherSubmissionNotificationHistoryModel();
|
|
$mailer = new EmailController();
|
|
|
|
$classSections = $classSectionModel
|
|
->select('class_section_id, class_section_name')
|
|
->findAll();
|
|
$classLookup = [];
|
|
foreach ($classSections as $section) {
|
|
$classLookup[(int) ($section['class_section_id'] ?? 0)] = $section['class_section_name'] ?? '';
|
|
}
|
|
|
|
$todayStamp = $today->format('Y-m-d');
|
|
foreach ($missingByTeacher as $teacherId => $sectionIds) {
|
|
$teacher = $userModel->find($teacherId);
|
|
$email = $teacher['email'] ?? '';
|
|
if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
|
continue;
|
|
}
|
|
|
|
$alreadySent = $historyModel
|
|
->where('teacher_id', $teacherId)
|
|
->where('notification_category', 'exam_draft_deadline')
|
|
->where('school_year', $schoolYear)
|
|
->where('semester', $semester)
|
|
->like('sent_at', $todayStamp)
|
|
->countAllResults() > 0;
|
|
if ($alreadySent) {
|
|
continue;
|
|
}
|
|
|
|
$classNames = array_map(static function ($id) use ($classLookup) {
|
|
return $classLookup[(int) $id] ?? "Class {$id}";
|
|
}, $sectionIds);
|
|
$classList = implode(', ', $classNames);
|
|
$teacherName = trim(($teacher['firstname'] ?? '') . ' ' . ($teacher['lastname'] ?? ''));
|
|
if ($teacherName === '') {
|
|
$teacherName = 'Teacher';
|
|
}
|
|
|
|
$subject = 'Reminder: Exam draft submission deadline';
|
|
$body = '<p>Dear ' . esc($teacherName) . ',</p>'
|
|
. '<p>This is a reminder to submit your exam draft(s) for: ' . esc($classList) . '.</p>'
|
|
. '<p>Deadline: <strong>' . esc($deadline->format('Y-m-d')) . '</strong></p>'
|
|
. '<p>Please submit your draft at <a href="' . esc(base_url('teacher/exam-drafts')) . '">Teacher Exam Drafts</a>.</p>'
|
|
. '<p>Thank you.</p>';
|
|
|
|
$status = 'failed';
|
|
if ($mailer->sendEmail($email, $subject, $body, 'notifications')) {
|
|
$status = 'sent';
|
|
CLI::write("Sent reminder to {$email}", 'green');
|
|
}
|
|
|
|
$historyModel->insert([
|
|
'teacher_id' => $teacherId,
|
|
'class_section_id' => 0,
|
|
'admin_id' => null,
|
|
'notification_category' => 'exam_draft_deadline',
|
|
'message' => strip_tags($body),
|
|
'status' => $status,
|
|
'school_year' => $schoolYear,
|
|
'semester' => $semester,
|
|
'sent_at' => utc_now(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
private function shouldSendOnDay(int $daysToDeadline): bool
|
|
{
|
|
if ($daysToDeadline < 0) {
|
|
return false;
|
|
}
|
|
if ($daysToDeadline <= 4) {
|
|
return true;
|
|
}
|
|
return in_array($daysToDeadline, [28, 21, 14, 7], true);
|
|
}
|
|
}
|