$mform->addElement('text', 'fullname', get_string('categoryname', 'grades'));
$options = array(GRADE_AGGREGATE_MEAN_ALL =>get_string('aggregatemeanall', 'grades'),
- GRADE_AGGREGATE_MEDIAN =>get_string('aggregatemedian', 'grades'),
GRADE_AGGREGATE_MEAN_GRADED=>get_string('aggregatemeangraded', 'grades'),
+ GRADE_AGGREGATE_MEDIAN =>get_string('aggregatemedian', 'grades'),
GRADE_AGGREGATE_MIN =>get_string('aggregatemin', 'grades'),
GRADE_AGGREGATE_MAX =>get_string('aggregatemax', 'grades'),
- GRADE_AGGREGATE_MODE =>get_string('aggregatemode', 'grades'));
+ GRADE_AGGREGATE_MODE =>get_string('aggregatemode', 'grades'),
+ GRADE_AGGREGATE_WEIGHTED_MEAN_ALL =>get_string('aggregateweightedmeanall', 'grades'),
+ GRADE_AGGREGATE_WEIGHTED_MEAN_GRADED =>get_string('aggregateweightedgraded', 'grades'),
+ GRADE_AGGREGATE_EXTRACREDIT_MEAN_ALL =>get_string('aggregateextracreditmeanall', 'grades'),
+ GRADE_AGGREGATE_EXTRACREDIT_MEAN_GRADED=>get_string('aggregateextracreditmeangraded', 'grades'));
+
$mform->addElement('select', 'aggregation', get_string('aggregation', 'grades'), $options);
$mform->setDefault('gradetype', GRADE_AGGREGATE_MEAN_ALL);
$item->calculation = grade_item::denormalize_formula($item->calculation, $course->id);
$mform->set_data($item);
-
-} else {
- // defaults for new items
- $mform->set_data(array('courseid'=>$course->id, 'itemtype'=>'manual'));
}
if ($data = $mform->get_data()) {
}
}
- redirect($returnurl, 'temporary debug delay', 50);
+ redirect($returnurl, 'temporary debug delay', 5);
}
$strgrades = get_string('grades');
$mform->addElement('text', 'grademax', get_string('grademax', 'grades'));
$mform->disabledIf('grademax', 'gradetype', 'noteq', GRADE_TYPE_VALUE);
- $mform->setDefault('grademax', 100);
+ $mform->setDefault('grademax', 100.0);
$mform->addElement('text', 'grademin', get_string('grademin', 'grades'));
$mform->disabledIf('grademin', 'gradetype', 'noteq', GRADE_TYPE_VALUE);
- $mform->setDefault('grademin', 0);
+ $mform->setDefault('grademin', 0.0);
$mform->addElement('text', 'gradepass', get_string('gradepass', 'grades'));
$mform->disabledIf('gradepass', 'gradetype', 'eq', GRADE_TYPE_NONE);
$mform->disabledIf('gradepass', 'gradetype', 'eq', GRADE_TYPE_TEXT);
- $mform->setDefault('gradepass', 0);
+ $mform->setDefault('gradepass', 0.0);
$mform->addElement('text', 'multfactor', get_string('multfactor', 'grades'));
$mform->disabledIf('multfactor', 'gradetype', 'eq', GRADE_TYPE_NONE);
$mform->disabledIf('multfactor', 'gradetype', 'eq', GRADE_TYPE_TEXT);
- $mform->setDefault('multfactor', 1);
+ $mform->setDefault('multfactor', 1.0);
$mform->addElement('text', 'plusfactor', get_string('plusfactor', 'grades'));
$mform->disabledIf('plusfactor', 'gradetype', 'eq', GRADE_TYPE_NONE);
$mform->disabledIf('plusfactor', 'gradetype', 'eq', GRADE_TYPE_TEXT);
- $mform->setDefault('plusfactor', 0);
+ $mform->setDefault('plusfactor', 0.0);
+
+ $mform->addElement('text', 'aggregationcoef', get_string('aggregationcoef', 'grades'));
+ $mform->setDefault('aggregationcoef', 0.0);
$mform->addElement('advcheckbox', 'locked', get_string('locked', 'grades'));
$mform->addElement('hidden', 'id', 0);
$mform->setType('id', PARAM_INT);
- $mform->addElement('hidden', 'courseid', 0);
+ $mform->addElement('hidden', 'courseid', $COURSE->id);
$mform->setType('courseid', PARAM_INT);
- $mform->addElement('hidden', 'itemtype', 0);
+ $mform->addElement('hidden', 'itemtype', 'manual'); // all new items are manual only
$mform->setType('itemtype', PARAM_ALPHA);
/// add return tracking info
/// tweak the form - depending on existing data
function definition_after_data() {
- global $CFG;
+ global $CFG, $COURSE;
$mform =& $this->_form;
if ($id = $mform->getElementValue('id')) {
$grade_item = grade_item::fetch(array('id'=>$id));
+
if ($grade_item->is_normal_item()) {
// following items are set up from modules and should not be overrided by user
- $mform->hardFreeze('itemname,idnumber,calculation,gradetype,grademax,grademin,scaleid');
- }
- if ($grade_item->is_manual_item()) {
+ $mform->hardFreeze('itemname,idnumber,gradetype,grademax,grademin,scaleid');
+ $mform->removeElement('calculation');
+
+ } else if ($grade_item->is_manual_item()) {
// manual grade item does not use these - uses only final grades
- $mform->hardFreeze('plusfactor,multfactor');
+ $mform->removeElement('plusfactor');
+ $mform->removeElement('multfactor');
+
+ }
+
+ //remove the aggregation coef element if not needed
+ if ($grade_item->is_course_item()) {
+ $mform->removeElement('aggregationcoef');
+
+ } else if ($grade_item->is_category_item()) {
+ $category = $grade_item->get_item_category();
+ $parent_category = $category->get_parent_category();
+ if (!$parent_category->is_aggregationcoef_used()) {
+ $mform->removeElement('aggregationcoef');
+ }
+
+ } else {
+ $parent_category = $grade_item->get_parent_category();
+ if (!$parent_category->is_aggregationcoef_used()) {
+ $mform->removeElement('aggregationcoef');
+ }
+ }
+
+ } else {
+ // all new items are manual, children of course category
+ $mform->removeElement('plusfactor');
+ $mform->removeElement('multfactor');
+
+ $course_category = grade_category::fetch_course_category($COURSE->id);
+ if (!$course_category->is_aggregationcoef_used()) {
+ $mform->removeElement('aggregationcoef');
}
}
}
$gradedisplaytype = $this->get_pref('gradedisplaytype', $item->id);
$percentsign = '';
- $grademin = $this->finalgrades[$userid][$item->id]->grademin;
- $grademax = $this->finalgrades[$userid][$item->id]->grademax;
+ $grademin = $item->grademin;
+ $grademax = $item->grademax;
if ($gradedisplaytype == GRADE_REPORT_GRADE_DISPLAY_TYPE_PERCENTAGE) {
if (!is_null($gradeval)) {
WHERE gi.id = g.itemid AND gi.id IN ($gis) $usersql
ORDER BY g.userid";
- // group the results by userid and aggregate the grades in this group
+ // group the results by userid and aggregate the grades for this user
if ($rs = get_recordset_sql($sql)) {
if ($rs->RecordCount() > 0) {
$prevuser = 0;
- $grade_records = array();
- $oldgrade = null;
+ $grade_values = array();
+ $oldgrade = null;
while ($used = rs_fetch_next_record($rs)) {
if ($used->userid != $prevuser) {
- $this->aggregate_grades($prevuser, $items, $grade_records, $oldgrade);
+ $this->aggregate_grades($prevuser, $items, $grade_values, $oldgrade);
$prevuser = $used->userid;
- $grade_records = array();
- $oldgrade = null;
+ $grade_values = array();
+ $oldgrade = null;
}
- $grade_records[$used->itemid] = $used->finalgrade;
+ $grade_values[$used->itemid] = $used->finalgrade;
if ($this->grade_item->id == $used->itemid) {
$oldgrade = $used;
}
}
- $this->aggregate_grades($prevuser, $items, $grade_records, $oldgrade);//the last one
+ $this->aggregate_grades($prevuser, $items, $grade_values, $oldgrade);//the last one
}
}
/**
* internal function for category grades aggregation
*/
- function aggregate_grades($userid, $items, $grade_records, $oldgrade) {
+ function aggregate_grades($userid, $items, $grade_values, $oldgrade) {
if (empty($userid)) {
//ignore first call
return;
}
// can not use own final category grade in calculation
- unset($grade_records[$this->grade_item->id]);
+ unset($grade_values[$this->grade_item->id]);
// if no grades calculation possible or grading not allowed clear both final and raw
- if (empty($grade_records) or empty($items) or ($this->grade_item->gradetype != GRADE_TYPE_VALUE and $this->grade_item->gradetype != GRADE_TYPE_SCALE)) {
+ 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->rawgrade = null;
if ($grade->finalgrade !== $oldgrade->finalgrade or $grade->rawgrade !== $oldgrade->rawgrade) {
/// normalize the grades first - all will have value 0...1
// ungraded items are not used in aggreagation
- foreach ($grade_records as $k=>$v) {
+ foreach ($grade_values as $k=>$v) {
if (is_null($v)) {
// null means no grade
- unset($grade_records[$k]);
+ unset($grade_values[$k]);
continue;
}
- $grade_records[$k] = grade_grades::standardise_score($v, $items[$k]->grademin, $items[$k]->grademax, 0, 1);
+ $grade_values[$k] = grade_grades::standardise_score($v, $items[$k]->grademin, $items[$k]->grademax, 0, 1);
}
//limit and sort
- $this->apply_limit_rules($grade_records);
- sort($grade_records, SORT_NUMERIC);
+ $this->apply_limit_rules($grade_values);
+ asort($grade_values, SORT_NUMERIC);
// let's see we have still enough grades to do any statisctics
- if (count($grade_records) == 0) {
+ if (count($grade_values) == 0) {
// not enough attempts yet
$grade->finalgrade = null;
$grade->rawgrade = null;
/// start the aggregation
switch ($this->aggregation) {
case GRADE_AGGREGATE_MEDIAN: // Middle point value in the set: ignores frequencies
- $num = count($grade_records);
+ $num = count($grade_values);
$halfpoint = intval($num / 2);
if($num % 2 == 0) {
- $rawgrade = ($grade_records[ceil($halfpoint)] + $grade_records[floor($halfpoint)]) / 2;
+ $rawgrade = ($grade_values[ceil($halfpoint)] + $grade_values[floor($halfpoint)]) / 2;
} else {
- $rawgrade = $grade_records[$halfpoint];
+ $rawgrade = $grade_values[$halfpoint];
}
break;
case GRADE_AGGREGATE_MIN:
- $rawgrade = reset($grade_records);
+ $rawgrade = reset($grade_values);
break;
case GRADE_AGGREGATE_MAX:
- $rawgrade = array_pop($grade_records);
- break;
-
- case GRADE_AGGREGATE_MEAN_ALL: // Arithmetic average of all grade items including even NULLs; NULL grade caunted as minimum
- $num = count($items); // you can calculate sum from this one if you multiply it with count($this->depends_on() ;-)
- $sum = array_sum($grade_records);
- $rawgrade = $sum / $num;
+ $rawgrade = array_pop($grade_values);
break;
case GRADE_AGGREGATE_MODE: // the most common value, the highest one if multimode
- $freq = array_count_values($grade_records);
+ $freq = array_count_values($grade_values);
arsort($freq); // sort by frequency keeping keys
$top = reset($freq); // highest frequency count
$modes = array_keys($freq, $top); // search for all modes (have the same highest count)
$rawgrade = reset($modes);
break;
+ case GRADE_AGGREGATE_WEIGHTED_MEAN_ALL: // Weighted average of all possible final grades
+ $weightsum = 0;
+ $sum = 0;
+ foreach($items as $key=>$value) {
+ $grade_value = isset($grade_values[$key]) ? $grade_values[$key] : 0;
+ if ($items[$key]->aggregationcoef <= 0) {
+ continue;
+ }
+ $weightsum += $items[$key]->aggregationcoef;
+ $sum += $items[$key]->aggregationcoef * $grade_value;
+ }
+ if ($weightsum == 0) {
+ $rawgrade = null;
+ } else {
+ $rawgrade = $sum / $weightsum;
+ }
+ break;
+
+ case GRADE_AGGREGATE_WEIGHTED_MEAN_GRADED: // Weighted average of all existing final grades
+ $weightsum = 0;
+ $sum = 0;
+ foreach($grade_values as $key=>$grade_value) {
+ if ($items[$key]->aggregationcoef <= 0) {
+ continue;
+ }
+ $weightsum += $items[$key]->aggregationcoef;
+ $sum += $items[$key]->aggregationcoef * $grade_value;
+ }
+ if ($weightsum == 0) {
+ $rawgrade = null;
+ } else {
+ $rawgrade = $sum / $weightsum;
+ }
+ break;
+
+ case GRADE_AGGREGATE_EXTRACREDIT_MEAN_ALL: // special average
+ $num = 0;
+ $sum = 0;
+ foreach($items as $key=>$value) {
+ $grade_value = isset($grade_values[$key]) ? $grade_values[$key] : 0;
+ if ($items[$key]->aggregationcoef == 0) {
+ $num += 1;
+ $sum += $grade_value;
+ } else if ($items[$key]->aggregationcoef > 0) {
+ $sum += $items[$key]->aggregationcoef * $grade_value;
+ }
+ }
+ if ($num == 0) {
+ $rawgrade = $sum; // only extra credits or wrong coefs
+ } else {
+ $rawgrade = $sum / $num;
+ }
+ break;
+
+ case GRADE_AGGREGATE_EXTRACREDIT_MEAN_GRADED: // special average
+ $num = 0;
+ $sum = 0;
+ foreach($grade_values as $key=>$grade_value) {
+ if ($items[$key]->aggregationcoef == 0) {
+ $num += 1;
+ $sum += $grade_value;
+ } else if ($items[$key]->aggregationcoef > 0) {
+ $sum += $items[$key]->aggregationcoef * $grade_value;
+ }
+ }
+ if ($num == 0) {
+ $rawgrade = $sum; // only extra credits or wrong coefs
+ } else {
+ $rawgrade = $sum / $num;
+ }
+ break;
+
+ case GRADE_AGGREGATE_MEAN_ALL: // Arithmetic average of all grade items including even NULLs; NULL grade caunted as minimum
+ $num = count($items); // you can calculate sum from this one if you multiply it with count($this->depends_on() ;-)
+ $sum = array_sum($grade_values);
+ $rawgrade = $sum / $num;
+ break;
+
case GRADE_AGGREGATE_MEAN_GRADED: // Arithmetic average of all final grades, unfinished are not calculated
default:
- $num = count($grade_records);
- $sum = array_sum($grade_records);
+ $num = count($grade_values);
+ $sum = array_sum($grade_values);
$rawgrade = $sum / $num;
break;
}
/**
* Given an array of grade values (numerical indices), applies droplow or keephigh
* rules to limit the final array.
- * @param array $grades
+ * @param array $grade_values
* @return array Limited grades.
*/
- function apply_limit_rules(&$grades) {
- rsort($grades, SORT_NUMERIC);
+ function apply_limit_rules(&$grade_values) {
+ arsort($grade_values, SORT_NUMERIC);
if (!empty($this->droplow)) {
for ($i = 0; $i < $this->droplow; $i++) {
- array_pop($grades);
+ array_pop($grade_values);
}
} elseif (!empty($this->keephigh)) {
- while (count($grades) > $this->keephigh) {
- array_pop($grades);
+ while (count($grade_values) > $this->keephigh) {
+ array_pop($grade_values);
}
}
}
+
+ /**
+ * Returns true if category uses special aggregation coeficient
+ * @return boolean true if coeficient used
+ */
+ function is_aggregationcoef_used() {
+ return ($this->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN_ALL
+ or $this->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN_GRADED
+ or $this->aggregation == GRADE_AGGREGATE_EXTRACREDIT_MEAN_ALL
+ or $this->aggregation == GRADE_AGGREGATE_EXTRACREDIT_MEAN_GRADED);
+
+ }
+
/**
* Returns true if this category has any child grade_category or grade_item.
* @return int number of direct children, or false if none found.
* Sets the grade_item's locked variable and updates the grade_item.
* Method named after grade_item::set_locked().
* @param int $locked 0, 1 or a timestamp int(10) after which date the item will be locked.
- * @return boolean success if categroy locked (not all children mayb be locked though)
+ * @return boolean success if category locked (not all children mayb be locked though)
*/
function set_locked($lockedstate) {
$this->load_grade_item();
* @return float Converted value
*/
function standardise_score($rawgrade, $source_min, $source_max, $target_min, $target_max) {
+ if (is_null($rawgrade)) {
+ return null;
+ }
+
$factor = ($rawgrade - $source_min) / ($source_max - $source_min);
$diff = $target_max - $target_min;
$standardised_value = $factor * $diff + $target_min;
$outcomeiddiff = $db_item->outcomeid != $this->outcomeid;
$multfactordiff = $db_item->multfactor != $this->multfactor;
$plusfactordiff = $db_item->plusfactor != $this->plusfactor;
+ $acoefdiff = $db_item->aggregationcoef != $this->aggregationcoef;
$needsupdatediff = !$db_item->needsupdate && $this->needsupdate; // force regrading only if setting the flag first time
$lockeddiff = !empty($db_item->locked) && empty($this->locked); // force regrading only when unlocking
return ($calculationdiff || $categorydiff || $gradetypediff || $grademaxdiff || $grademindiff || $scaleiddiff
|| $outcomeiddiff || $multfactordiff || $plusfactordiff || $needsupdatediff
- || $lockeddiff);
+ || $lockeddiff || $acoefdiff);
}
/**
*/
define('GRADE_AGGREGATE_MEAN_ALL', 0);
-define('GRADE_AGGREGATE_MEDIAN', 1);
-define('GRADE_AGGREGATE_MEAN_GRADED', 2);
+define('GRADE_AGGREGATE_MEAN_GRADED', 1);
+define('GRADE_AGGREGATE_MEDIAN', 2);
define('GRADE_AGGREGATE_MIN', 3);
define('GRADE_AGGREGATE_MAX', 4);
define('GRADE_AGGREGATE_MODE', 5);
+define('GRADE_AGGREGATE_WEIGHTED_MEAN_ALL', 6);
+define('GRADE_AGGREGATE_WEIGHTED_MEAN_GRADED', 7);
+define('GRADE_AGGREGATE_EXTRACREDIT_MEAN_ALL', 8);
+define('GRADE_AGGREGATE_EXTRACREDIT_MEAN_GRADED', 9);
define('GRADE_CHILDTYPE_ITEM', 0);
define('GRADE_CHILDTYPE_CAT', 1);