MDL-10392 updated links to edit_grade form, corrected the loop for table rows, which...
[moodle-pu.git] / grade / report / grader / index.php
blob98d7bc5cd792922f33ecba41ce84f32c04bb08f1
1 <?php // $Id$
3 /// This creates and handles the whole grader report interface, sans header and footer
5 require_once($CFG->libdir.'/tablelib.php');
6 require_once($CFG->libdir.'/gradelib.php');
7 require_once($CFG->dirroot.'/grade/report/lib.php');
8 $gradeserror = array();
10 /**
11 * Shortcut function for printing the grader report toggles.
12 * @param string $type The type of toggle
13 * @param string $baseurl The base of the URL the toggles will link to
14 * @param bool $return Whether to return the HTML string rather than printing it
15 * @return void
17 function grader_report_print_toggle($type, $baseurl, $return=false) {
18 global $CFG;
20 $icons = array('eyecons' => 'hide',
21 'calculations' => 'calc',
22 'locks' => 'lock',
23 'grandtotals' => 'sigma');
25 $pref_name = 'grade_report_show' . $type;
26 $show_pref = get_user_preferences($pref_name, $CFG->$pref_name);
28 $strshow = get_string('show' . $type, 'grades');
29 $strhide = get_string('hide' . $type, 'grades');
31 $show_hide = 'show';
32 $toggle_action = 1;
34 if ($show_pref) {
35 $show_hide = 'hide';
36 $toggle_action = 0;
39 if (array_key_exists($type, $icons)) {
40 $image_name = $icons[$type];
41 } else {
42 $image_name = $type;
45 $string = ${'str' . $show_hide};
47 $img = '<img src="'.$CFG->pixpath.'/t/'.$image_name.'.gif" class="iconsmall" alt="'
48 .$string.'" title="'.$string.'" />'. "\n";
50 $retval = '<div class="gradertoggle">' . $img . '<a href="' . $baseurl . "&amp;toggle=$toggle_action&amp;toggle_type=$type\">"
51 . $string . '</a></div>';
53 if ($return) {
54 return $retval;
55 } else {
56 echo $retval;
61 /// processing posted grades here
63 if ($data = data_submitted() and confirm_sesskey()) {
65 // always initialize all arrays
66 $queue = array();
68 foreach ($data as $varname => $postedgrade) {
69 // this is a bit tricky - we have to first load all grades into memory,
70 // check if changed and only then start updating the final grades because
71 // columns might depend one on another - the result would be overriden calculated and category grades
73 // skip, not a grade
74 if (!strstr($varname, 'grade')) {
75 continue;
78 $gradeinfo = explode("_", $varname);
80 $userid = clean_param($gradeinfo[1], PARAM_INT);
81 $itemid = clean_param($gradeinfo[2], PARAM_INT);
83 if (!$grade_item = grade_item::fetch(array('id'=>$itemid, 'courseid'=>$course->id))) { // we must verify course id here!
84 error('Incorrect grade item id');
87 if ($grade_item->gradetype == GRADE_TYPE_SCALE) {
88 if ($postedgrade == -1) { // -1 means no grade
89 $finalgrade = null;
90 } else {
91 $finalgrade = (float)$postedgrade;
93 } else {
94 if ($postedgrade == '') { // empty string means no grade
95 $finalgrade = null;
96 } else {
97 $finalgrade = format_grade($postedgrade);
101 if (!is_null($finalgrade) and ($finalgrade < $grade_item->grademin or $finalgrade > $grade_item->grademax)) {
102 $gradeserror[$grade_item->id][$userid] = 'outofrange'; //TODO: localize
103 // another possiblity is to use bounded number instead
104 continue;
107 if ($grade = grade_grades::fetch(array('userid'=>$userid, 'itemid'=>$grade_item->id))) {
108 if (!is_null($grade->finalgrade)) {
109 $grade->finalgrade = (float)$grade->finalgrade;
111 if ($grade->finalgrade === $finalgrade) {
112 // we must not update all grades, only changed ones - we do not want to mark everything as overriden
113 continue;
117 $gradedata = new object();
118 $gradedata->grade_item = $grade_item;
119 $gradedata->finalgrade = $finalgrade;
120 $gradedata->userid = $userid;
122 $queue[] = $gradedata;
125 // now we update the new final grade for each changed grade
126 foreach ($queue as $gradedata) {
127 $gradedata->grade_item->update_final_grade($gradedata->userid, $gradedata->finalgrade, 'gradebook');
131 // get the params
132 $courseid = required_param('id', PARAM_INT);
133 $context = get_context_instance(CONTEXT_COURSE, $courseid);
134 $page = optional_param('page', 0, PARAM_INT);
135 $sortitemid = optional_param('sortitemid', 0, PARAM_ALPHANUM); // sort by which grade item
136 $report = optional_param('report', 0, PARAM_ALPHANUM);
137 $action = optional_param('action', 0, PARAM_ALPHA);
138 $move = optional_param('move', 0, PARAM_INT);
139 $type = optional_param('type', 0, PARAM_ALPHA);
140 $target = optional_param('target', 0, PARAM_ALPHANUM);
141 $toggle = optional_param('toggle', NULL, PARAM_INT);
142 $toggle_type = optional_param('toggle_type', 0, PARAM_ALPHANUM);
144 // Handle toggle change request
145 // TODO print visual feedback
146 if (!is_null($toggle) && !empty($toggle_type)) {
147 set_user_preferences(array('grade_report_show' . $toggle_type => $toggle));
150 // Get the user preferences
151 $perpage = get_user_preferences('grade_report_studentsperpage', $CFG->grade_report_studentsperpage); // number of users on a page
152 $decimals = get_user_preferences('grade_report_decimalpoints', $CFG->grade_report_decimalpoints); // decimals in grades
153 $showgrandtotals = get_user_preferences('grade_report_showgrandtotals', $CFG->grade_report_showgrandtotals);
154 $showgroups = get_user_preferences('grade_report_showgroups', $CFG->grade_report_showgroups);
155 $aggregation_position = get_user_preferences('grade_report_aggregationposition', $CFG->grade_report_aggregationposition);
156 $showscales = get_user_preferences('grade_report_showscales', $CFG->grade_report_showscales);
157 $quickgrading = get_user_preferences('grade_report_quickgrading', $CFG->grade_report_quickgrading);
158 $quickfeedback = get_user_preferences('grade_report_quickfeedback', $CFG->grade_report_quickfeedback);
160 // Override perpage if set in URL
161 if ($perpageurl = optional_param('perpage', 0, PARAM_INT)) {
162 $perpage = $perpageurl;
165 // Prepare language strings
166 $strsortasc = get_string('sortasc', 'grades');
167 $strsortdesc = get_string('sortdesc', 'grades');
168 $strfeedback = get_string("feedback");
170 // base url for sorting by first/last name
171 $baseurl = 'report.php?id='.$courseid.'&amp;perpage='.$perpage.'&amp;report=grader&amp;page='.$page;
172 // base url for paging
173 $pbarurl = 'report.php?id='.$courseid.'&amp;perpage='.$perpage.'&amp;report=grader&amp;';
175 /// setting up groups
176 $groupsql = '';
177 $groupwheresql = '';
178 $group_selector = null;
179 $currentgroup = null;
181 if ($showgroups) {
182 /// find out current groups mode
183 $course = get_record('course', 'id', $courseid);
184 $groupmode = $course->groupmode;
185 ob_start();
186 $currentgroup = setup_and_print_groups($course, $groupmode, $baseurl);
187 $group_selector = ob_get_clean();
189 // update paging after group
190 $baseurl .= 'group='.$currentgroup.'&amp;';
191 $pbarurl .= 'group='.$currentgroup.'&amp;';
193 if ($currentgroup) {
194 $groupsql = " LEFT JOIN {$CFG->prefix}groups_members gm ON gm.userid = u.id ";
195 $groupwheresql = " AND gm.groupid = $currentgroup ";
199 // Grab the grade_tree for this course
200 $gtree = new grade_tree($courseid, true, false, $aggregation_position);
202 // setting the sort order, this depends on last state
203 // all this should be in the new table class that we might need to use
204 // for displaying grades
206 // already in not requesting sort, i.e. normal paging
208 if ($sortitemid) {
209 if (!isset($SESSION->gradeuserreport->sort)) {
210 $sortorder = $SESSION->gradeuserreport->sort = 'ASC';
211 } else {
212 // this is the first sort, i.e. by last name
213 if (!isset($SESSION->gradeuserreport->sortitemid)) {
214 $sortorder = $SESSION->gradeuserreport->sort = 'ASC';
215 } else if ($SESSION->gradeuserreport->sortitemid == $sortitemid) {
216 // same as last sort
217 if ($SESSION->gradeuserreport->sort == 'ASC') {
218 $sortorder = $SESSION->gradeuserreport->sort = 'DESC';
219 } else {
220 $sortorder = $SESSION->gradeuserreport->sort = 'ASC';
222 } else {
223 $sortorder = $SESSION->gradeuserreport->sort = 'ASC';
226 $SESSION->gradeuserreport->sortitemid = $sortitemid;
227 } else {
228 // not requesting sort, use last setting (for paging)
230 if (isset($SESSION->gradeuserreport->sortitemid)) {
231 $sortitemid = $SESSION->gradeuserreport->sortitemid;
233 if (isset($SESSION->gradeuserreport->sort)) {
234 $sortorder = $SESSION->gradeuserreport->sort;
235 } else {
236 $sortorder = 'ASC';
240 /// end of setting sort order code
242 // Perform actions on categories, items and grades
243 if (!empty($target) && !empty($action) && confirm_sesskey()) {
245 $element = $gtree->locate_element($target);
247 switch ($action) {
248 case 'edit':
249 break;
250 case 'delete':
251 if ($confirm == 1) { // Perform the deletion
252 //TODO: add proper delete support for grade items and categories
253 //$element['object']->delete();
254 // Print result message
256 } else { // Print confirmation dialog
257 $eid = $element['eid'];
258 $strdeletecheckfull = get_string('deletecheck', '', $element['object']->get_name());
259 $linkyes = "category.php?target=$eid&amp;action=delete&amp;confirm=1$gtree->commonvars";
260 $linkno = "category.php?$gtree->commonvars";
261 notice_yesno($strdeletecheckfull, $linkyes, $linkno);
263 break;
265 case 'hide':
266 // TODO Implement calendar for selection of a date to hide element until
267 $element['object']->set_hidden(1);
268 $gtree = new grade_tree($courseid);
269 break;
270 case 'show':
271 $element['object']->set_hidden(0);
272 $gtree = new grade_tree($courseid);
273 break;
274 case 'lock':
275 // TODO Implement calendar for selection of a date to lock element after
276 if (!$element['object']->set_locked(1)) {
277 debugging("Could not update the element's locked state!");
279 $gtree = new grade_tree($courseid);
280 break;
281 case 'unlock':
282 if (!$element['object']->set_locked(0)) {
283 debugging("Could not update the element's locked state!");
285 $gtree = new grade_tree($courseid);
286 break;
287 default:
288 break;
292 // first make sure we have all final grades
293 // TODO: check that no grade_item has needsupdate set
294 grade_regrade_final_grades($courseid);
296 // roles to be displaye in the gradebook
297 $gradebookroles = $CFG->gradebookroles;
300 * pulls out the userids of the users to be display, and sort them
301 * the right outer join is needed because potentially, it is possible not
302 * to have the corresponding entry in grade_grades table for some users
303 * this is check for user roles because there could be some users with grades
304 * but not supposed to be displayed
306 if (is_numeric($sortitemid)) {
307 $sql = "SELECT u.id, u.firstname, u.lastname
308 FROM {$CFG->prefix}grade_grades g RIGHT OUTER JOIN
309 {$CFG->prefix}user u ON (u.id = g.userid AND g.itemid = $sortitemid)
310 LEFT JOIN {$CFG->prefix}role_assignments ra ON u.id = ra.userid
311 $groupsql
312 WHERE ra.roleid in ($gradebookroles)
313 $groupwheresql
314 AND ra.contextid ".get_related_contexts_string($context)."
315 ORDER BY g.finalgrade $sortorder";
316 $users = get_records_sql($sql, $perpage * $page, $perpage);
317 } else {
318 // default sort
319 // get users sorted by lastname
320 $users = get_role_users(@implode(',', $CFG->gradebookroles), $context, false, 'u.id, u.firstname, u.lastname', 'u.'.$sortitemid .' '. $sortorder, false, $page * $perpage, $perpage, $currentgroup);
321 // need to cut users down by groups
325 /// count total records for paging
327 $countsql = "SELECT COUNT(DISTINCT u.id)
328 FROM {$CFG->prefix}grade_grades g RIGHT OUTER JOIN
329 {$CFG->prefix}user u ON (u.id = g.userid AND g.itemid = $sortitemid)
330 LEFT JOIN {$CFG->prefix}role_assignments ra ON u.id = ra.userid
331 $groupsql
332 WHERE ra.roleid in ($gradebookroles)
333 $groupwheresql
334 AND ra.contextid ".get_related_contexts_string($context);
335 $numusers = count_records_sql($countsql);
337 // print_object($users); // debug
339 if (empty($users)) {
340 $userselect = '';
341 $users = array();
342 } else {
343 $userselect = 'AND g.userid in ('.implode(',', array_keys($users)).')';
347 // phase 2 sql, we supply the userids in this query, and get all the grades
348 // pulls out all the grades, this does not need to worry about paging
349 $sql = "SELECT g.id, g.itemid, g.userid, g.finalgrade, g.hidden, g.locked, g.locktime, g.overridden, gt.feedback
350 FROM {$CFG->prefix}grade_items gi,
351 {$CFG->prefix}grade_grades g
352 LEFT JOIN {$CFG->prefix}grade_grades_text gt ON g.id = gt.gradeid
353 WHERE g.itemid = gi.id
354 AND gi.courseid = $courseid $userselect";
356 ///print_object($grades); //debug
358 $finalgrades = array();
359 // needs to be formatted into an array for easy retrival
361 if ($grades = get_records_sql($sql)) {
362 foreach ($grades as $grade) {
363 $finalgrades[$grade->userid][$grade->itemid] = $grade;
367 /// With the users in an sorted array and grades fetched, we can not print the main html table
369 // 1. Fetch all top-level categories for this course, with all children preloaded, sorted by sortorder
371 // Fetch array of students enroled in this course
372 if (!$context = get_context_instance(CONTEXT_COURSE, $gtree->courseid)) {
373 return false;
375 //$users = get_role_users(@implode(',', $CFG->gradebookroles), $context);
377 if ($sortitemid === 'lastname') {
378 if ($sortorder == 'ASC') {
379 $lastarrow = print_arrow('up', $strsortasc, true);
380 } else {
381 $lastarrow = print_arrow('down', $strsortdesc, true);
383 } else {
384 $lastarrow = '';
387 if ($sortitemid === 'firstname') {
388 if ($sortorder == 'ASC') {
389 $firstarrow = print_arrow('up', $strsortasc, true);
390 } else {
391 $firstarrow = print_arrow('down', $strsortdesc, true);
393 } else {
394 $firstarrow = '';
397 /********* BEGIN OUTPUT *********/
399 print_heading('Grader Report');
401 // Add tabs
402 $currenttab = 'graderreport';
403 include('tabs.php');
405 // Group selection drop-down
406 echo $group_selector;
408 // Show/hide toggles
409 echo '<div id="grade-report-toggles">';
410 if ($USER->gradeediting) {
411 grader_report_print_toggle('eyecons', $baseurl);
412 grader_report_print_toggle('locks', $baseurl);
413 grader_report_print_toggle('calculations', $baseurl);
416 grader_report_print_toggle('grandtotals', $baseurl);
417 grader_report_print_toggle('groups', $baseurl);
418 grader_report_print_toggle('scales', $baseurl);
419 echo '</div>';
421 // Paging bar
422 print_paging_bar($numusers, $page, $perpage, $pbarurl);
424 $items = array();
426 // Prepare Table Headers
427 $headerhtml = '';
429 $numrows = count($gtree->levels);
431 foreach ($gtree->levels as $key=>$row) {
432 if ($key == 0) {
433 // do not diplay course grade category
434 // continue;
437 $headerhtml .= '<tr class="heading">';
439 if ($key == $numrows - 1) {
440 $headerhtml .= '<th class="user"><a href="'.$baseurl.'&amp;sortitemid=firstname">Firstname</a> ' //TODO: localize
441 . $firstarrow. '/ <a href="'.$baseurl.'&amp;sortitemid=lastname">Lastname </a>'. $lastarrow .'</th>';
442 } else {
443 $headerhtml .= '<td class="topleft">&nbsp;</td>';
446 foreach ($row as $element) {
447 $eid = $element['eid'];
448 $object = $element['object'];
449 $type = $element['type'];
451 if (!empty($element['colspan'])) {
452 $colspan = 'colspan="'.$element['colspan'].'"';
453 } else {
454 $colspan = '';
457 if (!empty($element['depth'])) {
458 $catlevel = ' catlevel'.$element['depth'];
459 } else {
460 $catlevel = '';
464 if ($type == 'filler' or $type == 'fillerfirst' or $type == 'fillerlast') {
465 $headerhtml .= '<td class="'.$type.$catlevel.'" '.$colspan.'>&nbsp;</td>';
466 } else if ($type == 'category') {
467 $headerhtml .= '<td class="category'.$catlevel.'" '.$colspan.'>'.$element['object']->get_name();
469 // Print icons
470 if ($USER->gradeediting) {
471 $headerhtml .= grade_get_icons($element, $gtree);
474 $headerhtml .= '</td>';
475 } else {
476 if ($element['object']->id == $sortitemid) {
477 if ($sortorder == 'ASC') {
478 $arrow = print_arrow('up', $strsortasc, true);
479 } else {
480 $arrow = print_arrow('down', $strsortdesc, true);
482 } else {
483 $arrow = '';
486 $dimmed = '';
487 if ($element['object']->is_hidden()) {
488 $dimmed = ' dimmed_text ';
491 if ($object->itemtype == 'mod') {
492 $icon = '<img src="'.$CFG->modpixpath.'/'.$object->itemmodule.'/icon.gif" class="icon" alt="'
493 .get_string('modulename', $object->itemmodule).'"/>';
494 } else if ($object->itemtype == 'manual') {
495 //TODO: add manual grading icon
496 $icon = '<img src="'.$CFG->pixpath.'/t/edit.gif" class="icon" alt="'.get_string('manualgrade', 'grades')
497 .'"/>';
501 $headerhtml .= '<th class="'.$type.$catlevel.$dimmed.'"><a href="'.$baseurl.'&amp;sortitemid='
502 . $element['object']->id .'">'. $element['object']->get_name()
503 . '</a>' . $arrow;
505 $headerhtml .= grade_get_icons($element, $gtree) . '</th>';
507 $items[$element['object']->sortorder] =& $element['object'];
512 $headerhtml .= '</tr>';
515 // Prepare Table Rows
516 $studentshtml = '';
518 foreach ($users as $userid => $user) {
519 // Student name and link
520 $studentshtml .= '<tr><th class="user"><a href="' . $CFG->wwwroot . '/user/view.php?id='
521 . $user->id . '">' . fullname($user) . '</a></th>';
522 foreach ($items as $item) {
524 if (isset($finalgrades[$userid][$item->id])) {
525 $gradeval = $finalgrades[$userid][$item->id]->finalgrade;
526 $grade = new grade_grades($finalgrades[$userid][$item->id], false);
527 $grade->feedback = $finalgrades[$userid][$item->id]->feedback;
529 } else {
530 $gradeval = null;
531 $grade = new grade_grades(array('userid' => $userid, 'itemid' => $item->id), false);
532 $grade->feedback = '';
535 if ($grade->is_overridden()) {
536 $studentshtml .= '<td class="overridden">';
537 } else {
538 $studentshtml .= '<td>';
541 // Do not show any icons if no grade (no record in DB to match)
542 if (!empty($grade->id)) {
543 // emulate grade element
544 $grade->courseid = $course->id;
545 $grade->grade_item = $item; // this may speedup is_hidden() and other grade_grades methods
546 $element = array ('eid'=>'g'.$grade->id, 'object'=>$grade, 'type'=>'grade');
547 $studentshtml .= grade_get_icons($element, $gtree);
551 // if in editting mode, we need to print either a text box
552 // or a drop down (for scales)
554 // grades in item of type grade category or course are not directly editable
555 if ($USER->gradeediting) {
556 // We need to retrieve each grade_grade object from DB in order to
557 // know if they are hidden/locked
559 if ($item->scaleid) {
560 if ($scale = get_record('scale', 'id', $item->scaleid)) {
561 $scales = explode(",", $scale->scale);
562 // reindex because scale is off 1
563 $i = 0;
564 foreach ($scales as $scaleoption) {
565 $i++;
566 $scaleopt[$i] = $scaleoption;
569 if ($quickgrading) {
570 $studentshtml .= choose_from_menu($scaleopt, 'grade_'.$userid.'_'.$item->id,
571 $gradeval, get_string('nograde'), '', -1, true);
572 } elseif ($scale = get_record('scale', 'id', $item->scaleid)) {
573 $scales = explode(",", $scale->scale);
575 // invalid grade if gradeval < 1
576 if ((int) $gradeval < 1) {
577 $studentshtml .= '-';
578 } else {
579 $studentshtml .= $scales[$gradeval-1];
581 } else {
582 // no such scale, throw error?
585 } else {
586 if ($quickgrading) {
587 $studentshtml .= '<input size="6" type="text" name="grade_'.$userid.'_'.$item->id.'" value="'.get_grade_clean($gradeval).'"/>';
588 } else {
589 $studentshtml .= get_grade_clean($gradeval);
594 // If quickfeedback is on, print an input element
595 if ($quickfeedback) {
596 $studentshtml .= '<input size="6" type="text" name="feedback_'.$userid.'_'.$item->id.'" value="'. s($grade->feedback) . '"/>';
599 $studentshtml .= '<div class="grade_icons">' . grade_get_icons($element, $gtree, array('edit')) . '</div>';
600 } else {
601 // If feedback present, surround grade with feedback tooltip
602 if (!empty($grade->feedback)) {
603 $studentshtml .= '<span onmouseover="return overlib(\''.$grade->feedback.'\', CAPTION, \''
604 . $strfeedback.'\');" onmouseout="return nd();">';
607 // finalgrades[$userid][$itemid] could be null because of the outer join
608 // in this case it's different than a 0
609 if ($item->scaleid) {
610 if ($scale = get_record('scale', 'id', $item->scaleid)) {
611 $scales = explode(",", $scale->scale);
613 // invalid grade if gradeval < 1
614 if ((int) $gradeval < 1) {
615 $studentshtml .= '-';
616 } else {
617 $studentshtml .= $scales[$gradeval-1];
619 } else {
620 // no such scale, throw error?
622 } else {
623 if (is_null($gradeval)) {
624 $studentshtml .= '-';
625 } else {
626 $studentshtml .= get_grade_clean($gradeval);
629 if (!empty($grade->feedback)) {
630 $studentshtml .= '</span>';
634 if (!empty($gradeserror[$item->id][$userid])) {
635 $studentshtml .= $gradeserror[$item->id][$userid];
638 $studentshtml .= '</td>' . "\n";
640 $studentshtml .= '</tr>';
643 // if user preference to display group sum
644 $groupsumhtml = '';
646 if ($currentgroup && $showgroups) {
648 /** SQL for finding group sum */
649 $SQL = "SELECT g.itemid, SUM(g.finalgrade) as sum
650 FROM {$CFG->prefix}grade_items gi LEFT JOIN
651 {$CFG->prefix}grade_grades g ON gi.id = g.itemid RIGHT OUTER JOIN
652 {$CFG->prefix}user u ON u.id = g.userid LEFT JOIN
653 {$CFG->prefix}role_assignments ra ON u.id = ra.userid
654 $groupsql
655 WHERE gi.courseid = $courseid
656 $groupwheresql
657 AND ra.roleid in ($gradebookroles)
658 AND ra.contextid ".get_related_contexts_string($context)."
659 GROUP BY g.itemid";
661 $groupsum = array();
662 $sums = get_records_sql($SQL);
663 foreach ($sums as $itemid => $csum) {
664 $groupsum[$itemid] = $csum;
667 $groupsumhtml = '<tr><th>Group total</th>';
668 foreach ($items as $item) {
669 if (!isset($groupsum[$item->id])) {
670 $groupsumhtml .= '<td>-</td>';
671 } else {
672 $sum = $groupsum[$item->id];
673 $groupsumhtml .= '<td>'.get_grade_clean($sum->sum).'</td>';
676 $groupsumhtml .= '</tr>';
679 // Grand totals
680 $gradesumhtml = '';
681 if ($showgrandtotals) {
683 /** SQL for finding the SUM grades of all visible users ($CFG->gradebookroles) */
685 $SQL = "SELECT g.itemid, SUM(g.finalgrade) as sum
686 FROM {$CFG->prefix}grade_items gi LEFT JOIN
687 {$CFG->prefix}grade_grades g ON gi.id = g.itemid RIGHT OUTER JOIN
688 {$CFG->prefix}user u ON u.id = g.userid LEFT JOIN
689 {$CFG->prefix}role_assignments ra ON u.id = ra.userid
690 WHERE gi.courseid = $courseid
691 AND ra.roleid in ($gradebookroles)
692 AND ra.contextid ".get_related_contexts_string($context)."
693 GROUP BY g.itemid";
695 $classsum = array();
696 $sums = get_records_sql($SQL);
697 foreach ($sums as $itemid => $csum) {
698 $classsum[$itemid] = $csum;
701 $gradesumhtml = '<tr><th>Total</th>';
702 foreach ($items as $item) {
703 if (!isset($classsum[$item->id])) {
704 $gradesumhtml .= '<td>-</td>';
705 } else {
706 $sum = $classsum[$item->id];
707 $gradesumhtml .= '<td>'.get_grade_clean($sum->sum).'</td>';
710 $gradesumhtml .= '</tr>';
713 // finding the ranges of each gradeitem
714 $scalehtml = '';
715 if ($showscales) {
716 $scalehtml = '<tr><td>'.get_string('range','grades').'</td>';
717 foreach ($items as $item) {
718 $scalehtml .= '<td>'. get_grade_clean($item->grademin).'-'. get_grade_clean($item->grademax).'</td>';
720 $scalehtml .= '</tr>';
723 $reporthtml = "<table class=\"boxaligncenter\">$headerhtml";
724 $reporthtml .= $scalehtml;
725 $reporthtml .= $studentshtml;
726 $reporthtml .= $groupsumhtml;
727 $reporthtml .= $gradesumhtml;
728 $reporthtml .= "</table>";
730 // print submit button
731 if ($USER->gradeediting) {
732 echo '<form action="report.php" method="post">';
733 echo '<div>';
734 echo '<input type="hidden" value="'.$courseid.'" name="id" />';
735 echo '<input type="hidden" value="'.sesskey().'" name="sesskey" />';
736 echo '<input type="hidden" value="grader" name="report"/>';
739 echo $reporthtml;
741 // print submit button
742 if ($USER->gradeediting && ($quickfeedback || $quickgrading)) {
743 echo '<div class="submit"><input type="submit" value="'.get_string('update').'" /></div>';
744 echo '</div></form>';