Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [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 // User three in no group. 714 $userthree = $this->getDataGenerator()->create_and_enrol($course, 'student'); 715 716 // User four in a non-participation group. 717 $userfour = $this->getDataGenerator()->create_and_enrol($course, 'student'); 718 $groupthree = $this->getDataGenerator()->create_group(['courseid' => $course->id, 'participation' => 0]); 719 $this->getDataGenerator()->create_group_member(['groupid' => $groupthree->id, 'userid' => $userfour->id]); 720 721 $activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]); 722 $manager = manager::create_from_instance($activity); 723 724 // Admin user can view all participants (any group and none). 725 $usersjoin = $manager->get_active_users_join(true, 0); 726 $users = $DB->get_fieldset_sql("SELECT u.username FROM {user} u {$usersjoin->joins} WHERE {$usersjoin->wheres}", 727 $usersjoin->params); 728 729 $this->assertEqualsCanonicalizing( 730 [$teacher->username, $userone->username, $usertwo->username, $userthree->username, $userfour->username], $users); 731 732 // Switch to teacher, who cannot view all participants. 733 $this->setUser($teacher); 734 735 $usersjoin = $manager->get_active_users_join(true, 0); 736 $users = $DB->get_fieldset_sql("SELECT u.username FROM {user} u {$usersjoin->joins} WHERE {$usersjoin->wheres}", 737 $usersjoin->params); 738 739 $this->assertEmpty($users); 740 741 // Teacher can view participants inside group. 742 $usersjoin = $manager->get_active_users_join(true, $groupone->id); 743 $users = $DB->get_fieldset_sql("SELECT u.username FROM {user} u {$usersjoin->joins} WHERE {$usersjoin->wheres}", 744 $usersjoin->params); 745 746 $this->assertEqualsCanonicalizing([$teacher->username, $userone->username], $users); 747 } 748 749 /** 750 * Test getting active users join where there are no roles with 'mod/h5pactivity:reviewattempts' capability 751 */ 752 public function test_get_active_users_join_no_reviewers(): void { 753 global $DB; 754 755 $this->resetAfterTest(); 756 $this->setAdminUser(); 757 758 $course = $this->getDataGenerator()->create_course(); 759 $activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]); 760 $user = $this->getDataGenerator()->create_and_enrol($course, 'student'); 761 762 $manager = manager::create_from_instance($activity); 763 764 // By default manager and editingteacher can review attempts, prohibit both. 765 $rolemanager = $DB->get_field('role', 'id', ['shortname' => 'manager']); 766 role_change_permission($rolemanager, $manager->get_context(), 'mod/h5pactivity:reviewattempts', CAP_PROHIBIT); 767 768 $roleeditingteacher = $DB->get_field('role', 'id', ['shortname' => 'editingteacher']); 769 role_change_permission($roleeditingteacher, $manager->get_context(), 'mod/h5pactivity:reviewattempts', CAP_PROHIBIT); 770 771 // Generate users join SQL to find matching users. 772 $usersjoin = $manager->get_active_users_join(true); 773 $usernames = $DB->get_fieldset_sql( 774 "SELECT u.username 775 FROM {user} u 776 {$usersjoin->joins} 777 WHERE {$usersjoin->wheres}", 778 $usersjoin->params 779 ); 780 781 $this->assertEquals([$user->username], $usernames); 782 } 783 784 /** 785 * Test static count_attempts. 786 */ 787 public function test_count_users_attempts() { 788 789 $this->resetAfterTest(); 790 $this->setAdminUser(); 791 792 $course = $this->getDataGenerator()->create_course(); 793 $activity = $this->getDataGenerator()->create_module('h5pactivity', 794 ['course' => $course]); 795 796 $manager = manager::create_from_instance($activity); 797 798 // User without attempts. 799 $user1 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 800 801 // User with 1 attempt. 802 $user2 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 803 $this->generate_fake_attempts($activity, $user2, 1); 804 805 // User with 2 attempts. 806 $user3 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 807 $this->generate_fake_attempts($activity, $user3, 1); 808 809 $attempts = $manager->count_users_attempts(); 810 $this->assertArrayNotHasKey($user1->id, $attempts); 811 $this->assertArrayHasKey($user2->id, $attempts); 812 $this->assertEquals(4, $attempts[$user2->id]); 813 $this->assertArrayHasKey($user3->id, $attempts); 814 $this->assertEquals(4, $attempts[$user3->id]); 815 } 816 817 /** 818 * Test static get_report. 819 * 820 * @dataProvider get_report_data 821 * @param int $enabletracking if tracking is enabled 822 * @param int $reviewmode the attempt review mode 823 * @param bool $createattempts if the student have attempts 824 * @param string $role the user role (student or editingteacher) 825 * @param array $results the expected classname (or null) 826 */ 827 public function test_get_report(int $enabletracking, int $reviewmode, bool $createattempts, 828 string $role, array $results) { 829 830 $this->resetAfterTest(); 831 $this->setAdminUser(); 832 833 $course = $this->getDataGenerator()->create_course(); 834 $activity = $this->getDataGenerator()->create_module('h5pactivity', 835 ['course' => $course, 'enabletracking' => $enabletracking, 'reviewmode' => $reviewmode]); 836 837 $manager = manager::create_from_instance($activity); 838 $cm = get_coursemodule_from_id('h5pactivity', $activity->cmid, 0, false, MUST_EXIST); 839 840 $users = [ 841 'editingteacher' => $this->getDataGenerator()->create_and_enrol($course, 'editingteacher'), 842 'student' => $this->getDataGenerator()->create_and_enrol($course, 'student'), 843 'otheruser' => $this->getDataGenerator()->create_and_enrol($course, 'student'), 844 ]; 845 846 $attempts = []; 847 if ($createattempts) { 848 $this->generate_fake_attempts($activity, $users['student'], 1); 849 $this->generate_fake_attempts($activity, $users['otheruser'], 2); 850 $attempts['student'] = attempt::last_attempt($users['student'], $cm); 851 $attempts['otheruser'] = attempt::last_attempt($users['otheruser'], $cm); 852 } 853 854 $classnamebase = 'mod_h5pactivity\\local\\report\\'; 855 856 $attemptid = null; 857 if (isset($attempts['student'])) { 858 $attemptid = $attempts['student']->get_id() ?? null; 859 } 860 $userid = $users['student']->id; 861 862 // Check reports. 863 $this->setUser($users[$role]); 864 865 $report = $manager->get_report(null, null); 866 if ($results[0] === null) { 867 $this->assertNull($report); 868 } else { 869 $this->assertEquals($classnamebase.$results[0], get_class($report)); 870 } 871 872 $report = $manager->get_report($userid, null); 873 if ($results[1] === null) { 874 $this->assertNull($report); 875 } else { 876 $this->assertEquals($classnamebase.$results[1], get_class($report)); 877 } 878 879 $report = $manager->get_report($userid, $attemptid); 880 if ($results[2] === null) { 881 $this->assertNull($report); 882 } else { 883 $this->assertEquals($classnamebase.$results[2], get_class($report)); 884 } 885 886 // Check that student cannot access another student reports. 887 if ($role == 'student') { 888 $attemptid = null; 889 if (isset($attempts['otheruser'])) { 890 $attemptid = $attempts['otheruser']->get_id() ?? null; 891 } 892 $userid = $users['otheruser']->id; 893 894 $report = $manager->get_report($userid, null); 895 $this->assertNull($report); 896 897 $report = $manager->get_report($userid, $attemptid); 898 $this->assertNull($report); 899 } 900 } 901 902 /** 903 * Data provider for test_get_report. 904 * 905 * @return array 906 */ 907 public function get_report_data(): array { 908 return [ 909 // No tracking scenarios. 910 'No tracking, review none, no attempts, teacher' => [ 911 0, manager::REVIEWNONE, false, 'editingteacher', [null, null, null] 912 ], 913 'No tracking, review own, no attempts, teacher' => [ 914 0, manager::REVIEWCOMPLETION, false, 'editingteacher', [null, null, null] 915 ], 916 'No tracking, review none, no attempts, student' => [ 917 0, manager::REVIEWNONE, false, 'student', [null, null, null] 918 ], 919 'No tracking, review own, no attempts, student' => [ 920 0, manager::REVIEWCOMPLETION, false, 'student', [null, null, null] 921 ], 922 'No tracking, review none, with attempts, teacher' => [ 923 0, manager::REVIEWNONE, true, 'editingteacher', [null, null, null] 924 ], 925 'No tracking, review own, with attempts, teacher' => [ 926 0, manager::REVIEWCOMPLETION, true, 'editingteacher', [null, null, null] 927 ], 928 'No tracking, review none, with attempts, student' => [ 929 0, manager::REVIEWNONE, true, 'student', [null, null, null] 930 ], 931 'No tracking, review own, with attempts, student' => [ 932 0, manager::REVIEWCOMPLETION, true, 'student', [null, null, null] 933 ], 934 // Tracking enabled scenarios. 935 'Tracking enabled, review none, no attempts, teacher' => [ 936 1, manager::REVIEWNONE, false, 'editingteacher', ['participants', 'attempts', 'attempts'] 937 ], 938 'Tracking enabled, review own, no attempts, teacher' => [ 939 1, manager::REVIEWCOMPLETION, false, 'editingteacher', ['participants', 'attempts', 'attempts'] 940 ], 941 'Tracking enabled, review none, no attempts, student' => [ 942 1, manager::REVIEWNONE, false, 'student', [null, null, null] 943 ], 944 'Tracking enabled, review own, no attempts, student' => [ 945 1, manager::REVIEWCOMPLETION, false, 'student', ['attempts', 'attempts', 'attempts'] 946 ], 947 'Tracking enabled, review none, with attempts, teacher' => [ 948 1, manager::REVIEWNONE, true, 'editingteacher', ['participants', 'attempts', 'results'] 949 ], 950 'Tracking enabled, review own, with attempts, teacher' => [ 951 1, manager::REVIEWCOMPLETION, true, 'editingteacher', ['participants', 'attempts', 'results'] 952 ], 953 'Tracking enabled, review none, with attempts, student' => [ 954 1, manager::REVIEWNONE, true, 'student', [null, null, null] 955 ], 956 'Tracking enabled, review own, with attempts, student' => [ 957 1, manager::REVIEWCOMPLETION, true, 'student', ['attempts', 'attempts', 'results'] 958 ], 959 ]; 960 } 961 962 /** 963 * Test get_attempt method. 964 * 965 * @dataProvider get_attempt_data 966 * @param string $attemptname the attempt to use 967 * @param string|null $result the expected attempt ID or null for none 968 */ 969 public function test_get_attempt(string $attemptname, ?string $result): void { 970 971 $this->resetAfterTest(); 972 $this->setAdminUser(); 973 974 $course = $this->getDataGenerator()->create_course(); 975 976 $activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]); 977 $cm = get_coursemodule_from_id('h5pactivity', $activity->cmid, 0, false, MUST_EXIST); 978 979 $otheractivity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]); 980 $othercm = get_coursemodule_from_id('h5pactivity', $otheractivity->cmid, 0, false, MUST_EXIST); 981 982 $manager = manager::create_from_instance($activity); 983 984 $user = $this->getDataGenerator()->create_and_enrol($course, 'student'); 985 986 $attempts = ['inexistent' => 0]; 987 988 $this->generate_fake_attempts($activity, $user, 1); 989 $attempt = attempt::last_attempt($user, $cm); 990 $attempts['current'] = $attempt->get_id(); 991 992 $this->generate_fake_attempts($otheractivity, $user, 1); 993 $attempt = attempt::last_attempt($user, $othercm); 994 $attempts['other'] = $attempt->get_id(); 995 996 $attempt = $manager->get_attempt($attempts[$attemptname]); 997 if ($result === null) { 998 $this->assertNull($attempt); 999 } else { 1000 $this->assertEquals($attempts[$attemptname], $attempt->get_id()); 1001 $this->assertEquals($activity->id, $attempt->get_h5pactivityid()); 1002 $this->assertEquals($user->id, $attempt->get_userid()); 1003 $this->assertEquals(4, $attempt->get_attempt()); 1004 } 1005 } 1006 1007 /** 1008 * Data provider for test_get_attempt. 1009 * 1010 * @return array 1011 */ 1012 public function get_attempt_data(): array { 1013 return [ 1014 'Get the current activity attempt' => [ 1015 'current', 'current' 1016 ], 1017 'Try to get another activity attempt' => [ 1018 'other', null 1019 ], 1020 'Try to get an inexistent attempt' => [ 1021 'inexistent', null 1022 ], 1023 ]; 1024 } 1025 1026 /** 1027 * Insert fake attempt data into h5pactiviyt_attempts. 1028 * 1029 * This function insert 4 attempts. 3 of them finished with different gradings 1030 * and timestamps and 1 unfinished. 1031 * 1032 * @param stdClass $activity the activity record 1033 * @param stdClass $user user record 1034 * @param int $basescore a score to be used to generate all attempts 1035 */ 1036 private function generate_fake_attempts(stdClass $activity, stdClass $user, int $basescore) { 1037 global $DB; 1038 1039 $attempt = (object)[ 1040 'h5pactivityid' => $activity->id, 1041 'userid' => $user->id, 1042 'timecreated' => $basescore, 1043 'timemodified' => ($basescore + 10), 1044 'attempt' => 1, 1045 'rawscore' => $basescore, 1046 'maxscore' => ($basescore + $basescore), 1047 'duration' => $basescore, 1048 'completion' => 1, 1049 'success' => 1, 1050 ]; 1051 $attempt->scaled = $attempt->rawscore / $attempt->maxscore; 1052 $DB->insert_record('h5pactivity_attempts', $attempt); 1053 1054 $attempt = (object)[ 1055 'h5pactivityid' => $activity->id, 1056 'userid' => $user->id, 1057 'timecreated' => ($basescore + 20), 1058 'timemodified' => ($basescore + 30), 1059 'attempt' => 2, 1060 'rawscore' => $basescore, 1061 'maxscore' => ($basescore + $basescore - 1), 1062 'duration' => $basescore, 1063 'completion' => 1, 1064 'success' => 1, 1065 ]; 1066 $attempt->scaled = $attempt->rawscore / $attempt->maxscore; 1067 $DB->insert_record('h5pactivity_attempts', $attempt); 1068 1069 $attempt = (object)[ 1070 'h5pactivityid' => $activity->id, 1071 'userid' => $user->id, 1072 'timecreated' => ($basescore + 40), 1073 'timemodified' => ($basescore + 50), 1074 'attempt' => 3, 1075 'rawscore' => $basescore, 1076 'maxscore' => ($basescore + $basescore + 1), 1077 'duration' => $basescore, 1078 'completion' => 1, 1079 'success' => 0, 1080 ]; 1081 $attempt->scaled = $attempt->rawscore / $attempt->maxscore; 1082 $DB->insert_record('h5pactivity_attempts', $attempt); 1083 1084 // Unfinished attempt. 1085 $attempt = (object)[ 1086 'h5pactivityid' => $activity->id, 1087 'userid' => $user->id, 1088 'timecreated' => ($basescore + 60), 1089 'timemodified' => ($basescore + 60), 1090 'attempt' => 4, 1091 'rawscore' => $basescore, 1092 'maxscore' => $basescore, 1093 'duration' => $basescore, 1094 ]; 1095 $DB->insert_record('h5pactivity_attempts', $attempt); 1096 } 1097 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body