Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]

   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  namespace quiz_statistics\task;
  18  
  19  use core\dml\sql_join;
  20  use quiz_attempt;
  21  use quiz;
  22  use quiz_statistics_report;
  23  
  24  defined('MOODLE_INTERNAL') || die();
  25  
  26  require_once($CFG->dirroot . '/mod/quiz/locallib.php');
  27  require_once($CFG->dirroot . '/mod/quiz/report/statistics/statisticslib.php');
  28  require_once($CFG->dirroot . '/mod/quiz/report/reportlib.php');
  29  require_once($CFG->dirroot . '/mod/quiz/report/statistics/report.php');
  30  
  31  /**
  32   * Re-calculate question statistics.
  33   *
  34   * @package    quiz_statistics
  35   * @copyright  2022 Catalyst IT Australia Pty Ltd
  36   * @author     Nathan Nguyen <nathannguyen@catalyst-au.net>
  37   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  38   */
  39  class recalculate extends \core\task\scheduled_task {
  40      /** @var int the maximum length of time one instance of this task will run. */
  41      const TIME_LIMIT = 3600;
  42  
  43      public function get_name(): string {
  44          return get_string('recalculatetask', 'quiz_statistics');
  45      }
  46  
  47      public function execute(): void {
  48          global $DB;
  49          $stoptime = time() + self::TIME_LIMIT;
  50          $dateformat = get_string('strftimedatetimeshortaccurate', 'core_langconfig');
  51  
  52          // TODO: MDL-75197, add quizid in quiz_statistics so that it is simpler to find quizzes for stats calculation.
  53          // Only calculate stats for quizzes which have recently finished attempt.
  54          $latestattempts = $DB->get_records_sql("
  55                  SELECT q.id AS quizid,
  56                         q.name AS quizname,
  57                         q.grademethod AS quizgrademethod,
  58                         c.id AS courseid,
  59                         c.shortname AS courseshortname,
  60                         MAX(quiza.timefinish) AS mostrecentattempttime,
  61                         COUNT(1) AS numberofattempts
  62  
  63                    FROM {quiz_attempts} quiza
  64                    JOIN {quiz} q ON q.id = quiza.quiz
  65                    JOIN {course} c ON c.id = q.course
  66  
  67                   WHERE quiza.preview = 0
  68                     AND quiza.state = :quizstatefinished
  69  
  70                GROUP BY q.id, q.name, q.grademethod, c.id, c.shortname
  71                ORDER BY MAX(quiza.timefinish) DESC
  72              ", ["quizstatefinished" => quiz_attempt::FINISHED]);
  73  
  74          $anyexception = null;
  75          foreach ($latestattempts as $latestattempt) {
  76              if (time() >= $stoptime) {
  77                  mtrace("This task has been running for more than " .
  78                          format_time(self::TIME_LIMIT) . " so stopping this execution.");
  79                  break;
  80              }
  81  
  82              // Check if there is any existing question stats, and it has been calculated after latest quiz attempt.
  83              $qubaids = quiz_statistics_qubaids_condition($latestattempt->quizid,
  84                      new sql_join(), $latestattempt->quizgrademethod);
  85              $lateststatstime = $DB->get_field('quiz_statistics', 'COALESCE(MAX(timemodified), 0)',
  86                      ['hashcode' => $qubaids->get_hash_code()]);
  87  
  88              $quizinfo = "'$latestattempt->quizname' ($latestattempt->quizid) in course " .
  89                      "$latestattempt->courseshortname ($latestattempt->courseid) has most recent attempt finished at " .
  90                          userdate($latestattempt->mostrecentattempttime, $dateformat);
  91              if ($lateststatstime) {
  92                  $quizinfo .= " and statistics from " . userdate($lateststatstime, $dateformat);
  93              }
  94  
  95              if ($lateststatstime >= $latestattempt->mostrecentattempttime) {
  96                  mtrace("  " . $quizinfo . " so nothing to do.");
  97                  continue;
  98              }
  99  
 100              // OK, so we need to calculate for this quiz.
 101              mtrace("  " . $quizinfo . " so re-calculating statistics for $latestattempt->numberofattempts attempts, start time " .
 102                      userdate(time(), $dateformat) . " ...");
 103  
 104              try {
 105                  $quizobj = quiz::create($latestattempt->quizid);
 106                  $report = new quiz_statistics_report();
 107                  $report->clear_cached_data($qubaids);
 108                  $report->calculate_questions_stats_for_question_bank($quizobj->get_quizid());
 109                  mtrace("    Calculations completed at " . userdate(time(), $dateformat) . ".");
 110  
 111              } catch (\Throwable $e) {
 112                  // We don't want an exception from one quiz to stop processing of other quizzes.
 113                  mtrace_exception($e);
 114                  $anyexception = $e;
 115              }
 116          }
 117  
 118          if ($anyexception) {
 119              // If there was any error, ensure the task fails.
 120              throw $anyexception;
 121          }
 122      }
 123  }