Differences Between: [Versions 310 and 311] [Versions 39 and 311]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * A scheduled task. 19 * 20 * @package core 21 * @copyright 2013 onwards Martin Dougiamas http://dougiamas.com 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 namespace core\task; 25 26 /** 27 * Simple task to run the daily completion cron. 28 * @copyright 2013 onwards Martin Dougiamas http://dougiamas.com. 29 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later. 30 */ 31 class completion_daily_task extends scheduled_task { 32 33 /** 34 * Get a descriptive name for this task (shown to admins). 35 * 36 * @return string 37 */ 38 public function get_name() { 39 return get_string('taskcompletiondaily', 'admin'); 40 } 41 42 /** 43 * Do the job. 44 * Throw exceptions on errors (the job will be retried). 45 */ 46 public function execute() { 47 global $CFG, $DB; 48 49 if ($CFG->enablecompletion) { 50 require_once($CFG->libdir . "/completionlib.php"); 51 52 if (debugging()) { 53 mtrace('Marking users as started'); 54 } 55 56 // This causes it to default to everyone (if there is no student role). 57 $sqlroles = ''; 58 if (!empty($CFG->gradebookroles)) { 59 $sqlroles = ' AND ra.roleid IN (' . $CFG->gradebookroles.')'; 60 } 61 62 // It's purpose is to locate all the active participants of a course with course completion enabled. 63 // We also only want the users with no course_completions record as this functions job is to create 64 // the missing ones :) 65 // We want to record the user's enrolment start time for the course. This gets tricky because there can be 66 // multiple enrolment plugins active in a course, hence the possibility of multiple records for each 67 // couse/user in the results. 68 $sql = "SELECT c.id AS course, u.id AS userid, crc.id AS completionid, ue.timestart AS timeenrolled, 69 ue.timecreated 70 FROM {user} u 71 INNER JOIN {user_enrolments} ue ON ue.userid = u.id 72 INNER JOIN {enrol} e ON e.id = ue.enrolid 73 INNER JOIN {course} c ON c.id = e.courseid 74 INNER JOIN {context} con ON con.contextlevel = ? AND con.instanceid = c.id 75 INNER JOIN {role_assignments} ra ON ra.userid = u.id AND ra.contextid = con.id 76 LEFT JOIN {course_completions} crc ON crc.course = c.id AND crc.userid = u.id 77 WHERE c.enablecompletion = 1 78 AND crc.timeenrolled IS NULL 79 AND ue.status = 0 80 AND e.status = 0 81 AND u.deleted = 0 82 AND ue.timestart < ? 83 AND (ue.timeend > ? OR ue.timeend = 0) 84 $sqlroles 85 ORDER BY course, userid"; 86 $now = time(); 87 $rs = $DB->get_recordset_sql($sql, [CONTEXT_COURSE, $now, $now]); 88 89 // Check if result is empty. 90 if (!$rs->valid()) { 91 // Not going to iterate (but exit), close rs. 92 $rs->close(); 93 return; 94 } 95 96 // We are essentially doing a group by in the code here (as I can't find a decent way of doing it 97 // in the sql). Since there can be multiple enrolment plugins for each course, we can have multiple rows 98 // for each participant in the query result. This isn't really a problem until you combine it with the fact 99 // that the enrolment plugins can save the enrol start time in either timestart or timeenrolled. 100 // The purpose of the loop is to find the earliest enrolment start time for each participant in each course. 101 $prev = null; 102 while ($rs->valid() || $prev) { 103 $current = $rs->current(); 104 if (!isset($current->course)) { 105 $current = false; 106 } else { 107 // Not all enrol plugins fill out timestart correctly, so use whichever is non-zero. 108 $current->timeenrolled = max($current->timecreated, $current->timeenrolled); 109 } 110 111 // If we are at the last record, or we aren't at the first and the record is for a diff user/course. 112 if ($prev && (!$rs->valid() || 113 ($current->course != $prev->course || $current->userid != $prev->userid))) { 114 115 $completion = new \completion_completion(); 116 $completion->userid = $prev->userid; 117 $completion->course = $prev->course; 118 $completion->timeenrolled = (string) $prev->timeenrolled; 119 $completion->timestarted = 0; 120 $completion->reaggregate = time(); 121 if ($prev->completionid) { 122 $completion->id = $prev->completionid; 123 } 124 125 try { 126 $completion->mark_enrolled(); 127 128 if (debugging()) { 129 mtrace('Marked started user '.$prev->userid.' in course '.$prev->course); 130 } 131 } catch (\dml_write_exception $e) { 132 // Most likely this happened because the completion object was created while we were working. 133 // So get the record and make sure it has a time enrolled set. 134 if (debugging()) { 135 mtrace('Exception while marking started user '.$prev->userid.' in course '.$prev->course.', retrying'); 136 } 137 138 $params = ['userid' => $completion->userid, 'course' => $completion->course]; 139 $existing = new \completion_completion($params); 140 if (!empty($existing->id) && empty($existing->timeenrolled)) { 141 $existing->timeenrolled = $completion->timeenrolled; 142 try { 143 $existing->mark_enrolled(); 144 } catch (\Exception $e) { 145 // Catch everything, so we can continue on to other records. 146 if (debugging()) { 147 mtrace('Exception again while marking started user '.$prev->userid.' in course '.$prev->course. 148 ': '.$e->getMessage()."\n".$e->getTraceAsString()); 149 } 150 } 151 } 152 } catch (\Exception $e) { 153 // Catch anything else, so we can continue on to other records. 154 if (debugging()) { 155 mtrace('Exception while marking started user '.$prev->userid.' in course '.$prev->course. 156 ': '.$e->getMessage()."\n".$e->getTraceAsString()); 157 } 158 } 159 } else if ($prev && $current) { 160 // Else, if this record is for the same user/course use oldest timeenrolled. 161 $current->timeenrolled = min($current->timeenrolled, $prev->timeenrolled); 162 } 163 // Move current record to previous. 164 $prev = $current; 165 // Move to next record. 166 $rs->next(); 167 } 168 $rs->close(); 169 } 170 } 171 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body