See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 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 * @package core_grades 19 * @category phpunit 20 * @copyright nicolas@moodle.com 21 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 22 */ 23 24 defined('MOODLE_INTERNAL') || die(); 25 26 require_once (__DIR__.'/fixtures/lib.php'); 27 28 29 class core_grade_category_testcase extends grade_base_testcase { 30 31 public function test_grade_category() { 32 $this->sub_test_grade_category_construct(); 33 $this->sub_test_grade_category_build_path(); 34 $this->sub_test_grade_category_fetch(); 35 $this->sub_test_grade_category_fetch_all(); 36 $this->sub_test_grade_category_update(); 37 $this->sub_test_grade_category_delete(); 38 $this->sub_test_grade_category_insert(); 39 $this->sub_test_grade_category_qualifies_for_regrading(); 40 $this->sub_test_grade_category_force_regrading(); 41 $this->sub_test_grade_category_aggregate_grades(); 42 $this->sub_test_grade_category_apply_limit_rules(); 43 $this->sub_test_grade_category_is_aggregationcoef_used(); 44 $this->sub_test_grade_category_aggregation_uses_aggregationcoef(); 45 $this->sub_test_grade_category_fetch_course_tree(); 46 $this->sub_test_grade_category_get_children(); 47 $this->sub_test_grade_category_load_grade_item(); 48 $this->sub_test_grade_category_get_grade_item(); 49 $this->sub_test_grade_category_load_parent_category(); 50 $this->sub_test_grade_category_get_parent_category(); 51 $this->sub_test_grade_category_get_name(); 52 $this->sub_test_grade_category_generate_grades_aggregationweight(); 53 $this->sub_test_grade_category_set_parent(); 54 $this->sub_test_grade_category_get_final(); 55 $this->sub_test_grade_category_get_sortorder(); 56 $this->sub_test_grade_category_set_sortorder(); 57 $this->sub_test_grade_category_is_editable(); 58 $this->sub_test_grade_category_move_after_sortorder(); 59 $this->sub_test_grade_category_is_course_category(); 60 $this->sub_test_grade_category_fetch_course_category(); 61 $this->sub_test_grade_category_is_locked(); 62 $this->sub_test_grade_category_set_locked(); 63 $this->sub_test_grade_category_is_hidden(); 64 $this->sub_test_grade_category_set_hidden(); 65 $this->sub_test_grade_category_can_control_visibility(); 66 $this->sub_test_grade_category_total_visibility(); 67 68 // This won't work until MDL-11837 is complete. 69 // $this->sub_test_grade_category_generate_grades(); 70 71 // Do this last as adding a second course category messes up the data. 72 $this->sub_test_grade_category_insert_course_category(); 73 $this->sub_test_grade_category_is_extracredit_used(); 74 $this->sub_test_grade_category_aggregation_uses_extracredit(); 75 } 76 77 // Adds 3 new grade categories at various depths. 78 protected function sub_test_grade_category_construct() { 79 $course_category = grade_category::fetch_course_category($this->courseid); 80 81 $params = new stdClass(); 82 83 $params->courseid = $this->courseid; 84 $params->fullname = 'unittestcategory4'; 85 86 $grade_category = new grade_category($params, false); 87 $grade_category->insert(); 88 $this->grade_categories[] = $grade_category; 89 90 $this->assertEquals($params->courseid, $grade_category->courseid); 91 $this->assertEquals($params->fullname, $grade_category->fullname); 92 $this->assertEquals(2, $grade_category->depth); 93 $this->assertEquals("/$course_category->id/$grade_category->id/", $grade_category->path); 94 $parentpath = $grade_category->path; 95 96 // Test a child category. 97 $params->parent = $grade_category->id; 98 $params->fullname = 'unittestcategory5'; 99 $grade_category = new grade_category($params, false); 100 $grade_category->insert(); 101 $this->grade_categories[] = $grade_category; 102 103 $this->assertEquals(3, $grade_category->depth); 104 $this->assertEquals($parentpath.$grade_category->id."/", $grade_category->path); 105 $parentpath = $grade_category->path; 106 107 // Test a third depth category. 108 $params->parent = $grade_category->id; 109 $params->fullname = 'unittestcategory6'; 110 $grade_category = new grade_category($params, false); 111 $grade_category->insert(); 112 $this->grade_categories[50] = $grade_category;// Going to delete this one later hence the special index. 113 114 $this->assertEquals(4, $grade_category->depth); 115 $this->assertEquals($parentpath.$grade_category->id."/", $grade_category->path); 116 } 117 118 protected function sub_test_grade_category_build_path() { 119 $grade_category = new grade_category($this->grade_categories[1]); 120 $this->assertTrue(method_exists($grade_category, 'build_path')); 121 $path = grade_category::build_path($grade_category); 122 $this->assertEquals($grade_category->path, $path); 123 } 124 125 protected function sub_test_grade_category_fetch() { 126 $grade_category = new grade_category(); 127 $this->assertTrue(method_exists($grade_category, 'fetch')); 128 129 $grade_category = grade_category::fetch(array('id'=>$this->grade_categories[0]->id)); 130 $this->assertEquals($this->grade_categories[0]->id, $grade_category->id); 131 $this->assertEquals($this->grade_categories[0]->fullname, $grade_category->fullname); 132 } 133 134 protected function sub_test_grade_category_fetch_all() { 135 $grade_category = new grade_category(); 136 $this->assertTrue(method_exists($grade_category, 'fetch_all')); 137 138 $grade_categories = grade_category::fetch_all(array('courseid'=>$this->courseid)); 139 $this->assertEquals(count($this->grade_categories), count($grade_categories)-1); 140 } 141 142 protected function sub_test_grade_category_update() { 143 global $DB; 144 $grade_category = new grade_category($this->grade_categories[0]); 145 $this->assertTrue(method_exists($grade_category, 'update')); 146 147 $grade_category->fullname = 'Updated info for this unittest grade_category'; 148 $grade_category->path = null; // Path must be recalculated if missing. 149 $grade_category->depth = null; 150 $grade_category->aggregation = GRADE_AGGREGATE_MAX; // Should force regrading. 151 152 $grade_item = $grade_category->get_grade_item(); 153 $this->assertEquals(0, $grade_item->needsupdate); 154 155 $this->assertTrue($grade_category->update()); 156 157 $fullname = $DB->get_field('grade_categories', 'fullname', array('id' => $this->grade_categories[0]->id)); 158 $this->assertEquals($grade_category->fullname, $fullname); 159 160 $path = $DB->get_field('grade_categories', 'path', array('id' => $this->grade_categories[0]->id)); 161 $this->assertEquals($grade_category->path, $path); 162 163 $depth = $DB->get_field('grade_categories', 'depth', array('id' => $this->grade_categories[0]->id)); 164 $this->assertEquals($grade_category->depth, $depth); 165 166 $grade_item = $grade_category->get_grade_item(); 167 $this->assertEquals(1, $grade_item->needsupdate); 168 } 169 170 protected function sub_test_grade_category_delete() { 171 global $DB; 172 173 $grade_category = new grade_category($this->grade_categories[50]); 174 $this->assertTrue(method_exists($grade_category, 'delete')); 175 176 $this->assertTrue($grade_category->delete()); 177 $this->assertFalse($DB->get_record('grade_categories', array('id' => $grade_category->id))); 178 } 179 180 protected function sub_test_grade_category_insert() { 181 $course_category = grade_category::fetch_course_category($this->courseid); 182 183 $grade_category = new grade_category(); 184 $this->assertTrue(method_exists($grade_category, 'insert')); 185 186 $grade_category->fullname = 'unittestcategory4'; 187 $grade_category->courseid = $this->courseid; 188 $grade_category->aggregation = GRADE_AGGREGATE_MEAN; 189 $grade_category->aggregateonlygraded = 1; 190 $grade_category->keephigh = 100; 191 $grade_category->droplow = 10; 192 $grade_category->hidden = 0; 193 $grade_category->parent = $this->grade_categories[1]->id; // sub_test_grade_category_delete() removed the category at 0. 194 195 $grade_category->insert(); 196 197 $this->assertEquals('/'.$course_category->id.'/'.$this->grade_categories[1]->parent.'/'.$this->grade_categories[1]->id.'/'.$grade_category->id.'/', $grade_category->path); 198 $this->assertEquals(4, $grade_category->depth); 199 200 $last_grade_category = end($this->grade_categories); 201 202 $this->assertFalse(empty($grade_category->grade_item)); 203 $this->assertEquals($grade_category->id, $grade_category->grade_item->iteminstance); 204 $this->assertEquals('category', $grade_category->grade_item->itemtype); 205 206 $this->assertEquals($grade_category->id, $last_grade_category->id + 1); 207 $this->assertFalse(empty($grade_category->timecreated)); 208 $this->assertFalse(empty($grade_category->timemodified)); 209 } 210 211 protected function sub_test_grade_category_qualifies_for_regrading() { 212 $grade_category = new grade_category($this->grade_categories[1]); 213 $this->assertTrue(method_exists($grade_category, 'qualifies_for_regrading')); 214 $this->assertFalse($grade_category->qualifies_for_regrading()); 215 216 $grade_category->aggregation = GRADE_AGGREGATE_MAX; 217 $this->assertTrue($grade_category->qualifies_for_regrading()); 218 219 $grade_category = new grade_category($this->grade_categories[1]); 220 $grade_category->droplow = 99; 221 $this->assertTrue($grade_category->qualifies_for_regrading()); 222 223 $grade_category = new grade_category($this->grade_categories[1]); 224 $grade_category->keephigh = 99; 225 $this->assertTrue($grade_category->qualifies_for_regrading()); 226 } 227 228 protected function sub_test_grade_category_force_regrading() { 229 $grade_category = new grade_category($this->grade_categories[1]); 230 $this->assertTrue(method_exists($grade_category, 'force_regrading')); 231 232 $grade_category->load_grade_item(); 233 $this->assertEquals(0, $grade_category->grade_item->needsupdate); 234 235 $grade_category->force_regrading(); 236 237 $grade_category->grade_item = null; 238 $grade_category->load_grade_item(); 239 240 $this->assertEquals(1, $grade_category->grade_item->needsupdate); 241 } 242 243 /** 244 * Tests the setting of the grade_grades aggregationweight column. 245 * Currently, this is only a regression test for MDL-51715. 246 * This must be run before sub_test_grade_category_set_parent(), which alters 247 * the fixture. 248 */ 249 protected function sub_test_grade_category_generate_grades_aggregationweight() { 250 global $DB; 251 252 // Start of regression test for MDL-51715. 253 // grade_categories [1] and [2] are child categories of [0] 254 // Ensure that grades have been generated with fixture data. 255 $childcat1 = new grade_category($this->grade_categories[1]); 256 $childcat1itemid = $childcat1->load_grade_item()->id; 257 $childcat1->generate_grades(); 258 $childcat2 = new grade_category($this->grade_categories[2]); 259 $childcat2itemid = $childcat2->load_grade_item()->id; 260 $childcat2->generate_grades(); 261 $parentcat = new grade_category($this->grade_categories[0]); 262 $parentcat->generate_grades(); 263 264 // Drop low and and re-generate to produce 'dropped' aggregation status. 265 $parentcat->droplow = 1; 266 $parentcat->generate_grades(); 267 268 $this->assertTrue($DB->record_exists_select( 269 'grade_grades', 270 "aggregationstatus='dropped' and itemid in (?,?)", 271 array($childcat1itemid, $childcat2itemid))); 272 $this->assertFalse($DB->record_exists_select( 273 'grade_grades', 274 "aggregationstatus='dropped' and aggregationweight > 0.00"), 275 "aggregationweight should be 0.00 if aggregationstatus=='dropped'"); 276 277 // Reset grade data to be consistent with fixture data. 278 $parentcat->droplow = 0; 279 $parentcat->generate_grades(); 280 281 // Blank out the final grade for one of the child categories and re-generate 282 // to produce 'novalue' aggregationstatus. Direct DB update is testing shortcut. 283 $DB->set_field('grade_grades', 'finalgrade', null, array('itemid'=>$childcat1itemid)); 284 $parentcat->generate_grades(); 285 286 $this->assertFalse($DB->record_exists_select( 287 'grade_grades', 288 "aggregationstatus='dropped' and itemid in (?,?)", 289 array($childcat1itemid, $childcat2itemid))); 290 $this->assertTrue($DB->record_exists_select( 291 'grade_grades', 292 "aggregationstatus='novalue' and itemid = ?", 293 array($childcat1itemid))); 294 $this->assertFalse($DB->record_exists_select( 295 'grade_grades', 296 "aggregationstatus='novalue' and aggregationweight > 0.00"), 297 "aggregationweight should be 0.00 if aggregationstatus=='novalue'"); 298 299 // Re-generate to be consistent with fixture data. 300 $childcat1->generate_grades(); 301 $parentcat->generate_grades(); 302 // End of regression test for MDL-51715. 303 } 304 305 /** 306 * Tests the calculation of grades using the various aggregation methods with and without hidden grades 307 * This will not work entirely until MDL-11837 is done 308 */ 309 protected function sub_test_grade_category_generate_grades() { 310 global $DB; 311 312 // Inserting some special grade items to make testing the final grade calculation easier. 313 $params = new stdClass(); 314 $params->courseid = $this->courseid; 315 $params->fullname = 'unittestgradecalccategory'; 316 $params->aggregation = GRADE_AGGREGATE_MEAN; 317 $params->aggregateonlygraded = 0; 318 $grade_category = new grade_category($params, false); 319 $grade_category->insert(); 320 321 $this->assertTrue(method_exists($grade_category, 'generate_grades')); 322 323 $grade_category->load_grade_item(); 324 $cgi = $grade_category->get_grade_item(); 325 $cgi->grademin = 0; 326 $cgi->grademax = 20; // 3 grade items out of 10 but category is out of 20 to force scaling to occur. 327 $cgi->update(); 328 329 // 3 grade items each with a maximum grade of 10. 330 $grade_items = array(); 331 for ($i=0; $i<3; $i++) { 332 $grade_items[$i] = new grade_item(); 333 $grade_items[$i]->courseid = $this->courseid; 334 $grade_items[$i]->categoryid = $grade_category->id; 335 $grade_items[$i]->itemname = 'manual grade_item '.$i; 336 $grade_items[$i]->itemtype = 'manual'; 337 $grade_items[$i]->itemnumber = 0; 338 $grade_items[$i]->needsupdate = false; 339 $grade_items[$i]->gradetype = GRADE_TYPE_VALUE; 340 $grade_items[$i]->grademin = 0; 341 $grade_items[$i]->grademax = 10; 342 $grade_items[$i]->iteminfo = 'Manual grade item used for unit testing'; 343 $grade_items[$i]->timecreated = time(); 344 $grade_items[$i]->timemodified = time(); 345 346 // Used as the weight by weighted mean and as extra credit by mean with extra credit. 347 // Will be 0, 1 and 2. 348 $grade_items[$i]->aggregationcoef = $i; 349 350 $grade_items[$i]->insert(); 351 } 352 353 // A grade for each grade item. 354 $grade_grades = array(); 355 for ($i=0; $i<3; $i++) { 356 $grade_grades[$i] = new grade_grade(); 357 $grade_grades[$i]->itemid = $grade_items[$i]->id; 358 $grade_grades[$i]->userid = $this->userid; 359 $grade_grades[$i]->rawgrade = ($i+1)*2; // Produce grade grades of 2, 4 and 6. 360 $grade_grades[$i]->finalgrade = ($i+1)*2; 361 $grade_grades[$i]->timecreated = time(); 362 $grade_grades[$i]->timemodified = time(); 363 $grade_grades[$i]->information = '1 of 2 grade_grades'; 364 $grade_grades[$i]->informationformat = FORMAT_PLAIN; 365 $grade_grades[$i]->feedback = 'Good, but not good enough..'; 366 $grade_grades[$i]->feedbackformat = FORMAT_PLAIN; 367 368 $grade_grades[$i]->insert(); 369 } 370 371 // 3 grade items with 1 grade_grade each. 372 // grade grades have the values 2, 4 and 6. 373 374 // First correct answer is the aggregate with all 3 grades. 375 // Second correct answer is with the first grade (value 2) hidden. 376 377 $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MEDIAN, 'GRADE_AGGREGATE_MEDIAN', 8, 8); 378 $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MAX, 'GRADE_AGGREGATE_MAX', 12, 12); 379 $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MODE, 'GRADE_AGGREGATE_MODE', 12, 12); 380 381 // Weighted mean. note grade totals are rounded to an int to prevent rounding discrepancies. correct final grade isnt actually exactly 10 382 // 3 items with grades 2, 4 and 6 with weights 0, 1 and 2 and all out of 10. then doubled to be out of 20. 383 $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_WEIGHTED_MEAN, 'GRADE_AGGREGATE_WEIGHTED_MEAN', 10, 10); 384 385 // Simple weighted mean. 386 // 3 items with grades 2, 4 and 6 equally weighted and all out of 10. then doubled to be out of 20. 387 $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_WEIGHTED_MEAN2, 'GRADE_AGGREGATE_WEIGHTED_MEAN2', 8, 10); 388 389 // Mean of grades with extra credit. 390 // 3 items with grades 2, 4 and 6 with extra credit 0, 1 and 2 equally weighted and all out of 10. then doubled to be out of 20. 391 $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_EXTRACREDIT_MEAN, 'GRADE_AGGREGATE_EXTRACREDIT_MEAN', 10, 13); 392 393 // Aggregation tests the are affected by a hidden grade currently dont work as we dont store the altered grade in the database 394 // instead an in memory recalculation is done. This should be remedied by MDL-11837. 395 396 // Fails with 1 grade hidden. still reports 8 as being correct. 397 $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MEAN, 'GRADE_AGGREGATE_MEAN', 8, 10); 398 399 // Fails with 1 grade hidden. still reports 4 as being correct. 400 $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MIN, 'GRADE_AGGREGATE_MIN', 4, 8); 401 402 // Fails with 1 grade hidden. still reports 12 as being correct. 403 $this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_SUM, 'GRADE_AGGREGATE_SUM', 12, 10); 404 } 405 406 /** 407 * Test grade category aggregation using the supplied grade objects and aggregation method 408 * @param grade_category $grade_category the category to be tested 409 * @param array $grade_items array of instance of grade_item 410 * @param array $grade_grades array of instances of grade_grade 411 * @param int $aggmethod the aggregation method to apply ie GRADE_AGGREGATE_MEAN 412 * @param string $aggmethodname the name of the aggregation method to apply. Used to display any test failure messages 413 * @param int $correct1 the correct final grade for the category with NO items hidden 414 * @param int $correct2 the correct final grade for the category with the grade at $grade_grades[0] hidden 415 * @return void 416 */ 417 protected function helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, $aggmethod, $aggmethodname, $correct1, $correct2) { 418 $grade_category->aggregation = $aggmethod; 419 $grade_category->update(); 420 421 // Check grade_item isnt hidden from a previous test. 422 $grade_items[0]->set_hidden(0, true); 423 $this->helper_test_grade_aggregation_result($grade_category, $correct1, 'Testing aggregation method('.$aggmethodname.') with no items hidden %s'); 424 425 // Hide the grade item with grade of 2. 426 $grade_items[0]->set_hidden(1, true); 427 $this->helper_test_grade_aggregation_result($grade_category, $correct2, 'Testing aggregation method('.$aggmethodname.') with 1 item hidden %s'); 428 } 429 430 /** 431 * Verify the value of the category grade item for $this->userid 432 * @param grade_category $grade_category the category to be tested 433 * @param int $correctgrade the expected grade 434 * @param string msg The message that should be displayed if the correct grade is not found 435 * @return void 436 */ 437 protected function helper_test_grade_aggregation_result($grade_category, $correctgrade, $msg) { 438 global $DB; 439 440 $category_grade_item = $grade_category->get_grade_item(); 441 442 // This creates all the grade_grades we need. 443 grade_regrade_final_grades($this->courseid); 444 445 $grade = $DB->get_record('grade_grades', array('itemid'=>$category_grade_item->id, 'userid'=>$this->userid)); 446 $this->assertWithinMargin($grade->rawgrade, $grade->rawgrademin, $grade->rawgrademax); 447 $this->assertEquals(intval($correctgrade), intval($grade->finalgrade), $msg); 448 449 /* 450 * TODO this doesnt work as the grade_grades created by $grade_category->generate_grades(); dont 451 * observe the category's max grade 452 // delete the grade_grades for the category itself and check they get recreated correctly. 453 $DB->delete_records('grade_grades', array('itemid'=>$category_grade_item->id)); 454 $grade_category->generate_grades(); 455 456 $grade = $DB->get_record('grade_grades', array('itemid'=>$category_grade_item->id, 'userid'=>$this->userid)); 457 $this->assertWithinMargin($grade->rawgrade, $grade->rawgrademin, $grade->rawgrademax); 458 $this->assertEquals(intval($correctgrade), intval($grade->finalgrade), $msg); 459 * 460 */ 461 } 462 463 protected function sub_test_grade_category_aggregate_grades() { 464 $category = new grade_category($this->grade_categories[0]); 465 $this->assertTrue(method_exists($category, 'aggregate_grades')); 466 // Tested more fully via test_grade_category_generate_grades(). 467 } 468 469 protected function sub_test_grade_category_apply_limit_rules() { 470 $items[$this->grade_items[0]->id] = new grade_item($this->grade_items[0], false); 471 $items[$this->grade_items[1]->id] = new grade_item($this->grade_items[1], false); 472 $items[$this->grade_items[2]->id] = new grade_item($this->grade_items[2], false); 473 $items[$this->grade_items[4]->id] = new grade_item($this->grade_items[4], false); 474 475 // Test excluding the lowest 2 out of 4 grades from aggregation with no 0 grades. 476 $category = new grade_category(); 477 $category->droplow = 2; 478 $grades = array($this->grade_items[0]->id=>5.374, 479 $this->grade_items[1]->id=>9.4743, 480 $this->grade_items[2]->id=>2.5474, 481 $this->grade_items[4]->id=>7.3754); 482 $category->apply_limit_rules($grades, $items); 483 $this->assertEquals(count($grades), 2); 484 $this->assertEquals($grades[$this->grade_items[1]->id], 9.4743); 485 $this->assertEquals($grades[$this->grade_items[4]->id], 7.3754); 486 487 // Test aggregating only the highest 1 out of 4 grades. 488 $category = new grade_category(); 489 $category->keephigh = 1; 490 $category->droplow = 0; 491 $grades = array($this->grade_items[0]->id=>5.374, 492 $this->grade_items[1]->id=>9.4743, 493 $this->grade_items[2]->id=>2.5474, 494 $this->grade_items[4]->id=>7.3754); 495 $category->apply_limit_rules($grades, $items); 496 $this->assertEquals(count($grades), 1); 497 $grade = reset($grades); 498 $this->assertEquals(9.4743, $grade); 499 500 // Test excluding the lowest 2 out of 4 grades from aggregation with no 0 grades. 501 // An extra credit grade item should be kept even if droplow means it would otherwise be excluded. 502 $category = new grade_category(); 503 $category->droplow = 2; 504 $category->aggregation = GRADE_AGGREGATE_SUM; 505 $items[$this->grade_items[2]->id]->aggregationcoef = 1; // Mark grade item 2 as "extra credit". 506 $grades = array($this->grade_items[0]->id=>5.374, 507 $this->grade_items[1]->id=>9.4743, 508 $this->grade_items[2]->id=>2.5474, 509 $this->grade_items[4]->id=>7.3754); 510 $category->apply_limit_rules($grades, $items); 511 $this->assertEquals(count($grades), 2); 512 $this->assertEquals($grades[$this->grade_items[1]->id], 9.4743); 513 $this->assertEquals($grades[$this->grade_items[2]->id], 2.5474); 514 515 // Test only aggregating the highest 1 out of 4 grades. 516 // An extra credit grade item is retained in addition to the highest grade. 517 $category = new grade_category(); 518 $category->keephigh = 1; 519 $category->droplow = 0; 520 $category->aggregation = GRADE_AGGREGATE_SUM; 521 $items[$this->grade_items[2]->id]->aggregationcoef = 1; // Mark grade item 2 as "extra credit". 522 $grades = array($this->grade_items[0]->id=>5.374, 523 $this->grade_items[1]->id=>9.4743, 524 $this->grade_items[2]->id=>2.5474, 525 $this->grade_items[4]->id=>7.3754); 526 $category->apply_limit_rules($grades, $items); 527 $this->assertEquals(count($grades), 2); 528 $this->assertEquals($grades[$this->grade_items[1]->id], 9.4743); 529 $this->assertEquals($grades[$this->grade_items[2]->id], 2.5474); 530 531 // Test excluding the lowest 1 out of 4 grades from aggregation with two 0 grades. 532 $items[$this->grade_items[2]->id]->aggregationcoef = 0; // Undo marking grade item 2 as "extra credit". 533 $category = new grade_category(); 534 $category->droplow = 1; 535 $category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2; // Simple weighted mean. 536 $grades = array($this->grade_items[0]->id=>0, // 0 out of 110. Should be excluded from aggregation. 537 $this->grade_items[1]->id=>5, // 5 out of 100. 538 $this->grade_items[2]->id=>2, // 0 out of 6. 539 $this->grade_items[4]->id=>0); // 0 out of 100. 540 $category->apply_limit_rules($grades, $items); 541 $this->assertEquals(count($grades), 3); 542 $this->assertEquals($grades[$this->grade_items[1]->id], 5); 543 $this->assertEquals($grades[$this->grade_items[2]->id], 2); 544 $this->assertEquals($grades[$this->grade_items[4]->id], 0); 545 546 // Test excluding the lowest 2 out of 4 grades from aggregation with three 0 grades. 547 $category = new grade_category(); 548 $category->droplow = 2; 549 $category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2; // Simple weighted mean. 550 $grades = array($this->grade_items[0]->id=>0, // 0 out of 110. Should be excluded from aggregation. 551 $this->grade_items[1]->id=>5, // 5 out of 100. 552 $this->grade_items[2]->id=>0, // 0 out of 6. 553 $this->grade_items[4]->id=>0); // 0 out of 100. Should be excluded from aggregation. 554 $category->apply_limit_rules($grades, $items); 555 $this->assertEquals(count($grades), 2); 556 $this->assertEquals($grades[$this->grade_items[1]->id], 5); 557 $this->assertEquals($grades[$this->grade_items[2]->id], 0); 558 559 // Test excluding the lowest 5 out of 4 grades from aggregation. 560 // Just to check we handle this sensibly. 561 $category = new grade_category(); 562 $category->droplow = 5; 563 $category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2; // Simple weighted mean. 564 $grades = array($this->grade_items[0]->id=>0, // 0 out of 110. Should be excluded from aggregation. 565 $this->grade_items[1]->id=>5, // 5 out of 100. 566 $this->grade_items[2]->id=>6, // 6 out of 6. 567 $this->grade_items[4]->id=>1);// 1 out of 100. Should be excluded from aggregation. 568 $category->apply_limit_rules($grades, $items); 569 $this->assertEquals(count($grades), 0); 570 571 // Test excluding the lowest 4 out of 4 grades from aggregation with one marked as extra credit. 572 $category = new grade_category(); 573 $category->droplow = 4; 574 $category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2; // Simple weighted mean. 575 $items[$this->grade_items[2]->id]->aggregationcoef = 1; // Mark grade item 2 as "extra credit". 576 $grades = array($this->grade_items[0]->id=>0, // 0 out of 110. Should be excluded from aggregation. 577 $this->grade_items[1]->id=>5, // 5 out of 100. Should be excluded from aggregation. 578 $this->grade_items[2]->id=>6, // 6 out of 6. Extra credit. Should be retained. 579 $this->grade_items[4]->id=>1);// 1 out of 100. Should be excluded from aggregation. 580 $category->apply_limit_rules($grades, $items); 581 $this->assertEquals(count($grades), 1); 582 $this->assertEquals($grades[$this->grade_items[2]->id], 6); 583 584 // MDL-35667 - There was an infinite loop if several items had the same grade and at least one was extra credit. 585 $category = new grade_category(); 586 $category->droplow = 1; 587 $category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2; // Simple weighted mean. 588 $items[$this->grade_items[1]->id]->aggregationcoef = 1; // Mark grade item 1 as "extra credit". 589 $grades = array($this->grade_items[0]->id=>1, // 1 out of 110. Should be excluded from aggregation. 590 $this->grade_items[1]->id=>1, // 1 out of 100. Extra credit. Should be retained. 591 $this->grade_items[2]->id=>1, // 1 out of 6. Should be retained. 592 $this->grade_items[4]->id=>1);// 1 out of 100. Should be retained. 593 $category->apply_limit_rules($grades, $items); 594 $this->assertEquals(count($grades), 3); 595 $this->assertEquals($grades[$this->grade_items[1]->id], 1); 596 $this->assertEquals($grades[$this->grade_items[2]->id], 1); 597 $this->assertEquals($grades[$this->grade_items[4]->id], 1); 598 599 } 600 601 protected function sub_test_grade_category_is_aggregationcoef_used() { 602 $category = new grade_category(); 603 // Following use aggregationcoef. 604 $category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN; 605 $this->assertTrue($category->is_aggregationcoef_used()); 606 $category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2; 607 $this->assertTrue($category->is_aggregationcoef_used()); 608 $category->aggregation = GRADE_AGGREGATE_EXTRACREDIT_MEAN; 609 $this->assertTrue($category->is_aggregationcoef_used()); 610 $category->aggregation = GRADE_AGGREGATE_SUM; 611 $this->assertTrue($category->is_aggregationcoef_used()); 612 613 // Following don't use aggregationcoef. 614 $category->aggregation = GRADE_AGGREGATE_MAX; 615 $this->assertFalse($category->is_aggregationcoef_used()); 616 $category->aggregation = GRADE_AGGREGATE_MEAN; 617 $this->assertFalse($category->is_aggregationcoef_used()); 618 $category->aggregation = GRADE_AGGREGATE_MEDIAN; 619 $this->assertFalse($category->is_aggregationcoef_used()); 620 $category->aggregation = GRADE_AGGREGATE_MIN; 621 $this->assertFalse($category->is_aggregationcoef_used()); 622 $category->aggregation = GRADE_AGGREGATE_MODE; 623 $this->assertFalse($category->is_aggregationcoef_used()); 624 } 625 626 protected function sub_test_grade_category_aggregation_uses_aggregationcoef() { 627 628 $this->assertTrue(grade_category::aggregation_uses_aggregationcoef(GRADE_AGGREGATE_WEIGHTED_MEAN)); 629 $this->assertTrue(grade_category::aggregation_uses_aggregationcoef(GRADE_AGGREGATE_WEIGHTED_MEAN2)); 630 $this->assertTrue(grade_category::aggregation_uses_aggregationcoef(GRADE_AGGREGATE_EXTRACREDIT_MEAN)); 631 $this->assertTrue(grade_category::aggregation_uses_aggregationcoef(GRADE_AGGREGATE_SUM)); 632 633 $this->assertFalse(grade_category::aggregation_uses_aggregationcoef(GRADE_AGGREGATE_MAX)); 634 $this->assertFalse(grade_category::aggregation_uses_aggregationcoef(GRADE_AGGREGATE_MEAN)); 635 $this->assertFalse(grade_category::aggregation_uses_aggregationcoef(GRADE_AGGREGATE_MEDIAN)); 636 $this->assertFalse(grade_category::aggregation_uses_aggregationcoef(GRADE_AGGREGATE_MIN)); 637 $this->assertFalse(grade_category::aggregation_uses_aggregationcoef(GRADE_AGGREGATE_MODE)); 638 } 639 640 protected function sub_test_grade_category_fetch_course_tree() { 641 $category = new grade_category(); 642 $this->assertTrue(method_exists($category, 'fetch_course_tree')); 643 // TODO: add some tests. 644 } 645 646 protected function sub_test_grade_category_get_children() { 647 $course_category = grade_category::fetch_course_category($this->courseid); 648 649 $category = new grade_category($this->grade_categories[0]); 650 $this->assertTrue(method_exists($category, 'get_children')); 651 652 $children_array = $category->get_children(0); 653 654 $this->assertTrue(is_array($children_array)); 655 $this->assertFalse(empty($children_array[2])); 656 $this->assertFalse(empty($children_array[2]['object'])); 657 $this->assertFalse(empty($children_array[2]['children'])); 658 $this->assertEquals($this->grade_categories[1]->id, $children_array[2]['object']->id); 659 $this->assertEquals($this->grade_categories[2]->id, $children_array[5]['object']->id); 660 $this->assertEquals($this->grade_items[0]->id, $children_array[2]['children'][3]['object']->id); 661 $this->assertEquals($this->grade_items[1]->id, $children_array[2]['children'][4]['object']->id); 662 $this->assertEquals($this->grade_items[2]->id, $children_array[5]['children'][6]['object']->id); 663 } 664 665 protected function sub_test_grade_category_load_grade_item() { 666 $category = new grade_category($this->grade_categories[0]); 667 $this->assertTrue(method_exists($category, 'load_grade_item')); 668 $this->assertEquals(null, $category->grade_item); 669 $category->load_grade_item(); 670 $this->assertEquals($this->grade_items[3]->id, $category->grade_item->id); 671 } 672 673 protected function sub_test_grade_category_get_grade_item() { 674 $category = new grade_category($this->grade_categories[0]); 675 $this->assertTrue(method_exists($category, 'get_grade_item')); 676 $grade_item = $category->get_grade_item(); 677 $this->assertEquals($this->grade_items[3]->id, $grade_item->id); 678 } 679 680 protected function sub_test_grade_category_load_parent_category() { 681 $category = new grade_category($this->grade_categories[1]); 682 $this->assertTrue(method_exists($category, 'load_parent_category')); 683 $this->assertEquals(null, $category->parent_category); 684 $category->load_parent_category(); 685 $this->assertEquals($this->grade_categories[0]->id, $category->parent_category->id); 686 } 687 688 protected function sub_test_grade_category_get_parent_category() { 689 $category = new grade_category($this->grade_categories[1]); 690 $this->assertTrue(method_exists($category, 'get_parent_category')); 691 $parent_category = $category->get_parent_category(); 692 $this->assertEquals($this->grade_categories[0]->id, $parent_category->id); 693 } 694 695 protected function sub_test_grade_category_get_name() { 696 $category = new grade_category($this->grade_categories[0]); 697 $this->assertTrue(method_exists($category, 'get_name')); 698 $this->assertEquals($this->grade_categories[0]->fullname, $category->get_name()); 699 } 700 701 protected function sub_test_grade_category_set_parent() { 702 $category = new grade_category($this->grade_categories[1]); 703 $this->assertTrue(method_exists($category, 'set_parent')); 704 // TODO: implement detailed tests. 705 706 $course_category = grade_category::fetch_course_category($this->courseid); 707 $this->assertTrue($category->set_parent($course_category->id)); 708 $this->assertEquals($course_category->id, $category->parent); 709 } 710 711 protected function sub_test_grade_category_get_final() { 712 $category = new grade_category($this->grade_categories[0]); 713 $this->assertTrue(method_exists($category, 'get_final')); 714 $category->load_grade_item(); 715 $this->assertEquals($category->get_final(), $category->grade_item->get_final()); 716 } 717 718 protected function sub_test_grade_category_get_sortorder() { 719 $category = new grade_category($this->grade_categories[0]); 720 $this->assertTrue(method_exists($category, 'get_sortorder')); 721 $category->load_grade_item(); 722 $this->assertEquals($category->get_sortorder(), $category->grade_item->get_sortorder()); 723 } 724 725 protected function sub_test_grade_category_set_sortorder() { 726 $category = new grade_category($this->grade_categories[0]); 727 $this->assertTrue(method_exists($category, 'set_sortorder')); 728 $category->load_grade_item(); 729 $this->assertEquals($category->set_sortorder(10), $category->grade_item->set_sortorder(10)); 730 } 731 732 protected function sub_test_grade_category_move_after_sortorder() { 733 $category = new grade_category($this->grade_categories[0]); 734 $this->assertTrue(method_exists($category, 'move_after_sortorder')); 735 $category->load_grade_item(); 736 $this->assertEquals($category->move_after_sortorder(10), $category->grade_item->move_after_sortorder(10)); 737 } 738 739 protected function sub_test_grade_category_is_course_category() { 740 $category = grade_category::fetch_course_category($this->courseid); 741 $this->assertTrue(method_exists($category, 'is_course_category')); 742 $this->assertTrue($category->is_course_category()); 743 } 744 745 protected function sub_test_grade_category_fetch_course_category() { 746 $category = new grade_category(); 747 $this->assertTrue(method_exists($category, 'fetch_course_category')); 748 $category = grade_category::fetch_course_category($this->courseid); 749 $this->assertTrue(empty($category->parent)); 750 } 751 /** 752 * TODO implement 753 */ 754 protected function sub_test_grade_category_is_editable() { 755 756 } 757 758 protected function sub_test_grade_category_is_locked() { 759 $category = new grade_category($this->grade_categories[0]); 760 $this->assertTrue(method_exists($category, 'is_locked')); 761 $category->load_grade_item(); 762 $this->assertEquals($category->is_locked(), $category->grade_item->is_locked()); 763 } 764 765 protected function sub_test_grade_category_set_locked() { 766 $category = new grade_category($this->grade_categories[0]); 767 $this->assertTrue(method_exists($category, 'set_locked')); 768 769 // Will return false as cannot lock a grade that needs updating. 770 $this->assertFalse($category->set_locked(1)); 771 grade_regrade_final_grades($this->courseid); 772 773 // Get the category from the db again. 774 $category = new grade_category($this->grade_categories[0]); 775 $this->assertTrue($category->set_locked(1)); 776 } 777 778 protected function sub_test_grade_category_is_hidden() { 779 $category = new grade_category($this->grade_categories[0]); 780 $this->assertTrue(method_exists($category, 'is_hidden')); 781 $category->load_grade_item(); 782 $this->assertEquals($category->is_hidden(), $category->grade_item->is_hidden()); 783 } 784 785 protected function sub_test_grade_category_set_hidden() { 786 $category = new grade_category($this->grade_categories[0]); 787 $this->assertTrue(method_exists($category, 'set_hidden')); 788 $category->set_hidden(1, true); 789 $category->load_grade_item(); 790 $this->assertEquals(true, $category->grade_item->is_hidden()); 791 } 792 793 protected function sub_test_grade_category_can_control_visibility() { 794 $category = new grade_category($this->grade_categories[0]); 795 $this->assertTrue($category->can_control_visibility()); 796 } 797 798 protected function sub_test_grade_category_insert_course_category() { 799 // Beware: adding a duplicate course category messes up the data in a way that's hard to recover from. 800 $grade_category = new grade_category(); 801 $this->assertTrue(method_exists($grade_category, 'insert_course_category')); 802 803 $id = $grade_category->insert_course_category($this->courseid); 804 $this->assertNotNull($id); 805 $this->assertEquals('?', $grade_category->fullname); 806 $this->assertEquals(GRADE_AGGREGATE_WEIGHTED_MEAN2, $grade_category->aggregation); 807 $this->assertEquals("/$id/", $grade_category->path); 808 $this->assertEquals(1, $grade_category->depth); 809 $this->assertNull($grade_category->parent); 810 } 811 812 protected function generate_random_raw_grade($item, $userid) { 813 $grade = new grade_grade(); 814 $grade->itemid = $item->id; 815 $grade->userid = $userid; 816 $grade->grademin = 0; 817 $grade->grademax = 1; 818 $valuetype = "grade$item->gradetype"; 819 $grade->rawgrade = rand(0, 1000) / 1000; 820 $grade->insert(); 821 return $grade->rawgrade; 822 } 823 824 protected function sub_test_grade_category_is_extracredit_used() { 825 $category = new grade_category(); 826 // Following use aggregationcoef. 827 $category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2; 828 $this->assertTrue($category->is_extracredit_used()); 829 $category->aggregation = GRADE_AGGREGATE_EXTRACREDIT_MEAN; 830 $this->assertTrue($category->is_extracredit_used()); 831 $category->aggregation = GRADE_AGGREGATE_SUM; 832 $this->assertTrue($category->is_extracredit_used()); 833 834 // Following don't use aggregationcoef. 835 $category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN; 836 $this->assertFalse($category->is_extracredit_used()); 837 $category->aggregation = GRADE_AGGREGATE_MAX; 838 $this->assertFalse($category->is_extracredit_used()); 839 $category->aggregation = GRADE_AGGREGATE_MEAN; 840 $this->assertFalse($category->is_extracredit_used()); 841 $category->aggregation = GRADE_AGGREGATE_MEDIAN; 842 $this->assertFalse($category->is_extracredit_used()); 843 $category->aggregation = GRADE_AGGREGATE_MIN; 844 $this->assertFalse($category->is_extracredit_used()); 845 $category->aggregation = GRADE_AGGREGATE_MODE; 846 $this->assertFalse($category->is_extracredit_used()); 847 } 848 849 protected function sub_test_grade_category_aggregation_uses_extracredit() { 850 851 $this->assertTrue(grade_category::aggregation_uses_extracredit(GRADE_AGGREGATE_WEIGHTED_MEAN2)); 852 $this->assertTrue(grade_category::aggregation_uses_extracredit(GRADE_AGGREGATE_EXTRACREDIT_MEAN)); 853 $this->assertTrue(grade_category::aggregation_uses_extracredit(GRADE_AGGREGATE_SUM)); 854 855 $this->assertFalse(grade_category::aggregation_uses_extracredit(GRADE_AGGREGATE_WEIGHTED_MEAN)); 856 $this->assertFalse(grade_category::aggregation_uses_extracredit(GRADE_AGGREGATE_MAX)); 857 $this->assertFalse(grade_category::aggregation_uses_extracredit(GRADE_AGGREGATE_MEAN)); 858 $this->assertFalse(grade_category::aggregation_uses_extracredit(GRADE_AGGREGATE_MEDIAN)); 859 $this->assertFalse(grade_category::aggregation_uses_extracredit(GRADE_AGGREGATE_MIN)); 860 $this->assertFalse(grade_category::aggregation_uses_extracredit(GRADE_AGGREGATE_MODE)); 861 } 862 863 /** 864 * Test for category total visibility. 865 */ 866 protected function sub_test_grade_category_total_visibility() { 867 // 15 is a manual grade item in grade_categories[5]. 868 $category = new grade_category($this->grade_categories[5], true); 869 $gradeitem = new grade_item($this->grade_items[15], true); 870 871 // Hide grade category. 872 $category->set_hidden(true, true); 873 $this->assertTrue($category->is_hidden()); 874 // Category total is hidden. 875 $categorytotal = $category->get_grade_item(); 876 $this->assertTrue($categorytotal->is_hidden()); 877 // Manual grade is hidden. 878 $gradeitem->update_from_db(); 879 $this->assertTrue($gradeitem->is_hidden()); 880 881 // Unhide manual grade item. 882 $gradeitem->set_hidden(false); 883 $this->assertFalse($gradeitem->is_hidden()); 884 // Category is unhidden. 885 $category->update_from_db(); 886 $this->assertFalse($category->is_hidden()); 887 // Category total remain hidden. 888 $categorytotal = $category->get_grade_item(); 889 $this->assertTrue($categorytotal->is_hidden()); 890 891 // Edit manual grade item. 892 $this->assertFalse($gradeitem->is_locked()); 893 $gradeitem->set_locked(true); 894 $gradeitem->update_from_db(); 895 $this->assertTrue($gradeitem->is_locked()); 896 // Category total should still be hidden. 897 $category->update_from_db(); 898 $categorytotal = $category->get_grade_item(); 899 $this->assertTrue($categorytotal->is_hidden()); 900 } 901 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body