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.
This commit is contained in:
Tim Hunt 2023-01-31 15:51:56 +00:00
parent 67bbf6c416
commit 514b6f13d9

View File

@ -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) {