MDL-65276 core: delete orphaned cron.php
[moodle.git] / lib / classes / task / completion_regular_task.php
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/>.
17 /**
18  * A scheduled task.
19  *
20  * @package    core
21  * @copyright  2015 Josh Willcock
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
24 namespace core\task;
26 /**
27  * Simple task to run the regular completion cron.
28  * @copyright  2015 Josh Willcock
29  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
30  */
31 class completion_regular_task extends scheduled_task {
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('taskcompletionregular', 'admin');
40     }
42     /**
43      * Do the job.
44      * Throw exceptions on errors (the job will be retried).
45      */
46     public function execute() {
47         global $CFG, $COMPLETION_CRITERIA_TYPES, $DB;
49         if ($CFG->enablecompletion) {
50             require_once($CFG->libdir . "/completionlib.php");
52             // Process each criteria type.
53             foreach ($COMPLETION_CRITERIA_TYPES as $type) {
54                 $object = 'completion_criteria_'.$type;
55                 require_once $CFG->dirroot.'/completion/criteria/'.$object.'.php';
56                 $class = new $object();
57                 // Run the criteria type's cron method, if it has one
58                 if (method_exists($class, 'cron')) {
59                     if (debugging()) {
60                         mtrace('Running '.$object.'->cron()');
61                     }
62                     $class->cron();
63                 }
64             }
66             if (debugging()) {
67                 mtrace('Aggregating completions');
68             }
69             // Save time started
70             $timestarted = time();
71             // Grab all criteria and their associated criteria completions
72             $sql = '
73                 SELECT DISTINCT
74                     c.id AS course,
75                     cr.id AS criteriaid,
76                     crc.userid AS userid,
77                     cr.criteriatype AS criteriatype,
78                     cc.timecompleted AS timecompleted
79                 FROM
80                     {course_completion_criteria} cr
81                 INNER JOIN
82                     {course} c
83                  ON cr.course = c.id
84                 INNER JOIN
85                     {course_completions} crc
86                  ON crc.course = c.id
87                 LEFT JOIN
88                     {course_completion_crit_compl} cc
89                  ON cc.criteriaid = cr.id
90                 AND crc.userid = cc.userid
91                 WHERE
92                     c.enablecompletion = 1
93                 AND crc.timecompleted IS NULL
94                 AND crc.reaggregate > 0
95                 AND crc.reaggregate < :timestarted
96                 ORDER BY
97                     course,
98                     userid
99             ';
100             $rs = $DB->get_recordset_sql($sql, array('timestarted' => $timestarted));
101             // Check if result is empty
102             if (!$rs->valid()) {
103                 $rs->close(); // Not going to iterate (but exit), close rs
104                 return;
105             }
106             $current_user = null;
107             $current_course = null;
108             $completions = array();
109             while (1) {
110                 // Grab records for current user/course
111                 foreach ($rs as $record) {
112                     // If we are still grabbing the same users completions
113                     if ($record->userid === $current_user && $record->course === $current_course) {
114                         $completions[$record->criteriaid] = $record;
115                     } else {
116                         break;
117                     }
118                 }
119                 // Aggregate
120                 if (!empty($completions)) {
121                     if (debugging()) {
122                         mtrace('Aggregating completions for user '.$current_user.' in course '.$current_course);
123                     }
124                     // Get course info object
125                     $info = new \completion_info((object)array('id' => $current_course));
126                     // Setup aggregation
127                     $overall = $info->get_aggregation_method();
128                     $activity = $info->get_aggregation_method(COMPLETION_CRITERIA_TYPE_ACTIVITY);
129                     $prerequisite = $info->get_aggregation_method(COMPLETION_CRITERIA_TYPE_COURSE);
130                     $role = $info->get_aggregation_method(COMPLETION_CRITERIA_TYPE_ROLE);
131                     $overall_status = null;
132                     $activity_status = null;
133                     $prerequisite_status = null;
134                     $role_status = null;
135                     // Get latest timecompleted
136                     $timecompleted = null;
137                     // Check each of the criteria
138                     foreach ($completions as $params) {
139                         $timecompleted = max($timecompleted, $params->timecompleted);
140                         $completion = new \completion_criteria_completion((array)$params, false);
141                         // Handle aggregation special cases
142                         if ($params->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) {
143                             completion_cron_aggregate($activity, $completion->is_complete(), $activity_status);
144                         } else if ($params->criteriatype == COMPLETION_CRITERIA_TYPE_COURSE) {
145                             completion_cron_aggregate($prerequisite, $completion->is_complete(), $prerequisite_status);
146                         } else if ($params->criteriatype == COMPLETION_CRITERIA_TYPE_ROLE) {
147                             completion_cron_aggregate($role, $completion->is_complete(), $role_status);
148                         } else {
149                             completion_cron_aggregate($overall, $completion->is_complete(), $overall_status);
150                         }
151                     }
152                     // Include role criteria aggregation in overall aggregation
153                     if ($role_status !== null) {
154                         completion_cron_aggregate($overall, $role_status, $overall_status);
155                     }
156                     // Include activity criteria aggregation in overall aggregation
157                     if ($activity_status !== null) {
158                         completion_cron_aggregate($overall, $activity_status, $overall_status);
159                     }
160                     // Include prerequisite criteria aggregation in overall aggregation
161                     if ($prerequisite_status !== null) {
162                         completion_cron_aggregate($overall, $prerequisite_status, $overall_status);
163                     }
164                     // If aggregation status is true, mark course complete for user
165                     if ($overall_status) {
166                         if (debugging()) {
167                             mtrace('Marking complete');
168                         }
169                         $ccompletion = new \completion_completion(array('course' => $params->course, 'userid' => $params->userid));
170                         $ccompletion->mark_complete($timecompleted);
171                     }
172                 }
173                 // If this is the end of the recordset, break the loop
174                 if (!$rs->valid()) {
175                     $rs->close();
176                     break;
177                 }
178                 // New/next user, update user details, reset completions
179                 $current_user = $record->userid;
180                 $current_course = $record->course;
181                 $completions = array();
182                 $completions[$record->criteriaid] = $record;
183             }
184             // Mark all users as aggregated
185             $sql = "
186                 UPDATE
187                     {course_completions}
188                 SET
189                     reaggregate = 0
190                 WHERE
191                     reaggregate < :timestarted
192                 AND reaggregate > 0
193             ";
194             $DB->execute($sql, array('timestarted' => $timestarted));
195         }
196     }