Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 310 and 403] [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  namespace core_grades;
  18  
  19  use grade_item;
  20  
  21  /**
  22   * Tests grade_import_lib functions.
  23   *
  24   * @package   core_grades
  25   * @category  test
  26   * @copyright 2015 Adrian Greeve <adrian@moodle.com>
  27   * @license   http://www.gnu.org/copyleft/gpl.html GNU Public License
  28   */
  29  class importlib_test extends \advanced_testcase {
  30  
  31      /**
  32       * Load required test libraries
  33       */
  34      public static function setUpBeforeClass(): void {
  35          global $CFG;
  36          require_once("{$CFG->dirroot}/grade/import/lib.php");
  37      }
  38  
  39      /**
  40       * Import grades into 'grade_import_values' table. This is done differently in the various import plugins,
  41       * so there is no direct API to call.
  42       *
  43       * @param array $data Information to be inserted into the table.
  44       * @return int The insert ID of the sql statement.
  45       */
  46      private function import_grades($data) {
  47          global $DB, $USER;
  48          $graderecord = new \stdClass();
  49          $graderecord->importcode = $data['importcode'];
  50          if (isset($data['itemid'])) {
  51              $graderecord->itemid = $data['itemid'];
  52          }
  53          $graderecord->userid = $data['userid'];
  54          if (isset($data['importer'])) {
  55              $graderecord->importer = $data['importer'];
  56          } else {
  57              $graderecord->importer = $USER->id;
  58          }
  59          if (isset($data['finalgrade'])) {
  60              $graderecord->finalgrade = $data['finalgrade'];
  61          } else {
  62              $graderecord->finalgrade = rand(0, 100);
  63          }
  64          if (isset($data['feedback'])) {
  65              $graderecord->feedback = $data['feedback'];
  66          }
  67          if (isset($data['importonlyfeedback'])) {
  68              $graderecord->importonlyfeedback = $data['importonlyfeedback'];
  69          } else {
  70              $graderecord->importonlyfeedback = false;
  71          }
  72          if (isset($data['newgradeitem'])) {
  73              $graderecord->newgradeitem = $data['newgradeitem'];
  74          }
  75          return $DB->insert_record('grade_import_values', $graderecord);
  76      }
  77  
  78      /**
  79       * Tests for importing grades from an external source.
  80       *
  81       * @covers ::grade_import_commit
  82       */
  83      public function test_grade_import_commit() {
  84          global $USER, $DB, $CFG;
  85          $this->resetAfterTest();
  86  
  87          $importcode = get_new_importcode();
  88          $user1 = $this->getDataGenerator()->create_user();
  89          $user2 = $this->getDataGenerator()->create_user();
  90  
  91          $course = $this->getDataGenerator()->create_course();
  92          $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
  93          $itemname = $assign->name;
  94          $modulecontext = \context_module::instance($assign->cmid);
  95          // The generator returns a dummy object, lets get the real assign object.
  96          $assign = new \assign($modulecontext, false, false);
  97          $cm = $assign->get_course_module();
  98  
  99          // Enrol users in the course.
 100          $this->getDataGenerator()->enrol_user($user1->id, $course->id);
 101          $this->getDataGenerator()->enrol_user($user2->id, $course->id);
 102  
 103          // Enter a new grade into an existing grade item.
 104          $gradeitem = \grade_item::fetch(array('courseid' => $course->id, 'itemtype' => 'mod'));
 105  
 106          // Keep this value around for a test further down.
 107          $originalgrade = 55;
 108          $this->import_grades(array(
 109              'importcode' => $importcode,
 110              'itemid' => $gradeitem->id,
 111              'userid' => $user1->id,
 112              'finalgrade' => $originalgrade
 113          ));
 114  
 115          $status = grade_import_commit($course->id, $importcode, false, false);
 116          $this->assertTrue($status);
 117  
 118          // Get imported grade_grade.
 119          $gradegrade = \grade_grade::fetch(array('itemid' => $gradeitem->id, 'userid' => $user1->id));
 120          $this->assertEquals($originalgrade, $gradegrade->finalgrade);
 121          // Overriden field will be a timestamp and will evaluate out to true.
 122          $this->assertTrue($gradegrade->is_overridden());
 123  
 124          // Create a new grade item and import into that.
 125          $importcode = get_new_importcode();
 126          $record = new \stdClass();
 127          $record->itemname = 'New grade item';
 128          $record->importcode = $importcode;
 129          $record->importer = $USER->id;
 130          $insertid = $DB->insert_record('grade_import_newitem', $record);
 131  
 132          $finalgrade = 75;
 133          $this->import_grades(array(
 134              'importcode' => $importcode,
 135              'userid' => $user1->id,
 136              'finalgrade' => $finalgrade,
 137              'newgradeitem' => $insertid
 138          ));
 139  
 140          $status = grade_import_commit($course->id, $importcode, false, false);
 141          $this->assertTrue($status);
 142          // Check that we have a new \grade_item.
 143          $gradeitem = \grade_item::fetch(array('courseid' => $course->id, 'itemtype' => 'manual'));
 144          $this->assertEquals($record->itemname, $gradeitem->itemname);
 145          // Grades were imported.
 146          $gradegrade = \grade_grade::fetch(array('itemid' => $gradeitem->id, 'userid' => $user1->id));
 147          $this->assertEquals($finalgrade, $gradegrade->finalgrade);
 148          // As this is a new item the grade has not been overridden.
 149          $this->assertFalse($gradegrade->is_overridden());
 150  
 151          // Import feedback only.
 152          $importcode = get_new_importcode();
 153          $gradeitem = \grade_item::fetch(array('courseid' => $course->id, 'itemtype' => 'mod'));
 154  
 155          $originalfeedback = 'feedback can be useful';
 156          $this->import_grades(array(
 157              'importcode' => $importcode,
 158              'userid' => $user1->id,
 159              'itemid' => $gradeitem->id,
 160              'feedback' => $originalfeedback,
 161              'importonlyfeedback' => true
 162          ));
 163  
 164          $status = grade_import_commit($course->id, $importcode, true, false);
 165          $this->assertTrue($status);
 166          $gradegrade = \grade_grade::fetch(array('itemid' => $gradeitem->id, 'userid' => $user1->id));
 167          // The final grade should be the same as the first record further up. We are only altering the feedback.
 168          $this->assertEquals($originalgrade, $gradegrade->finalgrade);
 169          $this->assertTrue($gradegrade->is_overridden());
 170  
 171          // Import grades only.
 172          $importcode = get_new_importcode();
 173          $gradeitem = \grade_item::fetch(array('courseid' => $course->id, 'itemtype' => 'mod'));
 174  
 175          $finalgrade = 60;
 176          $this->import_grades(array(
 177              'importcode' => $importcode,
 178              'userid' => $user1->id,
 179              'itemid' => $gradeitem->id,
 180              'finalgrade' => $finalgrade,
 181              'feedback' => 'feedback can still be useful'
 182          ));
 183  
 184          $status = grade_import_commit($course->id, $importcode, false, false);
 185          $this->assertTrue($status);
 186          $gradegrade = \grade_grade::fetch(array('itemid' => $gradeitem->id, 'userid' => $user1->id));
 187          $this->assertEquals($finalgrade, $gradegrade->finalgrade);
 188          // The final feedback should not have changed.
 189          $this->assertEquals($originalfeedback, $gradegrade->feedback);
 190          $this->assertTrue($gradegrade->is_overridden());
 191  
 192          // Check that printing of import status is correct.
 193          $importcode = get_new_importcode();
 194          $gradeitem = \grade_item::fetch(array('courseid' => $course->id, 'itemtype' => 'mod'));
 195  
 196          $this->import_grades(array(
 197              'importcode' => $importcode,
 198              'userid' => $user1->id,
 199              'itemid' => $gradeitem->id
 200          ));
 201  
 202          ob_start();
 203          $status = grade_import_commit($course->id, $importcode);
 204          $output = ob_get_contents();
 205          ob_end_clean();
 206          $this->assertTrue($status);
 207          $this->assertStringContainsString("++ Grade import success ++", $output);
 208      }
 209  
 210      /**
 211       * Test grade import commit for users who aren't enrolled on the target course
 212       *
 213       * @covers ::grade_import_commit
 214       */
 215      public function test_grade_import_commit_unenrolled_user(): void {
 216          $this->resetAfterTest();
 217  
 218          $course = $this->getDataGenerator()->create_course();
 219          $assign = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]);
 220          $user = $this->getDataGenerator()->create_user(['firstname' => 'Lionel', 'lastname' => 'Doe']);
 221  
 222          // Enter a new grade into an existing grade item.
 223          $gradeitem = grade_item::fetch(['courseid' => $course->id, 'itemtype' => 'mod']);
 224  
 225          $importcode = get_new_importcode();
 226          $this->import_grades([
 227              'importcode' => $importcode,
 228              'itemid' => $gradeitem->id,
 229              'userid' => $user->id,
 230              'finalgrade' => 10,
 231          ]);
 232  
 233          ob_start();
 234          $status = grade_import_commit($course->id, $importcode);
 235          $output = ob_get_contents();
 236          ob_end_clean();
 237  
 238          // Assert commit succeeded and we didn't receive debugging about lack of name fields.
 239          $this->assertTrue($status);
 240          $this->assertStringContainsString('This import included the following grades for users not currently' .
 241              ' enrolled in this course', $output);
 242          $this->assertStringContainsString('User ' . fullname($user), $output);
 243          $this->assertDebuggingNotCalled();
 244      }
 245  
 246      /**
 247       * Test retrieving users included in impoty who aren't enrolled on the target course
 248       *
 249       * @covers ::get_unenrolled_users_in_import
 250       */
 251      public function test_get_unenrolled_users_in_import(): void {
 252          $this->resetAfterTest();
 253  
 254          $course = $this->getDataGenerator()->create_course();
 255          $assign = $this->getDataGenerator()->create_module('assign', ['course' => $course->id, 'idnumber' => 'gid101']);
 256          $user = $this->getDataGenerator()->create_user(['idnumber' => 'uid101']);
 257  
 258          // Enter a new grade into an existing grade item.
 259          $gradeitem = grade_item::fetch(['courseid' => $course->id, 'itemtype' => 'mod']);
 260  
 261          $importcode = get_new_importcode();
 262          $importgradeid = $this->import_grades([
 263              'importcode' => $importcode,
 264              'itemid' => $gradeitem->id,
 265              'userid' => $user->id,
 266              'finalgrade' => 10,
 267          ]);
 268  
 269          $unenrolledusers = get_unenrolled_users_in_import($importcode, $course->id);
 270          $this->assertCount(1, $unenrolledusers);
 271  
 272          $unenrolleduser = reset($unenrolledusers);
 273          $this->assertEquals((object) [
 274              'id' => $importgradeid,
 275              'firstnamephonetic' => $user->firstnamephonetic,
 276              'lastnamephonetic' => $user->lastnamephonetic,
 277              'middlename' => $user->middlename,
 278              'alternatename' => $user->alternatename,
 279              'firstname' => $user->firstname,
 280              'lastname' => $user->lastname,
 281              'useridnumber' => 'uid101',
 282              'gradeidnumber' => 'gid101',
 283          ], $unenrolleduser);
 284      }
 285  }