Search moodle.org's
Developer Documentation

See Release Notes

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

Differences Between: [Versions 311 and 400] [Versions 311 and 401] [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   * Definition of grade outcome class
  19   *
  20   * @package   core_grades
  21   * @category  grade
  22   * @copyright 2006 Nicolas Connault
  23   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  require_once ('grade_object.php');
  29  
  30  /**
  31   * Class representing a grade outcome.
  32   *
  33   * It is responsible for handling its DB representation, modifying and returning its metadata.
  34   *
  35   * @package   core_grades
  36   * @category  grade
  37   * @copyright 2006 Nicolas Connault
  38   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class grade_outcome extends grade_object {
  41      /**
  42       * DB Table (used by grade_object).
  43       * @var string $table
  44       */
  45      public $table = 'grade_outcomes';
  46  
  47      /**
  48       * Array of required table fields, must start with 'id'.
  49       * @var array $required_fields
  50       */
  51      public $required_fields = array('id', 'courseid', 'shortname', 'fullname', 'scaleid','description',
  52                                   'descriptionformat', 'timecreated', 'timemodified', 'usermodified');
  53  
  54      /**
  55       * The course this outcome belongs to.
  56       * @var int $courseid
  57       */
  58      public $courseid;
  59  
  60      /**
  61       * The shortname of the outcome.
  62       * @var string $shortname
  63       */
  64      public $shortname;
  65  
  66      /**
  67       * The fullname of the outcome.
  68       * @var string $fullname
  69       */
  70      public $fullname;
  71  
  72      /**
  73       * A full grade_scale object referenced by $this->scaleid.
  74       * @var object $scale
  75       */
  76      public $scale;
  77  
  78      /**
  79       * The id of the scale referenced by this outcome.
  80       * @var int $scaleid
  81       */
  82      public $scaleid;
  83  
  84      /**
  85       * The description of this outcome - FORMAT_MOODLE.
  86       * @var string $description
  87       */
  88      public $description;
  89  
  90      /**
  91       * The userid of the person who last modified this outcome.
  92       *
  93       * @var int $usermodified
  94       */
  95      public $usermodified;
  96  
  97      /**
  98       * Deletes this outcome from the database.
  99       *
 100       * @param string $source from where was the object deleted (mod/forum, manual, etc.)
 101       * @return bool success
 102       */
 103      public function delete($source=null) {
 104          global $DB;
 105          if (!empty($this->courseid)) {
 106              $DB->delete_records('grade_outcomes_courses', array('outcomeid' => $this->id, 'courseid' => $this->courseid));
 107          }
 108          if (parent::delete($source)) {
 109              $context = context_system::instance();
 110              $fs = get_file_storage();
 111              $files = $fs->get_area_files($context->id, 'grade', 'outcome', $this->id);
 112              foreach ($files as $file) {
 113                  $file->delete();
 114              }
 115              return true;
 116          }
 117          return false;
 118      }
 119  
 120      /**
 121       * Records this object in the Database, sets its id to the returned value, and returns that value.
 122       * If successful this function also fetches the new object data from database and stores it
 123       * in object properties.
 124       *
 125       * @param string $source from where was the object inserted (mod/forum, manual, etc.)
 126       * @return int PK ID if successful, false otherwise
 127       */
 128      public function insert($source=null) {
 129          global $DB;
 130  
 131          $this->timecreated = $this->timemodified = time();
 132  
 133          if ($result = parent::insert($source)) {
 134              if (!empty($this->courseid)) {
 135                  $goc = new stdClass();
 136                  $goc->courseid = $this->courseid;
 137                  $goc->outcomeid = $this->id;
 138                  $DB->insert_record('grade_outcomes_courses', $goc);
 139              }
 140          }
 141          return $result;
 142      }
 143  
 144      /**
 145       * In addition to update() it also updates grade_outcomes_courses if needed
 146       *
 147       * @param string $source from where was the object inserted
 148       * @return bool success
 149       */
 150      public function update($source=null) {
 151          $this->timemodified = time();
 152  
 153          if ($result = parent::update($source)) {
 154              if (!empty($this->courseid)) {
 155                  $this->use_in($this->courseid);
 156              }
 157          }
 158          return $result;
 159      }
 160  
 161      /**
 162       * Mark outcome as used in a course
 163       *
 164       * @param int $courseid
 165       * @return False if invalid courseid requested
 166       */
 167      public function use_in($courseid) {
 168          global $DB;
 169          if (!empty($this->courseid) and $courseid != $this->courseid) {
 170              return false;
 171          }
 172  
 173          if (!$DB->record_exists('grade_outcomes_courses', array('courseid' => $courseid, 'outcomeid' => $this->id))) {
 174              $goc = new stdClass();
 175              $goc->courseid  = $courseid;
 176              $goc->outcomeid = $this->id;
 177              $DB->insert_record('grade_outcomes_courses', $goc);
 178          }
 179          return true;
 180      }
 181  
 182      /**
 183       * Finds and returns a grade_outcome instance based on params.
 184       *
 185       * @static
 186       * @param array $params associative arrays varname=>value
 187       * @return object grade_outcome instance or false if none found.
 188       */
 189      public static function fetch($params) {
 190          return grade_object::fetch_helper('grade_outcomes', 'grade_outcome', $params);
 191      }
 192  
 193      /**
 194       * Finds and returns all grade_outcome instances based on params.
 195       *
 196       * @static
 197       * @param array $params associative arrays varname=>value
 198       * @return array array of grade_outcome insatnces or false if none found.
 199       */
 200      public static function fetch_all($params) {
 201          return grade_object::fetch_all_helper('grade_outcomes', 'grade_outcome', $params);
 202      }
 203  
 204      /**
 205       * Instantiates a grade_scale object whose data is retrieved from the database
 206       *
 207       * @return grade_scale
 208       */
 209      public function load_scale() {
 210          if (empty($this->scale->id) or $this->scale->id != $this->scaleid) {
 211              $this->scale = grade_scale::fetch(array('id'=>$this->scaleid));
 212              $this->scale->load_items();
 213          }
 214          return $this->scale;
 215      }
 216  
 217      /**
 218       * Static function returning all global outcomes
 219       *
 220       * @static
 221       * @return array
 222       */
 223      public static function fetch_all_global() {
 224          if (!$outcomes = grade_outcome::fetch_all(array('courseid'=>null))) {
 225              $outcomes = array();
 226          }
 227          return $outcomes;
 228      }
 229  
 230      /**
 231       * Static function returning all local course outcomes
 232       *
 233       * @static
 234       * @param int $courseid
 235       * @return array
 236       */
 237      public static function fetch_all_local($courseid) {
 238          if (!$outcomes =grade_outcome::fetch_all(array('courseid'=>$courseid))) {
 239              $outcomes = array();
 240          }
 241          return $outcomes;
 242      }
 243  
 244      /**
 245       * Static method that returns all outcomes available in course
 246       *
 247       * @static
 248       * @param int $courseid
 249       * @return array
 250       */
 251      public static function fetch_all_available($courseid) {
 252          global $CFG, $DB;
 253  
 254          $result = array();
 255          $params = array($courseid);
 256          $sql = "SELECT go.*
 257                    FROM {grade_outcomes} go, {grade_outcomes_courses} goc
 258                   WHERE go.id = goc.outcomeid AND goc.courseid = ?
 259                ORDER BY go.id ASC";
 260  
 261          if ($datas = $DB->get_records_sql($sql, $params)) {
 262              foreach($datas as $data) {
 263                  $instance = new grade_outcome();
 264                  grade_object::set_properties($instance, $data);
 265                  $result[$instance->id] = $instance;
 266              }
 267          }
 268          return $result;
 269      }
 270  
 271  
 272      /**
 273       * Returns the most descriptive field for this object. This is a standard method used
 274       * when we do not know the exact type of an object.
 275       *
 276       * @return string name
 277       */
 278      public function get_name() {
 279          // Grade outcomes can be created at site or course context, so set the filter context appropriately.
 280          $context = empty($this->courseid) ? context_system::instance() : context_course::instance($this->courseid);
 281          return format_string($this->fullname, false, ["context" => $context]);
 282      }
 283  
 284      /**
 285       * Returns unique outcome short name.
 286       *
 287       * @return string name
 288       */
 289      public function get_shortname() {
 290          return $this->shortname;
 291      }
 292  
 293      /**
 294       * Returns the formatted grade description with URLs converted
 295       *
 296       * @return string
 297       */
 298      public function get_description() {
 299          global $CFG;
 300          require_once($CFG->libdir . '/filelib.php');
 301  
 302          $options = new stdClass;
 303          $options->noclean = true;
 304          $systemcontext = context_system::instance();
 305          $description = file_rewrite_pluginfile_urls($this->description, 'pluginfile.php', $systemcontext->id, 'grade', 'outcome', $this->id);
 306          return format_text($description, $this->descriptionformat, $options);
 307      }
 308  
 309      /**
 310       * Checks if outcome can be deleted.
 311       *
 312       * @return bool
 313       */
 314      public function can_delete() {
 315          if ($this->get_item_uses_count()) {
 316              return false;
 317          }
 318          if (empty($this->courseid)) {
 319              if ($this->get_course_uses_count()) {
 320                  return false;
 321              }
 322          }
 323          return true;
 324      }
 325  
 326      /**
 327       * Returns the number of places where outcome is used.
 328       *
 329       * @return int
 330       */
 331      public function get_course_uses_count() {
 332          global $DB;
 333  
 334          if (!empty($this->courseid)) {
 335              return 1;
 336          }
 337  
 338          return $DB->count_records('grade_outcomes_courses', array('outcomeid' => $this->id));
 339      }
 340  
 341      /**
 342       * Returns the number of grade items that use this grade outcome
 343       *
 344       * @return int
 345       */
 346      public function get_item_uses_count() {
 347          global $DB;
 348          return $DB->count_records('grade_items', array('outcomeid' => $this->id));
 349      }
 350  
 351      /**
 352       * Computes then returns extra information about this outcome and other objects that are linked to it.
 353       * The average of all grades that use this outcome, for all courses (or 1 course if courseid is given) can
 354       * be requested, and is returned as a float if requested alone. If the list of items that use this outcome
 355       * is also requested, then a single array is returned, which contains the grade_items AND the average grade
 356       * if such is still requested (array('items' => array(...), 'avg' => 2.30)). This combining of two
 357       * methods into one is to save on DB queries, since both queries are similar and can be performed together.
 358       *
 359       * @param int $courseid An optional courseid to narrow down the average to 1 course only
 360       * @param bool $average Whether or not to return the average grade for this outcome
 361       * @param bool $items Whether or not to return the list of items using this outcome
 362       * @return float
 363       */
 364      public function get_grade_info($courseid=null, $average=true, $items=false) {
 365          global $CFG, $DB;
 366  
 367          if (!isset($this->id)) {
 368              debugging("You must setup the outcome's id before calling its get_grade_info() method!");
 369              return false; // id must be defined for this to work
 370          }
 371  
 372          if ($average === false && $items === false) {
 373              debugging('Either the 1st or 2nd param of grade_outcome::get_grade_info() must be true, or both, but not both false!');
 374              return false;
 375          }
 376  
 377          $params = array($this->id);
 378  
 379          $wheresql = '';
 380          if (!is_null($courseid)) {
 381              $wheresql = " AND {grade_items}.courseid = ? ";
 382              $params[] = $courseid;
 383          }
 384  
 385          $selectadd = '';
 386          if ($items !== false) {
 387              $selectadd = ", {grade_items}.* ";
 388          }
 389  
 390          $sql = "SELECT finalgrade $selectadd
 391                    FROM {grade_grades}, {grade_items}, {grade_outcomes}
 392                   WHERE {grade_outcomes}.id = {grade_items}.outcomeid
 393                     AND {grade_items}.id = {grade_grades}.itemid
 394                     AND {grade_outcomes}.id = ?
 395                     $wheresql";
 396  
 397          $grades = $DB->get_records_sql($sql, $params);
 398          $retval = array();
 399  
 400          if ($average !== false && count($grades) > 0) {
 401              $count = 0;
 402              $total = 0;
 403  
 404              foreach ($grades as $k => $grade) {
 405                  // Skip null finalgrades
 406                  if (!is_null($grade->finalgrade)) {
 407                      $total += $grade->finalgrade;
 408                      $count++;
 409                  }
 410                  unset($grades[$k]->finalgrade);
 411              }
 412  
 413              $retval['avg'] = $total / $count;
 414          }
 415  
 416          if ($items !== false) {
 417              foreach ($grades as $grade) {
 418                  $retval['items'][$grade->id] = new grade_item($grade);
 419              }
 420          }
 421  
 422          return $retval;
 423      }
 424  }