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