From 514b6f13d9bb2c44763800fe669baab2f88ebbbd Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Tue, 31 Jan 2023 15:51:56 +0000 Subject: [PATCH] MDL-77094 quiz_statistics: improve efficiency of the recalculate task quiz_settings::create() requires at least 3 DB queries, so the point is that we can avoid doing that until after we have worked out if we need to calculate statistics for this quiz. Also, we order the list of quizzes to consier, to process the ones with more recent attempts first. --- .../statistics/classes/task/recalculate.php | 67 ++++++++++--------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/mod/quiz/report/statistics/classes/task/recalculate.php b/mod/quiz/report/statistics/classes/task/recalculate.php index 4d59537f0e6..a8d9df68b86 100644 --- a/mod/quiz/report/statistics/classes/task/recalculate.php +++ b/mod/quiz/report/statistics/classes/task/recalculate.php @@ -16,6 +16,7 @@ namespace quiz_statistics\task; +use core\dml\sql_join; use quiz_attempt; use quiz; use quiz_statistics_report; @@ -50,59 +51,61 @@ class recalculate extends \core\task\scheduled_task { // TODO: MDL-75197, add quizid in quiz_statistics so that it is simpler to find quizzes for stats calculation. // Only calculate stats for quizzes which have recently finished attempt. - $sql = " - SELECT q.id AS quizid, - q.name AS quizname, - c.id AS courseid, - c.shortname AS courseshortname, - MAX(qa.timefinish) AS mostrecentattempttime, - COUNT(1) AS numberofattempts - FROM {quiz_attempts} qa - JOIN {quiz} q ON q.id = qa.quiz - JOIN {course} c ON c.id = q.course - WHERE qa.preview = 0 - AND qa.state = :quizstatefinished - GROUP BY q.id, q.name, c.id, c.shortname - "; + $latestattempts = $DB->get_records_sql(" + SELECT q.id AS quizid, + q.name AS quizname, + q.grademethod AS quizgrademethod, + c.id AS courseid, + c.shortname AS courseshortname, + MAX(quiza.timefinish) AS mostrecentattempttime, + COUNT(1) AS numberofattempts - $params = [ - "quizstatefinished" => quiz_attempt::FINISHED, - ]; + FROM {quiz_attempts} quiza + JOIN {quiz} q ON q.id = quiza.quiz + JOIN {course} c ON c.id = q.course - $latestattempts = $DB->get_records_sql($sql, $params); + WHERE quiza.preview = 0 + AND quiza.state = :quizstatefinished + + GROUP BY q.id, q.name, q.grademethod, c.id, c.shortname + ORDER BY MAX(quiza.timefinish) DESC + ", ["quizstatefinished" => quiz_attempt::FINISHED]); $anyexception = null; - foreach ($latestattempts as $attempt) { + foreach ($latestattempts as $latestattempt) { if (time() >= $stoptime) { mtrace("This task has been running for more than " . format_time(self::TIME_LIMIT) . " so stopping this execution."); break; } - mtrace(" Examining quiz '$attempt->quizname' ($attempt->quizid) in course " . - "$attempt->courseshortname ($attempt->courseid) with most recent attempt at " . - userdate($attempt->mostrecentattempttime, $dateformat) . "."); - $quizobj = quiz::create($attempt->quizid); - $quiz = $quizobj->get_quiz(); - // Hash code for question stats option in question bank. - $qubaids = quiz_statistics_qubaids_condition($quiz->id, new \core\dml\sql_join(), $quiz->grademethod); - // Check if there is any existing question stats, and it has been calculated after latest quiz attempt. + $qubaids = quiz_statistics_qubaids_condition($latestattempt->quizid, + new sql_join(), $latestattempt->quizgrademethod); $lateststatstime = $DB->get_field('quiz_statistics', 'COALESCE(MAX(timemodified), 0)', ['hashcode' => $qubaids->get_hash_code()]); - if ($lateststatstime >= $attempt->mostrecentattempttime) { - mtrace(" Statistics already calculated at " . userdate($lateststatstime, $dateformat) . - " so nothing to do for this quiz."); + $quizinfo = "'$latestattempt->quizname' ($latestattempt->quizid) in course " . + "$latestattempt->courseshortname ($latestattempt->courseid) has most recent attempt finished at " . + userdate($latestattempt->mostrecentattempttime, $dateformat); + if ($lateststatstime) { + $quizinfo .= " and statistics from " . userdate($lateststatstime, $dateformat); + } + + if ($lateststatstime >= $latestattempt->mostrecentattempttime) { + mtrace(" " . $quizinfo . " so nothing to do."); continue; } - mtrace(" Calculating statistics for $attempt->numberofattempts attempts, starting at " . + // OK, so we need to calculate for this quiz. + mtrace(" " . $quizinfo . " so re-calculating statistics for $latestattempt->numberofattempts attempts, start time " . userdate(time(), $dateformat) . " ..."); + try { + $quizobj = quiz::create($latestattempt->quizid); $report = new quiz_statistics_report(); $report->clear_cached_data($qubaids); - $report->calculate_questions_stats_for_question_bank($quiz->id); + $report->calculate_questions_stats_for_question_bank($quizobj->get_quizid()); mtrace(" Calculations completed at " . userdate(time(), $dateformat) . "."); } catch (\Throwable $e) {