mirror of
https://github.com/moodle/moodle.git
synced 2025-08-04 16:36:37 +02:00
Merge branch 'MDL-76649' of https://github.com/timhunt/moodle
This commit is contained in:
commit
e0d857f39f
4 changed files with 73 additions and 20 deletions
|
@ -9307,6 +9307,25 @@ function mtrace($string, $eol="\n", $sleep=0) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to {@see mtrace()} an exception or throwable, including all relevant information.
|
||||||
|
*
|
||||||
|
* @param Throwable $e the error to ouptput.
|
||||||
|
*/
|
||||||
|
function mtrace_exception(Throwable $e): void {
|
||||||
|
$info = get_exception_info($e);
|
||||||
|
|
||||||
|
$message = $info->message;
|
||||||
|
if ($info->debuginfo) {
|
||||||
|
$message .= "\n\n" . $info->debuginfo;
|
||||||
|
}
|
||||||
|
if ($info->backtrace) {
|
||||||
|
$message .= "\n\n" . format_backtrace($info->backtrace, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
mtrace($message);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace 1 or more slashes or backslashes to 1 slash
|
* Replace 1 or more slashes or backslashes to 1 slash
|
||||||
*
|
*
|
||||||
|
|
|
@ -482,11 +482,12 @@ function is_early_init($backtrace) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns detailed information about specified exception.
|
* Returns detailed information about specified exception.
|
||||||
* @param exception $ex
|
*
|
||||||
* @return object
|
* @param Throwable $ex any sort of exception or throwable.
|
||||||
|
* @return stdClass standardised info to display. Fields are clear if you look at the end of this function.
|
||||||
*/
|
*/
|
||||||
function get_exception_info($ex) {
|
function get_exception_info($ex): stdClass {
|
||||||
global $CFG, $DB, $SESSION;
|
global $CFG;
|
||||||
|
|
||||||
if ($ex instanceof moodle_exception) {
|
if ($ex instanceof moodle_exception) {
|
||||||
$errorcode = $ex->errorcode;
|
$errorcode = $ex->errorcode;
|
||||||
|
|
|
@ -24,6 +24,7 @@ information provided here is intended especially for developers.
|
||||||
* In outputcomponents.php, initials_bar() can now be rendered in a smaller (mini) way. This provides purely the
|
* In outputcomponents.php, initials_bar() can now be rendered in a smaller (mini) way. This provides purely the
|
||||||
initials bar without the bootstrapping and form handling on each initials bar. If you use this mini render,
|
initials bar without the bootstrapping and form handling on each initials bar. If you use this mini render,
|
||||||
you'll need to implement your own form handling. Example usage can be found within the grader report.
|
you'll need to implement your own form handling. Example usage can be found within the grader report.
|
||||||
|
* There is a new helper function mtrace_exception to help with reporting exceptions you have caught in scheduled tasks.
|
||||||
|
|
||||||
=== 4.1 ===
|
=== 4.1 ===
|
||||||
|
|
||||||
|
|
|
@ -36,21 +36,33 @@ require_once($CFG->dirroot . '/mod/quiz/report/statistics/report.php');
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
*/
|
*/
|
||||||
class recalculate extends \core\task\scheduled_task {
|
class recalculate extends \core\task\scheduled_task {
|
||||||
|
/** @var int the maximum length of time one instance of this task will run. */
|
||||||
|
const TIME_LIMIT = 3600;
|
||||||
|
|
||||||
public function get_name() {
|
public function get_name(): string {
|
||||||
return get_string('recalculatetask', 'quiz_statistics');
|
return get_string('recalculatetask', 'quiz_statistics');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute() {
|
public function execute(): void {
|
||||||
global $DB;
|
global $DB;
|
||||||
|
$stoptime = time() + self::TIME_LIMIT;
|
||||||
|
$dateformat = get_string('strftimedatetimeshortaccurate', 'core_langconfig');
|
||||||
|
|
||||||
// TODO: MDL-75197, add quizid in quiz_statistics so that it is simpler to find quizzes for stats calculation.
|
// 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.
|
// Only calculate stats for quizzes which have recently finished attempt.
|
||||||
$sql = "
|
$sql = "
|
||||||
SELECT qa.quiz, MAX(qa.timefinish) as timefinish
|
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
|
FROM {quiz_attempts} qa
|
||||||
|
JOIN {quiz} q ON q.id = qa.quiz
|
||||||
|
JOIN {course} c ON c.id = q.course
|
||||||
WHERE qa.preview = 0
|
WHERE qa.preview = 0
|
||||||
AND qa.state = :quizstatefinished
|
AND qa.state = :quizstatefinished
|
||||||
GROUP BY qa.quiz
|
GROUP BY q.id, q.name, c.id, c.shortname
|
||||||
";
|
";
|
||||||
|
|
||||||
$params = [
|
$params = [
|
||||||
|
@ -59,30 +71,50 @@ class recalculate extends \core\task\scheduled_task {
|
||||||
|
|
||||||
$latestattempts = $DB->get_records_sql($sql, $params);
|
$latestattempts = $DB->get_records_sql($sql, $params);
|
||||||
|
|
||||||
|
$anyexception = null;
|
||||||
foreach ($latestattempts as $attempt) {
|
foreach ($latestattempts as $attempt) {
|
||||||
|
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_settings::create($attempt->quiz);
|
$quizobj = quiz_settings::create($attempt->quiz);
|
||||||
$quiz = $quizobj->get_quiz();
|
$quiz = $quizobj->get_quiz();
|
||||||
// Hash code for question stats option in question bank.
|
// Hash code for question stats option in question bank.
|
||||||
$qubaids = quiz_statistics_qubaids_condition($quiz->id, new \core\dml\sql_join(), $quiz->grademethod);
|
$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.
|
// Check if there is any existing question stats, and it has been calculated after latest quiz attempt.
|
||||||
$records = $DB->get_records_select(
|
$lateststatstime = $DB->get_field('quiz_statistics', 'COALESCE(MAX(timemodified), 0)',
|
||||||
'quiz_statistics',
|
['hashcode' => $qubaids->get_hash_code()]);
|
||||||
'hashcode = :hashcode AND timemodified > :timefinish',
|
|
||||||
[
|
|
||||||
'hashcode' => $qubaids->get_hash_code(),
|
|
||||||
'timefinish' => $attempt->timefinish
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (empty($records)) {
|
if ($lateststatstime >= $attempt->mostrecentattempttime) {
|
||||||
|
mtrace(" Statistics already calculated at " . userdate($lateststatstime, $dateformat) .
|
||||||
|
" so nothing to do for this quiz.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
mtrace(" Calculating statistics for $attempt->numberofattempts attempts, starting at " .
|
||||||
|
userdate(time(), $dateformat) . " ...");
|
||||||
|
try {
|
||||||
$report = new quiz_statistics_report();
|
$report = new quiz_statistics_report();
|
||||||
// Clear old cache.
|
|
||||||
$report->clear_cached_data($qubaids);
|
$report->clear_cached_data($qubaids);
|
||||||
// Calculate new stats.
|
|
||||||
$report->calculate_questions_stats_for_question_bank($quiz->id);
|
$report->calculate_questions_stats_for_question_bank($quiz->id);
|
||||||
|
mtrace(" Calculations completed at " . userdate(time(), $dateformat) . ".");
|
||||||
|
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
// We don't want an exception from one quiz to stop processing of other quizzes.
|
||||||
|
mtrace_exception($e);
|
||||||
|
$anyexception = $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
|
if ($anyexception) {
|
||||||
|
// If there was any error, ensure the task fails.
|
||||||
|
throw $anyexception;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue