Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 311 and 400] [Versions 311 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  namespace core;
  18  
  19  use core_grades_external;
  20  
  21  defined('MOODLE_INTERNAL') || die();
  22  
  23  global $CFG;
  24  
  25  require_once($CFG->dirroot . '/webservice/tests/helpers.php');
  26  
  27  /**
  28   * Grades functions unit tests
  29   *
  30   * Unit tests for the grade API at /lib/classes/grades_external.php
  31   *
  32   * @package core
  33   * @category test
  34   * @copyright 2012 Andrew Davis
  35   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  36   */
  37  class grades_external_test extends \externallib_advanced_testcase {
  38  
  39      /**
  40       * Load initial test information
  41       *
  42       * @param  string $assignmentname   Assignment name
  43       * @param  int $student1rawgrade    Student 1 grade
  44       * @param  int $student2rawgrade    Student 2 grade
  45       * @return array                    Array of vars with test information
  46       */
  47      protected function load_test_data($assignmentname, $student1rawgrade, $student2rawgrade) {
  48          global $DB;
  49  
  50          // Adds a course, a teacher, 2 students, an assignment and grades for the students.
  51          $course = $this->getDataGenerator()->create_course();
  52          $coursecontext = \context_course::instance($course->id);
  53  
  54          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
  55  
  56          $student1 = $this->getDataGenerator()->create_user();
  57          $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
  58  
  59          $student2 = $this->getDataGenerator()->create_user();
  60          $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
  61  
  62          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
  63          $teacher = $this->getDataGenerator()->create_user();
  64          $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
  65  
  66          $parent = $this->getDataGenerator()->create_user();
  67          $this->setUser($parent);
  68          $student1context = \context_user::instance($student1->id);
  69          // Creates a new role, gives it the capability and gives $USER that role.
  70          $parentroleid = $this->assignUserCapability('moodle/grade:viewall', $student1context->id);
  71          // Enrol the user in the course using the new role.
  72          $this->getDataGenerator()->enrol_user($parent->id, $course->id, $parentroleid);
  73  
  74          $assignment = $this->getDataGenerator()->create_module('assign', array('name' => $assignmentname, 'course' => $course->id));
  75          $modcontext = get_coursemodule_from_instance('assign', $assignment->id, $course->id);
  76          $assignment->cmidnumber = $modcontext->id;
  77  
  78          $student1grade = array('userid' => $student1->id, 'rawgrade' => $student1rawgrade);
  79          $student2grade = array('userid' => $student2->id, 'rawgrade' => $student2rawgrade);
  80          $studentgrades = array($student1->id => $student1grade, $student2->id => $student2grade);
  81          assign_grade_item_update($assignment, $studentgrades);
  82  
  83          // Insert a custom grade scale to be used by an outcome.
  84          $gradescale = new \grade_scale();
  85          $gradescale->name        = 'unittestscale3';
  86          $gradescale->courseid    = $course->id;
  87          $gradescale->userid      = 0;
  88          $gradescale->scale       = 'Distinction, Very Good, Good, Pass, Fail';
  89          $gradescale->description = 'This scale is used to mark standard assignments.';
  90          $gradescale->insert();
  91  
  92          // Insert an outcome.
  93          $data = new \stdClass();
  94          $data->courseid = $course->id;
  95          $data->fullname = 'Team work';
  96          $data->shortname = 'Team work';
  97          $data->scaleid = $gradescale->id;
  98          $outcome = new \grade_outcome($data, false);
  99          $outcome->insert();
 100  
 101          $outcomegradeitem = new \grade_item();
 102          $outcomegradeitem->itemname = $outcome->shortname;
 103          $outcomegradeitem->itemtype = 'mod';
 104          $outcomegradeitem->itemmodule = 'assign';
 105          $outcomegradeitem->iteminstance = $assignment->id;
 106          $outcomegradeitem->outcomeid = $outcome->id;
 107          $outcomegradeitem->cmid = 0;
 108          $outcomegradeitem->courseid = $course->id;
 109          $outcomegradeitem->aggregationcoef = 0;
 110          $outcomegradeitem->itemnumber = 1; // The activity's original grade item will be 0.
 111          $outcomegradeitem->gradetype = GRADE_TYPE_SCALE;
 112          $outcomegradeitem->scaleid = $outcome->scaleid;
 113          // This next two values for testing that returns parameters are correcly formatted.
 114          $outcomegradeitem->set_locked(true);
 115          $outcomegradeitem->hidden = '';
 116          $outcomegradeitem->insert();
 117  
 118          $assignmentgradeitem = \grade_item::fetch(
 119              array(
 120                  'itemtype' => 'mod',
 121                  'itemmodule' => 'assign',
 122                  'iteminstance' => $assignment->id,
 123                  'itemnumber' => 0,
 124                  'courseid' => $course->id
 125              )
 126          );
 127          $outcomegradeitem->set_parent($assignmentgradeitem->categoryid);
 128          $outcomegradeitem->move_after_sortorder($assignmentgradeitem->sortorder);
 129  
 130          return array($course, $assignment, $student1, $student2, $teacher, $parent);
 131      }
 132  
 133      /**
 134       * Test get_grades()
 135       */
 136      public function test_get_grades() {
 137          global $CFG;
 138  
 139          $this->resetAfterTest(true);
 140          $CFG->enableoutcomes = 1;
 141  
 142          $assignmentname = 'The assignment';
 143          $student1rawgrade = 10;
 144          $student2rawgrade = 20;
 145          list($course, $assignment, $student1, $student2, $teacher, $parent) =
 146              $this->load_test_data($assignmentname, $student1rawgrade, $student2rawgrade);
 147          $assigmentcm = get_coursemodule_from_id('assign', $assignment->cmid, 0, false, MUST_EXIST);
 148  
 149          // Teacher requesting a student grade for the assignment.
 150          $this->setUser($teacher);
 151          $grades = core_grades_external::get_grades(
 152              $course->id,
 153              'mod_assign',
 154              $assigmentcm->id,
 155              array($student1->id)
 156          );
 157          $grades = \external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
 158          $this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id, $student1->id));
 159  
 160          // Teacher requesting all the grades of student1 in a course.
 161          $grades = core_grades_external::get_grades(
 162              $course->id,
 163              null,
 164              null,
 165              array($student1->id)
 166          );
 167          $grades = \external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
 168          $this->assertTrue(count($grades['items']) == 2);
 169          $this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id, $student1->id));
 170          $this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, 'course', $student1->id));
 171  
 172          $outcome = $this->get_outcome($grades, $assigmentcm->id);
 173          $this->assertEquals($outcome['name'], 'Team work');
 174          $this->assertEquals(0, $this->get_outcome_student_grade($grades, $assigmentcm->id, $student1->id));
 175  
 176          // Teacher requesting all the grades of all the students in a course.
 177          $grades = core_grades_external::get_grades(
 178              $course->id,
 179              null,
 180              null,
 181              array($student1->id, $student2->id)
 182          );
 183          $grades = \external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
 184          $this->assertTrue(count($grades['items']) == 2);
 185          $this->assertTrue(count($grades['items'][0]['grades']) == 2);
 186          $this->assertTrue(count($grades['items'][1]['grades']) == 2);
 187  
 188          // Student requesting another student's grade for the assignment (should fail).
 189          $this->setUser($student1);
 190          try {
 191              $grades = core_grades_external::get_grades(
 192                  $course->id,
 193                  'mod_assign',
 194                  $assigmentcm->id,
 195                  array($student2->id)
 196              );
 197              $this->fail('moodle_exception expected');
 198          } catch (\moodle_exception $ex) {
 199              $this->assertTrue(true);
 200          }
 201  
 202          // Parent requesting their child's grade for the assignment (should fail).
 203          $this->setUser($parent);
 204          try {
 205              $grades = core_grades_external::get_grades(
 206                  $course->id,
 207                  'mod_assign',
 208                  $assigmentcm->id,
 209                  array($student1->id)
 210              );
 211              $this->fail('moodle_exception expected');
 212          } catch (\moodle_exception $ex) {
 213              $this->assertTrue(true);
 214          }
 215  
 216          // Parent requesting another student's grade for the assignment(should fail).
 217          try {
 218              $grades = core_grades_external::get_grades(
 219                  $course->id,
 220                  'mod_assign',
 221                  $assigmentcm->id,
 222                  array($student2->id)
 223              );
 224              $this->fail('moodle_exception expected');
 225          } catch (\moodle_exception $ex) {
 226              $this->assertTrue(true);
 227          }
 228  
 229          // Student requesting all other student grades for the assignment (should fail).
 230          try {
 231              $grades = core_grades_external::get_grades(
 232                  $course->id,
 233                  'mod_assign',
 234                  $assigmentcm->id,
 235                  array($student1->id, $student2->id)
 236              );
 237              $this->fail('moodle_exception expected');
 238          } catch (\moodle_exception $ex) {
 239              $this->assertTrue(true);
 240          }
 241  
 242          // Student requesting only grade item information (should fail).
 243          try {
 244              $grades = core_grades_external::get_grades(
 245                  $course->id,
 246                  'mod_assign',
 247                  $assigmentcm->id,
 248                  array()
 249              );
 250              $this->fail('moodle_exception expected');
 251          } catch (\moodle_exception $ex) {
 252              $this->assertTrue(true);
 253          }
 254  
 255          // Teacher requesting student grades for a course.
 256          $this->setUser($teacher);
 257          $grades = core_grades_external::get_grades(
 258              $course->id,
 259              'mod_assign',
 260              $assigmentcm->id,
 261              array($student1->id, $student2->id)
 262          );
 263          $grades = \external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
 264          $this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id, $student1->id));
 265          $this->assertEquals($student2rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id, $student2->id));
 266  
 267          // Teacher requesting grade item information.
 268          $grades = core_grades_external::get_grades(
 269              $course->id,
 270              'mod_assign',
 271              $assigmentcm->id
 272          );
 273          $grades = \external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
 274          $activity = $this->get_activity($grades, $assigmentcm->id);
 275          $this->assertEquals($activity['name'], $assignmentname);
 276          $this->assertEquals(count($activity['grades']), 0);
 277  
 278          // Teacher requesting all grade items in a course.
 279          $grades = core_grades_external::get_grades(
 280              $course->id
 281          );
 282          $grades = \external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
 283          $this->assertTrue(count($grades['items']) == 2);
 284  
 285          $activity = $this->get_activity($grades, $assigmentcm->id);
 286          $this->assertEquals($activity['name'], $assignmentname);
 287          $this->assertEquals(count($activity['grades']), 0);
 288  
 289          $outcome = $this->get_outcome($grades, $assigmentcm->id);
 290          $this->assertEquals($outcome['name'], 'Team work');
 291  
 292          // Hide a grade item then have student request it.
 293          $result = core_grades_external::update_grades(
 294              'test',
 295              $course->id,
 296              'mod_assign',
 297              $assigmentcm->id,
 298              0,
 299              array(),
 300              array('hidden' => 1)
 301          );
 302          $result = \external_api::clean_returnvalue(core_grades_external::update_grades_returns(), $result);
 303          $this->assertTrue($result == GRADE_UPDATE_OK);
 304  
 305          // Check it's definitely hidden.
 306          $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id);
 307          $this->assertEquals($grades->items[0]->hidden, 1);
 308  
 309          // Teacher should still be able to see the hidden grades.
 310          $this->setUser($teacher);
 311          $grades = core_grades_external::get_grades(
 312              $course->id,
 313              'mod_assign',
 314              $assigmentcm->id,
 315              array($student1->id)
 316          );
 317          $grades = \external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
 318          $this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id, $student1->id));
 319      }
 320  
 321      /**
 322       * Get an activity
 323       *
 324       * @param  array $grades    Array of grades
 325       * @param  int $cmid        Activity course module id
 326       * @return strdClass        Activity object
 327       */
 328      private function get_activity($grades, $cmid) {
 329          foreach ($grades['items'] as $item) {
 330              if ($item['activityid'] == $cmid) {
 331                  return $item;
 332              }
 333          }
 334          return null;
 335      }
 336  
 337      /**
 338       * Get a grade for an activity
 339       *
 340       * @param  array $grades    Array of grades
 341       * @param  int $cmid        Activity course module id
 342       * @param  int $studentid   Student it
 343       * @return stdClass         Activity Object
 344       */
 345      private function get_activity_student_grade($grades, $cmid, $studentid) {
 346          $item = $this->get_activity($grades, $cmid);
 347          foreach ($item['grades'] as $grade) {
 348              if ($grade['userid'] == $studentid) {
 349                  return $grade['grade'];
 350              }
 351          }
 352          return null;
 353      }
 354  
 355      /**
 356       * Get an ouctome
 357       *
 358       * @param  array $grades    Array of grades
 359       * @param  int $cmid        Activity course module id
 360       * @return stdClass         Outcome object
 361       */
 362      private function get_outcome($grades, $cmid) {
 363          foreach ($grades['outcomes'] as $outcome) {
 364              if ($outcome['activityid'] == $cmid) {
 365                  return $outcome;
 366              }
 367          }
 368          return null;
 369      }
 370  
 371      /**
 372       * Get a grade from an outcome
 373       *
 374       * @param  array $grades    Array of grades
 375       * @param  int $cmid        Activity course module id
 376       * @param  int $studentid   Student id
 377       * @return stdClass         Outcome object
 378       */
 379      private function get_outcome_student_grade($grades, $cmid, $studentid) {
 380          $outcome = $this->get_outcome($grades, $cmid);
 381          foreach ($outcome['grades'] as $grade) {
 382              if ($grade['userid'] == $studentid) {
 383                  return $grade['grade'];
 384              }
 385          }
 386          return null;
 387      }
 388  
 389      /**
 390       * Test update_grades()
 391       */
 392      public function test_update_grades() {
 393          global $DB;
 394  
 395          $this->resetAfterTest(true);
 396  
 397          $assignmentname = 'The assignment';
 398          $student1rawgrade = 10;
 399          $student2rawgrade = 20;
 400          list($course, $assignment, $student1, $student2, $teacher, $parent) =
 401              $this->load_test_data($assignmentname, $student1rawgrade, $student2rawgrade);
 402          $assigmentcm = get_coursemodule_from_id('assign', $assignment->cmid, 0, false, MUST_EXIST);
 403  
 404          $this->setUser($teacher);
 405  
 406          // Teacher updating grade item information.
 407          $changedmax = 93;
 408          $result = core_grades_external::update_grades(
 409              'test',
 410              $course->id,
 411              'mod_assign',
 412              $assigmentcm->id,
 413              0,
 414              array(),
 415              array('grademax' => $changedmax)
 416          );
 417          $result = \external_api::clean_returnvalue(core_grades_external::update_grades_returns(), $result);
 418          $this->assertTrue($result == GRADE_UPDATE_OK);
 419          $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id);
 420          $this->assertTrue($grades->items[0]->grademax == $changedmax);
 421  
 422          // Teacher updating 1 student grade.
 423          $student1grade = 23;
 424          $result = core_grades_external::update_grades(
 425              'test',
 426              $course->id,
 427              'mod_assign',
 428              $assigmentcm->id,
 429              0,
 430              array(array('studentid' => $student1->id, 'grade' => $student1grade))
 431          );
 432          $result = \external_api::clean_returnvalue(core_grades_external::update_grades_returns(), $result);
 433          $this->assertTrue($result == GRADE_UPDATE_OK);
 434          $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id, array($student1->id));
 435          $this->assertTrue($grades->items[0]->grades[$student1->id]->grade == $student1grade);
 436  
 437          // Teacher updating multiple student grades.
 438          $student1grade = 11;
 439          $student2grade = 13;
 440          $result = core_grades_external::update_grades(
 441              'test',
 442              $course->id,
 443              'mod_assign',
 444              $assigmentcm->id,
 445              0,
 446              array(
 447                  array('studentid' => $student1->id, 'grade' => $student1grade),
 448                  array('studentid' => $student2->id, 'grade' => $student2grade)
 449              )
 450          );
 451          $result = \external_api::clean_returnvalue(core_grades_external::update_grades_returns(), $result);
 452          $this->assertTrue($result == GRADE_UPDATE_OK);
 453          $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id, array($student1->id, $student2->id));
 454          $this->assertTrue($grades->items[0]->grades[$student1->id]->grade == $student1grade);
 455          $this->assertTrue($grades->items[0]->grades[$student2->id]->grade == $student2grade);
 456  
 457          // Student attempting to update their own grade (should fail).
 458          $this->setUser($student1);
 459          try {
 460              $student1grade = 17;
 461              $result = core_grades_external::update_grades(
 462                  'test',
 463                  $course->id,
 464                  'mod_assign',
 465                  $assigmentcm->id,
 466                  0,
 467                  array( array('studentid' => $student1->id, 'grade' => $student1grade))
 468              );
 469              $this->fail('moodle_exception expected');
 470          } catch (\moodle_exception $ex) {
 471              $this->assertTrue(true);
 472          }
 473  
 474          // Parent attempting to update their child's grade (should fail).
 475          $this->setUser($parent);
 476          try {
 477              $student1grade = 13;
 478              $result = core_grades_external::update_grades(
 479                  'test',
 480                  $course->id,
 481                  'mod_assign',
 482                  $assigmentcm->id,
 483                  0,
 484                  array( array('studentid' => $student1->id, 'grade' => $student1grade))
 485              );
 486              $this->fail('moodle_exception expected');
 487          } catch (\moodle_exception $ex) {
 488              $this->assertTrue(true);
 489          }
 490  
 491          // Student trying to hide a grade item (should fail).
 492          $this->setUser($student1);
 493          try {
 494              $result = core_grades_external::update_grades(
 495                  'test',
 496                  $course->id,
 497                  'mod_assign',
 498                  $assigmentcm->id,
 499                  0,
 500                  array(),
 501                  array('hidden' => 1)
 502              );
 503              $this->fail('moodle_exception expected');
 504          } catch (\moodle_exception $ex) {
 505              $this->assertTrue(true);
 506          }
 507  
 508          // Give the student role 'moodle/grade:hide' and they should now be able to hide the grade item.
 509          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 510          $coursecontext = \context_course::instance($course->id);
 511          assign_capability('moodle/grade:hide', CAP_ALLOW, $studentrole->id, $coursecontext->id);
 512          accesslib_clear_all_caches_for_unit_testing();
 513  
 514          // Check the activity isn't already hidden.
 515          $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id);
 516          $this->assertTrue($grades->items[0]->hidden == 0);
 517  
 518          $result = core_grades_external::update_grades(
 519              'test',
 520              $course->id,
 521              'mod_assign',
 522              $assigmentcm->id,
 523              0,
 524              array(),
 525              array('hidden' => 1)
 526          );
 527          $result = \external_api::clean_returnvalue(core_grades_external::update_grades_returns(), $result);
 528          $this->assertTrue($result == GRADE_UPDATE_OK);
 529          $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id);
 530          $this->assertTrue($grades->items[0]->hidden == 1);
 531      }
 532  
 533      /**
 534       * Test create_gradecategory.
 535       *
 536       * @return void
 537       */
 538      public function test_create_gradecategory() {
 539          global $DB;
 540          $this->resetAfterTest(true);
 541          $course = $this->getDataGenerator()->create_course();
 542          $this->setAdminUser();
 543  
 544          // Test the most basic gradecategory creation.
 545          $status1 = core_grades_external::create_gradecategory($course->id, 'Test Category 1', []);
 546  
 547          $courseparentcat = new \grade_category(['courseid' => $course->id, 'depth' => 1], true);
 548          $record1 = $DB->get_record('grade_categories', ['id' => $status1['categoryid']]);
 549          $this->assertEquals('Test Category 1', $record1->fullname);
 550          // Confirm that the parent category for this category is the top level category for the course.
 551          $this->assertEquals($courseparentcat->id, $record1->parent);
 552          $this->assertEquals(2, $record1->depth);
 553  
 554          // Now create a category as a child of the newly created category.
 555          $status2 = core_grades_external::create_gradecategory($course->id, 'Test Category 2', ['parentcategoryid' => $record1->id]);
 556          $record2 = $DB->get_record('grade_categories', ['id' => $status2['categoryid']]);
 557          $this->assertEquals($record1->id, $record2->parent);
 558          $this->assertEquals(3, $record2->depth);
 559          // Check the path is correct.
 560          $this->assertEquals('/' . implode('/', [$courseparentcat->id, $record1->id, $record2->id]) . '/', $record2->path);
 561  
 562          // Now create a category with some customised data and check the returns. This customises every value.
 563          $customopts = [
 564              'aggregation' => GRADE_AGGREGATE_MEAN,
 565              'aggregateonlygraded' => 0,
 566              'aggregateoutcomes' => 1,
 567              'droplow' => 1,
 568              'itemname' => 'item',
 569              'iteminfo' => 'info',
 570              'idnumber' => 'idnumber',
 571              'gradetype' => GRADE_TYPE_TEXT,
 572              'grademax' => 5,
 573              'grademin' => 2,
 574              'gradepass' => 3,
 575              'display' => GRADE_DISPLAY_TYPE_LETTER,
 576              // Hack. This must be -2 to use the default setting.
 577              'decimals' => 3,
 578              'hiddenuntil' => time(),
 579              'locktime' => time(),
 580              'weightoverride' => 1,
 581              'aggregationcoef2' => 20,
 582              'parentcategoryid' => $record2->id
 583          ];
 584  
 585          $status3 = core_grades_external::create_gradecategory($course->id, 'Test Category 3', $customopts);
 586          $cat3 = new \grade_category(['courseid' => $course->id, 'id' => $status3['categoryid']], true);
 587          $cat3->load_grade_item();
 588  
 589          // Lets check all of the data is in the right shape.
 590          $this->assertEquals(GRADE_AGGREGATE_MEAN, $cat3->aggregation);
 591          $this->assertEquals(0, $cat3->aggregateonlygraded);
 592          $this->assertEquals(1, $cat3->aggregateoutcomes);
 593          $this->assertEquals(1, $cat3->droplow);
 594          $this->assertEquals('item', $cat3->grade_item->itemname);
 595          $this->assertEquals('info', $cat3->grade_item->iteminfo);
 596          $this->assertEquals('idnumber', $cat3->grade_item->idnumber);
 597          $this->assertEquals(GRADE_TYPE_TEXT, $cat3->grade_item->gradetype);
 598          $this->assertEquals(5, $cat3->grade_item->grademax);
 599          $this->assertEquals(2, $cat3->grade_item->grademin);
 600          $this->assertEquals(3, $cat3->grade_item->gradepass);
 601          $this->assertEquals(GRADE_DISPLAY_TYPE_LETTER, $cat3->grade_item->display);
 602          $this->assertEquals(3, $cat3->grade_item->decimals);
 603          $this->assertGreaterThanOrEqual($cat3->grade_item->hidden, time());
 604          $this->assertGreaterThanOrEqual($cat3->grade_item->locktime, time());
 605          $this->assertEquals(1, $cat3->grade_item->weightoverride);
 606          // Coefficient is converted to percentage.
 607          $this->assertEquals(0.2, $cat3->grade_item->aggregationcoef2);
 608          $this->assertEquals($record2->id, $cat3->parent);
 609      }
 610  
 611  }