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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body