diff --git a/mod/quiz/classes/grade_calculator.php b/mod/quiz/classes/grade_calculator.php index 60f87febd9e..f0d74f7735a 100644 --- a/mod/quiz/classes/grade_calculator.php +++ b/mod/quiz/classes/grade_calculator.php @@ -91,6 +91,11 @@ class grade_calculator { // we will get a divide by zero error. self::update_quiz_maximum_grade(0); } + + $callbackclasses = \core_component::get_plugin_list_with_class('quiz', 'quiz_structure_modified'); + foreach ($callbackclasses as $callbackclass) { + component_class_callback($callbackclass, 'callback', [$quiz->id]); + } } /** diff --git a/mod/quiz/report/statistics/classes/quiz_structure_modified.php b/mod/quiz/report/statistics/classes/quiz_structure_modified.php new file mode 100644 index 00000000000..1cd8b52e8c9 --- /dev/null +++ b/mod/quiz/report/statistics/classes/quiz_structure_modified.php @@ -0,0 +1,53 @@ +. + +namespace quiz_statistics; + +use core\dml\sql_join; + +/** + * Clear the statistics cache when the quiz structure is modified. + * + * @package quiz_statistics + * @copyright 2023 onwards Catalyst IT EU {@link https://catalyst-eu.net} + * @author Mark Johnson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class quiz_structure_modified { + /** + * Clear the statistics cache. + * + * @param int $quizid The quiz to clear the cache for. + * @return void + */ + public static function callback(int $quizid): void { + global $DB, $CFG; + require_once($CFG->dirroot . '/mod/quiz/report/statistics/statisticslib.php'); + require_once($CFG->dirroot . '/mod/quiz/report/statistics/report.php'); + $quiz = $DB->get_record('quiz', ['id' => $quizid]); + if (!$quiz) { + throw new \coding_exception('Could not find quiz with ID ' . $quizid . '.'); + } + $qubaids = quiz_statistics_qubaids_condition( + $quiz->id, + new sql_join(), + $quiz->grademethod + ); + + $report = new \quiz_statistics_report(); + $report->clear_cached_data($qubaids); + } +} diff --git a/mod/quiz/report/statistics/tests/quiz_structure_modified_test.php b/mod/quiz/report/statistics/tests/quiz_structure_modified_test.php new file mode 100644 index 00000000000..055a28a742f --- /dev/null +++ b/mod/quiz/report/statistics/tests/quiz_structure_modified_test.php @@ -0,0 +1,88 @@ +. +namespace quiz_statistics; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/mod/quiz/tests/quiz_question_helper_test_trait.php'); + +use core\progress\none; +use mod_quiz\grade_calculator; +use mod_quiz\quiz_settings; + +/** + * Unit tests for quiz_statistics\event\observer\slots_updated + * + * @package quiz_statistics + * @copyright 2023 onwards Catalyst IT EU {@link https://catalyst-eu.net} + * @author Mark Johnson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \quiz_statistics\quiz_structure_modified + */ +class quiz_structure_modified_test extends \advanced_testcase { + use \quiz_question_helper_test_trait; + + /** + * Clear the statistics cache for a quiz when it structure is modified. + * + * When recompute_quiz_sumgrades() is called, it should trigger this plugin's quiz_structure_modified callback + * which clears the statistics cache for the quiz. + * + * @return void + */ + public function test_clear_cache_on_structure_modified(): void { + global $DB; + $this->resetAfterTest(true); + + // Create and attempt a quiz. + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $course = $generator->create_course(); + $quiz = $this->create_test_quiz($course); + $questiongenerator = $generator->get_plugin_generator('core_question'); + $category = $questiongenerator->create_question_category(); + $question = $questiongenerator->create_question('match', null, ['category' => $category->id]); + $questiongenerator->update_question($question); + quiz_add_quiz_question($question->id, $quiz); + [, , $attempt] = $this->attempt_quiz($quiz, $user); + + // Run the statistics calculation to prime the cache. + $report = new \quiz_statistics_report(); + $questions = $report->load_and_initialise_questions_for_calculations($quiz); + $report->get_all_stats_and_analysis( + $quiz, + $quiz->grademethod, + \question_attempt::ALL_TRIES, + new \core\dml\sql_join(), + $questions, + new none(), + ); + + $hashcode = quiz_statistics_qubaids_condition($quiz->id, new \core\dml\sql_join(), $quiz->grademethod)->get_hash_code(); + + $this->assertTrue($DB->record_exists('quiz_statistics', ['hashcode' => $hashcode])); + $this->assertTrue($DB->record_exists('question_statistics', ['hashcode' => $hashcode])); + $this->assertTrue($DB->record_exists('question_response_analysis', ['hashcode' => $hashcode])); + + // Recompute sumgrades, which triggers the quiz_structure_modified callback. + grade_calculator::create($attempt->get_quizobj())->recompute_quiz_sumgrades(); + + $this->assertFalse($DB->record_exists('quiz_statistics', ['hashcode' => $hashcode])); + $this->assertFalse($DB->record_exists('question_statistics', ['hashcode' => $hashcode])); + $this->assertFalse($DB->record_exists('question_response_analysis', ['hashcode' => $hashcode])); + } +} diff --git a/mod/quiz/upgrade.txt b/mod/quiz/upgrade.txt index 8fd9bf70ac3..ba58d8552ec 100644 --- a/mod/quiz/upgrade.txt +++ b/mod/quiz/upgrade.txt @@ -1,5 +1,10 @@ This files describes API changes in the quiz code. +=== 4.4 === +* A quiz_structure_modified callback has been added for quiz_ plugins, called from + grade_calculator::recompute_quiz_sumgrades(). Plugins can implement this by creating a `quiz_structure_modified` + class in their namespace with a static `callback` method, see quiz_statistics as an example. + === 4.3 === * The method get_questions() has a new parameter 'requirequestionfullyloaded' which can be used to instruct whether the