Differences Between: [Versions 400 and 401] [Versions 400 and 402] [Versions 400 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 core_question_generator; 24 use Generator; 25 use quiz; 26 use quiz_attempt; 27 use question_engine; 28 use ReflectionMethod; 29 30 /** 31 * Tests for question statistics. 32 * 33 * @package core_question 34 * @copyright 2021 Catalyst IT Australia Pty Ltd 35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 36 * @covers \core_question\local\statistics\statistics_bulk_loader 37 */ 38 class statistics_bulk_loader_test extends advanced_testcase { 39 40 /** @var float Delta used when comparing statistics values out-of 1. */ 41 protected const DELTA = 0.00005; 42 43 /** @var float Delta used when comparing statistics values out-of 100. */ 44 protected const PERCENT_DELTA = 0.005; 45 46 /** 47 * Test quizzes that contain a specified question. 48 * 49 * @covers ::get_all_places_where_questions_were_attempted 50 */ 51 public function test_get_all_places_where_questions_were_attempted(): void { 52 $this->resetAfterTest(); 53 $this->setAdminUser(); 54 55 $rcm = new ReflectionMethod(statistics_bulk_loader::class, 'get_all_places_where_questions_were_attempted'); 56 $rcm->setAccessible(true); 57 58 // Create a course. 59 $course = $this->getDataGenerator()->create_course(); 60 61 // Create three quizzes. 62 $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); 63 $quiz1 = $quizgenerator->create_instance([ 64 'course' => $course->id, 65 'grade' => 100.0, 'sumgrades' => 2, 66 'layout' => '1,2,0' 67 ]); 68 $quiz1context = context_module::instance($quiz1->cmid); 69 70 $quiz2 = $quizgenerator->create_instance([ 71 'course' => $course->id, 72 'grade' => 100.0, 'sumgrades' => 2, 73 'layout' => '1,2,0' 74 ]); 75 $quiz2context = context_module::instance($quiz2->cmid); 76 77 $quiz3 = $quizgenerator->create_instance([ 78 'course' => $course->id, 79 'grade' => 100.0, 'sumgrades' => 2, 80 'layout' => '1,2,0' 81 ]); 82 $quiz3context = context_module::instance($quiz3->cmid); 83 84 // Create questions. 85 /** @var core_question_generator $questiongenerator */ 86 $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); 87 $cat = $questiongenerator->create_question_category(); 88 $question1 = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]); 89 $question2 = $questiongenerator->create_question('numerical', null, ['category' => $cat->id]); 90 91 // Add question 1 to quiz 1 and make an attempt. 92 quiz_add_quiz_question($question1->id, $quiz1); 93 // Quiz 1 attempt. 94 $this->submit_quiz($quiz1, [1 => ['answer' => 'frog']]); 95 96 // Add questions 1 and 2 to quiz 2. 97 quiz_add_quiz_question($question1->id, $quiz2); 98 quiz_add_quiz_question($question2->id, $quiz2); 99 $this->submit_quiz($quiz2, [1 => ['answer' => 'frog'], 2 => ['answer' => 10]]); 100 101 // Checking quizzes that use question 1. 102 $q1places = $rcm->invoke(null, [$question1->id]); 103 $this->assertCount(2, $q1places); 104 $this->assertEquals((object) ['component' => 'mod_quiz', 'contextid' => $quiz1context->id], $q1places[0]); 105 $this->assertEquals((object) ['component' => 'mod_quiz', 'contextid' => $quiz2context->id], $q1places[1]); 106 107 // Checking quizzes that contain question 2. 108 $q2places = $rcm->invoke(null, [$question2->id]); 109 $this->assertCount(1, $q2places); 110 $this->assertEquals((object) ['component' => 'mod_quiz', 'contextid' => $quiz2context->id], $q2places[0]); 111 112 // Add a random question to quiz3. 113 quiz_add_random_questions($quiz3, 0, $cat->id, 1, false); 114 $this->submit_quiz($quiz3, [1 => ['answer' => 'willbewrong']]); 115 116 // Quiz 3 will now be in one of these arrays. 117 $q1places = $rcm->invoke(null, [$question1->id]); 118 $q2places = $rcm->invoke(null, [$question2->id]); 119 if (count($q1places) == 3) { 120 $newplace = end($q1places); 121 } else { 122 $newplace = end($q2places); 123 } 124 $this->assertEquals((object) ['component' => 'mod_quiz', 'contextid' => $quiz3context->id], $newplace); 125 } 126 127 /** 128 * Create 2 quizzes. 129 * 130 * @return array return 2 quizzes 131 */ 132 private function prepare_quizzes(): array { 133 // Create a course. 134 $course = $this->getDataGenerator()->create_course(); 135 136 // Make 2 quizzes. 137 $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); 138 $layout = '1,2,0,3,4,0'; 139 $quiz1 = $quizgenerator->create_instance([ 140 'course' => $course->id, 141 'grade' => 100.0, 'sumgrades' => 2, 142 'layout' => $layout 143 ]); 144 145 $quiz2 = $quizgenerator->create_instance([ 146 'course' => $course->id, 147 'grade' => 100.0, 'sumgrades' => 2, 148 'layout' => $layout 149 ]); 150 151 /** @var core_question_generator $questiongenerator */ 152 $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); 153 $cat = $questiongenerator->create_question_category(); 154 155 $page = 1; 156 $questions = []; 157 foreach (explode(',', $layout) as $slot) { 158 if ($slot == 0) { 159 $page += 1; 160 continue; 161 } 162 163 $question = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]); 164 $questions[$slot] = $question; 165 quiz_add_quiz_question($question->id, $quiz1, $page); 166 quiz_add_quiz_question($question->id, $quiz2, $page); 167 } 168 169 return [$quiz1, $quiz2, $questions]; 170 } 171 172 /** 173 * Submit quiz answers 174 * 175 * @param object $quiz 176 * @param array $answers 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::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 * Generate quizzes and submit answers. 219 * 220 * @param array $quiz1attempts quiz 1 attempts 221 * @param array $quiz2attempts quiz 2 attempts 222 * 223 * @return array 224 */ 225 private function prepare_and_submit_quizzes(array $quiz1attempts, array $quiz2attempts): array { 226 list($quiz1, $quiz2, $questions) = $this->prepare_quizzes(); 227 // Submit attempts of quiz1. 228 foreach ($quiz1attempts as $attempt) { 229 $this->submit_quiz($quiz1, $attempt); 230 } 231 // Submit attempts of quiz2. 232 foreach ($quiz2attempts as $attempt) { 233 $this->submit_quiz($quiz2, $attempt); 234 } 235 236 // Calculate the statistics. 237 $this->expectOutputRegex('~.*Calculations completed.*~'); 238 $statisticstask = new \quiz_statistics\task\recalculate(); 239 $statisticstask->execute(); 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(statistics_bulk_loader::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(statistics_bulk_loader::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' => [1.0, 0.0, 0.0, 0.0], 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' => [1.0, 0.5, 0.0, 0.0], 287 'Expected average facilities' => [1.0, 0.25, 0.0, 0.0], 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' => [1.0, 0.6667, 0.3333, 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 $this->generate_attempt_answers([1, 1, 1, 0]), 300 $this->generate_attempt_answers([1, 1, 1, 1]), 301 ], 302 'Expected quiz 2 facilities' => [1.0, 0.75, 0.5, 0.25], 303 'Expected average facilities' => [1.0, 0.7083, 0.4167, 0.1250], 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 */ 318 public function test_load_question_facility( 319 array $quiz1attempts, 320 array $expectedquiz1facilities, 321 array $quiz2attempts, 322 array $expectedquiz2facilities, 323 array $expectedaveragefacilities) 324 : void { 325 $this->resetAfterTest(); 326 327 list($quiz1, $quiz2, $questions) = $this->prepare_and_submit_quizzes($quiz1attempts, $quiz2attempts); 328 329 // Quiz 1 facilities. 330 $stats = $this->load_quiz_statistics_for_place(context_module::instance($quiz1->cmid)); 331 $quiz1facility1 = $this->extract_item_value($stats, $questions[1]->id, 'facility'); 332 $quiz1facility2 = $this->extract_item_value($stats, $questions[2]->id, 'facility'); 333 $quiz1facility3 = $this->extract_item_value($stats, $questions[3]->id, 'facility'); 334 $quiz1facility4 = $this->extract_item_value($stats, $questions[4]->id, 'facility'); 335 336 $this->assertEqualsWithDelta($expectedquiz1facilities[0], $quiz1facility1, self::DELTA); 337 $this->assertEqualsWithDelta($expectedquiz1facilities[1], $quiz1facility2, self::DELTA); 338 $this->assertEqualsWithDelta($expectedquiz1facilities[2], $quiz1facility3, self::DELTA); 339 $this->assertEqualsWithDelta($expectedquiz1facilities[3], $quiz1facility4, self::DELTA); 340 341 // Quiz 2 facilities. 342 $stats = $this->load_quiz_statistics_for_place(context_module::instance($quiz2->cmid)); 343 $quiz2facility1 = $this->extract_item_value($stats, $questions[1]->id, 'facility'); 344 $quiz2facility2 = $this->extract_item_value($stats, $questions[2]->id, 'facility'); 345 $quiz2facility3 = $this->extract_item_value($stats, $questions[3]->id, 'facility'); 346 $quiz2facility4 = $this->extract_item_value($stats, $questions[4]->id, 'facility'); 347 348 $this->assertEqualsWithDelta($expectedquiz2facilities[0], $quiz2facility1, self::DELTA); 349 $this->assertEqualsWithDelta($expectedquiz2facilities[1], $quiz2facility2, self::DELTA); 350 $this->assertEqualsWithDelta($expectedquiz2facilities[2], $quiz2facility3, self::DELTA); 351 $this->assertEqualsWithDelta($expectedquiz2facilities[3], $quiz2facility4, self::DELTA); 352 353 // Average question facilities. 354 $stats = statistics_bulk_loader::load_aggregate_statistics( 355 [$questions[1]->id, $questions[2]->id, $questions[3]->id, $questions[4]->id], 356 ['facility'] 357 ); 358 359 $this->assertEqualsWithDelta($expectedaveragefacilities[0], 360 $stats[$questions[1]->id]['facility'], self::DELTA); 361 $this->assertEqualsWithDelta($expectedaveragefacilities[1], 362 $stats[$questions[2]->id]['facility'], self::DELTA); 363 $this->assertEqualsWithDelta($expectedaveragefacilities[2], 364 $stats[$questions[3]->id]['facility'], self::DELTA); 365 $this->assertEqualsWithDelta($expectedaveragefacilities[3], 366 $stats[$questions[4]->id]['facility'], self::DELTA); 367 } 368 369 /** 370 * Data provider for {@see test_load_question_discriminative_efficiency()}. 371 * @return Generator 372 */ 373 public function load_question_discriminative_efficiency_provider(): Generator { 374 yield 'Discriminative efficiency' => [ 375 'Quiz 1 attempts' => [ 376 $this->generate_attempt_answers([1, 0, 0, 0]), 377 $this->generate_attempt_answers([1, 1, 0, 0]), 378 $this->generate_attempt_answers([1, 0, 1, 0]), 379 $this->generate_attempt_answers([1, 1, 1, 1]), 380 ], 381 'Expected quiz 1 discriminative efficiency' => [null, 33.33, 33.33, 100.00], 382 'Quiz 2 attempts' => [ 383 $this->generate_attempt_answers([1, 1, 1, 1]), 384 $this->generate_attempt_answers([0, 0, 0, 0]), 385 $this->generate_attempt_answers([1, 0, 0, 1]), 386 $this->generate_attempt_answers([0, 1, 1, 0]), 387 ], 388 'Expected quiz 2 discriminative efficiency' => [50.00, 50.00, 50.00, 50.00], 389 'Expected average discriminative efficiency' => [50.00, 41.67, 41.67, 75.00], 390 ]; 391 } 392 393 /** 394 * Test discriminative efficiency 395 * 396 * @dataProvider load_question_discriminative_efficiency_provider 397 * 398 * @param array $quiz1attempts quiz 1 attempts 399 * @param array $expectedquiz1discriminativeefficiency expected quiz 1 discriminative efficiency 400 * @param array $quiz2attempts quiz 2 attempts 401 * @param array $expectedquiz2discriminativeefficiency expected quiz 2 discriminative efficiency 402 * @param array $expectedaveragediscriminativeefficiency expected average discriminative efficiency 403 */ 404 public function test_load_question_discriminative_efficiency( 405 array $quiz1attempts, 406 array $expectedquiz1discriminativeefficiency, 407 array $quiz2attempts, 408 array $expectedquiz2discriminativeefficiency, 409 array $expectedaveragediscriminativeefficiency 410 ): void { 411 $this->resetAfterTest(); 412 413 list($quiz1, $quiz2, $questions) = $this->prepare_and_submit_quizzes($quiz1attempts, $quiz2attempts); 414 415 // Quiz 1 discriminative efficiency. 416 $stats = $this->load_quiz_statistics_for_place(context_module::instance($quiz1->cmid)); 417 $discriminativeefficiency1 = $this->extract_item_value($stats, $questions[1]->id, 'discriminativeefficiency'); 418 $discriminativeefficiency2 = $this->extract_item_value($stats, $questions[2]->id, 'discriminativeefficiency'); 419 $discriminativeefficiency3 = $this->extract_item_value($stats, $questions[3]->id, 'discriminativeefficiency'); 420 $discriminativeefficiency4 = $this->extract_item_value($stats, $questions[4]->id, 'discriminativeefficiency'); 421 422 $this->assertEqualsWithDelta($expectedquiz1discriminativeefficiency[0], 423 $discriminativeefficiency1, self::PERCENT_DELTA); 424 $this->assertEqualsWithDelta($expectedquiz1discriminativeefficiency[1], 425 $discriminativeefficiency2, self::PERCENT_DELTA); 426 $this->assertEqualsWithDelta($expectedquiz1discriminativeefficiency[2], 427 $discriminativeefficiency3, self::PERCENT_DELTA); 428 $this->assertEqualsWithDelta($expectedquiz1discriminativeefficiency[3], 429 $discriminativeefficiency4, self::PERCENT_DELTA); 430 431 // Quiz 2 discriminative efficiency. 432 $stats = $this->load_quiz_statistics_for_place(context_module::instance($quiz2->cmid)); 433 $discriminativeefficiency1 = $this->extract_item_value($stats, $questions[1]->id, 'discriminativeefficiency'); 434 $discriminativeefficiency2 = $this->extract_item_value($stats, $questions[2]->id, 'discriminativeefficiency'); 435 $discriminativeefficiency3 = $this->extract_item_value($stats, $questions[3]->id, 'discriminativeefficiency'); 436 $discriminativeefficiency4 = $this->extract_item_value($stats, $questions[4]->id, 'discriminativeefficiency'); 437 438 $this->assertEqualsWithDelta($expectedquiz2discriminativeefficiency[0], 439 $discriminativeefficiency1, self::PERCENT_DELTA); 440 $this->assertEqualsWithDelta($expectedquiz2discriminativeefficiency[1], 441 $discriminativeefficiency2, self::PERCENT_DELTA); 442 $this->assertEqualsWithDelta($expectedquiz2discriminativeefficiency[2], 443 $discriminativeefficiency3, self::PERCENT_DELTA); 444 $this->assertEqualsWithDelta($expectedquiz2discriminativeefficiency[3], 445 $discriminativeefficiency4, self::PERCENT_DELTA); 446 447 // Average question discriminative efficiency. 448 $stats = statistics_bulk_loader::load_aggregate_statistics( 449 [$questions[1]->id, $questions[2]->id, $questions[3]->id, $questions[4]->id], 450 ['discriminativeefficiency'] 451 ); 452 453 $this->assertEqualsWithDelta($expectedaveragediscriminativeefficiency[0], 454 $stats[$questions[1]->id]['discriminativeefficiency'], self::PERCENT_DELTA); 455 $this->assertEqualsWithDelta($expectedaveragediscriminativeefficiency[1], 456 $stats[$questions[2]->id]['discriminativeefficiency'], self::PERCENT_DELTA); 457 $this->assertEqualsWithDelta($expectedaveragediscriminativeefficiency[2], 458 $stats[$questions[3]->id]['discriminativeefficiency'], self::PERCENT_DELTA); 459 $this->assertEqualsWithDelta($expectedaveragediscriminativeefficiency[3], 460 $stats[$questions[4]->id]['discriminativeefficiency'], self::PERCENT_DELTA); 461 } 462 463 /** 464 * Data provider for {@see test_load_question_discrimination_index()}. 465 * @return Generator 466 */ 467 public function load_question_discrimination_index_provider(): Generator { 468 yield 'Discrimination Index' => [ 469 'Quiz 1 attempts' => [ 470 $this->generate_attempt_answers([1, 0, 0, 0]), 471 $this->generate_attempt_answers([1, 1, 0, 0]), 472 $this->generate_attempt_answers([1, 0, 1, 0]), 473 $this->generate_attempt_answers([1, 1, 1, 1]), 474 ], 475 'Expected quiz 1 Discrimination Index' => [null, 30.15, 30.15, 81.65], 476 'Quiz 2 attempts' => [ 477 $this->generate_attempt_answers([1, 1, 1, 1]), 478 $this->generate_attempt_answers([0, 0, 0, 0]), 479 $this->generate_attempt_answers([1, 0, 0, 1]), 480 $this->generate_attempt_answers([0, 1, 1, 0]), 481 ], 482 'Expected quiz 2 discrimination Index' => [44.72, 44.72, 44.72, 44.72], 483 'Expected average discrimination Index' => [44.72, 37.44, 37.44, 63.19], 484 ]; 485 } 486 487 /** 488 * Test discrimination index 489 * 490 * @dataProvider load_question_discrimination_index_provider 491 * 492 * @param array $quiz1attempts quiz 1 attempts 493 * @param array $expectedquiz1discriminationindex expected quiz 1 discrimination index 494 * @param array $quiz2attempts quiz 2 attempts 495 * @param array $expectedquiz2discriminationindex expected quiz 2 discrimination index 496 * @param array $expectedaveragediscriminationindex expected average discrimination index 497 */ 498 public function test_load_question_discrimination_index( 499 array $quiz1attempts, 500 array $expectedquiz1discriminationindex, 501 array $quiz2attempts, 502 array $expectedquiz2discriminationindex, 503 array $expectedaveragediscriminationindex 504 ): void { 505 $this->resetAfterTest(); 506 507 list($quiz1, $quiz2, $questions) = $this->prepare_and_submit_quizzes($quiz1attempts, $quiz2attempts); 508 509 // Quiz 1 discrimination index. 510 $stats = $this->load_quiz_statistics_for_place(context_module::instance($quiz1->cmid)); 511 $discriminationindex1 = $this->extract_item_value($stats, $questions[1]->id, 'discriminationindex'); 512 $discriminationindex2 = $this->extract_item_value($stats, $questions[2]->id, 'discriminationindex'); 513 $discriminationindex3 = $this->extract_item_value($stats, $questions[3]->id, 'discriminationindex'); 514 $discriminationindex4 = $this->extract_item_value($stats, $questions[4]->id, 'discriminationindex'); 515 516 $this->assertEqualsWithDelta($expectedquiz1discriminationindex[0], 517 $discriminationindex1, self::PERCENT_DELTA); 518 $this->assertEqualsWithDelta($expectedquiz1discriminationindex[1], 519 $discriminationindex2, self::PERCENT_DELTA); 520 $this->assertEqualsWithDelta($expectedquiz1discriminationindex[2], 521 $discriminationindex3, self::PERCENT_DELTA); 522 $this->assertEqualsWithDelta($expectedquiz1discriminationindex[3], 523 $discriminationindex4, self::PERCENT_DELTA); 524 525 // Quiz 2 discrimination index. 526 $stats = $this->load_quiz_statistics_for_place(context_module::instance($quiz2->cmid)); 527 $discriminationindex1 = $this->extract_item_value($stats, $questions[1]->id, 'discriminationindex'); 528 $discriminationindex2 = $this->extract_item_value($stats, $questions[2]->id, 'discriminationindex'); 529 $discriminationindex3 = $this->extract_item_value($stats, $questions[3]->id, 'discriminationindex'); 530 $discriminationindex4 = $this->extract_item_value($stats, $questions[4]->id, 'discriminationindex'); 531 532 $this->assertEqualsWithDelta($expectedquiz2discriminationindex[0], 533 $discriminationindex1, self::PERCENT_DELTA); 534 $this->assertEqualsWithDelta($expectedquiz2discriminationindex[1], 535 $discriminationindex2, self::PERCENT_DELTA); 536 $this->assertEqualsWithDelta($expectedquiz2discriminationindex[2], 537 $discriminationindex3, self::PERCENT_DELTA); 538 $this->assertEqualsWithDelta($expectedquiz2discriminationindex[3], 539 $discriminationindex4, self::PERCENT_DELTA); 540 541 // Average question discrimination index. 542 $stats = statistics_bulk_loader::load_aggregate_statistics( 543 [$questions[1]->id, $questions[2]->id, $questions[3]->id, $questions[4]->id], 544 ['discriminationindex'] 545 ); 546 547 $this->assertEqualsWithDelta($expectedaveragediscriminationindex[0], 548 $stats[$questions[1]->id]['discriminationindex'], self::PERCENT_DELTA); 549 $this->assertEqualsWithDelta($expectedaveragediscriminationindex[1], 550 $stats[$questions[2]->id]['discriminationindex'], self::PERCENT_DELTA); 551 $this->assertEqualsWithDelta($expectedaveragediscriminationindex[2], 552 $stats[$questions[3]->id]['discriminationindex'], self::PERCENT_DELTA); 553 $this->assertEqualsWithDelta($expectedaveragediscriminationindex[3], 554 $stats[$questions[4]->id]['discriminationindex'], self::PERCENT_DELTA); 555 } 556 557 /** 558 * Test with question statistics disabled 559 */ 560 public function test_statistics_disabled(): void { 561 $this->resetAfterTest(); 562 563 // Prepare some quizzes and attempts. Exactly what is not important to this test. 564 $quiz1attempts = [$this->generate_attempt_answers([1, 0, 0, 0])]; 565 $quiz2attempts = [$this->generate_attempt_answers([1, 1, 1, 1])]; 566 [, , $questions] = $this->prepare_and_submit_quizzes($quiz1attempts, $quiz2attempts); 567 568 // Prepare some useful arrays. 569 $expectedstats = [ 570 $questions[1]->id => [], 571 $questions[2]->id => [], 572 $questions[3]->id => [], 573 $questions[4]->id => [], 574 ]; 575 $questionids = array_keys($expectedstats); 576 577 // Ask to load no statistics at all. 578 $stats = statistics_bulk_loader::load_aggregate_statistics($questionids, []); 579 580 // Verify we got the right thing. 581 $this->assertEquals($expectedstats, $stats); 582 } 583 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body