Merge branch 'MDL-79733-main' of https://github.com/davewoloszyn/moodle
This commit is contained in:
@@ -34,11 +34,21 @@ class notification_helper {
|
||||
*/
|
||||
private const INTERVAL_DUE_SOON = (DAYSECS * 2);
|
||||
|
||||
/**
|
||||
* @var int Overdue time interval of 2 hours.
|
||||
*/
|
||||
private const INTERVAL_OVERDUE = (HOURSECS * 2);
|
||||
|
||||
/**
|
||||
* @var string Due soon notification type.
|
||||
*/
|
||||
public const TYPE_DUE_SOON = 'assign_due_soon';
|
||||
|
||||
/**
|
||||
* @var string Overdue notification type.
|
||||
*/
|
||||
public const TYPE_OVERDUE = 'assign_overdue';
|
||||
|
||||
/**
|
||||
* Get all assignments that have an approaching due date (includes users and groups with due date overrides).
|
||||
*
|
||||
@@ -70,7 +80,48 @@ class notification_helper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all users that have an approaching due date within an assignment.
|
||||
* Get all assignments that are overdue, but not exceeding the cut-off date (includes users and groups with due date overrides).
|
||||
*
|
||||
* We don't want to get every single overdue assignment ever.
|
||||
* We just want the ones within the specified window.
|
||||
*
|
||||
* @return \moodle_recordset Returns the matching assignment records.
|
||||
*/
|
||||
public static function get_overdue_assignments(): \moodle_recordset {
|
||||
global $DB;
|
||||
|
||||
$timenow = self::get_time_now();
|
||||
$timewindow = self::get_time_now() - self::INTERVAL_OVERDUE;
|
||||
|
||||
// Get all assignments that:
|
||||
// - Are overdue.
|
||||
// - Do not exceed the window of time in the past.
|
||||
// - Are still within the cut-off (if it is set).
|
||||
$sql = "SELECT DISTINCT a.id
|
||||
FROM {assign} a
|
||||
JOIN {course_modules} cm ON a.id = cm.instance
|
||||
JOIN {modules} m ON cm.module = m.id AND m.name = :modulename
|
||||
LEFT JOIN {assign_overrides} ao ON a.id = ao.assignid
|
||||
WHERE (a.duedate < :dd_timenow OR ao.duedate < :dd_ao_timenow)
|
||||
AND (a.duedate > :dd_timewindow OR ao.duedate > :dd_ao_timewindow)
|
||||
AND ((a.cutoffdate > :co_timenow OR a.cutoffdate = 0) OR
|
||||
(ao.cutoffdate > :co_ao_timenow OR ao.cutoffdate = 0))";
|
||||
|
||||
$params = [
|
||||
'dd_timenow' => $timenow,
|
||||
'dd_ao_timenow' => $timenow,
|
||||
'dd_timewindow' => $timewindow,
|
||||
'dd_ao_timewindow' => $timewindow,
|
||||
'co_timenow' => $timenow,
|
||||
'co_ao_timenow' => $timenow,
|
||||
'modulename' => 'assign',
|
||||
];
|
||||
|
||||
return $DB->get_recordset_sql($sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all assignment users that we should send the notification to.
|
||||
*
|
||||
* @param int $assignmentid The assignment id.
|
||||
* @param string $type The notification type.
|
||||
@@ -90,8 +141,9 @@ class notification_helper {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine the user's due date with respect to any overrides.
|
||||
// Determine key dates with respect to any overrides.
|
||||
$duedate = $assignmentobj->override_exists($user->id)->duedate ?? $assignmentobj->get_instance()->duedate;
|
||||
$cutoffdate = $assignmentobj->override_exists($user->id)->cutoffdate ?? $assignmentobj->get_instance()->cutoffdate;
|
||||
|
||||
// If the due date has no value, unset this user.
|
||||
if (empty($duedate)) {
|
||||
@@ -117,6 +169,23 @@ class notification_helper {
|
||||
];
|
||||
break;
|
||||
|
||||
case self::TYPE_OVERDUE:
|
||||
if ($duedate > self::get_time_now()) {
|
||||
unset($users[$key]);
|
||||
break;
|
||||
}
|
||||
// Check if the cut-off date is set and passed already.
|
||||
if (!empty($cutoffdate) && self::get_time_now() > $cutoffdate) {
|
||||
unset($users[$key]);
|
||||
break;
|
||||
}
|
||||
$match = [
|
||||
'assignmentid' => $assignmentid,
|
||||
'duedate' => $duedate,
|
||||
'cutoffdate' => $cutoffdate,
|
||||
];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -202,6 +271,93 @@ class notification_helper {
|
||||
message_send($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the overdue notification to the user.
|
||||
*
|
||||
* @param int $assignmentid The assignment id.
|
||||
* @param int $userid The user id.
|
||||
*/
|
||||
public static function send_overdue_notification_to_user(int $assignmentid, int $userid): void {
|
||||
// Get assignment data.
|
||||
$assignmentobj = self::get_assignment_data($assignmentid);
|
||||
|
||||
// Get the user and check they are a still a valid participant.
|
||||
$user = $assignmentobj->get_participant($userid);
|
||||
if (empty($user)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the due date still considered overdue.
|
||||
$assignmentobj->update_effective_access($userid);
|
||||
$duedate = $assignmentobj->get_instance($userid)->duedate;
|
||||
if ($duedate > self::get_time_now()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the cut-off date is set and passed already.
|
||||
$cutoffdate = $assignmentobj->get_instance($userid)->cutoffdate;
|
||||
if (!empty($cutoffdate) && self::get_time_now() > $cutoffdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the user has submitted already.
|
||||
if ($assignmentobj->get_user_submission($userid, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the user's notification message.
|
||||
$urlparams = [
|
||||
'id' => $assignmentobj->get_course_module()->id,
|
||||
'action' => 'view',
|
||||
];
|
||||
$url = new \moodle_url('/mod/assign/view.php', $urlparams);
|
||||
|
||||
// Prepare the cut-off date html string.
|
||||
$snippet = '';
|
||||
if (!empty($cutoffdate)) {
|
||||
$snippet = get_string('assignmentoverduehtmlcutoffsnippet', 'mod_assign', ['cutoffdate' => userdate($cutoffdate)]);
|
||||
}
|
||||
|
||||
$stringparams = [
|
||||
'firstname' => $user->firstname,
|
||||
'assignmentname' => $assignmentobj->get_instance()->name,
|
||||
'coursename' => $assignmentobj->get_course()->fullname,
|
||||
'duedate' => userdate($duedate),
|
||||
'url' => $url,
|
||||
'cutoffsnippet' => $snippet,
|
||||
];
|
||||
|
||||
$messagedata = [
|
||||
'user' => \core_user::get_user($user->id),
|
||||
'url' => $url->out(false),
|
||||
'subject' => get_string('assignmentoverduesubject', 'mod_assign', $stringparams),
|
||||
'assignmentname' => $assignmentobj->get_instance()->name,
|
||||
'html' => get_string('assignmentoverduehtml', 'mod_assign', $stringparams),
|
||||
];
|
||||
|
||||
$message = new \core\message\message();
|
||||
$message->component = 'mod_assign';
|
||||
$message->name = self::TYPE_OVERDUE;
|
||||
$message->userfrom = \core_user::get_noreply_user();
|
||||
$message->userto = $messagedata['user'];
|
||||
$message->subject = $messagedata['subject'];
|
||||
$message->fullmessageformat = FORMAT_HTML;
|
||||
$message->fullmessage = html_to_text($messagedata['html']);
|
||||
$message->fullmessagehtml = $messagedata['html'];
|
||||
$message->smallmessage = $messagedata['subject'];
|
||||
$message->notification = 1;
|
||||
$message->contexturl = $messagedata['url'];
|
||||
$message->contexturlname = $messagedata['assignmentname'];
|
||||
// Use custom data to avoid future notifications being sent again.
|
||||
$message->customdata = [
|
||||
'assignmentid' => $assignmentid,
|
||||
'duedate' => $duedate,
|
||||
'cutoffdate' => $cutoffdate,
|
||||
];
|
||||
|
||||
message_send($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time now.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_assign\task;
|
||||
|
||||
use core\task\scheduled_task;
|
||||
use mod_assign\notification_helper;
|
||||
|
||||
/**
|
||||
* Scheduled task to queue tasks for notifying about assignments that are now overdue.
|
||||
*
|
||||
* @package mod_assign
|
||||
* @copyright 2024 David Woloszyn <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class queue_all_assignment_overdue_notification_tasks extends scheduled_task {
|
||||
|
||||
/**
|
||||
* Return the task name.
|
||||
*
|
||||
* @return string The name of the task.
|
||||
*/
|
||||
public function get_name(): string {
|
||||
return get_string('sendnotificationoverdue', 'mod_assign');
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the task.
|
||||
*/
|
||||
public function execute(): void {
|
||||
$assignments = notification_helper::get_overdue_assignments();
|
||||
foreach ($assignments as $assignment) {
|
||||
$task = new queue_assignment_overdue_notification_tasks_for_users();
|
||||
$task->set_custom_data($assignment);
|
||||
\core\task\manager::queue_adhoc_task($task, true);
|
||||
}
|
||||
$assignments->close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_assign\task;
|
||||
|
||||
use core\task\adhoc_task;
|
||||
use mod_assign\notification_helper;
|
||||
|
||||
/**
|
||||
* Ad-hoc task to queue another task for notifying a user about an overdue assignment.
|
||||
*
|
||||
* @package mod_assign
|
||||
* @copyright 2024 David Woloszyn <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class queue_assignment_overdue_notification_tasks_for_users extends adhoc_task {
|
||||
|
||||
/**
|
||||
* Execute the task.
|
||||
*/
|
||||
public function execute(): void {
|
||||
$assignmentid = $this->get_custom_data()->id;
|
||||
$type = notification_helper::TYPE_OVERDUE;
|
||||
$users = notification_helper::get_users_within_assignment($assignmentid, $type);
|
||||
foreach ($users as $user) {
|
||||
$task = new send_assignment_overdue_notification_to_user();
|
||||
$task->set_custom_data([
|
||||
'assignmentid' => $assignmentid,
|
||||
'userid' => $user->id,
|
||||
]);
|
||||
$task->set_userid($user->id);
|
||||
\core\task\manager::queue_adhoc_task($task, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace mod_assign\task;
|
||||
|
||||
use core\task\adhoc_task;
|
||||
use mod_assign\notification_helper;
|
||||
|
||||
/**
|
||||
* Ad-hoc task to send a notification to a user about an overdue assignment.
|
||||
*
|
||||
* @package mod_assign
|
||||
* @copyright 2024 David Woloszyn <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class send_assignment_overdue_notification_to_user extends adhoc_task {
|
||||
|
||||
/**
|
||||
* Execute the task.
|
||||
*/
|
||||
public function execute(): void {
|
||||
$assignmentid = $this->get_custom_data()->assignmentid;
|
||||
$userid = $this->get_custom_data()->userid;
|
||||
notification_helper::send_overdue_notification_to_user($assignmentid, $userid);
|
||||
}
|
||||
}
|
||||
@@ -39,4 +39,12 @@ $messageproviders = array (
|
||||
'airnotifier' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED,
|
||||
],
|
||||
],
|
||||
// Assignments that are overdue.
|
||||
'assign_overdue' => [
|
||||
'defaults' => [
|
||||
'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED,
|
||||
'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED,
|
||||
'airnotifier' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED,
|
||||
],
|
||||
],
|
||||
);
|
||||
|
||||
@@ -40,4 +40,13 @@ $tasks = array(
|
||||
'month' => '*',
|
||||
'dayofweek' => '*',
|
||||
],
|
||||
[
|
||||
'classname' => '\mod_assign\task\queue_all_assignment_overdue_notification_tasks',
|
||||
'blocking' => 0,
|
||||
'minute' => 'R',
|
||||
'hour' => '*/1',
|
||||
'day' => '*',
|
||||
'month' => '*',
|
||||
'dayofweek' => '*',
|
||||
],
|
||||
);
|
||||
|
||||
@@ -71,7 +71,13 @@ $string['assignmentduesoonhtml'] = '<p>Hi {$a->firstname},</p>
|
||||
<p>The assignment <strong>{$a->assignmentname}</strong> in course {$a->coursename} is due soon.</p>
|
||||
<p><strong>Due: {$a->duedate}</strong></p>
|
||||
<p><a href="{$a->url}">Go to activity</a></p>';
|
||||
$string['assignmentoverduehtml'] = '<p>Hi {$a->firstname},</p>
|
||||
<p><strong>{$a->assignmentname}</strong> in course {$a->coursename} was due on <strong>{$a->duedate}</strong>.</p>
|
||||
<p>You might still be able to submit your assignment{$a->cutoffsnippet}, but your submission will be marked as late.</p>
|
||||
<p><a href="{$a->url}">Go to activity</a></p>';
|
||||
$string['assignmentoverduehtmlcutoffsnippet'] = ' <strong>by {$a->cutoffdate}</strong>';
|
||||
$string['assignmentduesoonsubject'] = 'Due on {$a->duedate}: {$a->assignmentname}';
|
||||
$string['assignmentoverduesubject'] = 'Overdue: {$a->assignmentname}';
|
||||
$string['assignmentisdue'] = 'Assignment is due';
|
||||
$string['assignmentmail'] = '{$a->grader} has posted some feedback on your
|
||||
assignment submission for \'{$a->assignment}\'
|
||||
@@ -381,6 +387,7 @@ $string['maxgrade'] = 'Maximum Grade';
|
||||
$string['maxperpage'] = 'Maximum assignments per page';
|
||||
$string['maxperpage_help'] = 'The maximum number of assignments a grader can show in the assignment grading page. This setting is useful in preventing timeouts for courses with a large number of participants.';
|
||||
$string['messageprovider:assign_due_soon'] = 'Assignment due soon notification';
|
||||
$string['messageprovider:assign_overdue'] = 'Assignment overdue notification';
|
||||
$string['messageprovider:assign_notification'] = 'Assignment notifications';
|
||||
$string['modulename'] = 'Assignment';
|
||||
$string['modulename_help'] = 'The assignment activity module enables a teacher to communicate tasks, collect work and provide grades and feedback.
|
||||
@@ -528,6 +535,7 @@ $string['selectuser'] = 'Select {$a}';
|
||||
$string['sendlatenotifications'] = 'Notify graders about late submissions';
|
||||
$string['sendlatenotifications_help'] = 'If enabled, graders (usually teachers) receive a message whenever a student submits an assignment late. Message methods are configurable.';
|
||||
$string['sendnotificationduedatesoon'] = 'Notify user of an approaching assignment due date';
|
||||
$string['sendnotificationoverdue'] = 'Notify user of an assignment that is overdue';
|
||||
$string['sendsubmissionreceipts'] = 'Send submission receipt to students';
|
||||
$string['sendsubmissionreceipts_help'] = 'This switch enables submission receipts for students. Students will receive a notification every time they successfully submit an assignment.';
|
||||
$string['setmarkingallocation'] = 'Set allocated marker';
|
||||
|
||||
@@ -27,9 +27,9 @@ namespace mod_assign;
|
||||
*/
|
||||
final class notification_helper_test extends \advanced_testcase {
|
||||
/**
|
||||
* Run all the tasks related to the notifications.
|
||||
* Run all the tasks related to the 'due soon' notifications.
|
||||
*/
|
||||
protected function run_notification_helper_tasks(): void {
|
||||
protected function run_due_soon_notification_helper_tasks(): void {
|
||||
$task = \core\task\manager::get_scheduled_task(\mod_assign\task\queue_all_assignment_due_soon_notification_tasks::class);
|
||||
$task->execute();
|
||||
$clock = \core\di::get(\core\clock::class);
|
||||
@@ -50,7 +50,7 @@ final class notification_helper_test extends \advanced_testcase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getting assignments with a 'duedate' date within the date range.
|
||||
* Test getting due soon assignments.
|
||||
*/
|
||||
public function test_get_due_soon_assignments(): void {
|
||||
$this->resetAfterTest();
|
||||
@@ -75,10 +75,9 @@ final class notification_helper_test extends \advanced_testcase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getting users within an assignment that are within our date range.
|
||||
* Test getting users within an assignment that have a due date soon.
|
||||
*/
|
||||
public function test_get_users_within_assignment(): void {
|
||||
global $DB;
|
||||
public function test_get_due_soon_users_within_assignment(): void {
|
||||
$this->resetAfterTest();
|
||||
$generator = $this->getDataGenerator();
|
||||
$helper = \core\di::get(notification_helper::class);
|
||||
@@ -151,7 +150,7 @@ final class notification_helper_test extends \advanced_testcase {
|
||||
/**
|
||||
* Test sending the assignment due soon notification to a user.
|
||||
*/
|
||||
public function test_send_notification_to_user(): void {
|
||||
public function test_send_due_soon_notification_to_user(): void {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
$generator = $this->getDataGenerator();
|
||||
@@ -176,7 +175,7 @@ final class notification_helper_test extends \advanced_testcase {
|
||||
$clock->bump(5);
|
||||
|
||||
// Run the tasks.
|
||||
$this->run_notification_helper_tasks();
|
||||
$this->run_due_soon_notification_helper_tasks();
|
||||
|
||||
// Get the assignment object.
|
||||
[$course, $assigncm] = get_course_and_cm_from_instance($assignment->id, 'assign');
|
||||
@@ -202,7 +201,7 @@ final class notification_helper_test extends \advanced_testcase {
|
||||
$sink->clear();
|
||||
|
||||
// Run the tasks again.
|
||||
$this->run_notification_helper_tasks();
|
||||
$this->run_due_soon_notification_helper_tasks();
|
||||
|
||||
// There should be no notification because nothing has changed.
|
||||
$this->assertEmpty($sink->get_messages_by_component('mod_assign'));
|
||||
@@ -214,7 +213,7 @@ final class notification_helper_test extends \advanced_testcase {
|
||||
$DB->update_record('assign', $updatedata);
|
||||
|
||||
// Run the tasks again.
|
||||
$this->run_notification_helper_tasks();
|
||||
$this->run_due_soon_notification_helper_tasks();
|
||||
|
||||
// There should be a new notification because the 'duedate' has been updated.
|
||||
$this->assertCount(1, $sink->get_messages_by_component('mod_assign'));
|
||||
@@ -237,7 +236,7 @@ final class notification_helper_test extends \advanced_testcase {
|
||||
$clock->bump(5);
|
||||
|
||||
// Run the tasks again.
|
||||
$this->run_notification_helper_tasks();
|
||||
$this->run_due_soon_notification_helper_tasks();
|
||||
|
||||
// No new notification should have been sent.
|
||||
$this->assertEmpty($sink->get_messages_by_component('mod_assign'));
|
||||
@@ -245,4 +244,235 @@ final class notification_helper_test extends \advanced_testcase {
|
||||
// Clear sink.
|
||||
$sink->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all the tasks related to the 'overdue' notifications.
|
||||
*/
|
||||
protected function run_overdue_notification_helper_tasks(): void {
|
||||
$task = \core\task\manager::get_scheduled_task(\mod_assign\task\queue_all_assignment_overdue_notification_tasks::class);
|
||||
$task->execute();
|
||||
$clock = \core\di::get(\core\clock::class);
|
||||
|
||||
$adhoctask = \core\task\manager::get_next_adhoc_task($clock->time());
|
||||
if ($adhoctask) {
|
||||
$this->assertInstanceOf(\mod_assign\task\queue_assignment_overdue_notification_tasks_for_users::class, $adhoctask);
|
||||
$adhoctask->execute();
|
||||
\core\task\manager::adhoc_task_complete($adhoctask);
|
||||
}
|
||||
|
||||
$adhoctask = \core\task\manager::get_next_adhoc_task($clock->time());
|
||||
if ($adhoctask) {
|
||||
$this->assertInstanceOf(\mod_assign\task\send_assignment_overdue_notification_to_user::class, $adhoctask);
|
||||
$adhoctask->execute();
|
||||
\core\task\manager::adhoc_task_complete($adhoctask);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getting overdue assignments.
|
||||
*/
|
||||
public function test_get_overdue_assignments(): void {
|
||||
$this->resetAfterTest();
|
||||
$generator = $this->getDataGenerator();
|
||||
$helper = \core\di::get(notification_helper::class);
|
||||
$clock = $this->mock_clock_with_frozen();
|
||||
|
||||
// Create an overdue assignment.
|
||||
$course = $generator->create_course();
|
||||
$generator->create_module('assign', ['course' => $course->id, 'duedate' => $clock->time() - HOURSECS]);
|
||||
|
||||
// Check that we have a result returned.
|
||||
$result = $helper::get_overdue_assignments();
|
||||
$this->assertTrue($result->valid());
|
||||
$result->close();
|
||||
|
||||
// Time travel 2 hours into the future.
|
||||
// We should have no assignments found as we are only getting overdue assignments within a 2 hour window.
|
||||
$clock->bump(HOURSECS * 2);
|
||||
$result = $helper::get_overdue_assignments();
|
||||
$this->assertFalse($result->valid());
|
||||
$result->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getting users within an assignment that is overdue.
|
||||
*/
|
||||
public function test_get_overdue_users_within_assignment(): void {
|
||||
$this->resetAfterTest();
|
||||
$generator = $this->getDataGenerator();
|
||||
$helper = \core\di::get(notification_helper::class);
|
||||
$clock = $this->mock_clock_with_frozen();
|
||||
|
||||
// Create a course and enrol some users.
|
||||
$course = $generator->create_course();
|
||||
$user1 = $generator->create_and_enrol($course, 'student');
|
||||
$user2 = $generator->create_and_enrol($course, 'student');
|
||||
$user3 = $generator->create_and_enrol($course, 'student');
|
||||
$user4 = $generator->create_and_enrol($course, 'student');
|
||||
$user5 = $generator->create_and_enrol($course, 'student');
|
||||
$user6 = $generator->create_and_enrol($course, 'student');
|
||||
$user7 = $generator->create_and_enrol($course, 'teacher');
|
||||
|
||||
/** @var \mod_assign_generator $assignmentgenerator */
|
||||
$assignmentgenerator = $generator->get_plugin_generator('mod_assign');
|
||||
|
||||
// Create an overdue assignment.
|
||||
$duedate = $clock->time() - HOURSECS;
|
||||
$assignment = $assignmentgenerator->create_instance([
|
||||
'course' => $course->id,
|
||||
'duedate' => $duedate,
|
||||
]);
|
||||
|
||||
// User1 will have a user override, giving them an extra minute for 'duedate'.
|
||||
$userduedate = $duedate + MINSECS;
|
||||
$assignmentgenerator->create_override([
|
||||
'assignid' => $assignment->id,
|
||||
'userid' => $user1->id,
|
||||
'duedate' => $userduedate,
|
||||
]);
|
||||
|
||||
// User2 and user3 will have a group override, giving them an extra minute for 'duedate'.
|
||||
$groupduedate = $duedate + MINSECS;
|
||||
$group = $generator->create_group(['courseid' => $course->id]);
|
||||
$generator->create_group_member(['groupid' => $group->id, 'userid' => $user2->id]);
|
||||
$generator->create_group_member(['groupid' => $group->id, 'userid' => $user3->id]);
|
||||
$assignmentgenerator->create_override([
|
||||
'assignid' => $assignment->id,
|
||||
'groupid' => $group->id,
|
||||
'duedate' => $groupduedate,
|
||||
]);
|
||||
|
||||
// User4 will have a user override of one extra week, excluding them from the results.
|
||||
$userduedate = $duedate + WEEKSECS;
|
||||
$assignmentgenerator->create_override([
|
||||
'assignid' => $assignment->id,
|
||||
'userid' => $user4->id,
|
||||
'duedate' => $userduedate,
|
||||
]);
|
||||
|
||||
// User5 will submit the assignment, excluding them from the results.
|
||||
$assignmentgenerator->create_submission([
|
||||
'userid' => $user5->id,
|
||||
'assignid' => $assignment->cmid,
|
||||
'status' => 'submitted',
|
||||
'timemodified' => $clock->time(),
|
||||
]);
|
||||
|
||||
// User6 will have a cut-off date override that has already lapsed, excluding them from the results.
|
||||
$usercutoffdate = $clock->time() - MINSECS;
|
||||
$assignmentgenerator->create_override([
|
||||
'assignid' => $assignment->id,
|
||||
'userid' => $user6->id,
|
||||
'cutoffdate' => $usercutoffdate,
|
||||
]);
|
||||
|
||||
// There should be 3 users with the teacher excluded.
|
||||
$users = $helper::get_users_within_assignment($assignment->id, $helper::TYPE_OVERDUE);
|
||||
$this->assertCount(3, $users);
|
||||
$this->assertArrayHasKey($user1->id, $users);
|
||||
$this->assertArrayHasKey($user2->id, $users);
|
||||
$this->assertArrayHasKey($user3->id, $users);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test sending the assignment overdue notification to a user.
|
||||
*/
|
||||
public function test_send_overdue_notification_to_user(): void {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
$generator = $this->getDataGenerator();
|
||||
$clock = $this->mock_clock_with_frozen();
|
||||
$sink = $this->redirectMessages();
|
||||
|
||||
// Create a course and enrol a user.
|
||||
$course = $generator->create_course();
|
||||
$user1 = $generator->create_and_enrol($course, 'student');
|
||||
|
||||
/** @var \mod_assign_generator $assignmentgenerator */
|
||||
$assignmentgenerator = $generator->get_plugin_generator('mod_assign');
|
||||
|
||||
// Create an assignment that is overdue.
|
||||
$duedate = $clock->time() - HOURSECS;
|
||||
$cutoffdate = $clock->time() + DAYSECS;
|
||||
$assignment = $assignmentgenerator->create_instance([
|
||||
'course' => $course->id,
|
||||
'duedate' => $duedate,
|
||||
'cutoffdate' => $cutoffdate,
|
||||
]);
|
||||
$clock->bump(5);
|
||||
|
||||
// Run the tasks.
|
||||
$this->run_overdue_notification_helper_tasks();
|
||||
|
||||
// Get the notifications that should have been created during the adhoc task.
|
||||
$this->assertCount(1, $sink->get_messages());
|
||||
|
||||
// Check the subject matches.
|
||||
$messages = $sink->get_messages_by_component('mod_assign');
|
||||
$message = reset($messages);
|
||||
$expectedsubject = get_string('assignmentoverduesubject', 'mod_assign', ['assignmentname' => $assignment->name]);
|
||||
$this->assertEquals($expectedsubject, $message->subject);
|
||||
|
||||
// Clear sink.
|
||||
$sink->clear();
|
||||
|
||||
// Run the tasks again.
|
||||
$this->run_overdue_notification_helper_tasks();
|
||||
|
||||
// There should be no notification because nothing has changed.
|
||||
$this->assertEmpty($sink->get_messages_by_component('mod_assign'));
|
||||
|
||||
// Let's modify the 'duedate' for the assignment (it will still be overdue).
|
||||
$updatedata = new \stdClass();
|
||||
$updatedata->id = $assignment->id;
|
||||
$updatedata->duedate = $duedate + MINSECS;
|
||||
$DB->update_record('assign', $updatedata);
|
||||
|
||||
// Clear sink.
|
||||
$sink->clear();
|
||||
|
||||
// Run the tasks again.
|
||||
$this->run_overdue_notification_helper_tasks();
|
||||
|
||||
// There should be a new notification because the 'duedate' has been updated.
|
||||
$this->assertCount(1, $sink->get_messages_by_component('mod_assign'));
|
||||
|
||||
// Let's modify the 'cut-off date'.
|
||||
$updatedata = new \stdClass();
|
||||
$updatedata->id = $assignment->id;
|
||||
$updatedata->cutoffdate = $cutoffdate + MINSECS;
|
||||
$DB->update_record('assign', $updatedata);
|
||||
|
||||
// Clear sink.
|
||||
$sink->clear();
|
||||
|
||||
// Run the tasks again.
|
||||
$this->run_overdue_notification_helper_tasks();
|
||||
|
||||
// There should be a new notification because the 'cut-off date' has been updated.
|
||||
$this->assertCount(1, $sink->get_messages_by_component('mod_assign'));
|
||||
|
||||
// Let's modify the 'duedate' one more time.
|
||||
$updatedata = new \stdClass();
|
||||
$updatedata->id = $assignment->id;
|
||||
$updatedata->duedate = $duedate + (MINSECS * 2);
|
||||
$DB->update_record('assign', $updatedata);
|
||||
|
||||
// This time, the user will submit the assignment.
|
||||
$assignmentgenerator->create_submission([
|
||||
'userid' => $user1->id,
|
||||
'assignid' => $assignment->cmid,
|
||||
'status' => 'submitted',
|
||||
'timemodified' => $clock->time(),
|
||||
]);
|
||||
|
||||
// Clear sink.
|
||||
$sink->clear();
|
||||
|
||||
// Run the tasks again.
|
||||
$this->run_overdue_notification_helper_tasks();
|
||||
|
||||
// No new notification should have been sent.
|
||||
$this->assertEmpty($sink->get_messages_by_component('mod_assign'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,5 +25,5 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->component = 'mod_assign'; // Full name of the plugin (used for diagnostics).
|
||||
$plugin->version = 2024070201; // The current module version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2024070800; // The current module version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2024041600; // Requires this Moodle version.
|
||||
|
||||
Reference in New Issue
Block a user