[ Index ]

PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008]

title

Body

[close]

/lib/ -> gradelib.php (source)

   1  <?php // $Id: gradelib.php,v 1.120.2.26 2008/05/13 21:51:47 skodak Exp $
   2  
   3  ///////////////////////////////////////////////////////////////////////////
   4  // NOTICE OF COPYRIGHT                                                   //
   5  //                                                                       //
   6  // Moodle - Modular Object-Oriented Dynamic Learning Environment         //
   7  //          http://moodle.org                                            //
   8  //                                                                       //
   9  // Copyright (C) 1999 onwards  Martin Dougiamas  http://moodle.com       //
  10  //                                                                       //
  11  // This program is free software; you can redistribute it and/or modify  //
  12  // it under the terms of the GNU General Public License as published by  //
  13  // the Free Software Foundation; either version 2 of the License, or     //
  14  // (at your option) any later version.                                   //
  15  //                                                                       //
  16  // This program is distributed in the hope that it will be useful,       //
  17  // but WITHOUT ANY WARRANTY; without even the implied warranty of        //
  18  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         //
  19  // GNU General Public License for more details:                          //
  20  //                                                                       //
  21  //          http://www.gnu.org/copyleft/gpl.html                         //
  22  //                                                                       //
  23  ///////////////////////////////////////////////////////////////////////////
  24  
  25  /**
  26   * Library of functions for gradebook - both public and internal
  27   *
  28   * @author Moodle HQ developers
  29   * @version  $Id: gradelib.php,v 1.120.2.26 2008/05/13 21:51:47 skodak Exp $
  30   * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
  31   * @package moodlecore
  32   */
  33  
  34  require_once($CFG->libdir . '/grade/constants.php');
  35  
  36  require_once($CFG->libdir . '/grade/grade_category.php');
  37  require_once($CFG->libdir . '/grade/grade_item.php');
  38  require_once($CFG->libdir . '/grade/grade_grade.php');
  39  require_once($CFG->libdir . '/grade/grade_scale.php');
  40  require_once($CFG->libdir . '/grade/grade_outcome.php');
  41  
  42  /////////////////////////////////////////////////////////////////////
  43  ///// Start of public API for communication with modules/blocks /////
  44  /////////////////////////////////////////////////////////////////////
  45  
  46  /**
  47   * Submit new or update grade; update/create grade_item definition. Grade must have userid specified,
  48   * rawgrade and feedback with format are optional. rawgrade NULL means 'Not graded', missing property
  49   * or key means do not change existing.
  50   *
  51   * Only following grade item properties can be changed 'itemname', 'idnumber', 'gradetype', 'grademax',
  52   * 'grademin', 'scaleid', 'multfactor', 'plusfactor', 'deleted' and 'hidden'. 'reset' means delete all current grades including locked ones.
  53   *
  54   * Manual, course or category items can not be updated by this function.
  55   * @public
  56   * @param string $source source of the grade such as 'mod/assignment'
  57   * @param int $courseid id of course
  58   * @param string $itemtype type of grade item - mod, block
  59   * @param string $itemmodule more specific then $itemtype - assignment, forum, etc.; maybe NULL for some item types
  60   * @param int $iteminstance instance it of graded subject
  61   * @param int $itemnumber most probably 0, modules can use other numbers when having more than one grades for each user
  62   * @param mixed $grades grade (object, array) or several grades (arrays of arrays or objects), NULL if updating grade_item definition only
  63   * @param mixed $itemdetails object or array describing the grading item, NULL if no change
  64   */
  65  function grade_update($source, $courseid, $itemtype, $itemmodule, $iteminstance, $itemnumber, $grades=NULL, $itemdetails=NULL) {
  66      global $USER, $CFG;
  67  
  68      // only following grade_item properties can be changed in this function
  69      $allowed = array('itemname', 'idnumber', 'gradetype', 'grademax', 'grademin', 'scaleid', 'multfactor', 'plusfactor', 'deleted', 'hidden');
  70      // list of 10,5 numeric fields
  71      $floats  = array('grademin', 'grademax', 'multfactor', 'plusfactor');
  72  
  73      // grade item identification
  74      $params = compact('courseid', 'itemtype', 'itemmodule', 'iteminstance', 'itemnumber');
  75  
  76      if (is_null($courseid) or is_null($itemtype)) {
  77          debugging('Missing courseid or itemtype');
  78          return GRADE_UPDATE_FAILED;
  79      }
  80  
  81      if (!$grade_items = grade_item::fetch_all($params)) {
  82          // create a new one
  83          $grade_item = false;
  84  
  85      } else if (count($grade_items) == 1){
  86          $grade_item = reset($grade_items);
  87          unset($grade_items); //release memory
  88  
  89      } else {
  90          debugging('Found more than one grade item');
  91          return GRADE_UPDATE_MULTIPLE;
  92      }
  93  
  94      if (!empty($itemdetails['deleted'])) {
  95          if ($grade_item) {
  96              if ($grade_item->delete($source)) {
  97                  return GRADE_UPDATE_OK;
  98              } else {
  99                  return GRADE_UPDATE_FAILED;
 100              }
 101          }
 102          return GRADE_UPDATE_OK;
 103      }
 104  
 105  /// Create or update the grade_item if needed
 106  
 107      if (!$grade_item) {
 108          if ($itemdetails) {
 109              $itemdetails = (array)$itemdetails;
 110  
 111              // grademin and grademax ignored when scale specified
 112              if (array_key_exists('scaleid', $itemdetails)) {
 113                  if ($itemdetails['scaleid']) {
 114                      unset($itemdetails['grademin']);
 115                      unset($itemdetails['grademax']);
 116                  }
 117              }
 118  
 119              foreach ($itemdetails as $k=>$v) {
 120                  if (!in_array($k, $allowed)) {
 121                      // ignore it
 122                      continue;
 123                  }
 124                  if ($k == 'gradetype' and $v == GRADE_TYPE_NONE) {
 125                      // no grade item needed!
 126                      return GRADE_UPDATE_OK;
 127                  }
 128                  $params[$k] = $v;
 129              }
 130          }
 131          $grade_item = new grade_item($params);
 132          $grade_item->insert();
 133  
 134      } else {
 135          if ($grade_item->is_locked()) {
 136              // no notice() here, test returned value instead!
 137              return GRADE_UPDATE_ITEM_LOCKED;
 138          }
 139  
 140          if ($itemdetails) {
 141              $itemdetails = (array)$itemdetails;
 142              $update = false;
 143              foreach ($itemdetails as $k=>$v) {
 144                  if (!in_array($k, $allowed)) {
 145                      // ignore it
 146                      continue;
 147                  }
 148                  if (in_array($k, $floats)) {
 149                      if (grade_floats_different($grade_item->{$k}, $v)) {
 150                          $grade_item->{$k} = $v;
 151                          $update = true;
 152                      }
 153  
 154                  } else {
 155                      if ($grade_item->{$k} != $v) {
 156                          $grade_item->{$k} = $v;
 157                          $update = true;
 158                      }
 159                  }
 160              }
 161              if ($update) {
 162                  $grade_item->update();
 163              }
 164          }
 165      }
 166  
 167  /// reset grades if requested
 168      if (!empty($itemdetails['reset'])) {
 169          $grade_item->delete_all_grades('reset');
 170          return GRADE_UPDATE_OK;
 171      }
 172  
 173  /// Some extra checks
 174      // do we use grading?
 175      if ($grade_item->gradetype == GRADE_TYPE_NONE) {
 176          return GRADE_UPDATE_OK;
 177      }
 178  
 179      // no grade submitted
 180      if (empty($grades)) {
 181          return GRADE_UPDATE_OK;
 182      }
 183  
 184  /// Finally start processing of grades
 185      if (is_object($grades)) {
 186          $grades = array($grades->userid=>$grades);
 187      } else {
 188          if (array_key_exists('userid', $grades)) {
 189              $grades = array($grades['userid']=>$grades);
 190          }
 191      }
 192  
 193  /// normalize and verify grade array
 194      foreach($grades as $k=>$g) {
 195          if (!is_array($g)) {
 196              $g = (array)$g;
 197              $grades[$k] = $g;
 198          }
 199  
 200          if (empty($g['userid']) or $k != $g['userid']) {
 201              debugging('Incorrect grade array index, must be user id! Grade ignored.');
 202              unset($grades[$k]);
 203          }
 204      }
 205  
 206      if (empty($grades)) {
 207          return GRADE_UPDATE_FAILED;
 208      }
 209  
 210      $count = count($grades);
 211      if ($count == 1) {
 212          reset($grades);
 213          $uid = key($grades);
 214          $sql = "SELECT * FROM {$CFG->prefix}grade_grades WHERE itemid = $grade_item->id AND userid = $uid";
 215  
 216      } else if ($count < 200) {
 217          $uids = implode(',', array_keys($grades));
 218          $sql = "SELECT * FROM {$CFG->prefix}grade_grades WHERE itemid = $grade_item->id AND userid IN ($uids)";
 219  
 220      } else {
 221          $sql = "SELECT * FROM {$CFG->prefix}grade_grades WHERE itemid = $grade_item->id";
 222      }
 223  
 224      $rs = get_recordset_sql($sql);
 225  
 226      $failed = false;
 227  
 228      while (count($grades) > 0) {
 229          $grade_grade = null;
 230          $grade       = null;
 231  
 232          while ($rs and !rs_EOF($rs)) {
 233              if (!$gd = rs_fetch_next_record($rs)) {
 234                  break;
 235              }
 236              $userid = $gd->userid;
 237              if (!isset($grades[$userid])) {
 238                  // this grade not requested, continue
 239                  continue;
 240              }
 241              // existing grade requested
 242              $grade       = $grades[$userid];
 243              $grade_grade = new grade_grade($gd, false);
 244              unset($grades[$userid]);
 245              break;
 246          }
 247  
 248          if (is_null($grade_grade)) {
 249              if (count($grades) == 0) {
 250                  // no more grades to process
 251                  break;
 252              }
 253  
 254              $grade       = reset($grades);
 255              $userid      = $grade['userid'];
 256              $grade_grade = new grade_grade(array('itemid'=>$grade_item->id, 'userid'=>$userid), false);
 257              $grade_grade->load_optional_fields(); // add feedback and info too
 258              unset($grades[$userid]);
 259          }
 260  
 261          $rawgrade       = false;
 262          $feedback       = false;
 263          $feedbackformat = FORMAT_MOODLE;
 264          $usermodified   = $USER->id;
 265          $datesubmitted  = null;
 266          $dategraded     = null;
 267  
 268          if (array_key_exists('rawgrade', $grade)) {
 269              $rawgrade = $grade['rawgrade'];
 270          }
 271  
 272          if (array_key_exists('feedback', $grade)) {
 273              $feedback = $grade['feedback'];
 274          }
 275  
 276          if (array_key_exists('feedbackformat', $grade)) {
 277              $feedbackformat = $grade['feedbackformat'];
 278          }
 279  
 280          if (array_key_exists('usermodified', $grade)) {
 281              $usermodified = $grade['usermodified'];
 282          }
 283  
 284          if (array_key_exists('datesubmitted', $grade)) {
 285              $datesubmitted = $grade['datesubmitted'];
 286          }
 287  
 288          if (array_key_exists('dategraded', $grade)) {
 289              $dategraded = $grade['dategraded'];
 290          }
 291  
 292          // update or insert the grade
 293          if (!$grade_item->update_raw_grade($userid, $rawgrade, $source, $feedback, $feedbackformat, $usermodified, $dategraded, $datesubmitted, $grade_grade)) {
 294              $failed = true;
 295          }
 296      }
 297  
 298      if ($rs) {
 299          rs_close($rs);
 300      }
 301  
 302      if (!$failed) {
 303          return GRADE_UPDATE_OK;
 304      } else {
 305          return GRADE_UPDATE_FAILED;
 306      }
 307  }
 308  
 309  /**
 310   * Updates outcomes of user
 311   * Manual outcomes can not be updated.
 312   * @public
 313   * @param string $source source of the grade such as 'mod/assignment'
 314   * @param int $courseid id of course
 315   * @param string $itemtype 'mod', 'block'
 316   * @param string $itemmodule 'forum, 'quiz', etc.
 317   * @param int $iteminstance id of the item module
 318   * @param int $userid ID of the graded user
 319   * @param array $data array itemnumber=>outcomegrade
 320   */
 321  function grade_update_outcomes($source, $courseid, $itemtype, $itemmodule, $iteminstance, $userid, $data) {
 322      if ($items = grade_item::fetch_all(array('itemtype'=>$itemtype, 'itemmodule'=>$itemmodule, 'iteminstance'=>$iteminstance, 'courseid'=>$courseid))) {
 323          foreach ($items as $item) {
 324              if (!array_key_exists($item->itemnumber, $data)) {
 325                  continue;
 326              }
 327              $grade = $data[$item->itemnumber] < 1 ? null : $data[$item->itemnumber];
 328              $item->update_final_grade($userid, $grade, $source);
 329          }
 330      }
 331  }
 332  
 333  /**
 334   * Returns grading information for given activity - optionally with users grades
 335   * Manual, course or category items can not be queried.
 336   * @public
 337   * @param int $courseid id of course
 338   * @param string $itemtype 'mod', 'block'
 339   * @param string $itemmodule 'forum, 'quiz', etc.
 340   * @param int $iteminstance id of the item module
 341   * @param int $userid_or_ids optional id of the graded user or array of ids; if userid not used, returns only information about grade_item
 342   * @return array of grade information objects (scaleid, name, grade and locked status, etc.) indexed with itemnumbers
 343   */
 344  function grade_get_grades($courseid, $itemtype, $itemmodule, $iteminstance, $userid_or_ids=null) {
 345      global $CFG;
 346  
 347      $return = new object();
 348      $return->items    = array();
 349      $return->outcomes = array();
 350  
 351      $course_item = grade_item::fetch_course_item($courseid);
 352      $needsupdate = array();
 353      if ($course_item->needsupdate) {
 354          $result = grade_regrade_final_grades($courseid);
 355          if ($result !== true) {
 356              $needsupdate = array_keys($result);
 357          }
 358      }
 359  
 360      if ($grade_items = grade_item::fetch_all(array('itemtype'=>$itemtype, 'itemmodule'=>$itemmodule, 'iteminstance'=>$iteminstance, 'courseid'=>$courseid))) {
 361          foreach ($grade_items as $grade_item) {
 362              $decimalpoints = null;
 363  
 364              if (empty($grade_item->outcomeid)) {
 365                  // prepare information about grade item
 366                  $item = new object();
 367                  $item->itemnumber = $grade_item->itemnumber;
 368                  $item->scaleid    = $grade_item->scaleid;
 369                  $item->name       = $grade_item->get_name();
 370                  $item->grademin   = $grade_item->grademin;
 371                  $item->grademax   = $grade_item->grademax;
 372                  $item->gradepass  = $grade_item->gradepass;
 373                  $item->locked     = $grade_item->is_locked();
 374                  $item->hidden     = $grade_item->is_hidden();
 375                  $item->grades     = array();
 376  
 377                  switch ($grade_item->gradetype) {
 378                      case GRADE_TYPE_NONE:
 379                          continue;
 380  
 381                      case GRADE_TYPE_VALUE:
 382                          $item->scaleid = 0;
 383                          break;
 384  
 385                      case GRADE_TYPE_TEXT:
 386                          $item->scaleid   = 0;
 387                          $item->grademin   = 0;
 388                          $item->grademax   = 0;
 389                          $item->gradepass  = 0;
 390                          break;
 391                  }
 392  
 393                  if (empty($userid_or_ids)) {
 394                      $userids = array();
 395  
 396                  } else if (is_array($userid_or_ids)) {
 397                      $userids = $userid_or_ids;
 398  
 399                  } else {
 400                      $userids = array($userid_or_ids);
 401                  }
 402  
 403                  if ($userids) {
 404                      $grade_grades = grade_grade::fetch_users_grades($grade_item, $userids, true);
 405                      foreach ($userids as $userid) {
 406                          $grade_grades[$userid]->grade_item =& $grade_item;
 407  
 408                          $grade = new object();
 409                          $grade->grade          = $grade_grades[$userid]->finalgrade;
 410                          $grade->locked         = $grade_grades[$userid]->is_locked();
 411                          $grade->hidden         = $grade_grades[$userid]->is_hidden();
 412                          $grade->overridden     = $grade_grades[$userid]->overridden;
 413                          $grade->feedback       = $grade_grades[$userid]->feedback;
 414                          $grade->feedbackformat = $grade_grades[$userid]->feedbackformat;
 415                          $grade->usermodified   = $grade_grades[$userid]->usermodified;
 416                          $grade->datesubmitted  = $grade_grades[$userid]->get_datesubmitted();
 417                          $grade->dategraded     = $grade_grades[$userid]->get_dategraded();
 418  
 419                          // create text representation of grade
 420                          if ($grade_item->gradetype == GRADE_TYPE_TEXT or $grade_item->gradetype == GRADE_TYPE_NONE) {
 421                              $grade->grade          = null;
 422                              $grade->str_grade      = '-';
 423                              $grade->str_long_grade = $grade->str_grade;
 424  
 425                          } else if (in_array($grade_item->id, $needsupdate)) {
 426                              $grade->grade          = false;
 427                              $grade->str_grade      = get_string('error');
 428                              $grade->str_long_grade = $grade->str_grade;
 429  
 430                          } else if (is_null($grade->grade)) {
 431                              $grade->str_grade      = '-';
 432                              $grade->str_long_grade = $grade->str_grade;
 433  
 434                          } else {
 435                              $grade->str_grade = grade_format_gradevalue($grade->grade, $grade_item);
 436                              if ($grade_item->gradetype == GRADE_TYPE_SCALE or $grade_item->get_displaytype() != GRADE_DISPLAY_TYPE_REAL) {
 437                                  $grade->str_long_grade = $grade->str_grade;
 438                              } else {
 439                                  $a = new object();
 440                                  $a->grade = $grade->str_grade;
 441                                  $a->max   = grade_format_gradevalue($grade_item->grademax, $grade_item);
 442                                  $grade->str_long_grade = get_string('gradelong', 'grades', $a);
 443                              }
 444                          }
 445  
 446                          // create html representation of feedback
 447                          if (is_null($grade->feedback)) {
 448                              $grade->str_feedback = '';
 449                          } else {
 450                              $grade->str_feedback = format_text($grade->feedback, $grade->feedbackformat);
 451                          }
 452  
 453                          $item->grades[$userid] = $grade;
 454                      }
 455                  }
 456                  $return->items[$grade_item->itemnumber] = $item;
 457  
 458              } else {
 459                  if (!$grade_outcome = grade_outcome::fetch(array('id'=>$grade_item->outcomeid))) {
 460                      debugging('Incorect outcomeid found');
 461                      continue;
 462                  }
 463  
 464                  // outcome info
 465                  $outcome = new object();
 466                  $outcome->itemnumber = $grade_item->itemnumber;
 467                  $outcome->scaleid    = $grade_outcome->scaleid;
 468                  $outcome->name       = $grade_outcome->get_name();
 469                  $outcome->locked     = $grade_item->is_locked();
 470                  $outcome->hidden     = $grade_item->is_hidden();
 471  
 472                  if (empty($userid_or_ids)) {
 473                      $userids = array();
 474                  } else if (is_array($userid_or_ids)) {
 475                      $userids = $userid_or_ids;
 476                  } else {
 477                      $userids = array($userid_or_ids);
 478                  }
 479  
 480                  if ($userids) {
 481                      $grade_grades = grade_grade::fetch_users_grades($grade_item, $userids, true);
 482                      foreach ($userids as $userid) {
 483                          $grade_grades[$userid]->grade_item =& $grade_item;
 484  
 485                          $grade = new object();
 486                          $grade->grade          = $grade_grades[$userid]->finalgrade;
 487                          $grade->locked         = $grade_grades[$userid]->is_locked();
 488                          $grade->hidden         = $grade_grades[$userid]->is_hidden();
 489                          $grade->feedback       = $grade_grades[$userid]->feedback;
 490                          $grade->feedbackformat = $grade_grades[$userid]->feedbackformat;
 491                          $grade->usermodified   = $grade_grades[$userid]->usermodified;
 492  
 493                          // create text representation of grade
 494                          if (in_array($grade_item->id, $needsupdate)) {
 495                              $grade->grade     = false;
 496                              $grade->str_grade = get_string('error');
 497  
 498                          } else if (is_null($grade->grade)) {
 499                              $grade->grade = 0;
 500                              $grade->str_grade = get_string('nooutcome', 'grades');
 501  
 502                          } else {
 503                              $grade->grade = (int)$grade->grade;
 504                              $scale = $grade_item->load_scale();
 505                              $grade->str_grade = format_string($scale->scale_items[(int)$grade->grade-1]);
 506                          }
 507  
 508                          // create html representation of feedback
 509                          if (is_null($grade->feedback)) {
 510                              $grade->str_feedback = '';
 511                          } else {
 512                              $grade->str_feedback = format_text($grade->feedback, $grade->feedbackformat);
 513                          }
 514  
 515                          $outcome->grades[$userid] = $grade;
 516                      }
 517                  }
 518  
 519                  if (isset($return->outcomes[$grade_item->itemnumber])) {
 520                      // itemnumber duplicates - lets fix them!
 521                      $newnumber = $grade_item->itemnumber + 1;
 522                      while(grade_item::fetch(array('itemtype'=>$itemtype, 'itemmodule'=>$itemmodule, 'iteminstance'=>$iteminstance, 'courseid'=>$courseid, 'itemnumber'=>$newnumber))) {
 523                          $newnumber++;
 524                      }
 525                      $outcome->itemnumber    = $newnumber;
 526                      $grade_item->itemnumber = $newnumber;
 527                      $grade_item->update('system');
 528                  }
 529  
 530                  $return->outcomes[$grade_item->itemnumber] = $outcome;
 531  
 532              }
 533          }
 534      }
 535  
 536      // sort results using itemnumbers
 537      ksort($return->items, SORT_NUMERIC);
 538      ksort($return->outcomes, SORT_NUMERIC);
 539  
 540      return $return;
 541  }
 542  
 543  ///////////////////////////////////////////////////////////////////
 544  ///// End of public API for communication with modules/blocks /////
 545  ///////////////////////////////////////////////////////////////////
 546  
 547  
 548  
 549  ///////////////////////////////////////////////////////////////////
 550  ///// Internal API: used by gradebook plugins and Moodle core /////
 551  ///////////////////////////////////////////////////////////////////
 552  
 553  /**
 554   * Returns course gradebook setting
 555   * @param int $courseid
 556   * @param string $name of setting, maybe null if reset only
 557   * @param bool $resetcache force reset of internal static cache
 558   * @return string value, NULL if no setting
 559   */
 560  function grade_get_setting($courseid, $name, $default=null, $resetcache=false) {
 561      static $cache = array();
 562  
 563      if ($resetcache or !array_key_exists($courseid, $cache)) {
 564          $cache[$courseid] = array();
 565  
 566      } else if (is_null($name)) {
 567          return null;
 568  
 569      } else if (array_key_exists($name, $cache[$courseid])) {
 570          return $cache[$courseid][$name];
 571      }
 572  
 573      if (!$data = get_record('grade_settings', 'courseid', $courseid, 'name', addslashes($name))) {
 574          $result = null;
 575      } else {
 576          $result = $data->value;
 577      }
 578  
 579      if (is_null($result)) {
 580          $result = $default;
 581      }
 582  
 583      $cache[$courseid][$name] = $result;
 584      return $result;
 585  }
 586  
 587  /**
 588   * Returns all course gradebook settings as object properties
 589   * @param int $courseid
 590   * @return object
 591   */
 592  function grade_get_settings($courseid) {
 593       $settings = new object();
 594       $settings->id = $courseid;
 595  
 596      if ($records = get_records('grade_settings', 'courseid', $courseid)) {
 597          foreach ($records as $record) {
 598              $settings->{$record->name} = $record->value;
 599          }
 600      }
 601  
 602      return $settings;
 603  }
 604  
 605  /**
 606   * Add/update course gradebook setting
 607   * @param int $courseid
 608   * @param string $name of setting
 609   * @param string value, NULL means no setting==remove
 610   * @return void
 611   */
 612  function grade_set_setting($courseid, $name, $value) {
 613      if (is_null($value)) {
 614          delete_records('grade_settings', 'courseid', $courseid, 'name', addslashes($name));
 615  
 616      } else if (!$existing = get_record('grade_settings', 'courseid', $courseid, 'name', addslashes($name))) {
 617          $data = new object();
 618          $data->courseid = $courseid;
 619          $data->name     = addslashes($name);
 620          $data->value    = addslashes($value);
 621          insert_record('grade_settings', $data);
 622  
 623      } else {
 624          $data = new object();
 625          $data->id       = $existing->id;
 626          $data->value    = addslashes($value);
 627          update_record('grade_settings', $data);
 628      }
 629  
 630      grade_get_setting($courseid, null, null, true); // reset the cache
 631  }
 632  
 633  /**
 634   * Returns string representation of grade value
 635   * @param float $value grade value
 636   * @param object $grade_item - by reference to prevent scale reloading
 637   * @param bool $localized use localised decimal separator
 638   * @param int $displaytype type of display - GRADE_DISPLAY_TYPE_REAL, GRADE_DISPLAY_TYPE_PERCENTAGE, GRADE_DISPLAY_TYPE_LETTER
 639   * @param int $decimalplaces number of decimal places when displaying float values
 640   * @return string
 641   */
 642  function grade_format_gradevalue($value, &$grade_item, $localized=true, $displaytype=null, $decimals=null) {
 643      if ($grade_item->gradetype == GRADE_TYPE_NONE or $grade_item->gradetype == GRADE_TYPE_TEXT) {
 644          return '';
 645      }
 646  
 647      // no grade yet?
 648      if (is_null($value)) {
 649          return '-';
 650      }
 651  
 652      if ($grade_item->gradetype != GRADE_TYPE_VALUE and $grade_item->gradetype != GRADE_TYPE_SCALE) {
 653          //unknown type??
 654          return '';
 655      }
 656  
 657      if (is_null($displaytype)) {
 658          $displaytype = $grade_item->get_displaytype();
 659      }
 660  
 661      if (is_null($decimals)) {
 662          $decimals = $grade_item->get_decimals();
 663      }
 664  
 665      switch ($displaytype) {
 666          case GRADE_DISPLAY_TYPE_REAL:
 667              if ($grade_item->gradetype == GRADE_TYPE_SCALE) {
 668                  if (!$scale = $grade_item->load_scale()) {
 669                      return get_string('error');
 670                  }
 671  
 672                  $value = (int)bounded_number($grade_item->grademin, $value, $grade_item->grademax);
 673                  return format_string($scale->scale_items[$value-1]);
 674  
 675              } else {
 676                  return format_float($value, $decimals, $localized);
 677              }
 678  
 679          case GRADE_DISPLAY_TYPE_PERCENTAGE:
 680              $min = $grade_item->grademin;
 681              $max = $grade_item->grademax;
 682              if ($min == $max) {
 683                  return '';
 684              }
 685              $value = bounded_number($min, $value, $max);
 686              $percentage = (($value-$min)*100)/($max-$min);
 687              return format_float($percentage, $decimals, $localized).' %';
 688  
 689          case GRADE_DISPLAY_TYPE_LETTER:
 690              $context = get_context_instance(CONTEXT_COURSE, $grade_item->courseid);
 691              if (!$letters = grade_get_letters($context)) {
 692                  return ''; // no letters??
 693              }
 694  
 695              $value = grade_grade::standardise_score($value, $grade_item->grademin, $grade_item->grademax, 0, 100);
 696              $value = bounded_number(0, $value, 100); // just in case
 697              foreach ($letters as $boundary => $letter) {
 698                  if ($value >= $boundary) {
 699                      return format_string($letter);
 700                  }
 701              }
 702              return '-'; // no match? maybe '' would be more correct
 703  
 704          default:
 705              return '';
 706      }
 707  }
 708  
 709  /**
 710   * Returns grade options for gradebook category menu
 711   * @param int $courseid
 712   * @param bool $includenew include option for new category (-1)
 713   * @return array of grade categories in course
 714   */
 715  function grade_get_categories_menu($courseid, $includenew=false) {
 716      $result = array();
 717      if (!$categories = grade_category::fetch_all(array('courseid'=>$courseid))) {
 718          //make sure course category exists
 719          if (!grade_category::fetch_course_category($courseid)) {
 720              debugging('Can not create course grade category!');
 721              return $result;
 722          }
 723          $categories = grade_category::fetch_all(array('courseid'=>$courseid));
 724      }
 725      foreach ($categories as $key=>$category) {
 726          if ($category->is_course_category()) {
 727              $result[$category->id] = get_string('uncategorised', 'grades');
 728              unset($categories[$key]);
 729          }
 730      }
 731      if ($includenew) {
 732          $result[-1] = get_string('newcategory', 'grades');
 733      }
 734      $cats = array();
 735      foreach ($categories as $category) {
 736          $cats[$category->id] = $category->get_name();
 737      }
 738      asort($cats, SORT_LOCALE_STRING);
 739  
 740      return ($result+$cats);
 741  }
 742  
 743  /**
 744   * Returns grade letters array used in context
 745   * @param object $context object or null for defaults
 746   * @return array of grade_boundary=>letter_string
 747   */
 748  function grade_get_letters($context=null) {
 749      if (empty($context)) {
 750          //default grading letters
 751          return array('93'=>'A', '90'=>'A-', '87'=>'B+', '83'=>'B', '80'=>'B-', '77'=>'C+', '73'=>'C', '70'=>'C-', '67'=>'D+', '60'=>'D', '0'=>'F');
 752      }
 753  
 754      static $cache = array();
 755  
 756      if (array_key_exists($context->id, $cache)) {
 757          return $cache[$context->id];
 758      }
 759  
 760      if (count($cache) > 100) {
 761          $cache = array(); // cache size limit
 762      }
 763  
 764      $letters = array();
 765  
 766      $contexts = get_parent_contexts($context);
 767      array_unshift($contexts, $context->id);
 768  
 769      foreach ($contexts as $ctxid) {
 770          if ($records = get_records('grade_letters', 'contextid', $ctxid, 'lowerboundary DESC')) {
 771              foreach ($records as $record) {
 772                  $letters[$record->lowerboundary] = $record->letter;
 773              }
 774          }
 775  
 776          if (!empty($letters)) {
 777              $cache[$context->id] = $letters;
 778              return $letters;
 779          }
 780      }
 781  
 782      $letters = grade_get_letters(null);
 783      $cache[$context->id] = $letters;
 784      return $letters;
 785  }
 786  
 787  
 788  /**
 789   * Verify new value of idnumber - checks for uniqueness of new idnumbers, old are kept intact
 790   * @param string idnumber string (with magic quotes)
 791   * @param int $courseid - id numbers are course unique only
 792   * @param object $cm used for course module idnumbers and items attached to modules
 793   * @param object $gradeitem is item idnumber
 794   * @return boolean true means idnumber ok
 795   */
 796  function grade_verify_idnumber($idnumber, $courseid, $grade_item=null, $cm=null) {
 797      if ($idnumber == '') {
 798          //we allow empty idnumbers
 799          return true;
 800      }
 801  
 802      // keep existing even when not unique
 803      if ($cm and $cm->idnumber == $idnumber) {
 804          return true;
 805      } else if ($grade_item and $grade_item->idnumber == $idnumber) {
 806          return true;
 807      }
 808  
 809      if (get_records_select('course_modules', "course = $courseid AND idnumber='$idnumber'")) {
 810          return false;
 811      }
 812  
 813      if (get_records_select('grade_items', "courseid = $courseid AND idnumber='$idnumber'")) {
 814          return false;
 815      }
 816  
 817      return true;
 818  }
 819  
 820  /**
 821   * Force final grade recalculation in all course items
 822   * @param int $courseid
 823   */
 824  function grade_force_full_regrading($courseid) {
 825      set_field('grade_items', 'needsupdate', 1, 'courseid', $courseid);
 826  }
 827  
 828  /**
 829   * Updates all final grades in course.
 830   *
 831   * @param int $courseid
 832   * @param int $userid if specified, try to do a quick regrading of grades of this user only
 833   * @param object $updated_item the item in which
 834   * @return boolean true if ok, array of errors if problems found (item id is used as key)
 835   */
 836  function grade_regrade_final_grades($courseid, $userid=null, $updated_item=null) {
 837  
 838      $course_item = grade_item::fetch_course_item($courseid);
 839  
 840      if ($userid) {
 841          // one raw grade updated for one user
 842          if (empty($updated_item)) {
 843              error("updated_item_id can not be null!");
 844          }
 845          if ($course_item->needsupdate) {
 846              $updated_item->force_regrading();
 847              return array($course_item->id =>'Can not do fast regrading after updating of raw grades');
 848          }
 849  
 850      } else {
 851          if (!$course_item->needsupdate) {
 852              // nothing to do :-)
 853              return true;
 854          }
 855      }
 856  
 857      $grade_items = grade_item::fetch_all(array('courseid'=>$courseid));
 858      $depends_on = array();
 859  
 860      // first mark all category and calculated items as needing regrading
 861      // this is slower, but 100% accurate
 862      foreach ($grade_items as $gid=>$gitem) {
 863          if (!empty($updated_item) and $updated_item->id == $gid) {
 864              $grade_items[$gid]->needsupdate = 1;
 865  
 866          } else if ($gitem->is_course_item() or $gitem->is_category_item() or $gitem->is_calculated()) {
 867              $grade_items[$gid]->needsupdate = 1;
 868          }
 869  
 870          // construct depends_on lookup array
 871          $depends_on[$gid] = $grade_items[$gid]->depends_on();
 872      }
 873  
 874      $errors = array();
 875      $finalids = array();
 876      $gids     = array_keys($grade_items);
 877      $failed = 0;
 878  
 879      while (count($finalids) < count($gids)) { // work until all grades are final or error found
 880          $count = 0;
 881          foreach ($gids as $gid) {
 882              if (in_array($gid, $finalids)) {
 883                  continue; // already final
 884              }
 885  
 886              if (!$grade_items[$gid]->needsupdate) {
 887                  $finalids[] = $gid; // we can make it final - does not need update
 888                  continue;
 889              }
 890  
 891              $doupdate = true;
 892              foreach ($depends_on[$gid] as $did) {
 893                  if (!in_array($did, $finalids)) {
 894                      $doupdate = false;
 895                      continue; // this item depends on something that is not yet in finals array
 896                  }
 897              }
 898  
 899              //oki - let's update, calculate or aggregate :-)
 900              if ($doupdate) {
 901                  $result = $grade_items[$gid]->regrade_final_grades($userid);
 902  
 903                  if ($result === true) {
 904                      $grade_items[$gid]->regrading_finished();
 905                      $grade_items[$gid]->check_locktime(); // do the locktime item locking
 906                      $count++;
 907                      $finalids[] = $gid;
 908  
 909                  } else {
 910                      $grade_items[$gid]->force_regrading();
 911                      $errors[$gid] = $result;
 912                  }
 913              }
 914          }
 915  
 916          if ($count == 0) {
 917              $failed++;
 918          } else {
 919              $failed = 0;
 920          }
 921  
 922          if ($failed > 1) {
 923              foreach($gids as $gid) {
 924                  if (in_array($gid, $finalids)) {
 925                      continue; // this one is ok
 926                  }
 927                  $grade_items[$gid]->force_regrading();
 928                  $errors[$grade_items[$gid]->id] = 'Probably circular reference or broken calculation formula'; // TODO: localize
 929              }
 930              break; // oki, found error
 931          }
 932      }
 933  
 934      if (count($errors) == 0) {
 935          if (empty($userid)) {
 936              // do the locktime locking of grades, but only when doing full regrading
 937              grade_grade::check_locktime_all($gids);
 938          }
 939          return true;
 940      } else {
 941          return $errors;
 942      }
 943  }
 944  
 945  /**
 946   * For backwards compatibility with old third-party modules, this function can
 947   * be used to import all grades from activities with legacy grading.
 948   * @param int $courseid
 949   */
 950  function grade_grab_legacy_grades($courseid) {
 951      global $CFG;
 952  
 953      if (!$mods = get_list_of_plugins('mod') ) {
 954          error('No modules installed!');
 955      }
 956  
 957      foreach ($mods as $mod) {
 958          if ($mod == 'NEWMODULE') {   // Someone has unzipped the template, ignore it
 959              continue;
 960          }
 961  
 962          $fullmod = $CFG->dirroot.'/mod/'.$mod;
 963  
 964          // include the module lib once
 965          if (file_exists($fullmod.'/lib.php')) {
 966              include_once($fullmod.'/lib.php');
 967              // look for modname_grades() function - old gradebook pulling function
 968              // if present sync the grades with new grading system
 969              $gradefunc = $mod.'_grades';
 970              if (function_exists($gradefunc)) {
 971                  grade_grab_course_grades($courseid, $mod);
 972              }
 973          }
 974      }
 975  }
 976  
 977  /**
 978   * Refetches data from all course activities
 979   * @param int $courseid
 980   * @param string $modname
 981   * @return success
 982   */
 983  function grade_grab_course_grades($courseid, $modname=null) {
 984      global $CFG;
 985  
 986      if ($modname) {
 987          $sql = "SELECT a.*, cm.idnumber as cmidnumber, m.name as modname
 988                    FROM {$CFG->prefix}$modname a, {$CFG->prefix}course_modules cm, {$CFG->prefix}modules m
 989                   WHERE m.name='$modname' AND m.visible=1 AND m.id=cm.module AND cm.instance=a.id AND cm.course=$courseid";
 990  
 991          if ($modinstances = get_records_sql($sql)) {
 992              foreach ($modinstances as $modinstance) {
 993                  grade_update_mod_grades($modinstance);
 994              }
 995          }
 996          return;
 997      }
 998  
 999      if (!$mods = get_list_of_plugins('mod') ) {
1000          error('No modules installed!');
1001      }
1002  
1003      foreach ($mods as $mod) {
1004          if ($mod == 'NEWMODULE') {   // Someone has unzipped the template, ignore it
1005              continue;
1006          }
1007  
1008          $fullmod = $CFG->dirroot.'/mod/'.$mod;
1009  
1010          // include the module lib once
1011          if (file_exists($fullmod.'/lib.php')) {
1012              // get all instance of the activity
1013              $sql = "SELECT a.*, cm.idnumber as cmidnumber, m.name as modname
1014                        FROM {$CFG->prefix}$mod a, {$CFG->prefix}course_modules cm, {$CFG->prefix}modules m
1015                       WHERE m.name='$mod' AND m.visible=1 AND m.id=cm.module AND cm.instance=a.id AND cm.course=$courseid";
1016  
1017              if ($modinstances = get_records_sql($sql)) {
1018                  foreach ($modinstances as $modinstance) {
1019                      grade_update_mod_grades($modinstance);
1020                  }
1021              }
1022          }
1023      }
1024  }
1025  
1026  /**
1027   * Force full update of module grades in central gradebook - works for both legacy and converted activities.
1028   * @param object $modinstance object with extra cmidnumber and modname property
1029   * @return boolean success
1030   */
1031  function grade_update_mod_grades($modinstance, $userid=0) {
1032      global $CFG;
1033  
1034      $fullmod = $CFG->dirroot.'/mod/'.$modinstance->modname;
1035      if (!file_exists($fullmod.'/lib.php')) {
1036          debugging('missing lib.php file in module');
1037          return false;
1038      }
1039      include_once($fullmod.'/lib.php');
1040  
1041      // does it use legacy grading?
1042      $gradefunc        = $modinstance->modname.'_grades';
1043      $updategradesfunc = $modinstance->modname.'_update_grades';
1044      $updateitemfunc   = $modinstance->modname.'_grade_item_update';
1045  
1046      if (function_exists($gradefunc)) {
1047  
1048          // legacy module - not yet converted
1049          if ($oldgrades = $gradefunc($modinstance->id)) {
1050  
1051              $grademax = $oldgrades->maxgrade;
1052              $scaleid = NULL;
1053              if (!is_numeric($grademax)) {
1054                  // scale name is provided as a string, try to find it
1055                  if (!$scale = get_record('scale', 'name', $grademax)) {
1056                      debugging('Incorrect scale name! name:'.$grademax);
1057                      return false;
1058                  }
1059                  $scaleid = $scale->id;
1060              }
1061  
1062              if (!$grade_item = grade_get_legacy_grade_item($modinstance, $grademax, $scaleid)) {
1063                  debugging('Can not get/create legacy grade item!');
1064                  return false;
1065              }
1066  
1067              if (!empty($oldgrades->grades)) {
1068                  $grades = array();
1069  
1070                  foreach ($oldgrades->grades as $uid=>$usergrade) {
1071                      if ($userid and $uid != $userid) {
1072                          continue;
1073                      }
1074                      $grade = new object();
1075                      $grade->userid = $uid;
1076  
1077                      if ($usergrade == '-') {
1078                          // no grade
1079                          $grade->rawgrade = null;
1080  
1081                      } else if ($scaleid) {
1082                          // scale in use, words used
1083                          $gradescale = explode(",", $scale->scale);
1084                          $grade->rawgrade = array_search($usergrade, $gradescale) + 1;
1085  
1086                      } else {
1087                          // good old numeric value
1088                          $grade->rawgrade = $usergrade;
1089                      }
1090                      $grades[] = $grade;
1091                  }
1092  
1093                  grade_update('legacygrab', $grade_item->courseid, $grade_item->itemtype, $grade_item->itemmodule,
1094                               $grade_item->iteminstance, $grade_item->itemnumber, $grades);
1095              }
1096          }
1097  
1098      } else if (function_exists($updategradesfunc) and function_exists($updateitemfunc)) {
1099          //new grading supported, force updating of grades
1100          $updateitemfunc($modinstance);
1101          $updategradesfunc($modinstance, $userid);
1102  
1103      } else {
1104          // mudule does not support grading??
1105      }
1106  
1107      return true;
1108  }
1109  
1110  /**
1111   * Get and update/create grade item for legacy modules.
1112   */
1113  function grade_get_legacy_grade_item($modinstance, $grademax, $scaleid) {
1114  
1115      // does it already exist?
1116      if ($grade_items = grade_item::fetch_all(array('courseid'=>$modinstance->course, 'itemtype'=>'mod', 'itemmodule'=>$modinstance->modname, 'iteminstance'=>$modinstance->id, 'itemnumber'=>0))) {
1117          if (count($grade_items) > 1) {
1118              debugging('Multiple legacy grade_items found.');
1119              return false;
1120          }
1121  
1122          $grade_item = reset($grade_items);
1123  
1124          if (is_null($grademax) and is_null($scaleid)) {
1125             $grade_item->gradetype  = GRADE_TYPE_NONE;
1126  
1127          } else if ($scaleid) {
1128              $grade_item->gradetype = GRADE_TYPE_SCALE;
1129              $grade_item->scaleid   = $scaleid;
1130              $grade_item->grademin  = 1;
1131  
1132          } else {
1133              $grade_item->gradetype  = GRADE_TYPE_VALUE;
1134              $grade_item->grademax   = $grademax;
1135              $grade_item->grademin   = 0;
1136          }
1137  
1138          $grade_item->itemname = $modinstance->name;
1139          $grade_item->idnumber = $modinstance->cmidnumber;
1140  
1141          $grade_item->update();
1142  
1143          return $grade_item;
1144      }
1145  
1146      // create new one
1147      $params = array('courseid'    =>$modinstance->course,
1148                      'itemtype'    =>'mod',
1149                      'itemmodule'  =>$modinstance->modname,
1150                      'iteminstance'=>$modinstance->id,
1151                      'itemnumber'  =>0,
1152                      'itemname'    =>$modinstance->name,
1153                      'idnumber'    =>$modinstance->cmidnumber);
1154  
1155      if (is_null($grademax) and is_null($scaleid)) {
1156          $params['gradetype'] = GRADE_TYPE_NONE;
1157  
1158      } else if ($scaleid) {
1159          $params['gradetype'] = GRADE_TYPE_SCALE;
1160          $params['scaleid']   = $scaleid;
1161          $grade_item->grademin  = 1;
1162      } else {
1163          $params['gradetype'] = GRADE_TYPE_VALUE;
1164          $params['grademax']  = $grademax;
1165          $params['grademin']  = 0;
1166      }
1167  
1168      $grade_item = new grade_item($params);
1169      $grade_item->insert();
1170  
1171      return $grade_item;
1172  }
1173  
1174  /**
1175   * Remove grade letters for given context
1176   * @param object $context
1177   */
1178  function remove_grade_letters($context, $showfeedback) {
1179      $strdeleted = get_string('deleted');
1180  
1181      delete_records('grade_letters', 'contextid', $context->id);
1182      if ($showfeedback) {
1183          notify($strdeleted.' - '.get_string('letters', 'grades'));
1184      }
1185  }
1186  /**
1187   * Remove all grade related course data - history is kept
1188   * @param int $courseid
1189   * @param bool $showfeedback print feedback
1190   */
1191  function remove_course_grades($courseid, $showfeedback) {
1192      $strdeleted = get_string('deleted');
1193  
1194      $course_category = grade_category::fetch_course_category($courseid);
1195      $course_category->delete('coursedelete');
1196      if ($showfeedback) {
1197          notify($strdeleted.' - '.get_string('grades', 'grades').', '.get_string('items', 'grades').', '.get_string('categories', 'grades'));
1198      }
1199  
1200      if ($outcomes = grade_outcome::fetch_all(array('courseid'=>$courseid))) {
1201          foreach ($outcomes as $outcome) {
1202              $outcome->delete('coursedelete');
1203          }
1204      }
1205      delete_records('grade_outcomes_courses', 'courseid', $courseid);
1206      if ($showfeedback) {
1207          notify($strdeleted.' - '.get_string('outcomes', 'grades'));
1208      }
1209  
1210      if ($scales = grade_scale::fetch_all(array('courseid'=>$courseid))) {
1211          foreach ($scales as $scale) {
1212              $scale->delete('coursedelete');
1213          }
1214      }
1215      if ($showfeedback) {
1216          notify($strdeleted.' - '.get_string('scales'));
1217      }
1218  
1219      delete_records('grade_settings', 'courseid', $courseid);
1220      if ($showfeedback) {
1221          notify($strdeleted.' - '.get_string('settings', 'grades'));
1222      }
1223  }
1224  
1225  /**
1226   * Called when course category deleted - cleanup gradebook
1227   * @param int $categoryid course category id
1228   * @param int $newparentid empty means everything deleted, otherwise id of category where content moved
1229   * @param bool $showfeedback print feedback
1230   */
1231  function grade_course_category_delete($categoryid, $newparentid, $showfeedback) {
1232      $context = get_context_instance(CONTEXT_COURSECAT, $categoryid);
1233      delete_records('grade_letters', 'contextid', $context->id);
1234  }
1235  
1236  /**
1237   * Does gradebook cleanup when module uninstalled.
1238   */
1239  function grade_uninstalled_module($modname) {
1240      global $CFG;
1241  
1242      $sql = "SELECT *
1243                FROM {$CFG->prefix}grade_items
1244               WHERE itemtype='mod' AND itemmodule='$modname'";
1245  
1246      // go all items for this module and delete them including the grades
1247      if ($rs = get_recordset_sql($sql)) {
1248          while ($item = rs_fetch_next_record($rs)) {
1249              $grade_item = new grade_item($item, false);
1250              $grade_item->delete('moduninstall');
1251          }
1252          rs_close($rs);
1253      }
1254  }
1255  
1256  /**
1257   * Grading cron job
1258   */
1259  function grade_cron() {
1260      global $CFG;
1261  
1262      $now = time();
1263  
1264      $sql = "SELECT i.*
1265                FROM {$CFG->prefix}grade_items i
1266               WHERE i.locked = 0 AND i.locktime > 0 AND i.locktime < $now AND EXISTS (
1267                  SELECT 'x' FROM {$CFG->prefix}grade_items c WHERE c.itemtype='course' AND c.needsupdate=0 AND c.courseid=i.courseid)";
1268  
1269      // go through all courses that have proper final grades and lock them if needed
1270      if ($rs = get_recordset_sql($sql)) {
1271          while ($item = rs_fetch_next_record($rs)) {
1272              $grade_item = new grade_item($item, false);
1273              $grade_item->locked = $now;
1274              $grade_item->update('locktime');
1275          }
1276          rs_close($rs);
1277      }
1278  
1279      $grade_inst = new grade_grade();
1280      $fields = 'g.'.implode(',g.', $grade_inst->required_fields);
1281  
1282      $sql = "SELECT $fields
1283                FROM {$CFG->prefix}grade_grades g, {$CFG->prefix}grade_items i
1284               WHERE g.locked = 0 AND g.locktime > 0 AND g.locktime < $now AND g.itemid=i.id AND EXISTS (
1285                  SELECT 'x' FROM {$CFG->prefix}grade_items c WHERE c.itemtype='course' AND c.needsupdate=0 AND c.courseid=i.courseid)";
1286  
1287      // go through all courses that have proper final grades and lock them if needed
1288      if ($rs = get_recordset_sql($sql)) {
1289          while ($grade = rs_fetch_next_record($rs)) {
1290              $grade_grade = new grade_grade($grade, false);
1291              $grade_grade->locked = $now;
1292              $grade_grade->update('locktime');
1293          }
1294          rs_close($rs);
1295      }
1296  
1297      //TODO: do not run this cleanup every cron invocation
1298      // cleanup history tables
1299      if (!empty($CFG->gradehistorylifetime)) {  // value in days
1300          $histlifetime = $now - ($CFG->gradehistorylifetime * 3600 * 24);
1301          $tables = array('grade_outcomes_history', 'grade_categories_history', 'grade_items_history', 'grade_grades_history', 'scale_history');
1302          foreach ($tables as $table) {
1303              if (delete_records_select($table, "timemodified < $histlifetime")) {
1304                  mtrace("    Deleted old grade history records from '$table'");
1305              }
1306          }
1307      }
1308  }
1309  
1310  /**
1311   * Resel all course grades
1312   * @param int $courseid
1313   * @return success
1314   */
1315  function grade_course_reset($courseid) {
1316  
1317      // no recalculations
1318      grade_force_full_regrading($courseid);
1319  
1320      $grade_items = grade_item::fetch_all(array('courseid'=>$courseid));
1321      foreach ($grade_items as $gid=>$grade_item) {
1322          $grade_item->delete_all_grades('reset');
1323      }
1324  
1325      //refetch all grades
1326      grade_grab_course_grades($courseid);
1327  
1328      // recalculate all grades
1329      grade_regrade_final_grades($courseid);
1330      return true;
1331  }
1332  
1333  /**
1334   * Convert number to 5 decimalfloat, empty tring or null db compatible format
1335   * (we need this to decide if db value changed)
1336   * @param mixed number
1337   * @return mixed float or null
1338   */
1339  function grade_floatval($number) {
1340      if (is_null($number) or $number === '') {
1341          return null;
1342      }
1343      // we must round to 5 digits to get the same precision as in 10,5 db fields
1344      // note: db rounding for 10,5 is different from php round() function
1345      return round($number, 5);
1346  }
1347  
1348  /**
1349   * Compare two float numbers safely. Uses 5 decimals php precision. Nulls accepted too.
1350   * Used for skipping of db updates
1351   * @param float $f1
1352   * @param float $f2
1353   * @return true if different
1354   */
1355  function grade_floats_different($f1, $f2) {
1356      // note: db rounding for 10,5 is different from php round() function
1357      return (grade_floatval($f1) !== grade_floatval($f2));
1358  }
1359  
1360  ?>


Generated: Wed Jan 14 11:33:29 2009 Cross-referenced by PHPXref 0.7