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