$grademin = $coursegradegrade->rawgrademin;
$grademax = $coursegradegrade->rawgrademax;
}
+ $hint = $coursegradegrade->get_aggregation_hint();
+ $aggregationstatus = $hint['status'];
+ $aggregationweight = $hint['weight'];
if (!is_array($this->showtotalsifcontainhidden)) {
debugging('showtotalsifcontainhidden should be an array', DEBUG_DEVELOPER);
}
//if the item definitely depends on a hidden item
- if (array_key_exists($course_item->id, $hiding_affected['altered'])) {
+ if (array_key_exists($course_item->id, $hiding_affected['altered']) ||
+ array_key_exists($course_item->id, $hiding_affected['alteredgrademin']) ||
+ array_key_exists($course_item->id, $hiding_affected['alteredgrademax']) ||
+ array_key_exists($course_item->id, $hiding_affected['alteredaggregationstatus']) ||
+ array_key_exists($course_item->id, $hiding_affected['alteredaggregationweight'])) {
if (!$this->showtotalsifcontainhidden[$courseid]) {
//hide the grade
$finalgrade = null;
} else {
//use reprocessed marks that exclude hidden items
- $finalgrade = $hiding_affected['altered'][$course_item->id];
+ if (!empty($hiding_affected['altered'][$course_item->id])) {
+ $finalgrade = $hiding_affected['altered'][$course_item->id];
+ }
if (!empty($hiding_affected['alteredgrademin'][$course_item->id])) {
$grademin = $hiding_affected['alteredgrademin'][$course_item->id];
}
if (!empty($hiding_affected['alteredgrademax'][$course_item->id])) {
$grademax = $hiding_affected['alteredgrademax'][$course_item->id];
}
+ if (!empty($hiding_affected['alteredaggregationstatus'][$course_item->id])) {
+ $aggregationstatus = $hiding_affected['alteredaggregationstatus'][$course_item->id];
+ }
+ if (!empty($hiding_affected['alteredaggregationweight'][$course_item->id])) {
+ $aggregationweight = $hiding_affected['alteredaggregationweight'][$course_item->id];
+ }
}
} else if (!empty($hiding_affected['unknown'][$course_item->id])) {
//not sure whether or not this item depends on a hidden item
if (!empty($hiding_affected['alteredgrademax'][$course_item->id])) {
$grademax = $hiding_affected['alteredgrademax'][$course_item->id];
}
+ if (!empty($hiding_affected['alteredaggregationstatus'][$course_item->id])) {
+ $aggregationstatus = $hiding_affected['alteredaggregationstatus'][$course_item->id];
+ }
+ if (!empty($hiding_affected['alteredaggregationweight'][$course_item->id])) {
+ $aggregationweight = $hiding_affected['alteredaggregationweight'][$course_item->id];
+ }
}
}
- return array('grade' => $finalgrade, 'grademin' => $grademin, 'grademax' => $grademax);
+ return array('grade' => $finalgrade, 'grademin' => $grademin, 'grademax' => $grademax, 'aggregationstatus'=>$aggregationstatus, 'aggregationweight'=>$aggregationweight);
}
/**
/// Actual Grade
$gradeval = $grade_grade->finalgrade;
+ $hint = $grade_grade->get_aggregation_hint($grade_object);
if (!$this->canviewhidden) {
/// Virtual Grade (may be calculated excluding hidden items etc).
$adjustedgrade = $this->blank_hidden_total_and_adjust_bounds($this->courseid,
$grade_grade->grade_item,
$gradeval);
+
$gradeval = $adjustedgrade['grade'];
// We temporarily adjust the view of this grade item - because the min and
// max are affected by the hidden values in the aggregation.
$grade_grade->grade_item->grademax = $adjustedgrade['grademax'];
$grade_grade->grade_item->grademin = $adjustedgrade['grademin'];
+ $hint['status'] = $adjustedgrade['aggregationstatus'];
+ $hint['weight'] = $adjustedgrade['aggregationweight'];
} else {
// The max and min for an aggregation may be different to the grade_item.
if (!is_null($gradeval)) {
$data['weight']['headers'] = "$header_cat $header_row weight";
// has a weight assigned, might be extra credit
- $hint = $grade_grade->get_aggregation_hint($grade_object);
- if ($hint) {
- // This obliterates the weight because it provides a more informative description.
- if (is_numeric($hint)) {
- $hint = format_float($hint * 100.0, 2) . ' %';
- }
- $data['weight']['content'] = $hint;
+ // This obliterates the weight because it provides a more informative description.
+ if (is_numeric($hint['weight'])) {
+ $data['weight']['content'] = format_float($hint['weight'] * 100.0, 2) . ' %';
+ }
+ if ($hint['status'] != 'used' && $hint['status'] != 'unknown') {
+ $data['weight']['content'] .= '<br/>( ' . get_string('aggregationhint' . $hint['status'], 'grades') . ' )';
}
}
$data['contributiontocoursetotal']['class'] = $class;
$data['contributiontocoursetotal']['content'] = '-';
$data['contributiontocoursetotal']['headers'] = "$header_cat $header_row contributiontocoursetotal";
-
+ /**
if (($type != 'categoryitem') && ($type != 'courseitem')) {
- $hint = $grade_grade->get_aggregation_hint($grade_object);
- if ($hint && is_numeric($hint)) {
+ $weight = $grade_grade->get_aggregation_weight($grade_object);
+ if (is_numeric($weight)) {
$me = $grade_grade->grade_item;
$percentoftotal = $hint;
$validpercent = true;
- $limit = 0;
$parent = null;
while ((!$me->is_course_item()) && ($validpercent)) {
// The parent of a category grade item is itself (yes - how odd).
}
$thispercent = $hint;
$percentoftotal *= $thispercent;
- $limit++;
- if ($limit > 20) {
- die();
- }
}
if ($validpercent) {
$grademin = $grade_grade->grade_item->grademin;
$data['contributiontocoursetotal']['content'] = $contribution;
}
}
+ } **/
+ $hint = $grade_grade->get_aggregation_hint($grade_object);
+ if ($hint && is_numeric($hint)) {
+ $data['contributiontocoursetotal']['content'] = $hint;
+ $data['contributiontocoursetotal']['content'] .= ' ' . $grade_grade->finalgrade . ' ' . $grade_grade->rawgrademin . ' ' . $grade_grade->rawgrademax;
}
}
}
And I set the field "Grade report" to "Overview report"
And I should see "50.00 (50.00 %)" in the "overview-grade" "table"
- @javascript
+ @javascript @wip
Scenario: Natural aggregation
And I set the following settings for grade item "Sub category 1":
| Aggregation | Natural |
* Highest grade
* Mode of grades - The grade that occurs the most frequently
* Natural - The sum of all grade values scaled by weight';
+$string['aggregationhintnovalue'] = 'Empty';
+$string['aggregationhintdropped'] = 'Dropped';
+$string['aggregationhintexcluded'] = 'Excluded';
+$string['aggregationhintextra'] = 'Extra credit';
$string['aggregation_link'] = 'grade/aggregation';
$string['aggregationcoef'] = 'Aggregation coefficient';
$string['aggregationcoefextra'] = 'Extra credit'; // for the header of the table at Edit categories and items page
// Remember these so we can set flags on them to describe how they were used in the aggregation.
$novalue = array();
$dropped = array();
+ $extracredit = array();
$usedweights = array();
if (empty($userid)) {
// can not use own final category grade in calculation
unset($grade_values[$this->grade_item->id]);
+ // Make sure a grade_grade exists for every grade_item.
+ foreach ($items as $itemid => $gradeitem) {
+ $gradegrade = new grade_grade(array('itemid' => $gradeitem->id,
+ 'userid' => $userid,
+ 'rawgrademin' => $gradeitem->grademin,
+ 'rawgrademax' => $gradeitem->grademax), false);
+ $gradegrade->grade_item = $this->grade_item;
+ if (!$gradegrade->fetch(array('itemid'=>$gradeitem->id, 'userid'=>$userid))) {
+ $gradegrade->insert('system');
+ }
+
+ }
+
// if no grades calculation possible or grading not allowed clear final grade
if (empty($grade_values) or empty($items) or ($this->grade_item->gradetype != GRADE_TYPE_VALUE and $this->grade_item->gradetype != GRADE_TYPE_SCALE)) {
$grade->finalgrade = null;
$grade->update('aggregation');
}
$dropped = $grade_values;
- $this->set_usedinaggregation($userid, $usedweights, $novalue, $dropped);
+ $this->set_usedinaggregation($userid, $usedweights, $novalue, $dropped, $extracredit);
return;
}
- $minvisible = (bool) get_config('moodle', 'grade_report_showmin');
-
- // normalize the grades first - all will have value 0...1
- // ungraded items are not used in aggregation
+ // Normalize the grades first - all will have value 0...1
+ // ungraded items are not used in aggregation.
foreach ($grade_values as $itemid=>$v) {
- // Natural weighting currently cannot exclude empty grades, or grades from excluded items.
- if ($this->aggregation != GRADE_AGGREGATE_SUM) {
- if (is_null($v)) {
- // null means no grade
+ if (is_null($v)) {
+ // If null, it means no grade.
+ if ($this->aggregateonlygraded) {
unset($grade_values[$itemid]);
$novalue[$itemid] = 0;
continue;
- } else if (in_array($itemid, $excluded)) {
- unset($grade_values[$itemid]);
- $dropped[$itemid] = 0;
- continue;
}
}
+ if (in_array($itemid, $excluded)) {
+ unset($grade_values[$itemid]);
+ $dropped[$itemid] = 0;
+ continue;
+ }
// Check for user specific grade min/max overrides.
$usergrademin = $items[$itemid]->grademin;
$usergrademax = $items[$itemid]->grademax;
}
$grade_values[$itemid] = grade_grade::standardise_score($v, $usergrademin, $usergrademax, 0, 1);
}
- // use min grade if grade missing for these types
- if (!$this->aggregateonlygraded) {
- foreach ($items as $itemid=>$value) {
-
- if (!isset($grade_values[$itemid]) and !in_array($itemid, $excluded)) {
+ // Use min grade if grade missing for these types.
+ foreach ($items as $itemid=>$value) {
+ if (!isset($grade_values[$itemid]) and !in_array($itemid, $excluded)) {
+ if (!$this->aggregateonlygraded) {
$grade_values[$itemid] = 0;
+ } else {
+ $novalue[$itemid] = 0;
}
}
}
foreach ($moredropped as $drop => $unused) {
$dropped[$drop] = 0;
}
+
+ foreach ($grade_values as $itemid => $val) {
+ if (self::is_extracredit_used() && ($items[$itemid]->aggregationcoef > 0)) {
+ $extracredit[$itemid] = 0;
+ }
+ }
+
asort($grade_values, SORT_NUMERIC);
// let's see we have still enough grades to do any statistics
if (!is_null($oldfinalgrade)) {
$grade->update('aggregation');
}
- $this->set_usedinaggregation($userid, $usedweights, $novalue, $dropped);
+ $this->set_usedinaggregation($userid, $usedweights, $novalue, $dropped, $extracredit);
return;
}
$grade->update('aggregation');
}
- $this->set_usedinaggregation($userid, $usedweights, $novalue, $dropped);
+ $this->set_usedinaggregation($userid, $usedweights, $novalue, $dropped, $extracredit);
return;
}
* @param array $dropped An array with keys for each of the grade_item columns dropped
* because of any drop lowest/highest settings in the aggregation
*/
- private function set_usedinaggregation($userid, $usedweights, $novalue, $dropped) {
+ private function set_usedinaggregation($userid, $usedweights, $novalue, $dropped, $extracredit) {
global $DB;
// Included.
"itemid $itemsql AND userid = :userid",
$itemlist);
}
+ // Extra credit.
+ if (!empty($extracredit)) {
+ list($itemsql, $itemlist) = $DB->get_in_or_equal(array_keys($extracredit), SQL_PARAMS_NAMED, 'g');
+
+ $itemlist['userid'] = $userid;
+
+ $DB->set_field_select('grade_grades',
+ 'aggregationstatus',
+ 'extra',
+ "itemid $itemsql AND userid = :userid",
+ $itemlist);
+ }
}
/**
$altered = array(); // altered grades
$alteredgrademax = array(); // Altered grade max values.
$alteredgrademin = array(); // Altered grade min values.
+ $alteredaggregationstatus = array(); // Altered aggregation status.
+ $alteredaggregationweight = array(); // Altered aggregation weight.
$dependencydepth = array();
$hiddenfound = false;
return array('unknown' => array(),
'altered' => array(),
'alteredgrademax' => array(),
- 'alteredgrademin' => array());
+ 'alteredgrademin' => array(),
+ 'alteredaggregationstatus' => array(),
+ 'alteredaggregationweight' => array());
}
// This line ensures that $dependencydepth has the same number of items as $todo.
$dependencydepth = array_intersect_key($dependencydepth, array_flip($todo));
foreach ($values as $itemid=>$value) {
if ($grade_grades[$itemid]->is_excluded()) {
unset($values[$itemid]);
+ $alteredaggregationstatus[$itemid] = 'excluded';
+ $alteredaggregationweight[$itemid] = 0;
continue;
}
// The grade min/max may have been altered by hiding.
foreach ($values as $itemid=>$value) {
if (is_null($value)) {
unset($values[$itemid]);
+ $alteredaggregationstatus[$itemid] = 'novalue';
+ $alteredaggregationweight[$itemid] = 0;
}
}
} else {
}
// limit and sort
+ $allvalues = $values;
$grade_category->apply_limit_rules($values, $grade_items);
+
+ $moredropped = array_diff($allvalues, $values);
+ foreach ($moredropped as $drop => $unused) {
+ $alteredaggregationstatus[$drop] = 'dropped';
+ $alteredaggregationweight[$drop] = 0;
+ }
+
+ foreach ($values as $itemid => $val) {
+ if ($grade_category->is_extracredit_used() && ($grade_items[$itemid]->aggregationcoef > 0)) {
+ $alteredaggregationstatus[$itemid] = 'extra';
+ }
+ }
+
asort($values, SORT_NUMERIC);
// let's see we have still enough grades to do any statistics
continue;
}
- $adjustedgrade = $grade_category->aggregate_values_and_adjust_bounds($values, $grade_items);
+ $usedweights = array();
+ $adjustedgrade = $grade_category->aggregate_values_and_adjust_bounds($values, $grade_items, $usedweights);
// recalculate the rawgrade back to requested range
$finalgrade = grade_grade::standardise_score($adjustedgrade['grade'],
$adjustedgrade['grademin'],
$adjustedgrade['grademax']);
+ foreach ($usedweights as $itemid => $weight) {
+ if (!isset($alteredaggregationstatus[$itemid])) {
+ $alteredaggregationstatus[$itemid] = 'used';
+ }
+ $alteredaggregationweight[$itemid] = $weight;
+ }
+
$finalgrade = $grade_items[$do]->bounded_grade($finalgrade);
$alteredgrademin[$do] = $adjustedgrade['grademin'];
$alteredgrademax[$do] = $adjustedgrade['grademax'];
return array('unknown' => $unknown,
'altered' => $altered,
'alteredgrademax' => $alteredgrademax,
- 'alteredgrademin' => $alteredgrademin);
+ 'alteredgrademin' => $alteredgrademin,
+ 'alteredaggregationstatus' => $alteredaggregationstatus,
+ 'alteredaggregationweight' => $alteredaggregationweight);
}
/**
* dropped because it's in the X lowest or highest.
*
* @param grade_item $gradeitem An optional grade_item, saves having to load the grade_grade's grade_item
- * @return string - A list of keywords that hint at how this grade_grade is reflected in the aggregation.
+ * @return array(status, weight) - A keyword and a numerical weight that represents how this grade was included in the aggregation.
*/
function get_aggregation_hint($gradeitem = null) {
- $hint = '';
-
- if ($this->is_excluded()) {
- $hint = get_string('excluded', 'grades');
- } else {
- if (empty($grade_item)) {
- if (!isset($this->grade_item)) {
- $this->load_grade_item();
- }
- } else {
- $this->grade_item = $grade_item;
- $this->itemid = $grade_item->id;
- }
- $item = $this->grade_item;
-
- if (!$item->is_course_item()) {
- $parentcategory = $item->get_parent_category();
- // This is needed because get_parent_category() does not do the "parent" bit very well.
- if ($item->is_category_item()) {
- $parentcategory = $parentcategory->load_parent_category();
- }
- if ($parentcategory->is_extracredit_used() && ($item->aggregationcoef > 0)) {
- $hint = get_string('aggregationcoefextra', 'grades');
- }
- }
-
- }
-
- // Is it dropped?
- if ($hint == '') {
- $aggr = $this->get_aggregationstatus();
- if ($aggr == 'dropped') {
- $hint = get_string('dropped', 'grades');
- } else if ($aggr == 'used') {
- $hint = $this->get_aggregationweight();
- } else if ($aggr != 'unknown') {
- $hint = '-';
- }
- }
- return $hint;
+ return array('status' => $this->get_aggregationstatus(),
+ 'weight' => $this->aggregationweight);
}
}