Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 400 and 403] [Versions 401 and 403] [Versions 402 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  namespace qbank_statistics;
  18  
  19  defined('MOODLE_INTERNAL') || die();
  20  
  21  use core_question\statistics\questions\all_calculated_for_qubaid_condition;
  22  use quiz_statistics\tests\statistics_helper;
  23  use mod_quiz\quiz_attempt;
  24  use mod_quiz\quiz_settings;
  25  use question_engine;
  26  
  27  global $CFG;
  28  require_once($CFG->dirroot . '/mod/quiz/tests/quiz_question_helper_test_trait.php');
  29  
  30  /**
  31   * Tests for question statistics.
  32   *
  33   * @package    qbank_statistics
  34   * @copyright  2021 Catalyst IT Australia Pty Ltd
  35   * @author     Nathan Nguyen <nathannguyen@catalyst-au.net>
  36   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  37   */
  38  class helper_test extends \advanced_testcase {
  39  
  40      use \quiz_question_helper_test_trait;
  41  
  42      /**
  43       * Test quizzes that contain a specified question.
  44       *
  45       * @covers ::get_all_places_where_questions_were_attempted
  46       * @deprecated since Moodle 4.3 please use the method from statistics_bulk_loader.
  47       * @todo MDL-78090 Final deprecation in Moodle 4.7
  48       */
  49      public function test_get_all_places_where_questions_were_attempted(): void {
  50          $this->resetAfterTest();
  51          $this->setAdminUser();
  52  
  53          $rcm = new \ReflectionMethod(helper::class, 'get_all_places_where_questions_were_attempted');
  54          $rcm->setAccessible(true);
  55  
  56          // Create a course.
  57          $course = $this->getDataGenerator()->create_course();
  58  
  59          // Create three quizzes.
  60          $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
  61          $quiz1 = $quizgenerator->create_instance([
  62              'course' => $course->id,
  63              'grade' => 100.0, 'sumgrades' => 2,
  64              'layout' => '1,2,0'
  65          ]);
  66          $quiz1context = \context_module::instance($quiz1->cmid);
  67  
  68          $quiz2 = $quizgenerator->create_instance([
  69              'course' => $course->id,
  70              'grade' => 100.0, 'sumgrades' => 2,
  71              'layout' => '1,2,0'
  72          ]);
  73          $quiz2context = \context_module::instance($quiz2->cmid);
  74  
  75          $quiz3 = $quizgenerator->create_instance([
  76              'course' => $course->id,
  77              'grade' => 100.0, 'sumgrades' => 2,
  78              'layout' => '1,2,0'
  79          ]);
  80          $quiz3context = \context_module::instance($quiz3->cmid);
  81  
  82          // Create questions.
  83          /** @var \core_question_generator $questiongenerator */
  84          $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
  85          $cat = $questiongenerator->create_question_category();
  86          $question1 = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id));
  87          $question2 = $questiongenerator->create_question('numerical', null, array('category' => $cat->id));
  88  
  89          // Add question 1 to quiz 1 and make an attempt.
  90          quiz_add_quiz_question($question1->id, $quiz1);
  91          // Quiz 1 attempt.
  92          $this->submit_quiz($quiz1, [1 => ['answer' => 'frog']]);
  93  
  94          // Add questions 1 and 2 to quiz 2.
  95          quiz_add_quiz_question($question1->id, $quiz2);
  96          quiz_add_quiz_question($question2->id, $quiz2);
  97          $this->submit_quiz($quiz2, [1 => ['answer' => 'frog'], 2 => ['answer' => 10]]);
  98  
  99          // Checking quizzes that use question 1.
 100          $q1places = $rcm->invoke(null, [$question1->id]);
 101          $this->assertCount(2, $q1places);
 102          $this->assertEquals((object) ['component' => 'mod_quiz', 'contextid' => $quiz1context->id], $q1places[0]);
 103          $this->assertEquals((object) ['component' => 'mod_quiz', 'contextid' => $quiz2context->id], $q1places[1]);
 104  
 105          // Checking quizzes that contain question 2.
 106          $q2places = $rcm->invoke(null, [$question2->id]);
 107          $this->assertCount(1, $q2places);
 108          $this->assertEquals((object) ['component' => 'mod_quiz', 'contextid' => $quiz2context->id], $q2places[0]);
 109  
 110          // Add a random question to quiz3.
 111          $this->add_random_questions($quiz3->id, 0, $cat->id, 1);
 112          $this->submit_quiz($quiz3, [1 => ['answer' => 'willbewrong']]);
 113  
 114          // Quiz 3 will now be in one of these arrays.
 115          $q1places = $rcm->invoke(null, [$question1->id]);
 116          $q2places = $rcm->invoke(null, [$question2->id]);
 117          if (count($q1places) == 3) {
 118              $newplace = end($q1places);
 119          } else {
 120              $newplace = end($q2places);
 121          }
 122          $this->assertEquals((object) ['component' => 'mod_quiz', 'contextid' => $quiz3context->id], $newplace);
 123      }
 124  
 125      /**
 126       * Create 2 quizzes.
 127       *
 128       * @return array return 2 quizzes
 129       * @throws \coding_exception
 130       */
 131      private function prepare_quizzes(): array {
 132          // Create a course.
 133          $course = $this->getDataGenerator()->create_course();
 134  
 135          // Make 2 quizzes.
 136          $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
 137          $layout = '1,2,0,3,4,0';
 138          $quiz1 = $quizgenerator->create_instance([
 139              'course' => $course->id,
 140              'grade' => 100.0, 'sumgrades' => 2,
 141              'layout' => $layout
 142          ]);
 143  
 144          $quiz2 = $quizgenerator->create_instance([
 145              'course' => $course->id,
 146              'grade' => 100.0, 'sumgrades' => 2,
 147              'layout' => $layout
 148          ]);
 149  
 150          /** @var \core_question_generator $questiongenerator */
 151          $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
 152          $cat = $questiongenerator->create_question_category();
 153  
 154          $page = 1;
 155          $questions = [];
 156          foreach (explode(',', $layout) as $slot) {
 157              if ($slot == 0) {
 158                  $page += 1;
 159                  continue;
 160              }
 161  
 162              $question = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
 163              $questions[$slot] = $question;
 164              quiz_add_quiz_question($question->id, $quiz1, $page);
 165              quiz_add_quiz_question($question->id, $quiz2, $page);
 166          }
 167  
 168          return [$quiz1, $quiz2, $questions];
 169      }
 170  
 171      /**
 172       * Submit quiz answers
 173       *
 174       * @param object $quiz
 175       * @param array $answers
 176       * @throws \moodle_exception
 177       */
 178      private function submit_quiz(object $quiz, array $answers): void {
 179          // Create user.
 180          $user = $this->getDataGenerator()->create_user();
 181          // Create attempt.
 182          $quizobj = quiz_settings::create($quiz->id, $user->id);
 183          $quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
 184          $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
 185          $timenow = time();
 186          $attempt = quiz_create_attempt($quizobj, 1, null, $timenow, false, $user->id);
 187          quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
 188          quiz_attempt_save_started($quizobj, $quba, $attempt);
 189          // Submit attempt.
 190          $attemptobj = quiz_attempt::create($attempt->id);
 191          $attemptobj->process_submitted_actions($timenow, false, $answers);
 192          $attemptobj->process_finish($timenow, false);
 193      }
 194  
 195      /**
 196       * Generate attempt answers.
 197       *
 198       * @param array $correctanswerflags array of 1 or 0
 199       * 1 : generate correct answer
 200       * 0 : generate wrong answer
 201       *
 202       * @return array
 203       */
 204      private function generate_attempt_answers(array $correctanswerflags): array {
 205          $attempt = [];
 206          for ($i = 1; $i <= 4; $i++) {
 207              if (isset($correctanswerflags) && $correctanswerflags[$i - 1] == 1) {
 208                  // Correct answer.
 209                  $attempt[$i] = ['answer' => 'frog'];
 210              } else {
 211                  $attempt[$i] = ['answer' => 'false'];
 212              }
 213          }
 214          return $attempt;
 215      }
 216  
 217      /**
 218       *
 219       * Generate quizzes and submit answers.
 220       *
 221       * @param array $quiz1attempts quiz 1 attempts
 222       * @param array $quiz2attempts quiz 2 attempts
 223       *
 224       * @return array
 225       */
 226      private function prepare_and_submit_quizzes(array $quiz1attempts, array $quiz2attempts): array {
 227          list($quiz1, $quiz2, $questions) = $this->prepare_quizzes();
 228          // Submit attempts of quiz1.
 229          foreach ($quiz1attempts as $attempt) {
 230              $this->submit_quiz($quiz1, $attempt);
 231          }
 232          // Submit attempts of quiz2.
 233          foreach ($quiz2attempts as $attempt) {
 234              $this->submit_quiz($quiz2, $attempt);
 235          }
 236  
 237          // Calculate the statistics.
 238          $this->expectOutputRegex('~.*Calculations completed.*~');
 239          statistics_helper::run_pending_recalculation_tasks();
 240  
 241          return [$quiz1, $quiz2, $questions];
 242      }
 243  
 244      /**
 245       * To use private helper::extract_item_value function.
 246       *
 247       * @param all_calculated_for_qubaid_condition $statistics the batch of statistics.
 248       * @param int $questionid a question id.
 249       * @param string $item one of the field names in all_calculated_for_qubaid_condition, e.g. 'facility'.
 250       * @return float|null the required value.
 251       */
 252      private function extract_item_value(all_calculated_for_qubaid_condition $statistics,
 253                                          int $questionid, string $item): ?float {
 254          $rcm = new \ReflectionMethod(helper::class, 'extract_item_value');
 255          $rcm->setAccessible(true);
 256          return $rcm->invoke(null, $statistics, $questionid, $item);
 257      }
 258  
 259      /**
 260       * To use private helper::load_statistics_for_place function (with mod_quiz component).
 261       *
 262       * @param \context $context the context to load the statistics for.
 263       * @return all_calculated_for_qubaid_condition|null question statistics.
 264       */
 265      private function load_quiz_statistics_for_place(\context $context): ?all_calculated_for_qubaid_condition {
 266          $rcm = new \ReflectionMethod(helper::class, 'load_statistics_for_place');
 267          $rcm->setAccessible(true);
 268          return $rcm->invoke(null, 'mod_quiz', $context);
 269      }
 270  
 271      /**
 272       * Data provider for {@see test_load_question_facility()}.
 273       *
 274       * @return \Generator
 275       */
 276      public function load_question_facility_provider(): \Generator {
 277          yield 'Facility case 1' => [
 278              'Quiz 1 attempts' => [
 279                  $this->generate_attempt_answers([1, 0, 0, 0]),
 280              ],
 281              'Expected quiz 1 facilities' => ['100.00%', '0.00%', '0.00%', '0.00%'],
 282              'Quiz 2 attempts' => [
 283                  $this->generate_attempt_answers([1, 0, 0, 0]),
 284                  $this->generate_attempt_answers([1, 1, 0, 0]),
 285              ],
 286              'Expected quiz 2 facilities' => ['100.00%', '50.00%', '0.00%', '0.00%'],
 287              'Expected average facilities' => ['100.00%', '25.00%', '0.00%', '0.00%'],
 288          ];
 289          yield 'Facility case 2' => [
 290              'Quiz 1 attempts' => [
 291                  $this->generate_attempt_answers([1, 0, 0, 0]),
 292                  $this->generate_attempt_answers([1, 1, 0, 0]),
 293                  $this->generate_attempt_answers([1, 1, 1, 0]),
 294              ],
 295              'Expected quiz 1 facilities' => ['100.00%', '66.67%', '33.33%', '0.00%'],
 296              'Quiz 2 attempts' => [
 297                  $this->generate_attempt_answers([1, 0, 0, 0]),
 298                  $this->generate_attempt_answers([1, 1, 0, 0]),
 299                  $this->generate_attempt_answers([1, 1, 1, 0]),
 300                  $this->generate_attempt_answers([1, 1, 1, 1]),
 301              ],
 302              'Expected quiz 2 facilities' => ['100.00%', '75.00%', '50.00%', '25.00%'],
 303              'Expected average facilities' => ['100.00%', '70.83%', '41.67%', '12.50%'],
 304          ];
 305      }
 306  
 307      /**
 308       * Test question facility
 309       *
 310       * @dataProvider load_question_facility_provider
 311       *
 312       * @param array $quiz1attempts quiz 1 attempts
 313       * @param array $expectedquiz1facilities expected quiz 1 facilities
 314       * @param array $quiz2attempts quiz 2 attempts
 315       * @param array $expectedquiz2facilities  expected quiz 2 facilities
 316       * @param array $expectedaveragefacilities expected average facilities
 317       * @deprecated since Moodle 4.3 please use the method from statistics_bulk_loader.
 318       * @todo MDL-78090 Final deprecation in Moodle 4.7
 319       */
 320      public function test_load_question_facility(
 321          array $quiz1attempts,
 322          array $expectedquiz1facilities,
 323          array $quiz2attempts,
 324          array $expectedquiz2facilities,
 325          array $expectedaveragefacilities)
 326      : void {
 327          $this->resetAfterTest();
 328  
 329          list($quiz1, $quiz2, $questions) = $this->prepare_and_submit_quizzes($quiz1attempts, $quiz2attempts);
 330  
 331          // Quiz 1 facilities.
 332          $stats = $this->load_quiz_statistics_for_place(\context_module::instance($quiz1->cmid));
 333          $quiz1facility1 = $this->extract_item_value($stats, $questions[1]->id, 'facility');
 334          $quiz1facility2 = $this->extract_item_value($stats, $questions[2]->id, 'facility');
 335          $quiz1facility3 = $this->extract_item_value($stats, $questions[3]->id, 'facility');
 336          $quiz1facility4 = $this->extract_item_value($stats, $questions[4]->id, 'facility');
 337  
 338          $this->assertEquals($expectedquiz1facilities[0], helper::format_percentage($quiz1facility1));
 339          $this->assertEquals($expectedquiz1facilities[1], helper::format_percentage($quiz1facility2));
 340          $this->assertEquals($expectedquiz1facilities[2], helper::format_percentage($quiz1facility3));
 341          $this->assertEquals($expectedquiz1facilities[3], helper::format_percentage($quiz1facility4));
 342  
 343          // Quiz 2 facilities.
 344          $stats = $this->load_quiz_statistics_for_place(\context_module::instance($quiz2->cmid));
 345          $quiz2facility1 = $this->extract_item_value($stats, $questions[1]->id, 'facility');
 346          $quiz2facility2 = $this->extract_item_value($stats, $questions[2]->id, 'facility');
 347          $quiz2facility3 = $this->extract_item_value($stats, $questions[3]->id, 'facility');
 348          $quiz2facility4 = $this->extract_item_value($stats, $questions[4]->id, 'facility');
 349  
 350          $this->assertEquals($expectedquiz2facilities[0], helper::format_percentage($quiz2facility1));
 351          $this->assertEquals($expectedquiz2facilities[1], helper::format_percentage($quiz2facility2));
 352          $this->assertEquals($expectedquiz2facilities[2], helper::format_percentage($quiz2facility3));
 353          $this->assertEquals($expectedquiz2facilities[3], helper::format_percentage($quiz2facility4));
 354  
 355          // Average question facilities.
 356          $averagefacility1 = helper::calculate_average_question_facility($questions[1]->id);
 357          $averagefacility2 = helper::calculate_average_question_facility($questions[2]->id);
 358          $averagefacility3 = helper::calculate_average_question_facility($questions[3]->id);
 359          $averagefacility4 = helper::calculate_average_question_facility($questions[4]->id);
 360  
 361          $this->assertEquals($expectedaveragefacilities[0], helper::format_percentage($averagefacility1));
 362          $this->assertEquals($expectedaveragefacilities[1], helper::format_percentage($averagefacility2));
 363          $this->assertEquals($expectedaveragefacilities[2], helper::format_percentage($averagefacility3));
 364          $this->assertEquals($expectedaveragefacilities[3], helper::format_percentage($averagefacility4));
 365  
 366          $this->assertDebuggingCalledCount(4,
 367              [
 368                  'Deprecated: please use statistics_bulk_loader instead, or get_required_statistics_fields ' .
 369                      'in your question bank column class.',
 370                  'Deprecated: please use statistics_bulk_loader instead, or get_required_statistics_fields ' .
 371                      'in your question bank column class.',
 372                  'Deprecated: please use statistics_bulk_loader instead, or get_required_statistics_fields ' .
 373                      'in your question bank column class.',
 374                  'Deprecated: please use statistics_bulk_loader instead, or get_required_statistics_fields ' .
 375                      'in your question bank column class.',
 376              ],
 377              [DEBUG_DEVELOPER, DEBUG_DEVELOPER, DEBUG_DEVELOPER, DEBUG_DEVELOPER]);
 378      }
 379  
 380      /**
 381       * Data provider for {@see test_load_question_discriminative_efficiency()}.
 382       * @return \Generator
 383       */
 384      public function load_question_discriminative_efficiency_provider(): \Generator {
 385          yield 'Discriminative efficiency' => [
 386              'Quiz 1 attempts' => [
 387                  $this->generate_attempt_answers([1, 0, 0, 0]),
 388                  $this->generate_attempt_answers([1, 1, 0, 0]),
 389                  $this->generate_attempt_answers([1, 0, 1, 0]),
 390                  $this->generate_attempt_answers([1, 1, 1, 1]),
 391              ],
 392              'Expected quiz 1 discriminative efficiency' => ['N/A', '33.33%', '33.33%', '100.00%'],
 393              'Quiz 2 attempts' => [
 394                  $this->generate_attempt_answers([1, 1, 1, 1]),
 395                  $this->generate_attempt_answers([0, 0, 0, 0]),
 396                  $this->generate_attempt_answers([1, 0, 0, 1]),
 397                  $this->generate_attempt_answers([0, 1, 1, 0]),
 398              ],
 399              'Expected quiz 2 discriminative efficiency' => ['50.00%', '50.00%', '50.00%', '50.00%'],
 400              'Expected average discriminative efficiency' => ['50.00%', '41.67%', '41.67%', '75.00%'],
 401          ];
 402      }
 403  
 404      /**
 405       * Test discriminative efficiency
 406       *
 407       * @dataProvider load_question_discriminative_efficiency_provider
 408       *
 409       * @param array $quiz1attempts quiz 1 attempts
 410       * @param array $expectedquiz1discriminativeefficiency expected quiz 1 discriminative efficiency
 411       * @param array $quiz2attempts quiz 2 attempts
 412       * @param array $expectedquiz2discriminativeefficiency expected quiz 2 discriminative efficiency
 413       * @param array $expectedaveragediscriminativeefficiency expected average discriminative efficiency
 414       * @deprecated since Moodle 4.3 please use the method from statistics_bulk_loader.
 415       * @todo MDL-78090 Final deprecation in Moodle 4.7
 416       */
 417      public function test_load_question_discriminative_efficiency(
 418          array $quiz1attempts,
 419          array $expectedquiz1discriminativeefficiency,
 420          array $quiz2attempts,
 421          array $expectedquiz2discriminativeefficiency,
 422          array $expectedaveragediscriminativeefficiency
 423      ): void {
 424          $this->resetAfterTest();
 425  
 426          list($quiz1, $quiz2, $questions) = $this->prepare_and_submit_quizzes($quiz1attempts, $quiz2attempts);
 427  
 428          // Quiz 1 discriminative efficiency.
 429          $stats = $this->load_quiz_statistics_for_place(\context_module::instance($quiz1->cmid));
 430          $discriminativeefficiency1 = $this->extract_item_value($stats, $questions[1]->id, 'discriminativeefficiency');
 431          $discriminativeefficiency2 = $this->extract_item_value($stats, $questions[2]->id, 'discriminativeefficiency');
 432          $discriminativeefficiency3 = $this->extract_item_value($stats, $questions[3]->id, 'discriminativeefficiency');
 433          $discriminativeefficiency4 = $this->extract_item_value($stats, $questions[4]->id, 'discriminativeefficiency');
 434  
 435          $this->assertEquals($expectedquiz1discriminativeefficiency[0],
 436              helper::format_percentage($discriminativeefficiency1, false),
 437              "Failure in quiz 1 - question 1 discriminative efficiency");
 438          $this->assertEquals($expectedquiz1discriminativeefficiency[1],
 439              helper::format_percentage($discriminativeefficiency2, false),
 440              "Failure in quiz 1 - question 2 discriminative efficiency");
 441          $this->assertEquals($expectedquiz1discriminativeefficiency[2],
 442              helper::format_percentage($discriminativeefficiency3, false),
 443              "Failure in quiz 1 - question 3 discriminative efficiency");
 444          $this->assertEquals($expectedquiz1discriminativeefficiency[3],
 445              helper::format_percentage($discriminativeefficiency4, false),
 446              "Failure in quiz 1 - question 4 discriminative efficiency");
 447  
 448          // Quiz 2 discriminative efficiency.
 449          $stats = $this->load_quiz_statistics_for_place(\context_module::instance($quiz2->cmid));
 450          $discriminativeefficiency1 = $this->extract_item_value($stats, $questions[1]->id, 'discriminativeefficiency');
 451          $discriminativeefficiency2 = $this->extract_item_value($stats, $questions[2]->id, 'discriminativeefficiency');
 452          $discriminativeefficiency3 = $this->extract_item_value($stats, $questions[3]->id, 'discriminativeefficiency');
 453          $discriminativeefficiency4 = $this->extract_item_value($stats, $questions[4]->id, 'discriminativeefficiency');
 454  
 455          $this->assertEquals($expectedquiz2discriminativeefficiency[0],
 456              helper::format_percentage($discriminativeefficiency1, false),
 457              "Failure in quiz 2 - question 1 discriminative efficiency");
 458          $this->assertEquals($expectedquiz2discriminativeefficiency[1],
 459              helper::format_percentage($discriminativeefficiency2, false),
 460              "Failure in quiz 2 - question 2 discriminative efficiency");
 461          $this->assertEquals($expectedquiz2discriminativeefficiency[2],
 462              helper::format_percentage($discriminativeefficiency3, false),
 463              "Failure in quiz 2 - question 3 discriminative efficiency");
 464          $this->assertEquals($expectedquiz2discriminativeefficiency[3],
 465              helper::format_percentage($discriminativeefficiency4, false),
 466              "Failure in quiz 2 - question 4 discriminative efficiency");
 467  
 468          // Average question discriminative efficiency.
 469          $avgdiscriminativeefficiency1 = helper::calculate_average_question_discriminative_efficiency($questions[1]->id);
 470          $avgdiscriminativeefficiency2 = helper::calculate_average_question_discriminative_efficiency($questions[2]->id);
 471          $avgdiscriminativeefficiency3 = helper::calculate_average_question_discriminative_efficiency($questions[3]->id);
 472          $avgdiscriminativeefficiency4 = helper::calculate_average_question_discriminative_efficiency($questions[4]->id);
 473  
 474          $this->assertEquals($expectedaveragediscriminativeefficiency[0],
 475              helper::format_percentage($avgdiscriminativeefficiency1, false),
 476              "Failure in question 1 average discriminative efficiency");
 477          $this->assertEquals($expectedaveragediscriminativeefficiency[1],
 478              helper::format_percentage($avgdiscriminativeefficiency2, false),
 479              "Failure in question 2 average discriminative efficiency");
 480          $this->assertEquals($expectedaveragediscriminativeefficiency[2],
 481              helper::format_percentage($avgdiscriminativeefficiency3, false),
 482              "Failure in question 3 average discriminative efficiency");
 483          $this->assertEquals($expectedaveragediscriminativeefficiency[3],
 484              helper::format_percentage($avgdiscriminativeefficiency4, false),
 485              "Failure in question 4 average discriminative efficiency");
 486  
 487          $this->assertDebuggingCalledCount(4,
 488              [
 489                  'Deprecated: please use statistics_bulk_loader instead, or get_required_statistics_fields ' .
 490                      'in your question bank column class.',
 491                  'Deprecated: please use statistics_bulk_loader instead, or get_required_statistics_fields ' .
 492                      'in your question bank column class.',
 493                  'Deprecated: please use statistics_bulk_loader instead, or get_required_statistics_fields ' .
 494                      'in your question bank column class.',
 495                  'Deprecated: please use statistics_bulk_loader instead, or get_required_statistics_fields ' .
 496                      'in your question bank column class.',
 497              ],
 498              [DEBUG_DEVELOPER, DEBUG_DEVELOPER, DEBUG_DEVELOPER, DEBUG_DEVELOPER]);
 499      }
 500  
 501      /**
 502       * Data provider for {@see test_load_question_discrimination_index()}.
 503       * @return \Generator
 504       */
 505      public function load_question_discrimination_index_provider(): \Generator {
 506          yield 'Discrimination Index' => [
 507              'Quiz 1 attempts' => [
 508                  $this->generate_attempt_answers([1, 0, 0, 0]),
 509                  $this->generate_attempt_answers([1, 1, 0, 0]),
 510                  $this->generate_attempt_answers([1, 0, 1, 0]),
 511                  $this->generate_attempt_answers([1, 1, 1, 1]),
 512              ],
 513              'Expected quiz 1 Discrimination Index' => ['N/A', '30.15%', '30.15%', '81.65%'],
 514              'Quiz 2 attempts' => [
 515                  $this->generate_attempt_answers([1, 1, 1, 1]),
 516                  $this->generate_attempt_answers([0, 0, 0, 0]),
 517                  $this->generate_attempt_answers([1, 0, 0, 1]),
 518                  $this->generate_attempt_answers([0, 1, 1, 0]),
 519              ],
 520              'Expected quiz 2 discrimination Index' => ['44.72%', '44.72%', '44.72%', '44.72%'],
 521              'Expected average discrimination Index' => ['44.72%', '37.44%', '37.44%', '63.19%'],
 522          ];
 523      }
 524  
 525      /**
 526       * Test discrimination index
 527       *
 528       * @dataProvider load_question_discrimination_index_provider
 529       *
 530       * @param array $quiz1attempts quiz 1 attempts
 531       * @param array $expectedquiz1discriminationindex expected quiz 1 discrimination index
 532       * @param array $quiz2attempts quiz 2 attempts
 533       * @param array $expectedquiz2discriminationindex expected quiz 2 discrimination index
 534       * @param array $expectedaveragediscriminationindex expected average discrimination index
 535       * @deprecated since Moodle 4.3 please use the method from statistics_bulk_loader.
 536       * @todo MDL-78090 Final deprecation in Moodle 4.7
 537       */
 538      public function test_load_question_discrimination_index(
 539          array $quiz1attempts,
 540          array $expectedquiz1discriminationindex,
 541          array $quiz2attempts,
 542          array $expectedquiz2discriminationindex,
 543          array $expectedaveragediscriminationindex
 544      ): void {
 545          $this->resetAfterTest();
 546  
 547          list($quiz1, $quiz2, $questions) = $this->prepare_and_submit_quizzes($quiz1attempts, $quiz2attempts);
 548  
 549          // Quiz 1 discrimination index.
 550          $stats = $this->load_quiz_statistics_for_place(\context_module::instance($quiz1->cmid));
 551          $discriminationindex1 = $this->extract_item_value($stats, $questions[1]->id, 'discriminationindex');
 552          $discriminationindex2 = $this->extract_item_value($stats, $questions[2]->id, 'discriminationindex');
 553          $discriminationindex3 = $this->extract_item_value($stats, $questions[3]->id, 'discriminationindex');
 554          $discriminationindex4 = $this->extract_item_value($stats, $questions[4]->id, 'discriminationindex');
 555  
 556          $this->assertEquals($expectedquiz1discriminationindex[0],
 557              helper::format_percentage($discriminationindex1, false),
 558              "Failure in quiz 1 - question 1 discrimination index");
 559          $this->assertEquals($expectedquiz1discriminationindex[1],
 560              helper::format_percentage($discriminationindex2, false),
 561              "Failure in quiz 1 - question 2 discrimination index");
 562          $this->assertEquals($expectedquiz1discriminationindex[2],
 563              helper::format_percentage($discriminationindex3, false),
 564              "Failure in quiz 1 - question 3 discrimination index");
 565          $this->assertEquals($expectedquiz1discriminationindex[3],
 566              helper::format_percentage($discriminationindex4, false),
 567              "Failure in quiz 1 - question 4 discrimination index");
 568  
 569          // Quiz 2 discrimination index.
 570          $stats = $this->load_quiz_statistics_for_place(\context_module::instance($quiz2->cmid));
 571          $discriminationindex1 = $this->extract_item_value($stats, $questions[1]->id, 'discriminationindex');
 572          $discriminationindex2 = $this->extract_item_value($stats, $questions[2]->id, 'discriminationindex');
 573          $discriminationindex3 = $this->extract_item_value($stats, $questions[3]->id, 'discriminationindex');
 574          $discriminationindex4 = $this->extract_item_value($stats, $questions[4]->id, 'discriminationindex');
 575  
 576          $this->assertEquals($expectedquiz2discriminationindex[0],
 577              helper::format_percentage($discriminationindex1, false),
 578              "Failure in quiz 2 - question 1 discrimination index");
 579          $this->assertEquals($expectedquiz2discriminationindex[1],
 580              helper::format_percentage($discriminationindex2, false),
 581              "Failure in quiz 2 - question 2 discrimination index");
 582          $this->assertEquals($expectedquiz2discriminationindex[2],
 583              helper::format_percentage($discriminationindex3, false),
 584              "Failure in quiz 2 - question 3 discrimination index");
 585          $this->assertEquals($expectedquiz2discriminationindex[3],
 586              helper::format_percentage($discriminationindex4, false),
 587              "Failure in quiz 2 - question 4 discrimination index");
 588  
 589          // Average question discrimination index.
 590          $avgdiscriminationindex1 = helper::calculate_average_question_discrimination_index($questions[1]->id);
 591          $avgdiscriminationindex2 = helper::calculate_average_question_discrimination_index($questions[2]->id);
 592          $avgdiscriminationindex3 = helper::calculate_average_question_discrimination_index($questions[3]->id);
 593          $avgdiscriminationindex4 = helper::calculate_average_question_discrimination_index($questions[4]->id);
 594  
 595          $this->assertEquals($expectedaveragediscriminationindex[0],
 596              helper::format_percentage($avgdiscriminationindex1, false),
 597              "Failure in question 1 average discrimination index");
 598          $this->assertEquals($expectedaveragediscriminationindex[1],
 599              helper::format_percentage($avgdiscriminationindex2, false),
 600              "Failure in question 2 average discrimination index");
 601          $this->assertEquals($expectedaveragediscriminationindex[2],
 602              helper::format_percentage($avgdiscriminationindex3, false),
 603              "Failure in question 3 average discrimination index");
 604          $this->assertEquals($expectedaveragediscriminationindex[3],
 605              helper::format_percentage($avgdiscriminationindex4, false),
 606              "Failure in question 4 average discrimination index");
 607  
 608          $this->assertDebuggingCalledCount(4,
 609              [
 610                  'Deprecated: please use statistics_bulk_loader instead, or get_required_statistics_fields ' .
 611                      'in your question bank column class.',
 612                  'Deprecated: please use statistics_bulk_loader instead, or get_required_statistics_fields ' .
 613                      'in your question bank column class.',
 614                  'Deprecated: please use statistics_bulk_loader instead, or get_required_statistics_fields ' .
 615                      'in your question bank column class.',
 616                  'Deprecated: please use statistics_bulk_loader instead, or get_required_statistics_fields ' .
 617                      'in your question bank column class.',
 618              ],
 619              [DEBUG_DEVELOPER, DEBUG_DEVELOPER, DEBUG_DEVELOPER, DEBUG_DEVELOPER]);
 620      }
 621  }