merge prod to main
This commit is contained in:
+197
@@ -0,0 +1,197 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user