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 401] [Versions 311 and 402] [Versions 311 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   * A class for loading and preparing grade data from import.
  19   *
  20   * @package   gradeimport_csv
  21   * @copyright 2014 Adrian Greeve <adrian@moodle.com>
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  defined('MOODLE_INTERNAL') || die();
  26  
  27  /**
  28   * A class for loading and preparing grade data from import.
  29   *
  30   * @package   gradeimport_csv
  31   * @copyright 2014 Adrian Greeve <adrian@moodle.com>
  32   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  33   */
  34  class gradeimport_csv_load_data {
  35  
  36      /** @var string $error csv import error. */
  37      protected $error;
  38      /** @var int $iid Unique identifier for these csv records. */
  39      protected $iid;
  40      /** @var array $headers Column names for the data. */
  41      protected $headers;
  42      /** @var array $previewdata A subsection of the csv imported data. */
  43      protected $previewdata;
  44  
  45      // The map_user_data_with_value variables.
  46      /** @var array $newgrades Grades to be inserted into the gradebook. */
  47      protected $newgrades;
  48      /** @var array $newfeedbacks Feedback to be inserted into the gradebook. */
  49      protected $newfeedbacks;
  50      /** @var int $studentid Student ID*/
  51      protected $studentid;
  52  
  53      // The prepare_import_grade_data() variables.
  54      /** @var bool $status The current status of the import. True = okay, False = errors. */
  55      protected $status;
  56      /** @var int $importcode The code for this batch insert. */
  57      protected $importcode;
  58      /** @var array $gradebookerrors An array of errors from trying to import into the gradebook. */
  59      protected $gradebookerrors;
  60      /** @var array $newgradeitems An array of new grade items to be inserted into the gradebook. */
  61      protected $newgradeitems;
  62  
  63      /**
  64       * Load CSV content for previewing.
  65       *
  66       * @param string $text The grade data being imported.
  67       * @param string $encoding The type of encoding the file uses.
  68       * @param string $separator The separator being used to define each field.
  69       * @param int $previewrows How many rows are being previewed.
  70       */
  71      public function load_csv_content($text, $encoding, $separator, $previewrows) {
  72          $this->raise_limits();
  73  
  74          $this->iid = csv_import_reader::get_new_iid('grade');
  75          $csvimport = new csv_import_reader($this->iid, 'grade');
  76  
  77          $csvimport->load_csv_content($text, $encoding, $separator);
  78          $this->error = $csvimport->get_error();
  79  
  80          // If there are no import errors then proceed.
  81          if (empty($this->error)) {
  82  
  83              // Get header (field names).
  84              $this->headers = $csvimport->get_columns();
  85              $this->trim_headers();
  86  
  87              $csvimport->init();
  88              $this->previewdata = array();
  89  
  90              for ($numlines = 0; $numlines <= $previewrows; $numlines++) {
  91                  $lines = $csvimport->next();
  92                  if ($lines) {
  93                      $this->previewdata[] = $lines;
  94                  }
  95              }
  96          }
  97      }
  98  
  99      /**
 100       * Gets all of the grade items in this course.
 101       *
 102       * @param int $courseid Course id;
 103       * @return array An array of grade items for the course.
 104       */
 105      public static function fetch_grade_items($courseid) {
 106          $gradeitems = null;
 107          if ($allgradeitems = grade_item::fetch_all(array('courseid' => $courseid))) {
 108              foreach ($allgradeitems as $gradeitem) {
 109                  // Skip course type and category type.
 110                  if ($gradeitem->itemtype == 'course' || $gradeitem->itemtype == 'category') {
 111                      continue;
 112                  }
 113  
 114                  $displaystring = null;
 115                  if (!empty($gradeitem->itemmodule)) {
 116                      $displaystring = get_string('modulename', $gradeitem->itemmodule).get_string('labelsep', 'langconfig')
 117                              .$gradeitem->get_name();
 118                  } else {
 119                      $displaystring = $gradeitem->get_name();
 120                  }
 121                  $gradeitems[$gradeitem->id] = $displaystring;
 122              }
 123          }
 124          return $gradeitems;
 125      }
 126  
 127      /**
 128       * Cleans the column headers from the CSV file.
 129       */
 130      protected function trim_headers() {
 131          foreach ($this->headers as $i => $h) {
 132              $h = trim($h); // Remove whitespace.
 133              $h = clean_param($h, PARAM_RAW); // Clean the header.
 134              $this->headers[$i] = $h;
 135          }
 136      }
 137  
 138      /**
 139       * Raises the php execution time and memory limits for importing the CSV file.
 140       */
 141      protected function raise_limits() {
 142          // Large files are likely to take their time and memory. Let PHP know
 143          // that we'll take longer, and that the process should be recycled soon
 144          // to free up memory.
 145          core_php_time_limit::raise();
 146          raise_memory_limit(MEMORY_EXTRA);
 147      }
 148  
 149      /**
 150       * Inserts a record into the grade_import_values table. This also adds common record information.
 151       *
 152       * @param object $record The grade record being inserted into the database.
 153       * @param int $studentid The student ID.
 154       * @return bool|int true or insert id on success. Null if the grade value is too high.
 155       */
 156      protected function insert_grade_record($record, $studentid) {
 157          global $DB, $USER, $CFG;
 158          $record->importcode = $this->importcode;
 159          $record->userid     = $studentid;
 160          $record->importer   = $USER->id;
 161          // By default the maximum grade is 100.
 162          $gradepointmaximum = 100;
 163          // If the grade limit has been increased then use the gradepointmax setting.
 164          if ($CFG->unlimitedgrades) {
 165              $gradepointmaximum = $CFG->gradepointmax;
 166          }
 167          // If the record final grade is set then check that the grade value isn't too high.
 168          // Final grade will not be set if we are inserting feedback.
 169          if (!isset($record->finalgrade) || $record->finalgrade <= $gradepointmaximum) {
 170              return $DB->insert_record('grade_import_values', $record);
 171          } else {
 172              $this->cleanup_import(get_string('gradevaluetoobig', 'grades', $gradepointmaximum));
 173              return null;
 174          }
 175      }
 176  
 177      /**
 178       * Insert the new grade into the grade item buffer table.
 179       *
 180       * @param array $header The column headers from the CSV file.
 181       * @param int $key Current row identifier.
 182       * @param string $value The value for this row (final grade).
 183       * @return stdClass new grade that is ready for commiting to the gradebook.
 184       */
 185      protected function import_new_grade_item($header, $key, $value) {
 186          global $DB, $USER;
 187  
 188          // First check if header is already in temp database.
 189          if (empty($this->newgradeitems[$key])) {
 190  
 191              $newgradeitem = new stdClass();
 192              $newgradeitem->itemname = $header[$key];
 193              $newgradeitem->importcode = $this->importcode;
 194              $newgradeitem->importer = $USER->id;
 195  
 196              // Insert into new grade item buffer.
 197              $this->newgradeitems[$key] = $DB->insert_record('grade_import_newitem', $newgradeitem);
 198          }
 199          $newgrade = new stdClass();
 200          $newgrade->newgradeitem = $this->newgradeitems[$key];
 201  
 202          $trimmed = trim($value);
 203          if ($trimmed === '' or $trimmed == '-') {
 204              // Blank or dash grade means null, ie "no grade".
 205              $newgrade->finalgrade = null;
 206          } else {
 207              // We have an actual grade.
 208              $newgrade->finalgrade = $value;
 209          }
 210          $this->newgrades[] = $newgrade;
 211          return $newgrade;
 212      }
 213  
 214      /**
 215       * Check that the user is in the system.
 216       *
 217       * @param string $value The value, from the csv file, being mapped to identify the user.
 218       * @param array $userfields Contains the field and label being mapped from.
 219       * @return int Returns the user ID if it exists, otherwise null.
 220       */
 221      protected function check_user_exists($value, $userfields) {
 222          global $DB;
 223  
 224          $user = null;
 225          $errorkey = false;
 226          // The user may use the incorrect field to match the user. This could result in an exception.
 227          try {
 228              $field = $userfields['field'];
 229              // Fields that can be queried in a case-insensitive manner.
 230              $caseinsensitivefields = [
 231                  'email',
 232                  'username',
 233              ];
 234              // Build query predicate.
 235              if (in_array($field, $caseinsensitivefields)) {
 236                  // Case-insensitive.
 237                  $select = $DB->sql_equal($field, ':' . $field, false);
 238              } else {
 239                  // Exact-value.
 240                  $select = "{$field} = :{$field}";
 241              }
 242  
 243              // Validate if the user id value is numerical.
 244              if ($field === 'id' && !is_numeric($value)) {
 245                  $errorkey = 'usermappingerror';
 246              }
 247              // Make sure the record exists and that there's only one matching record found.
 248              $user = $DB->get_record_select('user', $select, array($userfields['field'] => $value), '*', MUST_EXIST);
 249          } catch (dml_missing_record_exception $missingex) {
 250              $errorkey = 'usermappingerror';
 251          } catch (dml_multiple_records_exception $multiex) {
 252              $errorkey = 'usermappingerrormultipleusersfound';
 253          }
 254          // Field may be fine, but no records were returned.
 255          if ($errorkey) {
 256              $usermappingerrorobj = new stdClass();
 257              $usermappingerrorobj->field = $userfields['label'];
 258              $usermappingerrorobj->value = $value;
 259              $this->cleanup_import(get_string($errorkey, 'grades', $usermappingerrorobj));
 260              unset($usermappingerrorobj);
 261              return null;
 262          }
 263          return $user->id;
 264      }
 265  
 266      /**
 267       * Check to see if the feedback matches a grade item.
 268       *
 269       * @param int $courseid The course ID.
 270       * @param int $itemid The ID of the grade item that the feedback relates to.
 271       * @param string $value The actual feedback being imported.
 272       * @return object Creates a feedback object with the item ID and the feedback value.
 273       */
 274      protected function create_feedback($courseid, $itemid, $value) {
 275          // Case of an id, only maps id of a grade_item.
 276          // This was idnumber.
 277          if (!new grade_item(array('id' => $itemid, 'courseid' => $courseid))) {
 278              // Supplied bad mapping, should not be possible since user
 279              // had to pick mapping.
 280              $this->cleanup_import(get_string('importfailed', 'grades'));
 281              return null;
 282          }
 283  
 284          // The itemid is the id of the grade item.
 285          $feedback = new stdClass();
 286          $feedback->itemid   = $itemid;
 287          $feedback->feedback = $value;
 288          return $feedback;
 289      }
 290  
 291      /**
 292       * This updates existing grade items.
 293       *
 294       * @param int $courseid The course ID.
 295       * @param array $map Mapping information provided by the user.
 296       * @param int $key The line that we are currently working on.
 297       * @param bool $verbosescales Form setting for grading with scales.
 298       * @param string $value The grade value.
 299       * @return array grades to be updated.
 300       */
 301      protected function update_grade_item($courseid, $map, $key, $verbosescales, $value) {
 302          // Case of an id, only maps id of a grade_item.
 303          // This was idnumber.
 304          if (!$gradeitem = new grade_item(array('id' => $map[$key], 'courseid' => $courseid))) {
 305              // Supplied bad mapping, should not be possible since user
 306              // had to pick mapping.
 307              $this->cleanup_import(get_string('importfailed', 'grades'));
 308              return null;
 309          }
 310  
 311          // Check if grade item is locked if so, abort.
 312          if ($gradeitem->is_locked()) {
 313              $this->cleanup_import(get_string('gradeitemlocked', 'grades'));
 314              return null;
 315          }
 316  
 317          $newgrade = new stdClass();
 318          $newgrade->itemid = $gradeitem->id;
 319          if ($gradeitem->gradetype == GRADE_TYPE_SCALE and $verbosescales) {
 320              if ($value === '' or $value == '-') {
 321                  $value = null; // No grade.
 322              } else {
 323                  $scale = $gradeitem->load_scale();
 324                  $scales = explode(',', $scale->scale);
 325                  $scales = array_map('trim', $scales); // Hack - trim whitespace around scale options.
 326                  array_unshift($scales, '-'); // Scales start at key 1.
 327                  $key = array_search($value, $scales);
 328                  if ($key === false) {
 329                      $this->cleanup_import(get_string('badgrade', 'grades'));
 330                      return null;
 331                  }
 332                  $value = $key;
 333              }
 334              $newgrade->finalgrade = $value;
 335          } else {
 336              if ($value === '' or $value == '-') {
 337                  $value = null; // No grade.
 338              } else {
 339                  // If the value has a local decimal or can correctly be unformatted, do it.
 340                  $validvalue = unformat_float($value, true);
 341                  if ($validvalue !== false) {
 342                      $value = $validvalue;
 343                  } else {
 344                      // Non numeric grade value supplied, possibly mapped wrong column.
 345                      $this->cleanup_import(get_string('badgrade', 'grades'));
 346                      return null;
 347                  }
 348              }
 349              $newgrade->finalgrade = $value;
 350          }
 351          $this->newgrades[] = $newgrade;
 352          return $this->newgrades;
 353      }
 354  
 355      /**
 356       * Clean up failed CSV grade import. Clears the temp table for inserting grades.
 357       *
 358       * @param string $notification The error message to display from the unsuccessful grade import.
 359       */
 360      protected function cleanup_import($notification) {
 361          $this->status = false;
 362          import_cleanup($this->importcode);
 363          $this->gradebookerrors[] = $notification;
 364      }
 365  
 366      /**
 367       * Check user mapping.
 368       *
 369       * @param string $mappingidentifier The user field that we are matching together.
 370       * @param string $value The value we are checking / importing.
 371       * @param array $header The column headers of the csv file.
 372       * @param array $map Mapping information provided by the user.
 373       * @param int $key Current row identifier.
 374       * @param int $courseid The course ID.
 375       * @param int $feedbackgradeid The ID of the grade item that the feedback relates to.
 376       * @param bool $verbosescales Form setting for grading with scales.
 377       */
 378      protected function map_user_data_with_value($mappingidentifier, $value, $header, $map, $key, $courseid, $feedbackgradeid,
 379              $verbosescales) {
 380  
 381          // Fields that the user can be mapped from.
 382          $userfields = array(
 383              'userid' => array(
 384                  'field' => 'id',
 385                  'label' => 'id',
 386              ),
 387              'useridnumber' => array(
 388                  'field' => 'idnumber',
 389                  'label' => 'idnumber',
 390              ),
 391              'useremail' => array(
 392                  'field' => 'email',
 393                  'label' => 'email address',
 394              ),
 395              'username' => array(
 396                  'field' => 'username',
 397                  'label' => 'username',
 398              ),
 399          );
 400  
 401          switch ($mappingidentifier) {
 402              case 'userid':
 403              case 'useridnumber':
 404              case 'useremail':
 405              case 'username':
 406                  $this->studentid = $this->check_user_exists($value, $userfields[$mappingidentifier]);
 407              break;
 408              case 'new':
 409                  $this->import_new_grade_item($header, $key, $value);
 410              break;
 411              case 'feedback':
 412                  if ($feedbackgradeid) {
 413                      $feedback = $this->create_feedback($courseid, $feedbackgradeid, $value);
 414                      if (isset($feedback)) {
 415                          $this->newfeedbacks[] = $feedback;
 416                      }
 417                  }
 418              break;
 419              default:
 420                  // Existing grade items.
 421                  if (!empty($map[$key])) {
 422                      $this->newgrades = $this->update_grade_item($courseid, $map, $key, $verbosescales, $value,
 423                              $mappingidentifier);
 424                  }
 425                  // Otherwise, we ignore this column altogether because user has chosen
 426                  // to ignore them (e.g. institution, address etc).
 427              break;
 428          }
 429      }
 430  
 431      /**
 432       * Checks and prepares grade data for inserting into the gradebook.
 433       *
 434       * @param array $header Column headers of the CSV file.
 435       * @param object $formdata Mapping information from the preview page.
 436       * @param object $csvimport csv import reader object for iterating over the imported CSV file.
 437       * @param int $courseid The course ID.
 438       * @param bool $separatemode If we have groups are they separate?
 439       * @param mixed $currentgroup current group information.
 440       * @param bool $verbosescales Form setting for grading with scales.
 441       * @return bool True if the status for importing is okay, false if there are errors.
 442       */
 443      public function prepare_import_grade_data($header, $formdata, $csvimport, $courseid, $separatemode, $currentgroup,
 444              $verbosescales) {
 445          global $DB, $USER;
 446  
 447          // The import code is used for inserting data into the grade tables.
 448          $this->importcode = $formdata->importcode;
 449          $this->status = true;
 450          $this->headers = $header;
 451          $this->studentid = null;
 452          $this->gradebookerrors = null;
 453          $forceimport = $formdata->forceimport;
 454          // Temporary array to keep track of what new headers are processed.
 455          $this->newgradeitems = array();
 456          $this->trim_headers();
 457          $timeexportkey = null;
 458          $map = array();
 459          // Loops mapping_0, mapping_1 .. mapping_n and construct $map array.
 460          foreach ($header as $i => $head) {
 461              if (isset($formdata->{'mapping_'.$i})) {
 462                  $map[$i] = $formdata->{'mapping_'.$i};
 463              }
 464              if ($head == get_string('timeexported', 'gradeexport_txt')) {
 465                  $timeexportkey = $i;
 466              }
 467          }
 468  
 469          // If mapping information is supplied.
 470          $map[clean_param($formdata->mapfrom, PARAM_RAW)] = clean_param($formdata->mapto, PARAM_RAW);
 471  
 472          // Check for mapto collisions.
 473          $maperrors = array();
 474          foreach ($map as $i => $j) {
 475              if ($j == 0) {
 476                  // You can have multiple ignores.
 477                  continue;
 478              } else {
 479                  if (!isset($maperrors[$j])) {
 480                      $maperrors[$j] = true;
 481                  } else {
 482                      // Collision.
 483                      print_error('cannotmapfield', '', '', $j);
 484                  }
 485              }
 486          }
 487  
 488          $this->raise_limits();
 489  
 490          $csvimport->init();
 491  
 492          while ($line = $csvimport->next()) {
 493              if (count($line) <= 1) {
 494                  // There is no data on this line, move on.
 495                  continue;
 496              }
 497  
 498              // Array to hold all grades to be inserted.
 499              $this->newgrades = array();
 500              // Array to hold all feedback.
 501              $this->newfeedbacks = array();
 502              // Each line is a student record.
 503              foreach ($line as $key => $value) {
 504  
 505                  $value = clean_param($value, PARAM_RAW);
 506                  $value = trim($value);
 507  
 508                  /*
 509                   * the options are
 510                   * 1) userid, useridnumber, usermail, username - used to identify user row
 511                   * 2) new - new grade item
 512                   * 3) id - id of the old grade item to map onto
 513                   * 3) feedback_id - feedback for grade item id
 514                   */
 515  
 516                  // Explode the mapping for feedback into a label 'feedback' and the identifying number.
 517                  $mappingbase = explode("_", $map[$key]);
 518                  $mappingidentifier = $mappingbase[0];
 519                  // Set the feedback identifier if it exists.
 520                  if (isset($mappingbase[1])) {
 521                      $feedbackgradeid = (int)$mappingbase[1];
 522                  } else {
 523                      $feedbackgradeid = '';
 524                  }
 525  
 526                  $this->map_user_data_with_value($mappingidentifier, $value, $header, $map, $key, $courseid, $feedbackgradeid,
 527                          $verbosescales);
 528                  if ($this->status === false) {
 529                      return $this->status;
 530                  }
 531              }
 532  
 533              // No user mapping supplied at all, or user mapping failed.
 534              if (empty($this->studentid) || !is_numeric($this->studentid)) {
 535                  // User not found, abort whole import.
 536                  $this->cleanup_import(get_string('usermappingerrorusernotfound', 'grades'));
 537                  break;
 538              }
 539  
 540              if ($separatemode and !groups_is_member($currentgroup, $this->studentid)) {
 541                  // Not allowed to import into this group, abort.
 542                  $this->cleanup_import(get_string('usermappingerrorcurrentgroup', 'grades'));
 543                  break;
 544              }
 545  
 546              // Insert results of this students into buffer.
 547              if ($this->status and !empty($this->newgrades)) {
 548  
 549                  foreach ($this->newgrades as $newgrade) {
 550  
 551                      // Check if grade_grade is locked and if so, abort.
 552                      if (!empty($newgrade->itemid) and $gradegrade = new grade_grade(array('itemid' => $newgrade->itemid,
 553                              'userid' => $this->studentid))) {
 554                          if ($gradegrade->is_locked()) {
 555                              // Individual grade locked.
 556                              $this->cleanup_import(get_string('gradelocked', 'grades'));
 557                              return $this->status;
 558                          }
 559                          // Check if the force import option is disabled and the last exported date column is present.
 560                          if (!$forceimport && !empty($timeexportkey)) {
 561                              $exportedtime = $line[$timeexportkey];
 562                              if (clean_param($exportedtime, PARAM_INT) != $exportedtime || $exportedtime > time() ||
 563                                      $exportedtime < strtotime("-1 year", time())) {
 564                                  // The date is invalid, or in the future, or more than a year old.
 565                                  $this->cleanup_import(get_string('invalidgradeexporteddate', 'grades'));
 566                                  return $this->status;
 567  
 568                              }
 569                              $timemodified = $gradegrade->get_dategraded();
 570                              if (!empty($timemodified) && ($exportedtime < $timemodified)) {
 571                                  // The item was graded after we exported it, we return here not to override it.
 572                                  $user = core_user::get_user($this->studentid);
 573                                  $this->cleanup_import(get_string('gradealreadyupdated', 'grades', fullname($user)));
 574                                  return $this->status;
 575                              }
 576                          }
 577                      }
 578                      $insertid = self::insert_grade_record($newgrade, $this->studentid);
 579                      // Check to see if the insert was successful.
 580                      if (empty($insertid)) {
 581                          return null;
 582                      }
 583                  }
 584              }
 585  
 586              // Updating/inserting all comments here.
 587              if ($this->status and !empty($this->newfeedbacks)) {
 588                  foreach ($this->newfeedbacks as $newfeedback) {
 589                      $sql = "SELECT *
 590                                FROM {grade_import_values}
 591                               WHERE importcode=? AND userid=? AND itemid=? AND importer=?";
 592                      if ($feedback = $DB->get_record_sql($sql, array($this->importcode, $this->studentid, $newfeedback->itemid,
 593                              $USER->id))) {
 594                          $newfeedback->id = $feedback->id;
 595                          $DB->update_record('grade_import_values', $newfeedback);
 596  
 597                      } else {
 598                          // The grade item for this is not updated.
 599                          $newfeedback->importonlyfeedback = true;
 600                          $insertid = self::insert_grade_record($newfeedback, $this->studentid);
 601                          // Check to see if the insert was successful.
 602                          if (empty($insertid)) {
 603                              return null;
 604                          }
 605                      }
 606                  }
 607              }
 608          }
 609          return $this->status;
 610      }
 611  
 612      /**
 613       * Returns the headers parameter for this class.
 614       *
 615       * @return array returns headers parameter for this class.
 616       */
 617      public function get_headers() {
 618          return $this->headers;
 619      }
 620  
 621      /**
 622       * Returns the error parameter for this class.
 623       *
 624       * @return string returns error parameter for this class.
 625       */
 626      public function get_error() {
 627          return $this->error;
 628      }
 629  
 630      /**
 631       * Returns the iid parameter for this class.
 632       *
 633       * @return int returns iid parameter for this class.
 634       */
 635      public function get_iid() {
 636          return $this->iid;
 637      }
 638  
 639      /**
 640       * Returns the preview_data parameter for this class.
 641       *
 642       * @return array returns previewdata parameter for this class.
 643       */
 644      public function get_previewdata() {
 645          return $this->previewdata;
 646      }
 647  
 648      /**
 649       * Returns the gradebookerrors parameter for this class.
 650       *
 651       * @return array returns gradebookerrors parameter for this class.
 652       */
 653      public function get_gradebookerrors() {
 654          return $this->gradebookerrors;
 655      }
 656  }