return true;
}
- $result = true;
-
$this->load_grade_item();
+ if ($this->grade_item->needsupdate) {
+ // this grade_item (and category) already needs update, no need to set it again here or in parent categories
+ return true;
+ }
+
$paths = explode('/', $this->path);
// Remove the first index, which is always empty
unset($paths[0]);
+ $result = true;
+
if (!empty($paths)) {
$wheresql = '';
}
- if (count($paths) == 1) {
- // we are the top category - force recalculation of all formulas in course
- $this->grade_item->force_recalculation();
- }
-
return $result;
}
if ($this->is_locked()) {
// locked grade items already have correct final grades
+ $this->needsupdate = false;
+ $this->update();
return true;
}
- if ($calculation = $this->get_calculation()) {
- if ($calculation->compute()) {
- $this->force_regrade();
+ if ($this->is_calculated()) {
+ if ($this->calculation->compute()) {
+ $this->needsupdate = false;
+ $this->update();
return true;
} else {
return array("Could not calculate grades for grade item id:".$this->id); // TODO: improve and localize
if ($category = $this->get_category()) {
$category->force_regrading(); // we can ignore the result
- } else {
- $this->force_recalculation(); // recalculate all formulas - we do not know if any of them depends on this item
}
-
return $result;
}
- /**
- * Force recalculation of all calculated grades in course.
- */
- function force_recalculation($courseid=null) {
- if (empty($courseid)) {
- $courseid = $this->courseid;
- }
- $grade_item = new grade_item(array('courseid'=>$courseid), false);
- $grade_items = $grade_item->fetch_all_using_this();
- foreach($grade_items as $gi) {
- if ($gi->get_calculation()) {
- $gi->needsupdate = true;
- $gi->update();
- }
- }
- }
-
/**
* Disassociates this item from its category parent(s). The object is then updated in DB.
* @return boolean Success or Failure
}
/**
- * Returns this object's calculation.
- * @param boolean $fetch Whether to fetch the value from the DB or not (false == just use the object's value)
- * @return mixed $calculation Object if found, false otherwise.
+ * Checks if grade calculated. Returns this object's calculation.
+ * @return mixed $calculation Calculation object if exists, false otherwise.
*/
- function get_calculation($nocache = false) {
+ function is_calculated() {
if (is_null($this->calculation)) {
- $nocache = true;
- }
-
- if ($nocache) {
$this->calculation = grade_calculation::fetch('itemid', $this->id);
}
- return $this->calculation;
+ return !empty($this->calculation);
+ }
+
+ /**
+ * Returns calculation string if grade calculated.
+ * @return mixed string if calculation used, null if not
+ */
+ function get_calculation() {
+ if ($this->is_calculated()) {
+ return $this->calculation->calculation;
+ } else {
+ return NULL;
+ }
}
/**
* Sets this item's calculation (creates it) if not yet set, or
* updates it if already set (in the DB). If no calculation is given,
* the calculation is removed.
- * @param string $formula
- * @return boolean
+ * @param string $formula string representation of formula used for calculation
+ * @return boolean success
*/
function set_calculation($formula) {
- // remove cached calculation object
+ // refresh cached calculation object
+ $this->calculation = null;
+
+ $result = true;
+
if (empty($formula)) { // We are removing this calculation
- if (!empty($this->id)) {
- if ($grade_calculation = $this->get_calculation(true)) {
- $grade_calculation->delete();
- }
+ if (!empty($this->id) and $this->is_calculated()) {
+ $this->calculation->delete();
+ $this->calculation = null; // remove cache
}
- $this->calculation = false; // cache no calculation present
- $this->force_regrading();
- return true;
} else { // We are updating or creating the calculation entry in the DB
- if ($grade_calculation = $this->get_calculation(true)) {
- $grade_calculation->calculation = $formula;
- if ($grade_calculation->update()) {
- $this->force_regrading();
- return true;
- } else {
+ if ($this->is_calculated()) {
+ $this->calculation->calculation = $formula;
+ if (!$this->calculation->update()) {
$this->calculation = null; // remove cache
+ $result = false;
debugging("Could not save the calculation in the database for this grade_item.");
- return false;
}
} else {
- $grade_calculation = new grade_calculation();
- $grade_calculation->calculation = $formula;
- $grade_calculation->itemid = $this->id;
-
- if ($grade_calculation->insert()) {
- return true;
- } else {
+ $grade_calculation = new grade_calculation(array('calculation'=>$formula, 'itemid'=>$this->id), false);
+ if (!$grade_calculation->insert()) {
$this->calculation = null; // remove cache
+ $result = false;
debugging("Could not save the calculation in the database for this grade_item.");
- return false;
}
+ $this->calculation = $grade_calculation;
}
}
+
+ $this->force_regrading();
+
+ return $result;
}
/**
return array();
}
- if ($calculation = $this->get_calculation()) {
+ if ($calculation = $this->is_calculated()) {
return $calculation->dependson();
} else if ($this->itemtype == 'category') {
function update_raw_grade($userid, $rawgrade=false, $howmodified='manual', $note=NULL, $feedback=false, $feedbackformat=FORMAT_MOODLE) {
// calculated grades can not be updated
- if ($this->get_calculation()) {
+ if ($this->is_calculated()) {
return false;
}
$params = compact('courseid', 'itemtype', 'itemmodule', 'iteminstance', 'itemnumber');
if ($itemdetails) {
$itemdetails = (array)$itemdetails;
+
+ // grademin and grademax ignored when scale specified
+ if (array_key_exists('scaleid', $itemdetails)) {
+ if ($itemdetails['scaleid']) {
+ unset($itemdetails['grademin']);
+ unset($itemdetails['grademax']);
+ }
+ }
+
foreach ($itemdetails as $k=>$v) {
if (!in_array($k, $allowed)) {
// ignore it
* Updates all final grades in course.
*
* @param int $courseid
+ * @param boolean $regradeall force regrading of all items
+ *
* @return boolean true if ok, array of errors if problems found
*/
-function grade_update_final_grades($courseid) {
- $errors = array();
- $grade_item = new grade_item();
- $grade_item->courseid = $courseid;
- if (!$grade_items = $grade_item->fetch_all_using_this()) {
- return true;
- }
+function grade_update_final_grades($courseid, $regradeall=false) {
- $needsupdate = false;
- $calculated = false;
- foreach ($grade_items as $gid=>$gitem) {
- $grade_item =& $grade_items[$gid];
- if ($grade_item->needsupdate) {
- $needsupdate = true;
- }
- if ($grade_item->get_calculation()) {
- $calculated = true;
- }
+ if ($regradeall) {
+ set_field('grade_items', 'needsupdate', 1, 'courseid', $courseid);
}
- // no update needed
- if (!$needsupdate) {
+ $grade_item = new grade_item(array('courseid'=>$courseid), false);
+
+ if (!$grade_items = $grade_item->fetch_all_using_this()) {
return true;
}
- // the easy way
- if (!$calculated) {
+ if (!$regradeall) {
+ $needsupdate = false;
+ $calculated = false;
foreach ($grade_items as $gid=>$gitem) {
$grade_item =& $grade_items[$gid];
if ($grade_item->needsupdate) {
- $result = $grade_item->update_final_grades();
- if ($result !== true) {
- $errors = array_merge($errors, $result);
- }
+ $needsupdate = true;
+ }
+ if ($grade_item->is_calculated()) {
+ $calculated = true;
}
}
-
- if (count($errors) == 0) {
+
+ if (!$needsupdate) {
+ // no update needed
return true;
- } else {
- return $errors;
+
+ } else if ($calculated) {
+ // flag all calculated grade items with needsupdate
+ // we want to make sure all are ok, this can be improved later with proper dependency calculation
+ foreach ($grade_items as $gid=>$gitem) {
+ $grade_item =& $grade_items[$gid];
+ if (!$grade_item->is_calculated()) {
+ continue;
+ }
+ $grade_item->update_from_db(); // make sure we have current data, it might have been updated in this loop already
+ if (!$grade_item->needsupdate) {
+ //force recalculation and forced update of all parents
+ $grade_item->force_regrading();
+ }
+ }
+
+ // again make sure all date is up-to-date - the needsupdate flag might have changed
+ foreach ($grade_items as $gid=>$gitem) {
+ $grade_item =& $grade_items[$gid];
+ $grade_item->update_from_db();
+ unset($grade_item->category);
+ }
}
}
+
+ $errors = array();
+
// now the hard way with calculated grade_items or categories
$finalitems = array();
$finalids = array();
$count = 0;
foreach ($grade_items as $gid=>$gitem) {
$grade_item =& $grade_items[$gid];
- if (!$grade_item->needsupdate and $grade_item->itemtype!='category' and !$grade_item->get_calculation()) {
+ if (!$grade_item->needsupdate) {
$finalitems[$gid] = $grade_item;
$finalids[] = $gid;
unset($grade_items[$gid]);
continue;
}
+ //do we have all data for finalizing of this item?
$dependson = $grade_item->dependson();
- //are we dealing with category with no calculated items?
- // we can not trust the needsupdate flag because category might contain calculated items
- if ($grade_item->itemtype=='category' and !$grade_item->needsupdate) {
- $forceupdate = false;
- foreach ($dependson as $childid) {
- if (in_array($childid, $finalids)) {
- $child = $finalitems[$childid];
- } else {
- $child = $grade_items[$childid];
- }
- if ($child->itemtype == 'category' or $child->get_calculation()) {
- $forceupdate = true;
- }
- }
-
- if ($forceupdate) {
- $grade_item->force_regrading();
- } else {
- $finalitems[$gid] = $grade_item;
- $finalids[] = $gid;
- unset($grade_items[$gid]);
- continue;
- }
- }
-
- //do we have all data for this item?
$doupdate = true;
foreach ($dependson as $did) {
if (!in_array($did, $finalids)) {
if ($count == 0) {
foreach($grade_items as $grade_item) {
- $errors[] = 'Probably circular reference in grade_item id:'.$grade_item->id; // TODO: localize
+ $errors[] = 'Probably circular reference or broken calculation formula in grade_item id:'.$grade_item->id; // TODO: localize
}
break;
}
$this->assertEqual($this->grade_grades[0]->finalgrade, $final_grade->finalgrade);\r
}\r
\r
- function test_grade_item_get_calculation() {\r
+ function test_grade_item_is_calculated() {\r
$grade_item = new grade_item($this->grade_items[1]);\r
- $this->assertTrue(method_exists($grade_item, 'get_calculation'));\r
- $grade_calculation = $grade_item->get_calculation();\r
-\r
- $this->assertEqual($this->grade_calculations[0]->id, $grade_calculation->id);\r
+ $this->assertTrue(method_exists($grade_item, 'is_calculated'));\r
+ $this->assertTrue($grade_item->is_calculated());\r
}\r
\r
function test_grade_item_set_calculation() {\r
/* $grade_item = new grade_item($this->grade_items[1]);\r
$this->assertTrue(method_exists($grade_item, 'set_calculation'));\r
- $this->assertTrue(method_exists($grade_item, 'get_calculation'));\r
+ $this->assertTrue(method_exists($grade_item, 'is_calculated'));\r
\r
$calculation = '=SUM([unittestgradeitem1], [unittestgradeitem3])';\r
$grade_item->set_calculation($calculation);\r
- $new_calculation = $grade_item->get_calculation();\r
+ $new_calculation = $grade_item->is_calculated();\r
\r
$this->assertEqual($calculation, $new_calculation->calculation);\r
*/ }\r