See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]
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 * Unit tests for grade/lib.php. 19 * 20 * @package core_grades 21 * @category test 22 * @copyright 2016 Jun Pataleta 23 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License 24 */ 25 namespace core_grades; 26 27 use assign; 28 use cm_info; 29 use grade_item; 30 use grade_plugin_return; 31 use grade_report_summary; 32 33 defined('MOODLE_INTERNAL') || die(); 34 35 global $CFG; 36 require_once($CFG->dirroot . '/grade/lib.php'); 37 38 /** 39 * Unit tests for grade/lib.php. 40 * 41 * @package core_grades 42 * @category test 43 * @copyright 2016 Jun Pataleta <jun@moodle.com> 44 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 45 */ 46 class lib_test extends \advanced_testcase { 47 48 /** 49 * Test can_output_item. 50 */ 51 public function test_can_output_item() { 52 $this->resetAfterTest(); 53 54 $generator = $this->getDataGenerator(); 55 56 // Course level grade category. 57 $course = $generator->create_course(); 58 // Grade tree looks something like: 59 // - Test course (Rendered). 60 $gradetree = \grade_category::fetch_course_tree($course->id); 61 $this->assertTrue(\grade_tree::can_output_item($gradetree)); 62 63 // Add a grade category with default settings. 64 $generator->create_grade_category(array('courseid' => $course->id)); 65 // Grade tree now looks something like: 66 // - Test course n (Rendered). 67 // -- Grade category n (Rendered). 68 $gradetree = \grade_category::fetch_course_tree($course->id); 69 $this->assertNotEmpty($gradetree['children']); 70 foreach ($gradetree['children'] as $child) { 71 $this->assertTrue(\grade_tree::can_output_item($child)); 72 } 73 74 // Add a grade category with grade type = None. 75 $nototalcategory = 'No total category'; 76 $nototalparams = [ 77 'courseid' => $course->id, 78 'fullname' => $nototalcategory, 79 'aggregation' => GRADE_AGGREGATE_WEIGHTED_MEAN 80 ]; 81 $nototal = $generator->create_grade_category($nototalparams); 82 $catnototal = \grade_category::fetch(array('id' => $nototal->id)); 83 // Set the grade type of the grade item associated to the grade category. 84 $catitemnototal = $catnototal->load_grade_item(); 85 $catitemnototal->gradetype = GRADE_TYPE_NONE; 86 $catitemnototal->update(); 87 88 // Grade tree looks something like: 89 // - Test course n (Rendered). 90 // -- Grade category n (Rendered). 91 // -- No total category (Not rendered). 92 $gradetree = \grade_category::fetch_course_tree($course->id); 93 foreach ($gradetree['children'] as $child) { 94 if ($child['object']->fullname == $nototalcategory) { 95 $this->assertFalse(\grade_tree::can_output_item($child)); 96 } else { 97 $this->assertTrue(\grade_tree::can_output_item($child)); 98 } 99 } 100 101 // Add another grade category with default settings under 'No total category'. 102 $normalinnototalparams = [ 103 'courseid' => $course->id, 104 'fullname' => 'Normal category in no total category', 105 'parent' => $nototal->id 106 ]; 107 $generator->create_grade_category($normalinnototalparams); 108 109 // Grade tree looks something like: 110 // - Test course n (Rendered). 111 // -- Grade category n (Rendered). 112 // -- No total category (Rendered). 113 // --- Normal category in no total category (Rendered). 114 $gradetree = \grade_category::fetch_course_tree($course->id); 115 foreach ($gradetree['children'] as $child) { 116 // All children are now visible. 117 $this->assertTrue(\grade_tree::can_output_item($child)); 118 if (!empty($child['children'])) { 119 foreach ($child['children'] as $grandchild) { 120 $this->assertTrue(\grade_tree::can_output_item($grandchild)); 121 } 122 } 123 } 124 125 // Add a grade category with grade type = None. 126 $nototalcategory2 = 'No total category 2'; 127 $nototal2params = [ 128 'courseid' => $course->id, 129 'fullname' => $nototalcategory2, 130 'aggregation' => GRADE_AGGREGATE_WEIGHTED_MEAN 131 ]; 132 $nototal2 = $generator->create_grade_category($nototal2params); 133 $catnototal2 = \grade_category::fetch(array('id' => $nototal2->id)); 134 // Set the grade type of the grade item associated to the grade category. 135 $catitemnototal2 = $catnototal2->load_grade_item(); 136 $catitemnototal2->gradetype = GRADE_TYPE_NONE; 137 $catitemnototal2->update(); 138 139 // Add a category with no total under 'No total category'. 140 $nototalinnototalcategory = 'Category with no total in no total category'; 141 $nototalinnototalparams = [ 142 'courseid' => $course->id, 143 'fullname' => $nototalinnototalcategory, 144 'aggregation' => GRADE_AGGREGATE_WEIGHTED_MEAN, 145 'parent' => $nototal2->id 146 ]; 147 $nototalinnototal = $generator->create_grade_category($nototalinnototalparams); 148 $catnototalinnototal = \grade_category::fetch(array('id' => $nototalinnototal->id)); 149 // Set the grade type of the grade item associated to the grade category. 150 $catitemnototalinnototal = $catnototalinnototal->load_grade_item(); 151 $catitemnototalinnototal->gradetype = GRADE_TYPE_NONE; 152 $catitemnototalinnototal->update(); 153 154 // Grade tree looks something like: 155 // - Test course n (Rendered). 156 // -- Grade category n (Rendered). 157 // -- No total category (Rendered). 158 // --- Normal category in no total category (Rendered). 159 // -- No total category 2 (Not rendered). 160 // --- Category with no total in no total category (Not rendered). 161 $gradetree = \grade_category::fetch_course_tree($course->id); 162 foreach ($gradetree['children'] as $child) { 163 if ($child['object']->fullname == $nototalcategory2) { 164 $this->assertFalse(\grade_tree::can_output_item($child)); 165 } else { 166 $this->assertTrue(\grade_tree::can_output_item($child)); 167 } 168 if (!empty($child['children'])) { 169 foreach ($child['children'] as $grandchild) { 170 if ($grandchild['object']->fullname == $nototalinnototalcategory) { 171 $this->assertFalse(\grade_tree::can_output_item($grandchild)); 172 } else { 173 $this->assertTrue(\grade_tree::can_output_item($grandchild)); 174 } 175 } 176 } 177 } 178 } 179 180 /** 181 * Tests that ungraded_counts calculates count and sum of grades correctly when there are graded users. 182 * 183 * @covers \grade_report::ungraded_counts 184 */ 185 public function test_ungraded_counts_count_sumgrades() { 186 global $DB; 187 188 $this->resetAfterTest(true); 189 190 $course1 = $this->getDataGenerator()->create_course(); 191 $course2 = $this->getDataGenerator()->create_course(); 192 193 $studentrole = $DB->get_record('role', ['shortname' => 'student'], '*', MUST_EXIST); 194 $teacherrole = $DB->get_record('role', ['shortname' => 'editingteacher'], '*', MUST_EXIST); 195 196 // Custom roles (gradable and non gradable). 197 $gradeblerole = create_role('New student role', 'gradable', 198 'Gradable role', 'student'); 199 $nongradeblerole = create_role('New student role', 'nongradable', 200 'Non gradable role', 'student'); 201 202 // Set up gradable roles. 203 set_config('gradebookroles', $studentrole->id . ',' . $gradeblerole); 204 205 // Create users. 206 207 // These will be gradable users. 208 $student1 = $this->getDataGenerator()->create_user(['username' => 'student1']); 209 $student2 = $this->getDataGenerator()->create_user(['username' => 'student2']); 210 $student3 = $this->getDataGenerator()->create_user(['username' => 'student3']); 211 $student5 = $this->getDataGenerator()->create_user(['username' => 'student5']); 212 213 // These will be non-gradable users. 214 $student4 = $this->getDataGenerator()->create_user(['username' => 'student4']); 215 $student6 = $this->getDataGenerator()->create_user(['username' => 'student6']); 216 $teacher = $this->getDataGenerator()->create_user(['username' => 'teacher']); 217 218 // Enrol students. 219 $this->getDataGenerator()->enrol_user($student1->id, $course1->id, $studentrole->id); 220 $this->getDataGenerator()->enrol_user($student2->id, $course1->id, $studentrole->id); 221 $this->getDataGenerator()->enrol_user($student3->id, $course1->id, $gradeblerole); 222 223 $this->getDataGenerator()->enrol_user($student5->id, $course1->id, $nongradeblerole); 224 $this->getDataGenerator()->enrol_user($student6->id, $course1->id, $studentrole->id); 225 $this->getDataGenerator()->enrol_user($teacher->id, $course1->id, $teacherrole->id); 226 227 // User that is enrolled in a different course. 228 $this->getDataGenerator()->enrol_user($student4->id, $course2->id, $studentrole->id); 229 230 // Mark user as deleted. 231 $student6->deleted = 1; 232 $DB->update_record('user', $student6); 233 234 // Create grade items in course 1. 235 $assign1 = $this->getDataGenerator()->create_module('assign', ['course' => $course1->id]); 236 $assign2 = $this->getDataGenerator()->create_module('assign', ['course' => $course1->id]); 237 $quiz1 = $this->getDataGenerator()->create_module('quiz', ['course' => $course1->id]); 238 239 $manuaitem = new \grade_item($this->getDataGenerator()->create_grade_item([ 240 'itemname' => 'Grade item1', 241 'idnumber' => 'git1', 242 'courseid' => $course1->id, 243 ])); 244 245 // Create grade items in course 2. 246 $assign3 = $this->getDataGenerator()->create_module('assign', ['course' => $course2->id]); 247 248 // Grade users in first course. 249 $cm = cm_info::create(get_coursemodule_from_instance('assign', $assign1->id)); 250 $assigninstance = new assign($cm->context, $cm, $course1); 251 $grade = $assigninstance->get_user_grade($student1->id, true); 252 $grade->grade = 40; 253 $assigninstance->update_grade($grade); 254 255 $cm = cm_info::create(get_coursemodule_from_instance('assign', $assign2->id)); 256 $assigninstance = new assign($cm->context, $cm, $course1); 257 $grade = $assigninstance->get_user_grade($student3->id, true); 258 $grade->grade = 50; 259 $assigninstance->update_grade($grade); 260 261 // Override grade for assignment in gradebook. 262 $gi = \grade_item::fetch([ 263 'itemtype' => 'mod', 264 'itemmodule' => 'assign', 265 'iteminstance' => $cm->instance, 266 'courseid' => $course1->id 267 ]); 268 $gi->update_final_grade($student3->id, 55); 269 270 // Grade user in second course. 271 $cm = cm_info::create(get_coursemodule_from_instance('assign', $assign3->id)); 272 $assigninstance = new assign($cm->context, $cm, $course2); 273 $grade = $assigninstance->get_user_grade($student4->id, true); 274 $grade->grade = 40; 275 $assigninstance->update_grade($grade); 276 277 $manuaitem->update_final_grade($student1->id, 1); 278 $manuaitem->update_final_grade($student3->id, 2); 279 280 // Trigger a regrade. 281 grade_force_full_regrading($course1->id); 282 grade_force_full_regrading($course2->id); 283 grade_regrade_final_grades($course1->id); 284 grade_regrade_final_grades($course2->id); 285 286 // Initialise reports. 287 $context1 = \context_course::instance($course1->id); 288 $context2 = \context_course::instance($course2->id); 289 290 $gpr1 = new grade_plugin_return( 291 [ 292 'type' => 'report', 293 'plugin' => 'summary', 294 'course' => $course1, 295 ] 296 ); 297 298 $gpr2 = new grade_plugin_return( 299 [ 300 'type' => 'report', 301 'plugin' => 'summary', 302 'course' => $course2, 303 ] 304 ); 305 306 $report1 = new grade_report_summary($course1->id, $gpr1, $context1); 307 $report2 = new grade_report_summary($course2->id, $gpr2, $context2); 308 309 $ungradedcounts = []; 310 $ungradedcounts[$course1->id] = $report1->ungraded_counts(); 311 $ungradedcounts[$course2->id] = $report2->ungraded_counts(); 312 313 foreach ($ungradedcounts as $key => $ungradedcount) { 314 $gradeitems = grade_item::fetch_all(['courseid' => $key]); 315 if ($key == $course1->id) { 316 $gradeitemkeys = array_keys($gradeitems); 317 $ungradedcountskeys = array_keys($ungradedcount['ungradedcounts']); 318 319 // For each grade item there is some student that is not graded yet in course 1. 320 $this->assertEmpty(array_diff_key($gradeitemkeys, $ungradedcountskeys)); 321 322 // Only quiz does not have any grades, the remaning 4 grade items should have some. 323 // We can do more and match by gradeitem id numbers. But feels like overengeneering. 324 $this->assertEquals(4, count($ungradedcount['sumarray'])); 325 } else { 326 327 // In course 2 there is one student, and he is graded. 328 $this->assertEmpty($ungradedcount['ungradedcounts']); 329 330 // There are 2 grade items and they both have some grades. 331 $this->assertEquals(2, count($ungradedcount['sumarray'])); 332 } 333 334 foreach ($gradeitems as $gradeitem) { 335 $sumgrades = null; 336 if (array_key_exists($gradeitem->id, $ungradedcount['ungradedcounts'])) { 337 $ungradeditem = $ungradedcount['ungradedcounts'][$gradeitem->id]; 338 if ($gradeitem->itemtype === 'course') { 339 $this->assertEquals(1, $ungradeditem->count); 340 } else if ($gradeitem->itemmodule === 'assign') { 341 $this->assertEquals(2, $ungradeditem->count); 342 } else if ($gradeitem->itemmodule === 'quiz') { 343 $this->assertEquals(3, $ungradeditem->count); 344 } else if ($gradeitem->itemtype === 'manual') { 345 $this->assertEquals(1, $ungradeditem->count); 346 } 347 } 348 349 if (array_key_exists($gradeitem->id, $ungradedcount['sumarray'])) { 350 $sumgrades = $ungradedcount['sumarray'][$gradeitem->id]; 351 if ($gradeitem->itemtype === 'course') { 352 if ($key == $course1->id) { 353 $this->assertEquals('98.00000', $sumgrades); // 40 + 55 + 1 + 2 354 } else { 355 $this->assertEquals('40.00000', $sumgrades); 356 } 357 } else if ($gradeitem->itemmodule === 'assign') { 358 if (($gradeitem->itemname === $assign1->name) || ($gradeitem->itemname === $assign3->name)) { 359 $this->assertEquals('40.00000', $sumgrades); 360 } else { 361 $this->assertEquals('55.00000', $sumgrades); 362 } 363 } else if ($gradeitem->itemtype === 'manual') { 364 $this->assertEquals('3.00000', $sumgrades); 365 } 366 } 367 } 368 } 369 } 370 371 /** 372 * Tests that ungraded_counts calculates count and sum of grades correctly for groups when there are graded users. 373 * 374 * @covers \grade_report::ungraded_counts 375 */ 376 public function test_ungraded_count_sumgrades_groups() { 377 global $DB; 378 379 $this->resetAfterTest(true); 380 381 $course = $this->getDataGenerator()->create_course(); 382 383 $studentrole = $DB->get_record('role', ['shortname' => 'student'], '*', MUST_EXIST); 384 385 // Create users. 386 387 $student1 = $this->getDataGenerator()->create_user(['username' => 'student1']); 388 $student2 = $this->getDataGenerator()->create_user(['username' => 'student2']); 389 $student3 = $this->getDataGenerator()->create_user(['username' => 'student3']); 390 391 // Enrol students. 392 $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id); 393 $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id); 394 $this->getDataGenerator()->enrol_user($student3->id, $course->id, $studentrole->id); 395 396 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]); 397 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]); 398 $this->getDataGenerator()->create_group_member(['userid' => $student1->id, 'groupid' => $group1->id]); 399 $this->getDataGenerator()->create_group_member(['userid' => $student2->id, 'groupid' => $group2->id]); 400 $this->getDataGenerator()->create_group_member(['userid' => $student3->id, 'groupid' => $group2->id]); 401 $DB->set_field('course', 'groupmode', SEPARATEGROUPS, ['id' => $course->id]); 402 403 // Create grade items in course 1. 404 $assign1 = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]); 405 $assign2 = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]); 406 $quiz1 = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]); 407 408 $manuaitem = new \grade_item($this->getDataGenerator()->create_grade_item([ 409 'itemname' => 'Grade item1', 410 'idnumber' => 'git1', 411 'courseid' => $course->id, 412 ])); 413 414 // Grade users in first course. 415 $cm = cm_info::create(get_coursemodule_from_instance('assign', $assign1->id)); 416 $assigninstance = new assign($cm->context, $cm, $course); 417 $grade = $assigninstance->get_user_grade($student1->id, true); 418 $grade->grade = 40; 419 $assigninstance->update_grade($grade); 420 421 $cm = cm_info::create(get_coursemodule_from_instance('assign', $assign2->id)); 422 $assigninstance = new assign($cm->context, $cm, $course); 423 $grade = $assigninstance->get_user_grade($student3->id, true); 424 $grade->grade = 50; 425 $assigninstance->update_grade($grade); 426 427 $manuaitem->update_final_grade($student1->id, 1); 428 $manuaitem->update_final_grade($student3->id, 2); 429 430 // Trigger a regrade. 431 grade_force_full_regrading($course->id); 432 grade_regrade_final_grades($course->id); 433 434 // Initialise report. 435 $context = \context_course::instance($course->id); 436 437 $gpr1 = new grade_plugin_return( 438 [ 439 'type' => 'report', 440 'plugin' => 'summary', 441 'course' => $course, 442 'groupid' => $group1->id, 443 ] 444 ); 445 446 $gpr2 = new grade_plugin_return( 447 [ 448 'type' => 'report', 449 'plugin' => 'summary', 450 'course' => $course, 451 'groupid' => $group2->id, 452 ] 453 ); 454 455 $report1 = new grade_report_summary($course->id, $gpr1, $context); 456 $report2 = new grade_report_summary($course->id, $gpr2, $context); 457 458 $ungradedcounts = []; 459 $ungradedcounts[$group1->id] = $report1->ungraded_counts(); 460 $ungradedcounts[$group2->id] = $report2->ungraded_counts(); 461 462 $gradeitems = grade_item::fetch_all(['courseid' => $course->id]); 463 464 // In group1 there is 1 student and assign1 and quiz1 are not graded for him. 465 $this->assertEquals(2, count($ungradedcounts[$group1->id]['ungradedcounts'])); 466 467 // In group1 manual grade item, assign1 and course total have some grades. 468 $this->assertEquals(3, count($ungradedcounts[$group1->id]['sumarray'])); 469 470 // In group2 student2 has no grades at all so all 5 grade items should present. 471 $this->assertEquals(5, count($ungradedcounts[$group2->id]['ungradedcounts'])); 472 473 // In group2 manual grade item, assign2 and course total have some grades. 474 $this->assertEquals(3, count($ungradedcounts[$group2->id]['sumarray'])); 475 476 foreach ($gradeitems as $gradeitem) { 477 $sumgrades = null; 478 479 foreach ($ungradedcounts as $key => $ungradedcount) { 480 if (array_key_exists($gradeitem->id, $ungradedcount['ungradedcounts'])) { 481 $ungradeditem = $ungradedcount['ungradedcounts'][$gradeitem->id]; 482 if ($key == $group1->id) { 483 // Both assign2 and quiz1 are not graded for student1. 484 $this->assertEquals(1, $ungradeditem->count); 485 } else { 486 if ($gradeitem->itemtype === 'course') { 487 $this->assertEquals(1, $ungradeditem->count); 488 } else if ($gradeitem->itemmodule === 'assign') { 489 if ($gradeitem->itemname === $assign1->name) { 490 // In group2 assign1 is not graded for anyone. 491 $this->assertEquals(2, $ungradeditem->count); 492 } else { 493 // In group2 assign2 is graded for student3. 494 $this->assertEquals(1, $ungradeditem->count); 495 } 496 } else if ($gradeitem->itemmodule === 'quiz') { 497 $this->assertEquals(2, $ungradeditem->count); 498 } else if ($gradeitem->itemtype === 'manual') { 499 $this->assertEquals(1, $ungradeditem->count); 500 } 501 } 502 } 503 504 if (array_key_exists($gradeitem->id, $ungradedcount['sumarray'])) { 505 $sumgrades = $ungradedcount['sumarray'][$gradeitem->id]; 506 if ($key == $group1->id) { 507 if ($gradeitem->itemtype === 'course') { 508 $this->assertEquals('41.00000', $sumgrades); 509 } else if ($gradeitem->itemmodule === 'assign') { 510 $this->assertEquals('40.00000', $sumgrades); 511 } else if ($gradeitem->itemtype === 'manual') { 512 $this->assertEquals('1.00000', $sumgrades); 513 } 514 } else { 515 if ($gradeitem->itemtype === 'course') { 516 $this->assertEquals('52.00000', $sumgrades); 517 } else if ($gradeitem->itemmodule === 'assign') { 518 $this->assertEquals('50.00000', $sumgrades); 519 } else if ($gradeitem->itemtype === 'manual') { 520 $this->assertEquals('2.00000', $sumgrades); 521 } 522 } 523 } 524 } 525 } 526 } 527 528 /** 529 * Tests for calculate_average. 530 * @dataProvider calculate_average_data() 531 * @param int $meanselection Whether to inlcude all grades or non-empty grades in aggregation. 532 * @param array $expectedmeancount expected meancount value 533 * @param array $expectedaverage expceted average value 534 * 535 * @covers \grade_report::calculate_average 536 */ 537 public function test_calculate_average(int $meanselection, array $expectedmeancount, array $expectedaverage) { 538 global $DB; 539 540 $this->resetAfterTest(true); 541 542 $course = $this->getDataGenerator()->create_course(); 543 544 $student1 = $this->getDataGenerator()->create_user(['username' => 'student1']); 545 $student2 = $this->getDataGenerator()->create_user(['username' => 'student2']); 546 $student3 = $this->getDataGenerator()->create_user(['username' => 'student3']); 547 548 $studentrole = $DB->get_record('role', ['shortname' => 'student'], '*', MUST_EXIST); 549 550 // Enrol students. 551 $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id); 552 $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id); 553 $this->getDataGenerator()->enrol_user($student3->id, $course->id, $studentrole->id); 554 555 // Create activities. 556 $assign1 = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]); 557 $assign2 = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]); 558 $quiz1 = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]); 559 560 // Grade users. 561 $cm = cm_info::create(get_coursemodule_from_instance('assign', $assign1->id)); 562 $assigninstance = new assign($cm->context, $cm, $course); 563 $grade = $assigninstance->get_user_grade($student1->id, true); 564 $grade->grade = 40; 565 $assigninstance->update_grade($grade); 566 567 $grade = $assigninstance->get_user_grade($student2->id, true); 568 $grade->grade = 30; 569 $assigninstance->update_grade($grade); 570 571 $cm = cm_info::create(get_coursemodule_from_instance('assign', $assign2->id)); 572 $assigninstance = new assign($cm->context, $cm, $course); 573 $grade = $assigninstance->get_user_grade($student3->id, true); 574 $grade->grade = 50; 575 $assigninstance->update_grade($grade); 576 577 $grade = $assigninstance->get_user_grade($student1->id, true); 578 $grade->grade = 100; 579 $assigninstance->update_grade($grade); 580 581 // Make a manual grade items. 582 $manuaitem = new \grade_item($this->getDataGenerator()->create_grade_item([ 583 'itemname' => 'Grade item1', 584 'idnumber' => 'git1', 585 'courseid' => $course->id, 586 ])); 587 $manuaitem->update_final_grade($student1->id, 1); 588 $manuaitem->update_final_grade($student3->id, 2); 589 590 // Initialise report. 591 $context = \context_course::instance($course->id); 592 593 $gpr = new grade_plugin_return( 594 [ 595 'type' => 'report', 596 'plugin' => 'summary', 597 'course' => $course, 598 ] 599 ); 600 601 $report = new grade_report_summary($course->id, $gpr, $context); 602 603 $ungradedcounts = $report->ungraded_counts(); 604 $ungradedcounts['report']['meanselection'] = $meanselection; 605 606 $gradeitems = grade_item::fetch_all(['courseid' => $course->id]); 607 608 foreach ($gradeitems as $gradeitem) { 609 $name = $gradeitem->itemname . ' ' . $gradeitem->itemtype; 610 $aggr = $report->calculate_average($gradeitem, $ungradedcounts); 611 612 $this->assertEquals($expectedmeancount[$name], $aggr['meancount']); 613 $this->assertEquals($expectedaverage[$name], $aggr['average']); 614 } 615 } 616 617 /** 618 * Data provider for test_calculate_average 619 * 620 * @return array of testing scenarios 621 */ 622 public function calculate_average_data() : array { 623 return [ 624 'Non-empty grades' => [ 625 'meanselection' => 1, 626 'expectedmeancount' => [' course' => 3, 'Assignment 1 mod' => 2, 'Assignment 2 mod' => 2, 627 'Quiz 1 mod' => 0, 'Grade item1 manual' => 2], 628 'expectedaverage' => [' course' => 73.33333333333333, 'Assignment 1 mod' => 35.0, 629 'Assignment 2 mod' => 75.0, 'Quiz 1 mod' => null, 'Grade item1 manual' => 1.5], 630 ], 631 'All grades' => [ 632 'meanselection' => 0, 633 'expectedmeancount' => [' course' => 3, 'Assignment 1 mod' => 3, 'Assignment 2 mod' => 3, 634 'Quiz 1 mod' => 3, 'Grade item1 manual' => 3], 635 'expectedaverage' => [' course' => 73.33333333333333, 'Assignment 1 mod' => 23.333333333333332, 636 'Assignment 2 mod' => 50.0, 'Quiz 1 mod' => null, 'Grade item1 manual' => 1.0], 637 ], 638 ]; 639 } 640 641 /** 642 * Tests for item types. 643 * 644 * @covers \grade_report::item_types 645 */ 646 public function test_item_types() { 647 $this->resetAfterTest(true); 648 649 $course1 = $this->getDataGenerator()->create_course(); 650 $course2 = $this->getDataGenerator()->create_course(); 651 652 // Create activities. 653 $this->getDataGenerator()->create_module('assign', ['course' => $course1->id]); 654 $this->getDataGenerator()->create_module('assign', ['course' => $course1->id]); 655 $this->getDataGenerator()->create_module('quiz', ['course' => $course1->id]); 656 657 $this->getDataGenerator()->create_module('assign', ['course' => $course2->id]); 658 659 // Create manual grade items. 660 new \grade_item($this->getDataGenerator()->create_grade_item([ 661 'itemname' => 'Grade item1', 662 'idnumber' => 'git1', 663 'courseid' => $course1->id, 664 ])); 665 666 new \grade_item($this->getDataGenerator()->create_grade_item([ 667 'itemname' => 'Grade item2', 668 'idnumber' => 'git2', 669 'courseid' => $course2->id, 670 ])); 671 672 // Create a grade category (it should not be fetched by item_types). 673 new \grade_category($this->getDataGenerator() 674 ->create_grade_category(['courseid' => $course1->id]), false); 675 676 // Initialise reports. 677 $context = \context_course::instance($course1->id); 678 679 $gpr = new grade_plugin_return( 680 [ 681 'type' => 'report', 682 'plugin' => 'summary', 683 'course' => $course1, 684 ] 685 ); 686 687 $report1 = new grade_report_summary($course1->id, $gpr, $context); 688 689 $context = \context_course::instance($course2->id); 690 691 $gpr = new grade_plugin_return( 692 [ 693 'type' => 'report', 694 'plugin' => 'summary', 695 'course' => $course2, 696 ] 697 ); 698 699 $report2 = new grade_report_summary($course2->id, $gpr, $context); 700 701 $gradeitems1 = $report1->item_types(); 702 $gradeitems2 = $report2->item_types(); 703 704 $this->assertEquals(3, count($gradeitems1)); 705 $this->assertEquals(2, count($gradeitems2)); 706 707 $this->assertArrayHasKey('assign', $gradeitems1); 708 $this->assertArrayHasKey('quiz', $gradeitems1); 709 $this->assertArrayHasKey('manual', $gradeitems1); 710 711 $this->assertArrayHasKey('assign', $gradeitems2); 712 $this->assertArrayHasKey('manual', $gradeitems2); 713 } 714 715 /** 716 * Test get_gradable_users() function. 717 * 718 * @covers ::get_gradable_users 719 */ 720 public function test_get_gradable_users() { 721 global $DB; 722 723 $this->setAdminUser(); 724 $this->resetAfterTest(true); 725 726 $roleteacher = $DB->get_record('role', ['shortname' => 'teacher'], '*', MUST_EXIST); 727 728 // Create a course. 729 $course = $this->getDataGenerator()->create_course(); 730 $coursecontext = \context_course::instance($course->id); 731 // Create groups. 732 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]); 733 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]); 734 // Create and enrol a teacher and some students into the course. 735 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 736 $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 737 $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 738 $student3 = $this->getDataGenerator()->create_and_enrol($course, 'student'); 739 // Add student1 and student2 to group1. 740 $this->getDataGenerator()->create_group_member(['groupid' => $group1->id, 'userid' => $student1->id]); 741 $this->getDataGenerator()->create_group_member(['groupid' => $group1->id, 'userid' => $student2->id]); 742 // Add student3 to group2. 743 $this->getDataGenerator()->create_group_member(['groupid' => $group2->id, 'userid' => $student3->id]); 744 745 // Perform a regrade before creating the report. 746 grade_regrade_final_grades($course->id); 747 // Should return all gradable users (only students). 748 $gradableusers = get_gradable_users($course->id); 749 $this->assertEqualsCanonicalizing([$student1->id, $student2->id, $student3->id], array_keys($gradableusers)); 750 751 // Now, let's suspend the enrolment of student2. 752 $this->getDataGenerator()->enrol_user($student2->id, $course->id, 'student', 'manual', 0, 0, ENROL_USER_SUSPENDED); 753 // Should return only the active gradable users (student1 and student3). 754 $gradableusers = \grade_report::get_gradable_users($course->id); 755 $this->assertEqualsCanonicalizing([$student1->id, $student3->id], array_keys($gradableusers)); 756 757 // Give teacher 'viewsuspendedusers' capability and set a preference to display suspended users. 758 assign_capability('moodle/course:viewsuspendedusers', CAP_ALLOW, $roleteacher->id, $coursecontext, true); 759 set_user_preference('grade_report_showonlyactiveenrol', false, $teacher); 760 accesslib_clear_all_caches_for_unit_testing(); 761 762 $this->setUser($teacher); 763 // Should return all gradable users (including suspended enrolments). 764 $gradableusers = \grade_report::get_gradable_users($course->id); 765 $this->assertEqualsCanonicalizing([$student1->id, $student2->id, $student3->id], array_keys($gradableusers)); 766 767 // Reactivate the course enrolment of student2. 768 $this->getDataGenerator()->enrol_user($student2->id, $course->id, 'student', 'manual', 0, 0, ENROL_USER_ACTIVE); 769 $this->setAdminUser(); 770 // Should return all gradable users from group1 (student1 and student2). 771 $gradableusers = \grade_report::get_gradable_users($course->id, $group1->id); 772 $this->assertEqualsCanonicalizing([$student1->id, $student2->id], array_keys($gradableusers)); 773 // Should return all gradable users from group2 (student3). 774 $gradableusers = \grade_report::get_gradable_users($course->id, $group2->id); 775 $this->assertEqualsCanonicalizing([$student3->id], array_keys($gradableusers)); 776 } 777 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body