[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/completion/ -> cron.php (source)

   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   * Code used by scheduled tasks for reviewing and aggregating course completion criteria.
  19   *
  20   * @package core_completion
  21   * @category completion
  22   * @copyright 2009 Catalyst IT Ltd
  23   * @author Aaron Barnes <aaronb@catalyst.net.nz>
  24   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25   */
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  require_once($CFG->libdir.'/completionlib.php');
  29  
  30  /**
  31   * Mark users as started if the config option is set
  32   *
  33   * @return void
  34   */
  35  function completion_cron_mark_started() {
  36      global $CFG, $DB;
  37  
  38      if (debugging()) {
  39          mtrace('Marking users as started');
  40      }
  41  
  42      if (!empty($CFG->gradebookroles)) {
  43          $roles = ' AND ra.roleid IN ('.$CFG->gradebookroles.')';
  44      } else {
  45          // This causes it to default to everyone (if there is no student role)
  46          $roles = '';
  47      }
  48  
  49      /**
  50       * A quick explaination of this horrible looking query
  51       *
  52       * It's purpose is to locate all the active participants
  53       * of a course with course completion enabled.
  54       *
  55       * We also only want the users with no course_completions
  56       * record as this functions job is to create the missing
  57       * ones :)
  58       *
  59       * We want to record the user's enrolment start time for the
  60       * course. This gets tricky because there can be multiple
  61       * enrolment plugins active in a course, hence the possibility
  62       * of multiple records for each couse/user in the results
  63       */
  64      $sql = "
  65          SELECT
  66              c.id AS course,
  67              u.id AS userid,
  68              crc.id AS completionid,
  69              ue.timestart AS timeenrolled,
  70              ue.timecreated
  71          FROM
  72              {user} u
  73          INNER JOIN
  74              {user_enrolments} ue
  75           ON ue.userid = u.id
  76          INNER JOIN
  77              {enrol} e
  78           ON e.id = ue.enrolid
  79          INNER JOIN
  80              {course} c
  81           ON c.id = e.courseid
  82          INNER JOIN
  83              {role_assignments} ra
  84           ON ra.userid = u.id
  85          LEFT JOIN
  86              {course_completions} crc
  87           ON crc.course = c.id
  88          AND crc.userid = u.id
  89          WHERE
  90              c.enablecompletion = 1
  91          AND crc.timeenrolled IS NULL
  92          AND ue.status = 0
  93          AND e.status = 0
  94          AND u.deleted = 0
  95          AND ue.timestart < ?
  96          AND (ue.timeend > ? OR ue.timeend = 0)
  97              $roles
  98          ORDER BY
  99              course,
 100              userid
 101      ";
 102  
 103      $now = time();
 104      $rs = $DB->get_recordset_sql($sql, array($now, $now, $now, $now));
 105  
 106      // Check if result is empty
 107      if (!$rs->valid()) {
 108          $rs->close(); // Not going to iterate (but exit), close rs
 109          return;
 110      }
 111  
 112      /**
 113       * An explaination of the following loop
 114       *
 115       * We are essentially doing a group by in the code here (as I can't find
 116       * a decent way of doing it in the sql).
 117       *
 118       * Since there can be multiple enrolment plugins for each course, we can have
 119       * multiple rows for each particpant in the query result. This isn't really
 120       * a problem until you combine it with the fact that the enrolment plugins
 121       * can save the enrol start time in either timestart or timeenrolled.
 122       *
 123       * The purpose of this loop is to find the earliest enrolment start time for
 124       * each participant in each course.
 125       */
 126      $prev = null;
 127      while ($rs->valid() || $prev) {
 128  
 129          $current = $rs->current();
 130  
 131          if (!isset($current->course)) {
 132              $current = false;
 133          }
 134          else {
 135              // Not all enrol plugins fill out timestart correctly, so use whichever
 136              // is non-zero
 137              $current->timeenrolled = max($current->timecreated, $current->timeenrolled);
 138          }
 139  
 140          // If we are at the last record,
 141          // or we aren't at the first and the record is for a diff user/course
 142          if ($prev &&
 143              (!$rs->valid() ||
 144              ($current->course != $prev->course || $current->userid != $prev->userid))) {
 145  
 146              $completion = new completion_completion();
 147              $completion->userid = $prev->userid;
 148              $completion->course = $prev->course;
 149              $completion->timeenrolled = (string) $prev->timeenrolled;
 150              $completion->timestarted = 0;
 151              $completion->reaggregate = time();
 152  
 153              if ($prev->completionid) {
 154                  $completion->id = $prev->completionid;
 155              }
 156  
 157              $completion->mark_enrolled();
 158  
 159              if (debugging()) {
 160                  mtrace('Marked started user '.$prev->userid.' in course '.$prev->course);
 161              }
 162          }
 163          // Else, if this record is for the same user/course
 164          elseif ($prev && $current) {
 165              // Use oldest timeenrolled
 166              $current->timeenrolled = min($current->timeenrolled, $prev->timeenrolled);
 167          }
 168  
 169          // Move current record to previous
 170          $prev = $current;
 171  
 172          // Move to next record
 173          $rs->next();
 174      }
 175  
 176      $rs->close();
 177  }
 178  
 179  /**
 180   * Run installed criteria's data aggregation methods
 181   *
 182   * Loop through each installed criteria and run the
 183   * cron() method if it exists
 184   *
 185   * @return void
 186   */
 187  function completion_cron_criteria() {
 188  
 189      // Process each criteria type
 190      global $CFG, $COMPLETION_CRITERIA_TYPES;
 191  
 192      foreach ($COMPLETION_CRITERIA_TYPES as $type) {
 193  
 194          $object = 'completion_criteria_'.$type;
 195          require_once $CFG->dirroot.'/completion/criteria/'.$object.'.php';
 196  
 197          $class = new $object();
 198  
 199          // Run the criteria type's cron method, if it has one
 200          if (method_exists($class, 'cron')) {
 201  
 202              if (debugging()) {
 203                  mtrace('Running '.$object.'->cron()');
 204              }
 205              $class->cron();
 206          }
 207      }
 208  }
 209  
 210  /**
 211   * Aggregate each user's criteria completions
 212   */
 213  function completion_cron_completions() {
 214      global $DB;
 215  
 216      if (debugging()) {
 217          mtrace('Aggregating completions');
 218      }
 219  
 220      // Save time started
 221      $timestarted = time();
 222  
 223      // Grab all criteria and their associated criteria completions
 224      $sql = '
 225          SELECT DISTINCT
 226              c.id AS course,
 227              cr.id AS criteriaid,
 228              crc.userid AS userid,
 229              cr.criteriatype AS criteriatype,
 230              cc.timecompleted AS timecompleted
 231          FROM
 232              {course_completion_criteria} cr
 233          INNER JOIN
 234              {course} c
 235           ON cr.course = c.id
 236          INNER JOIN
 237              {course_completions} crc
 238           ON crc.course = c.id
 239          LEFT JOIN
 240              {course_completion_crit_compl} cc
 241           ON cc.criteriaid = cr.id
 242          AND crc.userid = cc.userid
 243          WHERE
 244              c.enablecompletion = 1
 245          AND crc.timecompleted IS NULL
 246          AND crc.reaggregate > 0
 247          AND crc.reaggregate < :timestarted
 248          ORDER BY
 249              course,
 250              userid
 251      ';
 252  
 253      $rs = $DB->get_recordset_sql($sql, array('timestarted' => $timestarted));
 254  
 255      // Check if result is empty
 256      if (!$rs->valid()) {
 257          $rs->close(); // Not going to iterate (but exit), close rs
 258          return;
 259      }
 260  
 261      $current_user = null;
 262      $current_course = null;
 263      $completions = array();
 264  
 265      while (1) {
 266  
 267          // Grab records for current user/course
 268          foreach ($rs as $record) {
 269              // If we are still grabbing the same users completions
 270              if ($record->userid === $current_user && $record->course === $current_course) {
 271                  $completions[$record->criteriaid] = $record;
 272              } else {
 273                  break;
 274              }
 275          }
 276  
 277          // Aggregate
 278          if (!empty($completions)) {
 279  
 280              if (debugging()) {
 281                  mtrace('Aggregating completions for user '.$current_user.' in course '.$current_course);
 282              }
 283  
 284              // Get course info object
 285              $info = new completion_info((object)array('id' => $current_course));
 286  
 287              // Setup aggregation
 288              $overall = $info->get_aggregation_method();
 289              $activity = $info->get_aggregation_method(COMPLETION_CRITERIA_TYPE_ACTIVITY);
 290              $prerequisite = $info->get_aggregation_method(COMPLETION_CRITERIA_TYPE_COURSE);
 291              $role = $info->get_aggregation_method(COMPLETION_CRITERIA_TYPE_ROLE);
 292  
 293              $overall_status = null;
 294              $activity_status = null;
 295              $prerequisite_status = null;
 296              $role_status = null;
 297  
 298              // Get latest timecompleted
 299              $timecompleted = null;
 300  
 301              // Check each of the criteria
 302              foreach ($completions as $params) {
 303                  $timecompleted = max($timecompleted, $params->timecompleted);
 304  
 305                  $completion = new completion_criteria_completion((array)$params, false);
 306  
 307                  // Handle aggregation special cases
 308                  if ($params->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) {
 309                      completion_cron_aggregate($activity, $completion->is_complete(), $activity_status);
 310                  } else if ($params->criteriatype == COMPLETION_CRITERIA_TYPE_COURSE) {
 311                      completion_cron_aggregate($prerequisite, $completion->is_complete(), $prerequisite_status);
 312                  } else if ($params->criteriatype == COMPLETION_CRITERIA_TYPE_ROLE) {
 313                      completion_cron_aggregate($role, $completion->is_complete(), $role_status);
 314                  } else {
 315                      completion_cron_aggregate($overall, $completion->is_complete(), $overall_status);
 316                  }
 317              }
 318  
 319              // Include role criteria aggregation in overall aggregation
 320              if ($role_status !== null) {
 321                  completion_cron_aggregate($overall, $role_status, $overall_status);
 322              }
 323  
 324              // Include activity criteria aggregation in overall aggregation
 325              if ($activity_status !== null) {
 326                  completion_cron_aggregate($overall, $activity_status, $overall_status);
 327              }
 328  
 329              // Include prerequisite criteria aggregation in overall aggregation
 330              if ($prerequisite_status !== null) {
 331                  completion_cron_aggregate($overall, $prerequisite_status, $overall_status);
 332              }
 333  
 334              // If aggregation status is true, mark course complete for user
 335              if ($overall_status) {
 336                  if (debugging()) {
 337                      mtrace('Marking complete');
 338                  }
 339  
 340                  $ccompletion = new completion_completion(array('course' => $params->course, 'userid' => $params->userid));
 341                  $ccompletion->mark_complete($timecompleted);
 342              }
 343          }
 344  
 345          // If this is the end of the recordset, break the loop
 346          if (!$rs->valid()) {
 347              $rs->close();
 348              break;
 349          }
 350  
 351          // New/next user, update user details, reset completions
 352          $current_user = $record->userid;
 353          $current_course = $record->course;
 354          $completions = array();
 355          $completions[$record->criteriaid] = $record;
 356      }
 357  
 358      // Mark all users as aggregated
 359      $sql = "
 360          UPDATE
 361              {course_completions}
 362          SET
 363              reaggregate = 0
 364          WHERE
 365              reaggregate < :timestarted
 366          AND reaggregate > 0
 367      ";
 368  
 369      $DB->execute($sql, array('timestarted' => $timestarted));
 370  }
 371  
 372  /**
 373   * Aggregate criteria status's as per configured aggregation method
 374   *
 375   * @param int $method COMPLETION_AGGREGATION_* constant
 376   * @param bool $data Criteria completion status
 377   * @param bool|null $state Aggregation state
 378   */
 379  function completion_cron_aggregate($method, $data, &$state) {
 380      if ($method == COMPLETION_AGGREGATION_ALL) {
 381          if ($data && $state !== false) {
 382              $state = true;
 383          } else {
 384              $state = false;
 385          }
 386      } elseif ($method == COMPLETION_AGGREGATION_ANY) {
 387          if ($data) {
 388              $state = true;
 389          } else if (!$data && $state === null) {
 390              $state = false;
 391          }
 392      }
 393  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1