Merge branch 'MDL-69993-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE
[moodle.git] / grade / querylib.php
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/>.
17 /**
18  * Functions used to retrieve grades objects
19  *
20  * @package   core_grades
21  * @category  grade
22  * @copyright 2008 Petr Skoda and Nicolas Connault
23  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 /**
27  * Returns the aggregated or calculated course grade(s) for a single course for one or more users
28  *
29  * @param int $courseid The ID of course
30  * @param int|array $userid_or_ids Optional ID of the graded user or array of user IDs; if userid not used, returns only information about grade_item
31  * @return stdClass Returns an object containing information about course grade item. scaleid, name, grade
32  *         and locked status etc and user course grades: $item->grades[$userid] => $usercoursegrade
33  */
34 function grade_get_course_grades($courseid, $userid_or_ids=null) {
36     $grade_item = grade_item::fetch_course_item($courseid);
38     if ($grade_item->needsupdate) {
39         grade_regrade_final_grades($courseid);
40     }
42     $item = new stdClass();
43     $item->scaleid    = $grade_item->scaleid;
44     $item->name       = $grade_item->get_name();
45     $item->grademin   = $grade_item->grademin;
46     $item->grademax   = $grade_item->grademax;
47     $item->gradepass  = $grade_item->gradepass;
48     $item->locked     = $grade_item->is_locked();
49     $item->hidden     = $grade_item->is_hidden();
50     $item->grades     = array();
52     switch ($grade_item->gradetype) {
53         case GRADE_TYPE_NONE:
54             break;
56         case GRADE_TYPE_VALUE:
57             $item->scaleid = 0;
58             break;
60         case GRADE_TYPE_TEXT:
61             $item->scaleid   = 0;
62             $item->grademin   = 0;
63             $item->grademax   = 0;
64             $item->gradepass  = 0;
65             break;
66     }
68     if (empty($userid_or_ids)) {
69         $userids = array();
71     } else if (is_array($userid_or_ids)) {
72         $userids = $userid_or_ids;
74     } else {
75         $userids = array($userid_or_ids);
76     }
78     if ($userids) {
79         $grade_grades = grade_grade::fetch_users_grades($grade_item, $userids, true);
80         foreach ($userids as $userid) {
81             $grade_grades[$userid]->grade_item =& $grade_item;
83             $grade = new stdClass();
84             $grade->grade          = $grade_grades[$userid]->finalgrade;
85             $grade->locked         = $grade_grades[$userid]->is_locked();
86             $grade->hidden         = $grade_grades[$userid]->is_hidden();
87             $grade->overridden     = $grade_grades[$userid]->overridden;
88             $grade->feedback       = $grade_grades[$userid]->feedback;
89             $grade->feedbackformat = $grade_grades[$userid]->feedbackformat;
90             $grade->usermodified   = $grade_grades[$userid]->usermodified;
91             $grade->dategraded     = $grade_grades[$userid]->get_dategraded();
92             $grade->datesubmitted  = $grade_grades[$userid]->get_datesubmitted();
94             // create text representation of grade
95             if ($grade_item->needsupdate) {
96                 $grade->grade          = false;
97                 $grade->str_grade      = get_string('error');
98                 $grade->str_long_grade = $grade->str_grade;
100             } else if (is_null($grade->grade)) {
101                 $grade->str_grade      = '-';
102                 $grade->str_long_grade = $grade->str_grade;
104             } else {
105                 $grade->str_grade = grade_format_gradevalue($grade->grade, $grade_item);
106                 if ($grade_item->gradetype == GRADE_TYPE_SCALE or $grade_item->get_displaytype() != GRADE_DISPLAY_TYPE_REAL) {
107                     $grade->str_long_grade = $grade->str_grade;
108                 } else {
109                     $a = new stdClass();
110                     $a->grade = $grade->str_grade;
111                     $a->max   = grade_format_gradevalue($grade_item->grademax, $grade_item);
112                     $grade->str_long_grade = get_string('gradelong', 'grades', $a);
113                 }
114             }
116             // create html representation of feedback
117             if (is_null($grade->feedback)) {
118                 $grade->str_feedback = '';
119             } else {
120                 $grade->str_feedback = format_text($grade->feedback, $grade->feedbackformat);
121             }
123             $item->grades[$userid] = $grade;
124         }
125     }
127     return $item;
130 /**
131  * Returns the aggregated or calculated course grade for a single user for one or more courses
132  *
133  * @param int $userid The ID of the single user
134  * @param int|array $courseid_or_ids Optional ID of course or array of IDs, empty means all of the user's courses
135  * @return mixed grade info or grades array including item info, false if error
136  */
137 function grade_get_course_grade($userid, $courseid_or_ids=null) {
139     if (!is_array($courseid_or_ids)) {
140         if (empty($courseid_or_ids)) {
141             if (!$courses = enrol_get_users_courses($userid)) {
142                 return false;
143             }
144             $courseids = array_keys($courses);
145             return grade_get_course_grade($userid, $courseids);
146         }
147         if (!is_numeric($courseid_or_ids)) {
148             return false;
149         }
150         if (!$grades = grade_get_course_grade($userid, array($courseid_or_ids))) {
151             return false;
152         } else {
153             // only one grade - not array
154             $grade = reset($grades);
155             return $grade;
156         }
157     }
159     foreach ($courseid_or_ids as $courseid) {
160         $grade_item = grade_item::fetch_course_item($courseid);
161         $course_items[$grade_item->courseid] = $grade_item;
162     }
164     $grades = array();
165     foreach ($course_items as $grade_item) {
166         if ($grade_item->needsupdate) {
167             grade_regrade_final_grades($courseid);
168         }
170         $item = new stdClass();
171         $item->scaleid    = $grade_item->scaleid;
172         $item->name       = $grade_item->get_name();
173         $item->grademin   = $grade_item->grademin;
174         $item->grademax   = $grade_item->grademax;
175         $item->gradepass  = $grade_item->gradepass;
176         $item->locked     = $grade_item->is_locked();
177         $item->hidden     = $grade_item->is_hidden();
179         switch ($grade_item->gradetype) {
180             case GRADE_TYPE_NONE:
181                 break;
183             case GRADE_TYPE_VALUE:
184                 $item->scaleid = 0;
185                 break;
187             case GRADE_TYPE_TEXT:
188                 $item->scaleid   = 0;
189                 $item->grademin   = 0;
190                 $item->grademax   = 0;
191                 $item->gradepass  = 0;
192                 break;
193         }
194         $grade_grade = new grade_grade(array('userid'=>$userid, 'itemid'=>$grade_item->id));
195         $grade_grade->grade_item =& $grade_item;
197         $grade = new stdClass();
198         $grade->grade          = $grade_grade->finalgrade;
199         $grade->locked         = $grade_grade->is_locked();
200         $grade->hidden         = $grade_grade->is_hidden();
201         $grade->overridden     = $grade_grade->overridden;
202         $grade->feedback       = $grade_grade->feedback;
203         $grade->feedbackformat = $grade_grade->feedbackformat;
204         $grade->usermodified   = $grade_grade->usermodified;
205         $grade->dategraded     = $grade_grade->get_dategraded();
206         $grade->item           = $item;
208         // create text representation of grade
209         if ($grade_item->needsupdate) {
210             $grade->grade          = false;
211             $grade->str_grade      = get_string('error');
212             $grade->str_long_grade = $grade->str_grade;
214         } else if (is_null($grade->grade)) {
215             $grade->str_grade      = '-';
216             $grade->str_long_grade = $grade->str_grade;
218         } else {
219             $grade->str_grade = grade_format_gradevalue($grade->grade, $grade_item);
220             if ($grade_item->gradetype == GRADE_TYPE_SCALE or $grade_item->get_displaytype() != GRADE_DISPLAY_TYPE_REAL) {
221                 $grade->str_long_grade = $grade->str_grade;
222             } else {
223                 $a = new stdClass();
224                 $a->grade = $grade->str_grade;
225                 $a->max   = grade_format_gradevalue($grade_item->grademax, $grade_item);
226                 $grade->str_long_grade = get_string('gradelong', 'grades', $a);
227             }
228         }
230         // create html representation of feedback
231         if (is_null($grade->feedback)) {
232             $grade->str_feedback = '';
233         } else {
234             $grade->str_feedback = format_text($grade->feedback, $grade->feedbackformat);
235         }
237         $grades[$grade_item->courseid] = $grade;
238     }
240     return $grades;
243 /**
244  * Returns all grade items (including outcomes) or main item for a given activity identified by $cm object.
245  *
246  * @param cm_info $cm A course module object (preferably with modname property)
247  * @param bool $only_main_item Limit the search to the primary grade item for the activity, 'itemnumber'==0
248  * @return mixed An array of grade item instances, one grade item if $only_main_item == true, false if error or not found
249  */
250 function grade_get_grade_items_for_activity($cm, $only_main_item=false) {
251     global $CFG, $DB;
253     if (!isset($cm->modname)) {
254         $params = array($cm->id);
255         $cm = $DB->get_record_sql("SELECT cm.*, md.name as modname
256                                     FROM {course_modules} cm,
257                                          {modules} md
258                                    WHERE cm.id = ? AND md.id = cm.module", $params);
259     }
262     if (empty($cm) or empty($cm->instance) or empty($cm->course)) {
263         debugging("Incorrect cm parameter in grade_get_grade_items_for_activity()!");
264         return false;
265     }
267     if ($only_main_item) {
268         return grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$cm->modname, 'iteminstance'=>$cm->instance, 'courseid'=>$cm->course, 'itemnumber'=>0));
269     } else {
270         return grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$cm->modname, 'iteminstance'=>$cm->instance, 'courseid'=>$cm->course));
271     }
274 /**
275  * Returns whether or not a user received grades in main grade item for given activity
276  *
277  * @param cm_info $cm The activity context module
278  * @param int $userid The user ID
279  * @return bool True if graded, false if user not graded yet
280  */
281 function grade_is_user_graded_in_activity($cm, $userid) {
283     $grade_items = grade_get_grade_items_for_activity($cm, true);
284     if (empty($grade_items)) {
285         return false;
286     }
288     $grade_item = reset($grade_items);
290     if ($grade_item->gradetype == GRADE_TYPE_NONE) {
291         return false;
292     }
294     if ($grade_item->needsupdate) {
295         // activity items should never fail to regrade
296         grade_regrade_final_grades($grade_item->courseid);
297     }
299     if (!$grade = $grade_item->get_final($userid)) {
300         return false;
301     }
303     if (is_null($grade->finalgrade)) {
304         return false;
305     }
307     return true;
310 /**
311  * Returns an array of activities (defined as $cm objects) which are gradeable from gradebook, outcomes are ignored.
312  *
313  * @param int $courseid If provided then restrict to one course.
314  * @param string $modulename If defined (could be 'forum', 'assignment' etc) then only that type are returned.
315  * @return array $cm objects
316  */
317 function grade_get_gradable_activities($courseid, $modulename='') {
318     global $CFG, $DB;
320     if (empty($modulename)) {
321         if (!$modules = $DB->get_records('modules', array('visible' => '1'))) {
322             return false;
323         }
324         $result = array();
325         foreach ($modules as $module) {
326             if ($cms = grade_get_gradable_activities($courseid, $module->name)) {
327                 $result =  $result + $cms;
328             }
329         }
330         if (empty($result)) {
331             return false;
332         } else {
333             return $result;
334         }
335     }
337     $params = array($courseid, $modulename, GRADE_TYPE_NONE, $modulename);
338     $sql = "SELECT cm.*, m.name, md.name as modname
339               FROM {grade_items} gi, {course_modules} cm, {modules} md, {{$modulename}} m
340              WHERE gi.courseid = ? AND
341                    gi.itemtype = 'mod' AND
342                    gi.itemmodule = ? AND
343                    gi.itemnumber = 0 AND
344                    gi.gradetype != ? AND
345                    gi.iteminstance = cm.instance AND
346                    cm.instance = m.id AND
347                    md.name = ? AND
348                    md.id = cm.module";
350     return $DB->get_records_sql($sql, $params);