Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 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 /** 18 * Update Overdue Attempts Task 19 * 20 * @package mod_quiz 21 * @copyright 2017 Michael Hughes 22 * @author Michael Hughes 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 namespace mod_quiz\task; 26 27 use mod_quiz\quiz_attempt; 28 use moodle_exception; 29 use moodle_recordset; 30 31 defined('MOODLE_INTERNAL') || die(); 32 33 global $CFG; 34 require_once($CFG->dirroot . '/mod/quiz/locallib.php'); 35 36 /** 37 * Update Overdue Attempts Task 38 * 39 * @package mod_quiz 40 * @copyright 2017 Michael Hughes 41 * @author Michael Hughes 42 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 43 * 44 */ 45 class update_overdue_attempts extends \core\task\scheduled_task { 46 47 public function get_name(): string { 48 return get_string('updateoverdueattemptstask', 'mod_quiz'); 49 } 50 51 /** 52 * Close off any overdue attempts. 53 */ 54 public function execute() { 55 $timenow = time(); 56 $processto = $timenow - get_config('quiz', 'graceperiodmin'); 57 58 mtrace(' Looking for quiz overdue quiz attempts...'); 59 60 list($count, $quizcount) = $this->update_all_overdue_attempts($timenow, $processto); 61 62 mtrace(' Considered ' . $count . ' attempts in ' . $quizcount . ' quizzes.'); 63 } 64 65 /** 66 * Do the processing required. 67 * 68 * @param int $timenow the time to consider as 'now' during the processing. 69 * @param int $processto only process attempt with timecheckstate longer ago than this. 70 * @return array with two elements, the number of attempt considered, and how many different quizzes that was. 71 */ 72 public function update_all_overdue_attempts(int $timenow, int $processto): array { 73 global $DB; 74 75 $attemptstoprocess = $this->get_list_of_overdue_attempts($processto); 76 77 $course = null; 78 $quiz = null; 79 $cm = null; 80 81 $count = 0; 82 $quizcount = 0; 83 foreach ($attemptstoprocess as $attempt) { 84 try { 85 86 // If we have moved on to a different quiz, fetch the new data. 87 if (!$quiz || $attempt->quiz != $quiz->id) { 88 $quiz = $DB->get_record('quiz', ['id' => $attempt->quiz], '*', MUST_EXIST); 89 $cm = get_coursemodule_from_instance('quiz', $attempt->quiz); 90 $quizcount += 1; 91 } 92 93 // If we have moved on to a different course, fetch the new data. 94 if (!$course || $course->id != $quiz->course) { 95 $course = get_course($quiz->course); 96 } 97 98 // Make a specialised version of the quiz settings, with the relevant overrides. 99 $quizforuser = clone($quiz); 100 $quizforuser->timeclose = $attempt->usertimeclose; 101 $quizforuser->timelimit = $attempt->usertimelimit; 102 103 // Trigger any transitions that are required. 104 $attemptobj = new quiz_attempt($attempt, $quizforuser, $cm, $course); 105 $attemptobj->handle_if_time_expired($timenow, false); 106 $count += 1; 107 108 } catch (moodle_exception $e) { 109 // If an error occurs while processing one attempt, don't let that kill cron. 110 mtrace("Error while processing attempt $attempt->id at $attempt->quiz quiz:"); 111 mtrace($e->getMessage()); 112 mtrace($e->getTraceAsString()); 113 // Close down any currently open transactions, otherwise one error 114 // will stop following DB changes from being committed. 115 $DB->force_transaction_rollback(); 116 } 117 } 118 119 $attemptstoprocess->close(); 120 return [$count, $quizcount]; 121 } 122 123 /** 124 * Get a recordset of all the attempts that need to be processed now. 125 * 126 * (Only public to allow unit testing. Do not use!) 127 * 128 * @param int $processto timestamp to process up to. 129 * @return moodle_recordset of quiz_attempts that need to be processed because time has 130 * passed, sorted by courseid then quizid. 131 */ 132 public function get_list_of_overdue_attempts(int $processto): moodle_recordset { 133 global $DB; 134 135 // SQL to compute timeclose and timelimit for each attempt. 136 $quizausersql = quiz_get_attempt_usertime_sql( 137 "iquiza.state IN ('inprogress', 'overdue') AND iquiza.timecheckstate <= :iprocessto"); 138 139 // This query should have all the quiz_attempts columns. 140 return $DB->get_recordset_sql(" 141 SELECT quiza.*, 142 quizauser.usertimeclose, 143 quizauser.usertimelimit 144 145 FROM {quiz_attempts} quiza 146 JOIN {quiz} quiz ON quiz.id = quiza.quiz 147 JOIN ( $quizausersql ) quizauser ON quizauser.id = quiza.id 148 149 WHERE quiza.state IN ('inprogress', 'overdue') 150 AND quiza.timecheckstate <= :processto 151 ORDER BY quiz.course, quiza.quiz", 152 153 ['processto' => $processto, 'iprocessto' => $processto]); 154 } 155 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body