AutorÃa | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>.namespace mod_quiz\task;use core\task\scheduled_task;use mod_quiz\quiz_settings;use question_engine;/*** Pre-create attempts for quizzes that have passed their threshold.** @package mod_quiz* @copyright 2024 onwards Catalyst IT EU {@link https://catalyst-eu.net}* @author Mark Johnson <mark.johnson@catalyst-eu.net>* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/class precreate_attempts extends scheduled_task {/*** Create new instance of the task.** @param int $maxruntime The number of seconds to allow the task to start processing new quizzes.*/public function __construct(/** @var int $maxruntime The number of seconds to allow the task to start processing new quizzes. */protected int $maxruntime = 600,) {}#[\Override]public function get_name(): string {return get_string('precreatetask', 'mod_quiz');}/*** Pre-create quiz attempts for configured quizzes.** Find all quizzes with timeopen where the current time is later* than timeopen-precreateperiod, the quiz has questions, but no attempts.** If the precreateperiod setting is unlocked, also filter by quizzes with precreateattempts enabled.** Find all the users enrolled on the course who can attempt the quiz and create an attempt* in the NOT_STARTED state.** This will run for $this->maxruntime seconds, then stop to avoid hogging the cron process. Remaining quizzes will be* processed on subsequent runs.** @return void*/public function execute(): void {global $DB;$starttime = time();$precreateperiod = (int)get_config('quiz', 'precreateperiod');$precreatedefault = (int)get_config('quiz', 'precreateattempts');if ($precreateperiod === 0) {mtrace('Pre-creation of quiz attempts is disabled. Nothing to do.');return;}$sql = "SELECT DISTINCT q.id, q.name, q.course, q.timeopenFROM {quiz} qJOIN {quiz_slots} qs ON q.id = qs.quizidLEFT JOIN {quiz_attempts} qa ON q.id = qa.quizWHERE qa.id IS NULLAND q.timeopen > :nowAND q.timeopen < :thresholdAND (q.precreateattempts = :precreateattemptsOR (1 = :precreatedefault AND q.precreateattempts IS NULL))ORDER BY q.timeopen ASC";$params = ['now' => $starttime,'threshold' => $starttime + $precreateperiod,'precreateattempts' => 1,'precreatedefault' => $precreatedefault,];$quizzes = $DB->get_records_sql($sql, $params);mtrace('Found ' . count($quizzes) . ' quizzes to create attempts for.');$quizcount = 0;foreach ($quizzes as $quiz) {$transaction = $DB->start_delegated_transaction();try {$quizstart = microtime(true);mtrace('Creating attempts for ' . $quiz->name);$attemptcount = self::precreate_attempts_for_quiz($quiz->id, $quiz->course);$quizend = microtime(true);$quizduration = round($quizend - $quizstart, 2);mtrace('Created ' . $attemptcount . ' attempts for ' . $quiz->name . ' in ' . $quizduration . ' seconds');$quizcount++;$transaction->allow_commit();} catch (\Throwable $e) {mtrace('Failed to create attempts for ' . $quiz->name);$transaction->rollback($e);}if (microtime(true) - $starttime > $this->maxruntime) {// Stop to let other tasks run, then do some more next run.mtrace('Time limit reached.');break;}}mtrace('Created attempts for ' . $quizcount . ' quizzes.');}/*** Pre-create attempts for a quiz.** @param int $quizid* @param int $courseid* @return int The number of attempts created.*/public static function precreate_attempts_for_quiz(int $quizid, int $courseid): int {global $DB;$coursecontext = \context_course::instance($courseid);$users = get_enrolled_users($coursecontext, 'mod/quiz:attempt');$attemptcount = 0;$timenow = time();foreach ($users as $user) {if ($DB->record_exists('quiz_attempts', ['userid' => $user->id, 'quiz' => $quizid])) {// Last-minute safety check in case the quiz opened and the user started an attempt since the task started.continue;}$quizobj = quiz_settings::create($quizid, $user->id);$quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());$quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);$attempt = quiz_create_attempt($quizobj, 1, false, $timenow, false, $user->id);quiz_start_new_attempt($quizobj,$quba,$attempt,1,$timenow,);quiz_attempt_save_not_started($quba, $attempt);$attemptcount++;}return $attemptcount;}}