MDL-65276 core: delete orphaned cron.php
[moodle.git] / lib / classes / task / completion_daily_task.php
CommitLineData
309ae892
DW
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 */
24namespace core\task;
25
26/**
51e488ea
JW
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.
309ae892 30 */
160ccd3d 31class completion_daily_task extends scheduled_task {
309ae892
DW
32
33 /**
34 * Get a descriptive name for this task (shown to admins).
35 *
36 * @return string
37 */
38 public function get_name() {
160ccd3d 39 return get_string('taskcompletiondaily', 'admin');
309ae892
DW
40 }
41
42 /**
43 * Do the job.
44 * Throw exceptions on errors (the job will be retried).
45 */
46 public function execute() {
a5a0d10e 47 global $CFG, $DB;
309ae892
DW
48
49 if ($CFG->enablecompletion) {
a5a0d10e
SL
50 require_once($CFG->libdir . "/completionlib.php");
51
52 if (debugging()) {
53 mtrace('Marking users as started');
54 }
55 if (!empty($CFG->gradebookroles)) {
56 $roles = ' AND ra.roleid IN ('.$CFG->gradebookroles.')';
57 } else {
58 // This causes it to default to everyone (if there is no student role)
59 $roles = '';
60 }
61 /**
62 * A quick explaination of this horrible looking query
63 *
64 * It's purpose is to locate all the active participants
65 * of a course with course completion enabled.
66 *
67 * We also only want the users with no course_completions
68 * record as this functions job is to create the missing
69 * ones :)
70 *
71 * We want to record the user's enrolment start time for the
72 * course. This gets tricky because there can be multiple
73 * enrolment plugins active in a course, hence the possibility
74 * of multiple records for each couse/user in the results
75 */
76 $sql = "
77 SELECT
78 c.id AS course,
79 u.id AS userid,
80 crc.id AS completionid,
81 ue.timestart AS timeenrolled,
82 ue.timecreated
83 FROM
84 {user} u
85 INNER JOIN
86 {user_enrolments} ue
87 ON ue.userid = u.id
88 INNER JOIN
89 {enrol} e
90 ON e.id = ue.enrolid
91 INNER JOIN
92 {course} c
93 ON c.id = e.courseid
94 INNER JOIN
95 {role_assignments} ra
96 ON ra.userid = u.id
97 LEFT JOIN
98 {course_completions} crc
99 ON crc.course = c.id
100 AND crc.userid = u.id
101 WHERE
102 c.enablecompletion = 1
103 AND crc.timeenrolled IS NULL
104 AND ue.status = 0
105 AND e.status = 0
106 AND u.deleted = 0
107 AND ue.timestart < ?
108 AND (ue.timeend > ? OR ue.timeend = 0)
109 $roles
110 ORDER BY
111 course,
112 userid
113 ";
114 $now = time();
115 $rs = $DB->get_recordset_sql($sql, array($now, $now, $now, $now));
116 // Check if result is empty
117 if (!$rs->valid()) {
118 $rs->close(); // Not going to iterate (but exit), close rs
119 return;
120 }
121 /**
122 * An explaination of the following loop
123 *
124 * We are essentially doing a group by in the code here (as I can't find
125 * a decent way of doing it in the sql).
126 *
127 * Since there can be multiple enrolment plugins for each course, we can have
128 * multiple rows for each particpant in the query result. This isn't really
129 * a problem until you combine it with the fact that the enrolment plugins
130 * can save the enrol start time in either timestart or timeenrolled.
131 *
132 * The purpose of this loop is to find the earliest enrolment start time for
133 * each participant in each course.
134 */
135 $prev = null;
136 while ($rs->valid() || $prev) {
137 $current = $rs->current();
138 if (!isset($current->course)) {
139 $current = false;
140 }
141 else {
142 // Not all enrol plugins fill out timestart correctly, so use whichever
143 // is non-zero
144 $current->timeenrolled = max($current->timecreated, $current->timeenrolled);
145 }
146 // If we are at the last record,
147 // or we aren't at the first and the record is for a diff user/course
148 if ($prev &&
149 (!$rs->valid() ||
150 ($current->course != $prev->course || $current->userid != $prev->userid))) {
151 $completion = new completion_completion();
152 $completion->userid = $prev->userid;
153 $completion->course = $prev->course;
154 $completion->timeenrolled = (string) $prev->timeenrolled;
155 $completion->timestarted = 0;
156 $completion->reaggregate = time();
157 if ($prev->completionid) {
158 $completion->id = $prev->completionid;
159 }
160 $completion->mark_enrolled();
161 if (debugging()) {
162 mtrace('Marked started user '.$prev->userid.' in course '.$prev->course);
163 }
164 }
165 // Else, if this record is for the same user/course
166 elseif ($prev && $current) {
167 // Use oldest timeenrolled
168 $current->timeenrolled = min($current->timeenrolled, $prev->timeenrolled);
169 }
170 // Move current record to previous
171 $prev = $current;
172 // Move to next record
173 $rs->next();
174 }
175 $rs->close();
309ae892
DW
176 }
177 }
178
179}