See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 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 18 namespace mod_h5pactivity\local; 19 use context_module; 20 use stdClass; 21 22 /** 23 * Manager tests class for mod_h5pactivity. 24 * 25 * @package mod_h5pactivity 26 * @covers \mod_h5pactivity\local\manager 27 * @category test 28 * @copyright 2020 Ferran Recio <ferran@moodle.com> 29 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 30 */ 31 class manager_test extends \advanced_testcase { 32 33 /** 34 * Test for static create methods. 35 */ 36 public function test_create() { 37 38 $this->resetAfterTest(); 39 $this->setAdminUser(); 40 41 $course = $this->getDataGenerator()->create_course(); 42 $activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]); 43 $cm = get_coursemodule_from_id('h5pactivity', $activity->cmid, 0, false, MUST_EXIST); 44 $context = context_module::instance($cm->id); 45 46 $manager = manager::create_from_instance($activity); 47 $manageractivity = $manager->get_instance(); 48 $this->assertEquals($activity->id, $manageractivity->id); 49 $managercm = $manager->get_coursemodule(); 50 $this->assertEquals($cm->id, $managercm->id); 51 $managercontext = $manager->get_context(); 52 $this->assertEquals($context->id, $managercontext->id); 53 54 $manager = manager::create_from_coursemodule($cm); 55 $manageractivity = $manager->get_instance(); 56 $this->assertEquals($activity->id, $manageractivity->id); 57 $managercm = $manager->get_coursemodule(); 58 $this->assertEquals($cm->id, $managercm->id); 59 $managercontext = $manager->get_context(); 60 $this->assertEquals($context->id, $managercontext->id); 61 } 62 63 /** 64 * Test for is_tracking_enabled. 65 * 66 * @dataProvider is_tracking_enabled_data 67 * @param bool $login if the user is logged in 68 * @param string $role user role in course 69 * @param int $enabletracking if tracking is enabled 70 * @param bool $expected expected result 71 */ 72 public function test_is_tracking_enabled(bool $login, string $role, int $enabletracking, bool $expected) { 73 74 $this->resetAfterTest(); 75 $this->setAdminUser(); 76 77 $course = $this->getDataGenerator()->create_course(); 78 $activity = $this->getDataGenerator()->create_module('h5pactivity', 79 ['course' => $course, 'enabletracking' => $enabletracking]); 80 81 $user = $this->getDataGenerator()->create_and_enrol($course, $role); 82 if ($login) { 83 $this->setUser($user); 84 $param = null; 85 } else { 86 $param = $user; 87 } 88 89 $manager = manager::create_from_instance($activity); 90 $this->assertEquals($expected, $manager->is_tracking_enabled($param)); 91 } 92 93 /** 94 * Data provider for is_tracking_enabled. 95 * 96 * @return array 97 */ 98 public function is_tracking_enabled_data(): array { 99 return [ 100 'Logged student, tracking enabled' => [ 101 true, 'student', 1, true 102 ], 103 'Logged student, tracking disabled' => [ 104 true, 'student', 0, false 105 ], 106 'Logged teacher, tracking enabled' => [ 107 true, 'editingteacher', 1, false 108 ], 109 'Logged teacher, tracking disabled' => [ 110 true, 'editingteacher', 0, false 111 ], 112 'No logged student, tracking enabled' => [ 113 true, 'student', 1, true 114 ], 115 'No logged student, tracking disabled' => [ 116 true, 'student', 0, false 117 ], 118 'No logged teacher, tracking enabled' => [ 119 true, 'editingteacher', 1, false 120 ], 121 'No logged teacher, tracking disabled' => [ 122 true, 'editingteacher', 0, false 123 ], 124 ]; 125 } 126 127 /** 128 * Test for get_users_scaled_score. 129 * 130 * @dataProvider get_users_scaled_score_data 131 * @param int $enabletracking if tracking is enabled 132 * @param int $gradingmethod new grading method 133 * @param array $result1 student 1 results (scaled, timemodified, attempt number) 134 * @param array $result2 student 2 results (scaled, timemodified, attempt number) 135 */ 136 public function test_get_users_scaled_score(int $enabletracking, int $gradingmethod, array $result1, array $result2) { 137 global $DB; 138 139 $this->resetAfterTest(); 140 $this->setAdminUser(); 141 142 $course = $this->getDataGenerator()->create_course(); 143 $activity = $this->getDataGenerator()->create_module('h5pactivity', 144 ['course' => $course, 'enabletracking' => $enabletracking, 'grademethod' => $gradingmethod]); 145 146 // Generate two users with 4 attempts each. 147 $user1 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 148 $this->generate_fake_attempts($activity, $user1, 1); 149 $user2 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 150 $this->generate_fake_attempts($activity, $user2, 2); 151 152 $manager = manager::create_from_instance($activity); 153 154 // Get all users scaled scores. 155 $scaleds = $manager->get_users_scaled_score(); 156 157 // No results will be returned if tracking is dsabled or manual grading method is defined. 158 if (empty($result1)) { 159 $this->assertNull($scaleds); 160 return; 161 } 162 163 $this->assertCount(2, $scaleds); 164 165 // Check expected user1 scaled score. 166 $scaled = $scaleds[$user1->id]; 167 $this->assertEquals($user1->id, $scaled->userid); 168 $this->assertEquals($result1[0], $scaled->scaled); 169 $this->assertEquals($result1[1], $scaled->timemodified); 170 if ($result1[2]) { 171 $attempt = $DB->get_record('h5pactivity_attempts', ['id' => $scaled->attemptid]); 172 $this->assertEquals($attempt->h5pactivityid, $activity->id); 173 $this->assertEquals($attempt->userid, $scaled->userid); 174 $this->assertEquals($attempt->scaled, round($scaled->scaled, 5)); 175 $this->assertEquals($attempt->timemodified, $scaled->timemodified); 176 $this->assertEquals($result1[2], $attempt->attempt); 177 } else { 178 $this->assertEquals(0, $scaled->attemptid); 179 } 180 181 // Check expected user2 scaled score. 182 $scaled = $scaleds[$user2->id]; 183 $this->assertEquals($user2->id, $scaled->userid); 184 $this->assertEquals($result2[0], round($scaled->scaled, 5)); 185 $this->assertEquals($result2[1], $scaled->timemodified); 186 if ($result2[2]) { 187 $attempt = $DB->get_record('h5pactivity_attempts', ['id' => $scaled->attemptid]); 188 $this->assertEquals($attempt->h5pactivityid, $activity->id); 189 $this->assertEquals($attempt->userid, $scaled->userid); 190 $this->assertEquals($attempt->scaled, $scaled->scaled); 191 $this->assertEquals($attempt->timemodified, $scaled->timemodified); 192 $this->assertEquals($result2[2], $attempt->attempt); 193 } else { 194 $this->assertEquals(0, $scaled->attemptid); 195 } 196 197 // Now check a single user record. 198 $scaleds = $manager->get_users_scaled_score($user2->id); 199 $this->assertCount(1, $scaleds); 200 $scaled2 = $scaleds[$user2->id]; 201 $this->assertEquals($scaled->userid, $scaled2->userid); 202 $this->assertEquals($scaled->scaled, $scaled2->scaled); 203 $this->assertEquals($scaled->attemptid, $scaled2->attemptid); 204 $this->assertEquals($scaled->timemodified, $scaled2->timemodified); 205 } 206 207 /** 208 * Data provider for get_users_scaled_score. 209 * 210 * @return array 211 */ 212 public function get_users_scaled_score_data(): array { 213 return [ 214 'Tracking with max attempt method' => [ 215 1, manager::GRADEHIGHESTATTEMPT, [1.00000, 31, 2], [0.66667, 32, 2] 216 ], 217 'Tracking with average attempt method' => [ 218 1, manager::GRADEAVERAGEATTEMPT, [0.61111, 51, 0], [0.52222, 52, 0] 219 ], 220 'Tracking with last attempt method' => [ 221 1, manager::GRADELASTATTEMPT, [0.33333, 51, 3], [0.40000, 52, 3] 222 ], 223 'Tracking with first attempt method' => [ 224 1, manager::GRADEFIRSTATTEMPT, [0.50000, 11, 1], [0.50000, 12, 1] 225 ], 226 'Tracking with manual attempt grading' => [ 227 1, manager::GRADEMANUAL, [], [] 228 ], 229 'No tracking with max attempt method' => [ 230 0, manager::GRADEHIGHESTATTEMPT, [], [] 231 ], 232 'No tracking with average attempt method' => [ 233 0, manager::GRADEAVERAGEATTEMPT, [], [] 234 ], 235 'No tracking with last attempt method' => [ 236 0, manager::GRADELASTATTEMPT, [], [] 237 ], 238 'No tracking with first attempt method' => [ 239 0, manager::GRADEFIRSTATTEMPT, [], [] 240 ], 241 'No tracking with manual attempt grading' => [ 242 0, manager::GRADEMANUAL, [], [] 243 ], 244 ]; 245 } 246 247 /** 248 * Test static get_grading_methods. 249 */ 250 public function test_get_grading_methods() { 251 $methods = manager::get_grading_methods(); 252 $this->assertCount(5, $methods); 253 $this->assertNotEmpty($methods[manager::GRADEHIGHESTATTEMPT]); 254 $this->assertNotEmpty($methods[manager::GRADEAVERAGEATTEMPT]); 255 $this->assertNotEmpty($methods[manager::GRADELASTATTEMPT]); 256 $this->assertNotEmpty($methods[manager::GRADEFIRSTATTEMPT]); 257 $this->assertNotEmpty($methods[manager::GRADEMANUAL]); 258 } 259 260 /** 261 * Test static get_selected_attempt. 262 * 263 * @dataProvider get_selected_attempt_data 264 * @param int $enabletracking if tracking is enabled 265 * @param int $gradingmethod new grading method 266 * @param int $result the expected result 267 */ 268 public function test_get_selected_attempt(int $enabletracking, int $gradingmethod, int $result) { 269 $this->resetAfterTest(); 270 $this->setAdminUser(); 271 272 $course = $this->getDataGenerator()->create_course(); 273 $activity = $this->getDataGenerator()->create_module('h5pactivity', 274 ['course' => $course, 'enabletracking' => $enabletracking, 'grademethod' => $gradingmethod]); 275 276 $manager = manager::create_from_instance($activity); 277 278 $selected = $manager->get_selected_attempt(); 279 280 $this->assertEquals($result, $selected[0]); 281 $this->assertNotEmpty($selected[1]); 282 } 283 284 /** 285 * Data provider for get_users_scaled_score. 286 * 287 * @return array 288 */ 289 public function get_selected_attempt_data(): array { 290 return [ 291 'Tracking with max attempt method' => [ 292 1, manager::GRADEHIGHESTATTEMPT, manager::GRADEHIGHESTATTEMPT 293 ], 294 'Tracking with average attempt method' => [ 295 1, manager::GRADEAVERAGEATTEMPT, manager::GRADEAVERAGEATTEMPT 296 ], 297 'Tracking with last attempt method' => [ 298 1, manager::GRADELASTATTEMPT, manager::GRADELASTATTEMPT 299 ], 300 'Tracking with first attempt method' => [ 301 1, manager::GRADEFIRSTATTEMPT, manager::GRADEFIRSTATTEMPT 302 ], 303 'Tracking with manual attempt grading' => [ 304 1, manager::GRADEMANUAL, manager::GRADEMANUAL 305 ], 306 'No tracking with max attempt method' => [ 307 0, manager::GRADEHIGHESTATTEMPT, manager::GRADEMANUAL 308 ], 309 'No tracking with average attempt method' => [ 310 0, manager::GRADEAVERAGEATTEMPT, manager::GRADEMANUAL 311 ], 312 'No tracking with last attempt method' => [ 313 0, manager::GRADELASTATTEMPT, manager::GRADEMANUAL 314 ], 315 'No tracking with first attempt method' => [ 316 0, manager::GRADEFIRSTATTEMPT, manager::GRADEMANUAL 317 ], 318 'No tracking with manual attempt grading' => [ 319 0, manager::GRADEMANUAL, manager::GRADEMANUAL 320 ], 321 ]; 322 } 323 324 /** 325 * Test static get_review_modes. 326 */ 327 public function test_get_review_modes() { 328 $methods = manager::get_review_modes(); 329 $this->assertCount(2, $methods); 330 $this->assertNotEmpty($methods[manager::REVIEWCOMPLETION]); 331 $this->assertNotEmpty($methods[manager::REVIEWNONE]); 332 } 333 334 /** 335 * Test get_grader method. 336 */ 337 public function test_get_grader() { 338 $this->resetAfterTest(); 339 $this->setAdminUser(); 340 341 $course = $this->getDataGenerator()->create_course(); 342 $activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]); 343 $cm = get_coursemodule_from_id('h5pactivity', $activity->cmid, 0, false, MUST_EXIST); 344 $context = context_module::instance($cm->id); 345 346 $manager = manager::create_from_instance($activity); 347 $grader = $manager->get_grader(); 348 349 $this->assertInstanceOf('mod_h5pactivity\local\grader', $grader); 350 } 351 352 353 /** 354 * Test static can_view_all_attempts. 355 * 356 * @dataProvider can_view_all_attempts_data 357 * @param int $enabletracking if tracking is enabled 358 * @param bool $usestudent if test must be done with a user role 359 * @param bool $useloggedin if test must be done with the loggedin user 360 * @param bool $result the expected result 361 */ 362 public function test_can_view_all_attempts(int $enabletracking, bool $usestudent, bool $useloggedin, bool $result) { 363 global $USER; 364 365 $this->resetAfterTest(); 366 $this->setAdminUser(); 367 368 $course = $this->getDataGenerator()->create_course(); 369 $activity = $this->getDataGenerator()->create_module('h5pactivity', 370 ['course' => $course, 'enabletracking' => $enabletracking]); 371 372 $manager = manager::create_from_instance($activity); 373 374 $user = $this->getDataGenerator()->create_and_enrol($course, 'student'); 375 $loggedin = $USER; 376 377 // We want to test what when the method is called to check a different user than $USER. 378 if (!$usestudent) { 379 $loggedin = $user; 380 $user = $USER; 381 } 382 383 if ($useloggedin) { 384 $this->setUser($user); 385 $user = null; 386 } else { 387 $this->setUser($loggedin); 388 } 389 390 $this->assertEquals($result, $manager->can_view_all_attempts($user)); 391 } 392 393 /** 394 * Data provider for test_can_view_all_attempts. 395 * 396 * @return array 397 */ 398 public function can_view_all_attempts_data(): array { 399 return [ 400 // No tracking cases. 401 'No tracking with admin using $USER' => [ 402 0, false, false, false 403 ], 404 'No tracking with student using $USER' => [ 405 0, true, false, false 406 ], 407 'No tracking with admin loggedin' => [ 408 0, false, true, false 409 ], 410 'No tracking with student loggedin' => [ 411 0, true, true, false 412 ], 413 // Tracking enabled cases. 414 'Tracking with admin using $USER' => [ 415 1, false, false, true 416 ], 417 'Tracking with student using $USER' => [ 418 1, true, false, false 419 ], 420 'Tracking with admin loggedin' => [ 421 1, false, true, true 422 ], 423 'Tracking with student loggedin' => [ 424 1, true, true, false 425 ], 426 ]; 427 } 428 429 /** 430 * Test static can_view_own_attempts. 431 * 432 * @dataProvider can_view_own_attempts_data 433 * @param int $enabletracking if tracking is enabled 434 * @param int $reviewmode the attempt review mode 435 * @param bool $useloggedin if test must be done with the loggedin user 436 * @param bool $hasattempts if the student have attempts 437 * @param bool $result the expected result 438 */ 439 public function test_can_view_own_attempts(int $enabletracking, int $reviewmode, 440 bool $useloggedin, bool $hasattempts, bool $result) { 441 442 $this->resetAfterTest(); 443 $this->setAdminUser(); 444 445 $course = $this->getDataGenerator()->create_course(); 446 $activity = $this->getDataGenerator()->create_module('h5pactivity', 447 ['course' => $course, 'enabletracking' => $enabletracking, 'reviewmode' => $reviewmode]); 448 449 $manager = manager::create_from_instance($activity); 450 451 $user = $this->getDataGenerator()->create_and_enrol($course, 'student'); 452 453 if ($hasattempts) { 454 $this->generate_fake_attempts($activity, $user, 1); 455 } 456 457 if ($useloggedin) { 458 $this->setUser($user); 459 $user = null; 460 } 461 462 $this->assertEquals($result, $manager->can_view_own_attempts($user)); 463 } 464 465 /** 466 * Data provider for test_can_view_own_attempts. 467 * 468 * @return array 469 */ 470 public function can_view_own_attempts_data(): array { 471 return [ 472 // No tracking cases. 473 'No tracking, review none, using $USER, without attempts' => [ 474 0, manager::REVIEWNONE, false, false, false 475 ], 476 'No tracking, review enabled, using $USER, without attempts' => [ 477 0, manager::REVIEWCOMPLETION, false, false, false 478 ], 479 'No tracking, review none, loggedin, without attempts' => [ 480 0, manager::REVIEWNONE, true, false, false 481 ], 482 'No tracking, review enabled, loggedin, without attempts' => [ 483 0, manager::REVIEWCOMPLETION, true, false, false 484 ], 485 'No tracking, review none, using $USER, with attempts' => [ 486 0, manager::REVIEWNONE, false, true, false 487 ], 488 'No tracking, review enabled, using $USER, with attempts' => [ 489 0, manager::REVIEWCOMPLETION, false, true, false 490 ], 491 'No tracking, review none, loggedin, with attempts' => [ 492 0, manager::REVIEWNONE, true, true, false 493 ], 494 'No tracking, review enabled, loggedin, with attempts' => [ 495 0, manager::REVIEWCOMPLETION, true, true, false 496 ], 497 // Tracking enabled cases. 498 'Tracking enabled, review none, using $USER, without attempts' => [ 499 1, manager::REVIEWNONE, false, false, false 500 ], 501 'Tracking enabled, review enabled, using $USER, without attempts' => [ 502 1, manager::REVIEWCOMPLETION, false, false, true 503 ], 504 'Tracking enabled, review none, loggedin, without attempts' => [ 505 1, manager::REVIEWNONE, true, false, false 506 ], 507 'Tracking enabled, review enabled, loggedin, without attempts' => [ 508 1, manager::REVIEWCOMPLETION, true, false, true 509 ], 510 'Tracking enabled, review none, using $USER, with attempts' => [ 511 1, manager::REVIEWNONE, false, true, false 512 ], 513 'Tracking enabled, review enabled, using $USER, with attempts' => [ 514 1, manager::REVIEWCOMPLETION, false, true, true 515 ], 516 'Tracking enabled, review none, loggedin, with attempts' => [ 517 1, manager::REVIEWNONE, true, true, false 518 ], 519 'Tracking enabled, review enabled, loggedin, with attempts' => [ 520 1, manager::REVIEWCOMPLETION, true, true, true 521 ], 522 ]; 523 } 524 525 /** 526 * Test static count_attempts of one user. 527 */ 528 public function test_count_attempts() { 529 530 $this->resetAfterTest(); 531 $this->setAdminUser(); 532 533 $course = $this->getDataGenerator()->create_course(); 534 $activity = $this->getDataGenerator()->create_module('h5pactivity', 535 ['course' => $course]); 536 537 $manager = manager::create_from_instance($activity); 538 539 // User without attempts. 540 $user1 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 541 542 // User with 1 attempt. 543 $user2 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 544 $this->generate_fake_attempts($activity, $user2, 1); 545 546 // User with 2 attempts. 547 $user3 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 548 $this->generate_fake_attempts($activity, $user3, 1); 549 550 // Incomplete user2 and 3 has only 3 attempts completed. 551 $this->assertEquals(0, $manager->count_attempts($user1->id)); 552 $this->assertEquals(3, $manager->count_attempts($user2->id)); 553 $this->assertEquals(3, $manager->count_attempts($user3->id)); 554 } 555 556 /** 557 * Test static count_attempts of all active participants. 558 * 559 * @dataProvider count_attempts_all_data 560 * @param bool $canview if the student role has mod_h5pactivity/view capability 561 * @param bool $cansubmit if the student role has mod_h5pactivity/submit capability 562 * @param bool $extrarole if an extra role without submit capability is required 563 * @param int $result the expected result 564 */ 565 public function test_count_attempts_all(bool $canview, bool $cansubmit, bool $extrarole, int $result) { 566 global $DB; 567 568 $this->resetAfterTest(); 569 $this->setAdminUser(); 570 571 $course = $this->getDataGenerator()->create_course(); 572 $activity = $this->getDataGenerator()->create_module( 573 'h5pactivity', 574 ['course' => $course] 575 ); 576 577 $manager = manager::create_from_instance($activity); 578 579 $roleid = $DB->get_field('role', 'id', ['shortname' => 'student']); 580 581 $newcap = ($canview) ? CAP_ALLOW : CAP_PROHIBIT; 582 role_change_permission($roleid, $manager->get_context(), 'mod/h5pactivity:view', $newcap); 583 584 $newcap = ($cansubmit) ? CAP_ALLOW : CAP_PROHIBIT; 585 role_change_permission($roleid, $manager->get_context(), 'mod/h5pactivity:submit', $newcap); 586 587 // Teacher with review capability and attempts (should not be listed). 588 if ($extrarole) { 589 $user1 = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher'); 590 $this->generate_fake_attempts($activity, $user1, 1); 591 } 592 593 // Student with attempts. 594 $user2 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 595 $this->generate_fake_attempts($activity, $user2, 1); 596 597 // Another student with attempts. 598 $user3 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 599 $this->generate_fake_attempts($activity, $user3, 1); 600 601 $this->assertEquals($result, $manager->count_attempts()); 602 } 603 604 /** 605 * Data provider for test_count_attempts_all. 606 * 607 * @return array 608 */ 609 public function count_attempts_all_data(): array { 610 return [ 611 'Students with both view and submit capability' => [true, true, false, 6], 612 'Students without view but with submit capability' => [false, true, false, 0], 613 'Students with view but without submit capability' => [true, false, false, 6], 614 'Students without both view and submit capability' => [false, false, false, 0], 615 'Students with both view and submit capability and extra role' => [true, true, true, 6], 616 'Students without view but with submit capability and extra role' => [false, true, true, 0], 617 'Students with view but without submit capability and extra role' => [true, false, true, 6], 618 'Students without both view and submit capability and extra role' => [false, false, true, 0], 619 ]; 620 } 621 622 /** 623 * Test static test_get_active_users_join of all active participants. 624 * 625 * Most method scenarios are tested in test_count_attempts_all so we only 626 * need to test the with $allpotentialusers true and false. 627 * 628 * @dataProvider get_active_users_join_data 629 * @param bool $allpotentialusers if the join should return all potential users or only the submitted ones. 630 * @param int $result the expected result 631 */ 632 public function test_get_active_users_join(bool $allpotentialusers, int $result) { 633 global $DB; 634 635 $this->resetAfterTest(); 636 $this->setAdminUser(); 637 638 $course = $this->getDataGenerator()->create_course(); 639 $activity = $this->getDataGenerator()->create_module( 640 'h5pactivity', 641 ['course' => $course] 642 ); 643 644 $manager = manager::create_from_instance($activity); 645 646 $user1 = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher'); 647 $this->generate_fake_attempts($activity, $user1, 1); 648 649 // Student with attempts. 650 $user2 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 651 $this->generate_fake_attempts($activity, $user2, 1); 652 653 // 2 more students without attempts. 654 $this->getDataGenerator()->create_and_enrol($course, 'student'); 655 $this->getDataGenerator()->create_and_enrol($course, 'student'); 656 657 $usersjoin = $manager->get_active_users_join($allpotentialusers); 658 659 // Final SQL. 660 $num = $DB->count_records_sql( 661 "SELECT COUNT(DISTINCT u.id) 662 FROM {user} u $usersjoin->joins 663 WHERE $usersjoin->wheres", 664 array_merge($usersjoin->params) 665 ); 666 667 $this->assertEquals($result, $num); 668 } 669 670 /** 671 * Data provider for test_get_active_users_join. 672 * 673 * @return array 674 */ 675 public function get_active_users_join_data(): array { 676 return [ 677 'All potential users' => [ 678 'allpotentialusers' => true, 679 'result' => 3, 680 ], 681 'Users with attempts' => [ 682 'allpotentialusers' => false, 683 'result' => 1, 684 ], 685 ]; 686 } 687 688 /** 689 * Test active users joins returns appropriate results for groups 690 */ 691 public function test_get_active_users_join_groupmode(): void { 692 global $DB; 693 694 $this->resetAfterTest(); 695 $this->setAdminUser(); 696 697 $course = $this->getDataGenerator()->create_course(['groupmode' => SEPARATEGROUPS, 'groupmodeforce' => 1]); 698 699 // Teacher/user one in group one. 700 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 701 $userone = $this->getDataGenerator()->create_and_enrol($course, 'student'); 702 703 $groupone = $this->getDataGenerator()->create_group(['courseid' => $course->id]); 704 $this->getDataGenerator()->create_group_member(['groupid' => $groupone->id, 'userid' => $teacher->id]); 705 $this->getDataGenerator()->create_group_member(['groupid' => $groupone->id, 'userid' => $userone->id]); 706 707 // User two in group two. 708 $usertwo = $this->getDataGenerator()->create_and_enrol($course, 'student'); 709 710 $grouptwo = $this->getDataGenerator()->create_group(['courseid' => $course->id]); 711 $this->getDataGenerator()->create_group_member(['groupid' => $grouptwo->id, 'userid' => $usertwo->id]); 712 713 $activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]); 714 $manager = manager::create_from_instance($activity); 715 716 // Admin user can view all participants. 717 $usersjoin = $manager->get_active_users_join(true, 0); 718 $users = $DB->get_fieldset_sql("SELECT u.username FROM {user} u {$usersjoin->joins} WHERE {$usersjoin->wheres}", 719 $usersjoin->params); 720 721 $this->assertEqualsCanonicalizing([$teacher->username, $userone->username, $usertwo->username], $users); 722 723 // Switch to teacher, who cannot view all participants. 724 $this->setUser($teacher); 725 726 $usersjoin = $manager->get_active_users_join(true, 0); 727 $users = $DB->get_fieldset_sql("SELECT u.username FROM {user} u {$usersjoin->joins} WHERE {$usersjoin->wheres}", 728 $usersjoin->params); 729 730 $this->assertEmpty($users); 731 732 // Teacher can view participants inside group. 733 $usersjoin = $manager->get_active_users_join(true, $groupone->id); 734 $users = $DB->get_fieldset_sql("SELECT u.username FROM {user} u {$usersjoin->joins} WHERE {$usersjoin->wheres}", 735 $usersjoin->params); 736 737 $this->assertEqualsCanonicalizing([$teacher->username, $userone->username], $users); 738 } 739 740 /** 741 * Test getting active users join where there are no roles with 'mod/h5pactivity:reviewattempts' capability 742 */ 743 public function test_get_active_users_join_no_reviewers(): void { 744 global $DB; 745 746 $this->resetAfterTest(); 747 $this->setAdminUser(); 748 749 $course = $this->getDataGenerator()->create_course(); 750 $activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]); 751 $user = $this->getDataGenerator()->create_and_enrol($course, 'student'); 752 753 $manager = manager::create_from_instance($activity); 754 755 // By default manager and editingteacher can review attempts, prohibit both. 756 $rolemanager = $DB->get_field('role', 'id', ['shortname' => 'manager']); 757 role_change_permission($rolemanager, $manager->get_context(), 'mod/h5pactivity:reviewattempts', CAP_PROHIBIT); 758 759 $roleeditingteacher = $DB->get_field('role', 'id', ['shortname' => 'editingteacher']); 760 role_change_permission($roleeditingteacher, $manager->get_context(), 'mod/h5pactivity:reviewattempts', CAP_PROHIBIT); 761 762 // Generate users join SQL to find matching users. 763 $usersjoin = $manager->get_active_users_join(true); 764 $usernames = $DB->get_fieldset_sql( 765 "SELECT u.username 766 FROM {user} u 767 {$usersjoin->joins} 768 WHERE {$usersjoin->wheres}", 769 $usersjoin->params 770 ); 771 772 $this->assertEquals([$user->username], $usernames); 773 } 774 775 /** 776 * Test static count_attempts. 777 */ 778 public function test_count_users_attempts() { 779 780 $this->resetAfterTest(); 781 $this->setAdminUser(); 782 783 $course = $this->getDataGenerator()->create_course(); 784 $activity = $this->getDataGenerator()->create_module('h5pactivity', 785 ['course' => $course]); 786 787 $manager = manager::create_from_instance($activity); 788 789 // User without attempts. 790 $user1 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 791 792 // User with 1 attempt. 793 $user2 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 794 $this->generate_fake_attempts($activity, $user2, 1); 795 796 // User with 2 attempts. 797 $user3 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 798 $this->generate_fake_attempts($activity, $user3, 1); 799 800 $attempts = $manager->count_users_attempts(); 801 $this->assertArrayNotHasKey($user1->id, $attempts); 802 $this->assertArrayHasKey($user2->id, $attempts); 803 $this->assertEquals(4, $attempts[$user2->id]); 804 $this->assertArrayHasKey($user3->id, $attempts); 805 $this->assertEquals(4, $attempts[$user3->id]); 806 } 807 808 /** 809 * Test static get_report. 810 * 811 * @dataProvider get_report_data 812 * @param int $enabletracking if tracking is enabled 813 * @param int $reviewmode the attempt review mode 814 * @param bool $createattempts if the student have attempts 815 * @param string $role the user role (student or editingteacher) 816 * @param array $results the expected classname (or null) 817 */ 818 public function test_get_report(int $enabletracking, int $reviewmode, bool $createattempts, 819 string $role, array $results) { 820 821 $this->resetAfterTest(); 822 $this->setAdminUser(); 823 824 $course = $this->getDataGenerator()->create_course(); 825 $activity = $this->getDataGenerator()->create_module('h5pactivity', 826 ['course' => $course, 'enabletracking' => $enabletracking, 'reviewmode' => $reviewmode]); 827 828 $manager = manager::create_from_instance($activity); 829 $cm = get_coursemodule_from_id('h5pactivity', $activity->cmid, 0, false, MUST_EXIST); 830 831 $users = [ 832 'editingteacher' => $this->getDataGenerator()->create_and_enrol($course, 'editingteacher'), 833 'student' => $this->getDataGenerator()->create_and_enrol($course, 'student'), 834 'otheruser' => $this->getDataGenerator()->create_and_enrol($course, 'student'), 835 ]; 836 837 $attempts = []; 838 if ($createattempts) { 839 $this->generate_fake_attempts($activity, $users['student'], 1); 840 $this->generate_fake_attempts($activity, $users['otheruser'], 2); 841 $attempts['student'] = attempt::last_attempt($users['student'], $cm); 842 $attempts['otheruser'] = attempt::last_attempt($users['otheruser'], $cm); 843 } 844 845 $classnamebase = 'mod_h5pactivity\\local\\report\\'; 846 847 $attemptid = null; 848 if (isset($attempts['student'])) { 849 $attemptid = $attempts['student']->get_id() ?? null; 850 } 851 $userid = $users['student']->id; 852 853 // Check reports. 854 $this->setUser($users[$role]); 855 856 $report = $manager->get_report(null, null); 857 if ($results[0] === null) { 858 $this->assertNull($report); 859 } else { 860 $this->assertEquals($classnamebase.$results[0], get_class($report)); 861 } 862 863 $report = $manager->get_report($userid, null); 864 if ($results[1] === null) { 865 $this->assertNull($report); 866 } else { 867 $this->assertEquals($classnamebase.$results[1], get_class($report)); 868 } 869 870 $report = $manager->get_report($userid, $attemptid); 871 if ($results[2] === null) { 872 $this->assertNull($report); 873 } else { 874 $this->assertEquals($classnamebase.$results[2], get_class($report)); 875 } 876 877 // Check that student cannot access another student reports. 878 if ($role == 'student') { 879 $attemptid = null; 880 if (isset($attempts['otheruser'])) { 881 $attemptid = $attempts['otheruser']->get_id() ?? null; 882 } 883 $userid = $users['otheruser']->id; 884 885 $report = $manager->get_report($userid, null); 886 $this->assertNull($report); 887 888 $report = $manager->get_report($userid, $attemptid); 889 $this->assertNull($report); 890 } 891 } 892 893 /** 894 * Data provider for test_get_report. 895 * 896 * @return array 897 */ 898 public function get_report_data(): array { 899 return [ 900 // No tracking scenarios. 901 'No tracking, review none, no attempts, teacher' => [ 902 0, manager::REVIEWNONE, false, 'editingteacher', [null, null, null] 903 ], 904 'No tracking, review own, no attempts, teacher' => [ 905 0, manager::REVIEWCOMPLETION, false, 'editingteacher', [null, null, null] 906 ], 907 'No tracking, review none, no attempts, student' => [ 908 0, manager::REVIEWNONE, false, 'student', [null, null, null] 909 ], 910 'No tracking, review own, no attempts, student' => [ 911 0, manager::REVIEWCOMPLETION, false, 'student', [null, null, null] 912 ], 913 'No tracking, review none, with attempts, teacher' => [ 914 0, manager::REVIEWNONE, true, 'editingteacher', [null, null, null] 915 ], 916 'No tracking, review own, with attempts, teacher' => [ 917 0, manager::REVIEWCOMPLETION, true, 'editingteacher', [null, null, null] 918 ], 919 'No tracking, review none, with attempts, student' => [ 920 0, manager::REVIEWNONE, true, 'student', [null, null, null] 921 ], 922 'No tracking, review own, with attempts, student' => [ 923 0, manager::REVIEWCOMPLETION, true, 'student', [null, null, null] 924 ], 925 // Tracking enabled scenarios. 926 'Tracking enabled, review none, no attempts, teacher' => [ 927 1, manager::REVIEWNONE, false, 'editingteacher', ['participants', 'attempts', 'attempts'] 928 ], 929 'Tracking enabled, review own, no attempts, teacher' => [ 930 1, manager::REVIEWCOMPLETION, false, 'editingteacher', ['participants', 'attempts', 'attempts'] 931 ], 932 'Tracking enabled, review none, no attempts, student' => [ 933 1, manager::REVIEWNONE, false, 'student', [null, null, null] 934 ], 935 'Tracking enabled, review own, no attempts, student' => [ 936 1, manager::REVIEWCOMPLETION, false, 'student', ['attempts', 'attempts', 'attempts'] 937 ], 938 'Tracking enabled, review none, with attempts, teacher' => [ 939 1, manager::REVIEWNONE, true, 'editingteacher', ['participants', 'attempts', 'results'] 940 ], 941 'Tracking enabled, review own, with attempts, teacher' => [ 942 1, manager::REVIEWCOMPLETION, true, 'editingteacher', ['participants', 'attempts', 'results'] 943 ], 944 'Tracking enabled, review none, with attempts, student' => [ 945 1, manager::REVIEWNONE, true, 'student', [null, null, null] 946 ], 947 'Tracking enabled, review own, with attempts, student' => [ 948 1, manager::REVIEWCOMPLETION, true, 'student', ['attempts', 'attempts', 'results'] 949 ], 950 ]; 951 } 952 953 /** 954 * Test get_attempt method. 955 * 956 * @dataProvider get_attempt_data 957 * @param string $attemptname the attempt to use 958 * @param string|null $result the expected attempt ID or null for none 959 */ 960 public function test_get_attempt(string $attemptname, ?string $result): void { 961 962 $this->resetAfterTest(); 963 $this->setAdminUser(); 964 965 $course = $this->getDataGenerator()->create_course(); 966 967 $activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]); 968 $cm = get_coursemodule_from_id('h5pactivity', $activity->cmid, 0, false, MUST_EXIST); 969 970 $otheractivity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]); 971 $othercm = get_coursemodule_from_id('h5pactivity', $otheractivity->cmid, 0, false, MUST_EXIST); 972 973 $manager = manager::create_from_instance($activity); 974 975 $user = $this->getDataGenerator()->create_and_enrol($course, 'student'); 976 977 $attempts = ['inexistent' => 0]; 978 979 $this->generate_fake_attempts($activity, $user, 1); 980 $attempt = attempt::last_attempt($user, $cm); 981 $attempts['current'] = $attempt->get_id(); 982 983 $this->generate_fake_attempts($otheractivity, $user, 1); 984 $attempt = attempt::last_attempt($user, $othercm); 985 $attempts['other'] = $attempt->get_id(); 986 987 $attempt = $manager->get_attempt($attempts[$attemptname]); 988 if ($result === null) { 989 $this->assertNull($attempt); 990 } else { 991 $this->assertEquals($attempts[$attemptname], $attempt->get_id()); 992 $this->assertEquals($activity->id, $attempt->get_h5pactivityid()); 993 $this->assertEquals($user->id, $attempt->get_userid()); 994 $this->assertEquals(4, $attempt->get_attempt()); 995 } 996 } 997 998 /** 999 * Data provider for test_get_attempt. 1000 * 1001 * @return array 1002 */ 1003 public function get_attempt_data(): array { 1004 return [ 1005 'Get the current activity attempt' => [ 1006 'current', 'current' 1007 ], 1008 'Try to get another activity attempt' => [ 1009 'other', null 1010 ], 1011 'Try to get an inexistent attempt' => [ 1012 'inexistent', null 1013 ], 1014 ]; 1015 } 1016 1017 /** 1018 * Insert fake attempt data into h5pactiviyt_attempts. 1019 * 1020 * This function insert 4 attempts. 3 of them finished with different gradings 1021 * and timestamps and 1 unfinished. 1022 * 1023 * @param stdClass $activity the activity record 1024 * @param stdClass $user user record 1025 * @param int $basescore a score to be used to generate all attempts 1026 */ 1027 private function generate_fake_attempts(stdClass $activity, stdClass $user, int $basescore) { 1028 global $DB; 1029 1030 $attempt = (object)[ 1031 'h5pactivityid' => $activity->id, 1032 'userid' => $user->id, 1033 'timecreated' => $basescore, 1034 'timemodified' => ($basescore + 10), 1035 'attempt' => 1, 1036 'rawscore' => $basescore, 1037 'maxscore' => ($basescore + $basescore), 1038 'duration' => $basescore, 1039 'completion' => 1, 1040 'success' => 1, 1041 ]; 1042 $attempt->scaled = $attempt->rawscore / $attempt->maxscore; 1043 $DB->insert_record('h5pactivity_attempts', $attempt); 1044 1045 $attempt = (object)[ 1046 'h5pactivityid' => $activity->id, 1047 'userid' => $user->id, 1048 'timecreated' => ($basescore + 20), 1049 'timemodified' => ($basescore + 30), 1050 'attempt' => 2, 1051 'rawscore' => $basescore, 1052 'maxscore' => ($basescore + $basescore - 1), 1053 'duration' => $basescore, 1054 'completion' => 1, 1055 'success' => 1, 1056 ]; 1057 $attempt->scaled = $attempt->rawscore / $attempt->maxscore; 1058 $DB->insert_record('h5pactivity_attempts', $attempt); 1059 1060 $attempt = (object)[ 1061 'h5pactivityid' => $activity->id, 1062 'userid' => $user->id, 1063 'timecreated' => ($basescore + 40), 1064 'timemodified' => ($basescore + 50), 1065 'attempt' => 3, 1066 'rawscore' => $basescore, 1067 'maxscore' => ($basescore + $basescore + 1), 1068 'duration' => $basescore, 1069 'completion' => 1, 1070 'success' => 0, 1071 ]; 1072 $attempt->scaled = $attempt->rawscore / $attempt->maxscore; 1073 $DB->insert_record('h5pactivity_attempts', $attempt); 1074 1075 // Unfinished attempt. 1076 $attempt = (object)[ 1077 'h5pactivityid' => $activity->id, 1078 'userid' => $user->id, 1079 'timecreated' => ($basescore + 60), 1080 'timemodified' => ($basescore + 60), 1081 'attempt' => 4, 1082 'rawscore' => $basescore, 1083 'maxscore' => $basescore, 1084 'duration' => $basescore, 1085 ]; 1086 $DB->insert_record('h5pactivity_attempts', $attempt); 1087 } 1088 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body