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