From: skodak Date: Wed, 18 Jul 2007 19:29:32 +0000 (+0000) Subject: MDL-9137 added new aggregation needed for upgrade - weighted + extra credit X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=9580a21f1c0f8e5400d782d24b97f71733ce8da1;p=moodle.git MDL-9137 added new aggregation needed for upgrade - weighted + extra credit minor item edit form cleanup fixed minor bug in grader/lib.php --- diff --git a/grade/edit/category_form.php b/grade/edit/category_form.php index f82aa0e303..01597f1321 100644 --- a/grade/edit/category_form.php +++ b/grade/edit/category_form.php @@ -12,11 +12,16 @@ class edit_category_form extends moodleform { $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); diff --git a/grade/edit/item.php b/grade/edit/item.php index b7ca41d1ac..15796c8847 100644 --- a/grade/edit/item.php +++ b/grade/edit/item.php @@ -34,10 +34,6 @@ if ($item = get_record('grade_items', 'id', $id, 'courseid', $course->id)) { $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()) { @@ -69,7 +65,7 @@ if ($data = $mform->get_data()) { } } - redirect($returnurl, 'temporary debug delay', 50); + redirect($returnurl, 'temporary debug delay', 5); } $strgrades = get_string('grades'); diff --git a/grade/edit/item_form.php b/grade/edit/item_form.php index 80e8635313..9a588353d9 100644 --- a/grade/edit/item_form.php +++ b/grade/edit/item_form.php @@ -46,26 +46,29 @@ class edit_item_form extends moodleform { $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')); @@ -97,10 +100,10 @@ class edit_item_form extends moodleform { $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 @@ -115,19 +118,51 @@ class edit_item_form extends moodleform { /// 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'); } } } diff --git a/grade/report/grader/lib.php b/grade/report/grader/lib.php index c92e70bda8..63eebe7482 100644 --- a/grade/report/grader/lib.php +++ b/grade/report/grader/lib.php @@ -708,8 +708,8 @@ class grade_report_grader extends grade_report { $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)) { diff --git a/lib/grade/grade_category.php b/lib/grade/grade_category.php index 30a0fde2c9..b01fc8b909 100644 --- a/lib/grade/grade_category.php +++ b/lib/grade/grade_category.php @@ -352,25 +352,25 @@ class grade_category extends grade_object { 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 } } @@ -380,7 +380,7 @@ class grade_category extends grade_object { /** * 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; @@ -410,10 +410,10 @@ class grade_category extends grade_object { } // 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) { @@ -424,21 +424,21 @@ class grade_category extends grade_object { /// 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; @@ -451,32 +451,26 @@ class grade_category extends grade_object { /// 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) @@ -484,10 +478,88 @@ class grade_category extends grade_object { $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; } @@ -519,22 +591,35 @@ class grade_category extends grade_object { /** * 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. @@ -905,7 +990,7 @@ class grade_category extends grade_object { * 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(); diff --git a/lib/grade/grade_grades.php b/lib/grade/grade_grades.php index 53e85dcd09..d110d54048 100644 --- a/lib/grade/grade_grades.php +++ b/lib/grade/grade_grades.php @@ -422,6 +422,10 @@ class grade_grades extends grade_object { * @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; diff --git a/lib/grade/grade_item.php b/lib/grade/grade_item.php index 8f66544e8d..745cc39943 100644 --- a/lib/grade/grade_item.php +++ b/lib/grade/grade_item.php @@ -261,13 +261,14 @@ class grade_item extends grade_object { $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); } /** diff --git a/lib/gradelib.php b/lib/gradelib.php index a3702b0bce..0c6d420797 100644 --- a/lib/gradelib.php +++ b/lib/gradelib.php @@ -33,11 +33,15 @@ */ 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);