diff --git a/grade/report/user/lib.php b/grade/report/user/lib.php index d9232ed..4490586 100644 --- a/grade/report/user/lib.php +++ b/grade/report/user/lib.php @@ -299,15 +299,135 @@ class grade_report_user extends grade_report { } } + // 20130411 Colin. Added $emptygrades, get_gradeitems_with_empty_grades, + // adjust_max_sum_of_grades, and related code in order to + // address issues with the grade data displayed on this report. + + private $emptygrades; + function fill_table() { //print "
";
         //print_r($this->gtree->top_element);
+
+        // adjust_max_sum_of_grades will need to know which grades are empty.
+        $this->emptygrades = $this->get_gradeitems_with_empty_grades();
+
+        $this->adjust_max_sum_of_grades($this->gtree->top_element);
+
         $this->fill_table_recursive($this->gtree->top_element);
         //print_r($this->tabledata);
         //print "
"; return true; } + /** + * Returns the gradeitem ids for grade items for which this user has no grade. + */ + private function get_gradeitems_with_empty_grades() { + global $DB; + + $sql = "SELECT gi.id + FROM {grade_items} gi + LEFT JOIN {grade_grades} g ON g.itemid = gi.id and g.userid = :userid + WHERE gi.courseid = :courseid AND g.finalgrade IS NULL"; + + return $DB->get_fieldset_sql($sql, + array('userid' => $this->user->id, + 'courseid' => $this->courseid)); + } + + /** + * Logic should be similar to that in auto_update_max for GRADE_AGGREGATE_SUM. + * According to that function, we do not add in anything with aggregationcoef > 0 + * because that indicates extra credit. The key difference is that we do not add in + * the max for any hidden grades. + */ + private function adjust_max_sum_of_grades(&$element, &$current_category_element=null) { + global $CFG; + + // This function evolved quite a bit in the course of development and could probably + // use some refactoring. Maybe we should do some of the filtering just prior to the + // new grademax assignment. Then it would be in only one place. + + // We don't add in gradeitems with aggregationcoef > 0 because, for sum-of-grades, + // that indicates extra credit. We also don't add in any gradeitems if the parent + // category is set to aggregate only non-empty grades and the user has an empty + // grade for that item. + + if ($element['type'] == 'category') { + + // Using both of these to mimic the way that auto_update_max calls apply_limit_rules. + $element['gradeitems'] = array(); + $element['newgrademaxes'] = array(); + + $category = $element['object']; + $gradeitem = $category->load_grade_item(); + foreach($element['children'] as &$child) { + $this->adjust_max_sum_of_grades($child, $element); + } + if ($category->aggregation == GRADE_AGGREGATE_SUM) { + $newgrademaxes = $element['newgrademaxes']; + if (empty($newgrademaxes)) { + $gradeitem->grademax = 0; + } else { + $category->apply_limit_rules($newgrademaxes, $element['gradeitems']); + if (empty($newgrademaxes)) { + $gradeitem->grademax = 0; # TODO: Will array_sum return empty array as 0? + } else { + $gradeitem->grademax = array_sum($newgrademaxes); + } + } + } + if (empty($current_category_element)) { + return; + } + + if ($this->include_in_range($gradeitem, $current_category_element)) { + $current_category_element['gradeitems'][$gradeitem->id] = $gradeitem; + $current_category_element['newgrademaxes'][$gradeitem->id] = $gradeitem->grademax; + } + } else if ($element['type'] == 'courseitem' or $element['type'] == 'categoryitem') { + $gradeitem = $element['object']; + $current_category = $current_category_element['object']; + if ($gradeitem->id === $current_category->grade_item->id) { + // Course and category items each appear twice in the tree. They appear as + // the grade_item on the category object and as one of the children in the + // in the element array. Use the same object for both. + $element['object'] = $current_category->grade_item; + } + } else if ($element['type'] == 'item') { + $gradeitem = $element['object']; + if ($this->include_in_range($gradeitem, $current_category_element)) { + $current_category_element['gradeitems'][$gradeitem->id] = $gradeitem; + $current_category_element['newgrademaxes'][$gradeitem->id] = $gradeitem->grademax; + } + } + } + + private function include_in_range($gradeitem, $current_category_element) { + global $CFG; + + // We don't include in the range if + // - item is extra credit (related to aggregationcoef) + // - the grade is hidden and we must display the total without it + // - the grade is empty and we must not display empty grades + // - the grade is neither a value type nor a scale type with grade_includescalesinaggregation set + + if ($gradeitem->aggregationcoef <= 0 + and ! ($this->showtotalsifcontainhidden[$this->courseid]==GRADE_REPORT_SHOW_TOTAL_IF_CONTAINS_HIDDEN + and + $gradeitem->is_hidden()) + and ! ($current_category_element['object']->aggregateonlygraded + and + in_array($gradeitem->id, $this->emptygrades)) + and ($gradeitem->gradetype == GRADE_TYPE_VALUE or ($gradeitem->gradetype == GRADE_TYPE_SCALE + and $CFG->grade_includescalesinaggregation))) + { + return true; + } + return false; + } + private function fill_table_recursive(&$element) { global $DB, $CFG; @@ -337,6 +457,10 @@ class grade_report_user extends grade_report { /// Process those items that have scores associated if ($type == 'item' or $type == 'categoryitem' or $type == 'courseitem') { + + // Use (sometimes, anyway) the grade_item object that we processed in adjust_max_sum_of_grades. + $grade_item = $element['object']; + if (! $grade_grade = grade_grade::fetch(array('itemid'=>$grade_object->id,'userid'=>$this->user->id))) { $grade_grade = new grade_grade(); $grade_grade->userid = $this->user->id; @@ -425,15 +549,15 @@ class grade_report_user extends grade_report { $data['grade']['content'] = '-'; } else { $data['grade']['class'] = $class; - $gradeval = $this->blank_hidden_total($this->courseid, $grade_grade->grade_item, $gradeval); - $data['grade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true); + $gradeval = $this->blank_hidden_total($this->courseid, $grade_item, $gradeval); + $data['grade']['content'] = grade_format_gradevalue($gradeval, $grade_item, true); } } // Range if ($this->showrange) { $data['range']['class'] = $class; - $data['range']['content'] = $grade_grade->grade_item->get_formatted_range(GRADE_DISPLAY_TYPE_REAL, $this->rangedecimals); + $data['range']['content'] = $grade_item->get_formatted_range(GRADE_DISPLAY_TYPE_REAL, $this->rangedecimals); } // Percentage @@ -446,7 +570,7 @@ class grade_report_user extends grade_report { $data['percentage']['content'] = '-'; } else { $data['percentage']['class'] = $class; - $data['percentage']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE); + $data['percentage']['content'] = grade_format_gradevalue($gradeval, $grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE); } } @@ -460,11 +584,11 @@ class grade_report_user extends grade_report { if (!$this->canviewhidden) { $data['lettergrade']['content'] = '-'; } else { - $data['lettergrade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_LETTER); + $data['lettergrade']['content'] = grade_format_gradevalue($gradeval, $grade_item, true, GRADE_DISPLAY_TYPE_LETTER); } } else { $data['lettergrade']['class'] = $class; - $data['lettergrade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_LETTER); + $data['lettergrade']['content'] = grade_format_gradevalue($gradeval, $grade_item, true, GRADE_DISPLAY_TYPE_LETTER); } } diff --git a/lib/grade/grade_grade.php b/lib/grade/grade_grade.php index a51586b..b5bd84b 100644 --- a/lib/grade/grade_grade.php +++ b/lib/grade/grade_grade.php @@ -651,12 +651,16 @@ class grade_grade extends grade_object { } } + // 20130411 Colin. Added code specific to GRADE_AGGREGATE_SUM to make this logic + // more consistent with that used to calculate persisted grades. foreach ($values as $itemid=>$value) { if ($grade_grades[$itemid]->is_excluded()) { unset($values[$itemid]); continue; } - $values[$itemid] = grade_grade::standardise_score($value, $grade_items[$itemid]->grademin, $grade_items[$itemid]->grademax, 0, 1); + if ($grade_category->aggregation != GRADE_AGGREGATE_SUM) { + $values[$itemid] = grade_grade::standardise_score($value, $grade_items[$itemid]->grademin, $grade_items[$itemid]->grademax, 0, 1); + } } if ($grade_category->aggregateonlygraded) { @@ -686,10 +690,14 @@ class grade_grade extends grade_object { continue; } - $agg_grade = $grade_category->aggregate_values($values, $grade_items); + if ($grade_category->aggregation == GRADE_AGGREGATE_SUM) { + $finalgrade = array_sum($values); + } else { + $agg_grade = $grade_category->aggregate_values($values, $grade_items); - // recalculate the rawgrade back to requested range - $finalgrade = grade_grade::standardise_score($agg_grade, 0, 1, $grade_items[$do]->grademin, $grade_items[$do]->grademax); + // recalculate the rawgrade back to requested range + $finalgrade = grade_grade::standardise_score($agg_grade, 0, 1, $grade_items[$do]->grademin, $grade_items[$do]->grademax); + } $finalgrade = $grade_items[$do]->bounded_grade($finalgrade);