Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scheduled task related performance related improvement and minor changes #155

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion classes/table/reengagement_participants.php
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,8 @@ public function other_cols($colname, $data) {
*/
public function query_db($pagesize, $useinitialsbar = true) {
list($twhere, $tparams) = $this->get_sql_where();
$psearch = new \mod_reengagement\table\reengagement_search($this->course, $this->context, $this->filterset);
$psearch = new \mod_reengagement\table\reengagement_search($this->course, $this->context, $this->filterset,
$this->reengagement->id);

$total = $psearch->get_total_participants_count($twhere, $tparams);

Expand Down
24 changes: 23 additions & 1 deletion classes/table/reengagement_search.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@

namespace mod_reengagement\table;

use context;
use core_table\local\filter\filterset;
use core_user\table\participants_search;
use stdClass;

defined('MOODLE_INTERNAL') || die;

Expand All @@ -40,6 +43,23 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class reengagement_search extends participants_search {
/**
* @var int $reengagementid The reengagement id
*/
private $reengagementid;

/**
* Constructor
*
* @param stdClass $course The course object
* @param context $context The context
* @param filterset $filterset The filterset
* @param int $reengagementid The reengagement id
*/
public function __construct(stdClass $course, context $context, filterset $filterset, int $reengagementid) {
parent::__construct($course, $context, $filterset);
$this->reengagementid = $reengagementid;
}

/**
* Generate the SQL used to fetch filtered data for the reengagement table.
Expand All @@ -50,7 +70,9 @@ class reengagement_search extends participants_search {
*/
protected function get_participants_sql(string $additionalwhere, array $additionalparams): array {
$sql = parent::get_participants_sql($additionalwhere, $additionalparams);
$sql['outerjoins'] .= 'LEFT JOIN {reengagement_inprogress} rip ON rip.userid = u.id';
$sql['params']['reengagementid'] = $this->reengagementid;
$sql['outerjoins'] .= 'LEFT JOIN {reengagement_inprogress} rip ON rip.userid = u.id ' .
'AND rip.reengagement = :reengagementid';
$sql['outerselect'] .= ', rip.completiontime AS completiontime, rip.emailtime AS emailtime, '
. 'rip.emailsent AS emailsent, rip.completed AS completed';
return $sql;
Expand Down
142 changes: 142 additions & 0 deletions classes/task/mark_complete.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?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/>.

/**
* Ad-hoc task to process in progress reengagement completion.
*
* @package mod_reengagement
* @copyright (c) 2024, Enovation Solutions
* @author Lai Wei <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

namespace mod_reengagement\task;

use cache;
use core\event\course_module_completion_updated;
use moodle_exception;
use stdClass;

defined('MOODLE_INTERNAL') || die();

require_once($CFG->dirroot . '/mod/reengagement/lib.php');
require_once($CFG->libdir . '/completionlib.php');
require_once($CFG->libdir . '/enrollib.php');

/**
* Ad-hoc task to process in progress reengagement completion.
*
* @copyright (c) 2024, Enovation Solutions
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mark_complete extends reengagement_adhoc_task {

/**
* Get a descriptive name for this task (shown to admins).
*
* @return string
*/
public function get_name() {
return get_string('adhoctaskmarkcompletetask', 'reengagement');
}

/**
* Do the job.
*/
public function execute() {
global $DB;

$data = $this->get_custom_data();

$timenow = time();

$reengagementdata = $data->reengagement;
$cmid = $reengagementdata->cmid;
$inprogressdata = $data->inprogress;
$userid = $inprogressdata->userid;

// Check if user is still enrolled in the course.
try {
[$reengagement, $inprogress, $context] = self::validate_task($reengagementdata, $inprogressdata);
} catch (moodle_exception $e) {
// Validation failed, end task.
return;
}

// Update completion record to indicate completion so the user can continue with any dependant activities.
$completionrecord = $DB->get_record('course_modules_completion', ['coursemoduleid' => $cmid, 'userid' => $userid]);
if (empty($completionrecord)) {
mtrace("Could not find completion record to update complete state, userid: $userid, cmid: $cmid - recreating record.");
// This might happen when reset_all_state has been triggered, deleting an "in-progress" record. so recreate it.
$completionrecord = new stdClass();
$completionrecord->coursemoduleid = $cmid;
$completionrecord->completionstate = COMPLETION_COMPLETE_PASS;
$completionrecord->viewed = COMPLETION_VIEWED;
$completionrecord->overrideby = null;
$completionrecord->timemodified = $timenow;
$completionrecord->userid = $userid;
$completionrecord->id = $DB->insert_record('course_modules_completion', $completionrecord);
} else {
mtrace("Updating activity complete state to completed, userid: $userid, cmid: $cmid.");
$updaterecord = new stdClass();
$updaterecord->id = $completionrecord->id;
$updaterecord->completionstate = COMPLETION_COMPLETE_PASS;
$updaterecord->timemodified = $timenow;
$DB->update_record('course_modules_completion', $updaterecord) . " \n";
}
$completioncache = cache::make('core', 'completion');
$completioncache->delete($userid . '_' . $reengagement->course);

// Trigger an event for course module completion changed.
$event = course_module_completion_updated::create([
'objectid' => $completionrecord->id,
'context' => $context,
'relateduserid' => $userid,
'other' => [
'relateduserid' => $userid,
],
]);
$event->add_record_snapshot('course_modules_completion', $completionrecord);
$event->trigger();

if (($reengagement->emailuser == REENGAGEMENT_EMAILUSER_COMPLETION) ||
($reengagement->emailuser == REENGAGEMENT_EMAILUSER_NEVER) ||
($reengagement->emailuser == REENGAGEMENT_EMAILUSER_TIME && !empty($inprogressdata->emailsent))) {
// No need to keep 'inprogress' record for later emailing.
// Delete inprogress record.
mtrace("mode $reengagement->emailuser reengagementid $reengagement->id. " .
"User marked complete, deleting inprogress record for user $userid");
$result = $DB->delete_records('reengagement_inprogress', ['id' => $inprogress->id]);
} else {
// Update inprogress record to indicate completion done.
mtrace("mode $reengagement->emailuser reengagementid $reengagement->id. " .
"Updating inprogress record for user $userid to indicate completion");
$updaterecord = new stdClass();
$updaterecord->id = $inprogress->id;
$updaterecord->completed = COMPLETION_COMPLETE;
$result = $DB->update_record('reengagement_inprogress', $updaterecord);
}
if (empty($result)) {
// Skip emailing. Go on to next completion record so we don't risk emailing users continuously each cron.
mtrace("Reengagement: not sending email to $userid regarding reengagementid $reengagement->id " .
"due to failure to update db.");
} else if ($reengagement->emailuser == REENGAGEMENT_EMAILUSER_COMPLETION) {
mtrace("Reengagement: sending email to $userid regarding reengagementid $reengagement->id due to completion.");
reengagement_email_user($reengagement, $inprogress);
}
}
}

111 changes: 111 additions & 0 deletions classes/task/reengagement_adhoc_task.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?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/>.

/**
* Parent ad-hoc task class for reengagement that contains common methods.
*
* @package mod_reengagement
* @copyright (c) 2024, Enovation Solutions
* @author Lai Wei <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

namespace mod_reengagement\task;

use context_module;
use core\task\adhoc_task;
use moodle_exception;

/**
* Parent ad-hoc task class for reengagement that contains common methods.
*
* @copyright (c) 2024, Enovation Solutions
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class reengagement_adhoc_task extends adhoc_task {
/**
* Get a descriptive name for this task (shown to admins).
*
* @return string
*/
public function get_name() {
return get_string('adhoctaskreengagementtask', 'reengagement');
}

/**
* Do the job - nothing to execute.
*/
public function execute() {
// Nothing to execute.
}

/**
* Validate the task.
*
* @param object $reengagementdata The reengagement data
* @param object $inprogressdata The in progress data
* @return array
* @throws moodle_exception
*/
protected function validate_task($reengagementdata, $inprogressdata) {
global $DB;

$userid = $inprogressdata->userid;

// Ensure the user still exists.
if (!$DB->record_exists('user', ['id' => $userid, 'deleted' => 0])) {
mtrace("Reengagement: invalid userid $userid. Delete the in progress record and the task.");
$DB->delete_records('reengagement_inprogress', ['id' => $inprogressdata->id]);

throw new moodle_exception('errorinvalidtask', 'mod_reengagement');
}

// Check if the course module is still valid.
$context = context_module::instance($reengagementdata->cmid);
if (!$context) {
mtrace("Reengagement: invalid cmid $reengagementdata->cmid. Delete the in progress record and the task.");
$DB->delete_records('reengagement_inprogress', ['id' => $inprogressdata->id]);

throw new moodle_exception('errorinvalidtask', 'mod_reengagement');
}

// Check if the user is still enrolled in the course.
if (!is_enrolled($context, $userid, 'mod/reengagement:startreengagement', true)) {
mtrace("Reengagement: user $userid is not enrolled in the course, or cannot start reengagement any more. " .
"Delete the in progress record and the task.");
$DB->delete_records('reengagement_inprogress', ['id' => $inprogressdata->id]);

throw new moodle_exception('errorinvalidtask', 'mod_reengagement');
}

// Ensure the in progress record still exists.
if (!$inprogress = $DB->get_record('reengagement_inprogress', ['id' => $inprogressdata->id])) {
mtrace("Reengagement: invalid inprogressid $inprogressdata->id. Delete the task.");

throw new moodle_exception('errorinvalidtask', 'mod_reengagement');
}

// Ensure the reengagement activity is still exists.
if (!$reengagement = $DB->get_record('reengagement', ['id' => $reengagementdata->rid])) {
mtrace("Reengagement: invalid reengagementid $reengagementdata->rid. Delete the in progress record and the task.");
$DB->delete_records('reengagement_inprogress', ['id' => $inprogress->id]);

throw new moodle_exception('errorinvalidtask', 'mod_reengagement');
}

return [$reengagement, $inprogress, $context];
}
}
Loading