MDL-9506 Issue 1: adjusting a gradeitem's value would result in a grade_final object with gradevalue assigned, even when a gradescale should have been assigned. Issue 2: double grade_final entries when calling grade_item->load_final() after grade_item->generate_final(). Issue 3: Calling grade_item->update_final_grade() without first calling grade_item->generate_final() would result in fatal error. generate_final() is now called if the raw and final arrays' sizes don't match.

This commit is contained in:
nicolasconnault 2007-05-08 02:20:26 +00:00
parent 3504e07b1d
commit 0aa32279b7
13 changed files with 190 additions and 52 deletions

View File

@ -70,9 +70,10 @@ class grade_calculation extends grade_object {
/**
* Applies the formula represented by this object to the value given, and returns the result.
* @param float $oldvalue
* @param string $valuetype Either 'gradevalue' or 'gradescale'
* @return float result
*/
function compute($oldvalue) {
function compute($oldvalue, $valuetype = 'gradevalue') {
return $oldvalue; // TODO implement computation using parser
}

View File

@ -210,7 +210,55 @@ class grade_category extends grade_object {
return $result;
}
/**
* Generates and saves raw_grades, based on this category's immediate children, then uses the
* associated grade_item to generate matching final grades. These immediate children must first have their own
* raw and final grades, which means that ultimately we must get grade_items as children. The category's aggregation
* method is used to generate these raw grades, which can then be used by the category's associated grade_item
* to apply calculations to and generate final grades.
*/
function generate_grades() {
// Check that the children have final grades. If not, call their generate_raw_grades method (recursion)
if (empty($this->children)) {
$this->children = $this->get_children(1, 'flat');
}
$category_raw_grades = array();
$aggregated_grades = array();
foreach ($this->children as $child) {
if (get_class($child) == 'grade_item') {
$category_raw_grades[$child->id] = $child->load_final();
} elseif ($get_class($child) == 'grade_category') {
$category_raw_grades[$child->id] = $child->load_final();
if (empty($category_raw_grades)) {
$category_raw_grades[$child->id] = $child->generate_grades();
}
}
}
if (empty($category_raw_grades)) {
return null;
} else {
$aggregated_grades = $this->aggregate_grades($category_raw_grades);
foreach ($aggregated_grades as $raw_grade) {
$raw_grade->insert();
}
$this->grade_item->generate_final();
}
}
/**
* Given an array of arrays of grade objects (raw or final), uses this category's aggregation method to
* compute and return a single array of grade_raw objects with the aggregated gradevalue.
* @param array $raw_grade_sets
* @return array Raw grade objects
*/
function aggregate_grades($raw_grade_sets) {
}
/**
* Looks at a path string (e.g. /2/45/56) and returns the depth level represented by this path (in this example, 3).
* If no string is given, it looks at the obect's path and assigns the resulting depth to its $depth variable.

View File

@ -59,9 +59,9 @@ class grade_grades_raw extends grade_object {
/**
* The scale value of this raw grade, if such was provided by the module.
* @var int $scalevalue
* @var int $gradescale
*/
var $scalevalue;
var $gradescale;
/**
* The maximum allowable grade when this grade was created.

View File

@ -328,12 +328,47 @@ class grade_item extends grade_object {
*/
function load_final() {
$grade_final_array = get_records('grade_grades_final', 'itemid', $this->id);
if (empty($grade_final_array)) {
$this->generate_final();
$grade_final_array = get_records('grade_grades_final', 'itemid', $this->id);
}
if (empty($grade_final_array)) {
return false;
}
foreach ($grade_final_array as $f) {
$this->grade_grades_final[$f->userid] = new grade_grades_final($f);
}
return $this->grade_grades_final;
}
/**
* Once the raw_grades are imported or entered, this method uses the grade_item's calculation and rules to
* generate final grade entries in the DB.
* @return array final grade objects (grade_grades_final).
*/
function generate_final() {
if (empty($this->grade_grades_raw)) {
$this->load_raw();
}
$success = true;
foreach ($this->grade_grades_raw as $raw_grade) {
$final_grade = new grade_grades_final();
$final_grade->gradevalue = $this->adjust_grade($raw_grade, null, 'gradevalue');
$final_grade->gradescale = $this->adjust_grade($raw_grade, null, 'gradescale');
$final_grade->itemid = $this->id;
$final_grade->userid = $raw_grade->userid;
$success = $success & $final_grade->insert();
$this->grade_grades_final[$final_grade->userid] = $final_grade;
}
return $success;
}
/**
* 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)
@ -522,19 +557,36 @@ class grade_item extends grade_object {
$grade_raw_array = $this->grade_grades_raw;
}
// The following code assumes that there is a grade_final object in DB for every
// grade_raw object. This assumption depends on the correct creation of grade_final entries.
// This also assumes that the two arrays $this->grade_grades_raw and its final counterpart are
// indexed by userid, not sequentially or by grade_id
if (count($this->grade_grades_final) != count($this->grade_grades_raw)) {
$this->generate_final();
}
foreach ($grade_raw_array as $userid => $raw) {
$newgradevalue = $raw->gradevalue;
// the value could be gradevalue or gradescale
$valuetype = null;
if (!empty($raw->gradevalue)) {
$valuetype = 'gradevalue';
} elseif (!empty($raw->gradescale)) {
$valuetype = 'gradescale';
}
$newgradevalue = $raw->$valuetype;
if (!empty($this->calculation)) {
$this->upgrade_calculation_to_object();
$newgradevalue = $this->calculation->compute($raw->gradevalue);
$newgradevalue = $this->calculation->compute($raw->$valuetype, $valuetype);
}
$final = $this->grade_grades_final[$userid];
$final->gradevalue = $this->adjust_grade($raw, $newgradevalue);
$final->$valuetype = $this->adjust_grade($raw, $newgradevalue, $valuetype);
if ($final->update($newgradevalue)) {
if ($final->update()) {
$count++;
} else {
return false;
@ -558,19 +610,21 @@ class grade_item extends grade_object {
* Given a float grade value or integer grade scale, applies a number of adjustment based on
* grade_item variables and returns the result.
* @param object $grade_raw The raw object to compare with this grade_item's rules
* @param mixed $gradevalue The new gradevalue (after calculations are performed)
* @param mixed $gradevalue The new gradevalue (after calculations are performed).
* If null, the raw_grade's gradevalue or gradescale will be used.
* @param string $valuetype Either 'gradevalue' or 'gradescale'
* @return mixed
*/
function adjust_grade($grade_raw, $gradevalue=NULL) {
function adjust_grade($grade_raw, $gradevalue=NULL, $valuetype='gradevalue') {
$raw_offset = 0;
$item_offset = 0;
if (!empty($grade_raw->gradevalue)) { // Dealing with numerical grade
if ($valuetype == 'gradevalue') { // Dealing with numerical grade
if (empty($gradevalue)) {
$gradevalue = $grade_raw->gradevalue;
}
} elseif(!empty($grade_raw->gradescale)) { // Dealing with a scale value
} elseif($valuetype == 'gradescale') { // Dealing with a scale value
if (empty($gradevalue)) {
$gradevalue = $grade_raw->gradescale;
}
@ -601,11 +655,11 @@ class grade_item extends grade_object {
$gradevalue = $factor * $diff + $this->grademin;
// Apply rounding or factors, depending on whether it's a scale or value
if (!empty($grade_raw->gradevalue)) {
if ($valuetype == 'gradevalue') {
// Apply other grade_item factors
$gradevalue *= $this->multfactor;
$gradevalue += $this->plusfactor;
} elseif (!empty($grade_raw->gradescale)) {
} elseif ($valuetype == 'gradescale') {
$gradevalue = (int) round($gradevalue);
}

View File

@ -61,7 +61,6 @@ class grade_calculation_test extends gradelib_test {
$this->assertEqual($grade_calculation->id, $last_grade_calculation->id + 1);
$this->assertFalse(empty($grade_calculation->timecreated));
$this->assertFalse(empty($grade_calculation->timemodified));
$this->grade_calculations[] = $grade_calculation;
}

View File

@ -45,8 +45,6 @@ class grade_category_test extends gradelib_test {
$grade_category = new grade_category($params, false);
$grade_category->insert();
$this->grade_categories[] = $grade_category;
$this->grade_items[] = $grade_category->grade_item;
$this->assertEqual($params->courseid, $grade_category->courseid);
$this->assertEqual($params->fullname, $grade_category->fullname);
$this->assertEqual(1, $grade_category->depth);
@ -58,8 +56,6 @@ class grade_category_test extends gradelib_test {
$params->fullname = 'unittestcategory5';
$grade_category = new grade_category($params, false);
$grade_category->insert();
$this->grade_categories[] = $grade_category;
$this->grade_items[] = $grade_category->grade_item;
$this->assertEqual(2, $grade_category->depth);
$this->assertEqual("$parentpath/$grade_category->id", $grade_category->path);
$parentpath = $grade_category->path;
@ -69,8 +65,6 @@ class grade_category_test extends gradelib_test {
$params->fullname = 'unittestcategory6';
$grade_category = new grade_category($params, false);
$grade_category->insert();
$this->grade_categories[] = $grade_category;
$this->grade_items[] = $grade_category->grade_item;
$this->assertEqual(3, $grade_category->depth);
$this->assertEqual("$parentpath/$grade_category->id", $grade_category->path);
}
@ -90,12 +84,14 @@ class grade_category_test extends gradelib_test {
$grade_category->insert();
$last_grade_category = end($this->grade_categories);
$this->assertFalse(empty($grade_category->grade_item));
$this->assertEqual($grade_category->id, $grade_category->grade_item->iteminstance);
$this->assertEqual('category', $grade_category->grade_item->itemtype);
$this->assertEqual($grade_category->id, $last_grade_category->id + 1);
$this->assertTrue(!empty($grade_category->timecreated));
$this->assertTrue(!empty($grade_category->timemodified));
$this->grade_categories[] = $grade_category;
$this->grade_items[] = $grade_category->grade_item;
$this->assertFalse(empty($grade_category->timecreated));
$this->assertFalse(empty($grade_category->timemodified));
}
function test_grade_category_update() {

View File

@ -63,8 +63,6 @@ class grade_final_test extends gradelib_test {
$this->assertEqual($grade_grades_final->id, $last_grade_grades_final->id + 1);
$this->assertFalse(empty($grade_grades_final->timecreated));
$this->assertFalse(empty($grade_grades_final->timemodified));
$this->grade_grades_final[] = $grade_grades_final;
}
function test_grade_grades_final_update() {

View File

@ -69,8 +69,6 @@ class grade_history_test extends gradelib_test {
$this->assertEqual($grade_history->id, $last_grade_history->id + 1);
$this->assertFalse(empty($grade_history->timecreated));
$this->assertFalse(empty($grade_history->timemodified));
$this->grade_history[] = $grade_history;
}
function test_grade_history_update() {

View File

@ -40,11 +40,10 @@ class grade_item_test extends gradelib_test {
$params = new stdClass();
$params->courseid = $this->courseid;
$params->categoryid = $this->grade_categories[0]->id;
$params->categoryid = $this->grade_categories[1]->id;
$params->itemname = 'unittestgradeitem4';
$params->itemtype = 'mod';
$params->itemmodule = 'database';
$params->iteminstance = 4;
$params->iteminfo = 'Grade item used for unit testing';
$grade_item = new grade_item($params, false);
@ -59,11 +58,10 @@ class grade_item_test extends gradelib_test {
$this->assertTrue(method_exists($grade_item, 'insert'));
$grade_item->courseid = $this->courseid;
$grade_item->categoryid = $this->grade_categories[0]->id;
$grade_item->categoryid = $this->grade_categories[1]->id;
$grade_item->itemname = 'unittestgradeitem4';
$grade_item->itemtype = 'mod';
$grade_item->itemmodule = 'quiz';
$grade_item->iteminstance = 1;
$grade_item->iteminfo = 'Grade item used for unit testing';
$grade_item->insert();
@ -71,7 +69,6 @@ class grade_item_test extends gradelib_test {
$last_grade_item = end($this->grade_items);
$this->assertEqual($grade_item->id, $last_grade_item->id + 1);
$this->grade_items[] = $grade_item;
}
function test_grade_item_delete() {
@ -191,7 +188,6 @@ class grade_item_test extends gradelib_test {
$calculation = 'SUM([unittestgradeitem1], [unittestgradeitem3])';
$grade_item->set_calculation($calculation);
$new_calculation = $grade_item->get_calculation();
$this->grade_calculations[] = $new_calculation;
$this->assertEqual($calculation, $new_calculation->calculation);
}
@ -301,21 +297,33 @@ class grade_item_test extends gradelib_test {
}
function test_grade_item_adjust_scale_grade() {
// Load raw grade and its scale
$grade_raw = new grade_grades_raw(array('scaleid' => $this->scale[0]->id));
$grade_raw->gradescale = 4;
$grade_raw->load_scale();
$this->assertEqual('Fairly neutral', $grade_raw->scale->scale_items[2]);
// Load grade item and its scale
$grade_item = new grade_item(array('scaleid' => $this->scale[1]->id));
$grade_item = new grade_item(array('scaleid' => $this->scale[1]->id), false);
$grade_item->insert();
$grade_item->load_scale();
$this->assertEqual('Very Good', $grade_item->scale->scale_items[1]);
// Load raw grade and its scale
$grade_raw = new grade_grades_raw(array('scaleid' => $this->scale[0]->id), false);
$grade_raw->gradescale = 4;
$grade_raw->itemid = $grade_item->id;
$grade_raw->userid = 1;
$grade_raw->insert();
$grade_raw->load_scale();
$this->assertEqual('Fairly neutral', $grade_raw->scale->scale_items[2]);
// Test grade_item::adjust_scale
$this->assertEqual(3, $grade_item->adjust_grade($grade_raw));
$this->assertEqual(3, $grade_item->adjust_grade($grade_raw, null, 'gradescale'));
$grade_raw->gradescale = 6;
$this->assertEqual(4, $grade_item->adjust_grade($grade_raw));
$this->assertEqual(4, $grade_item->adjust_grade($grade_raw, null, 'gradescale'));
// Check that the final grades have the correct values now
$grade_item->load_raw();
$grade_item->update_final_grade();
$this->assertFalse(empty($grade_item->grade_grades_final));
$this->assertEqual($grade_item->id, $grade_item->grade_grades_final[1]->itemid);
$this->assertEqual(3, $grade_item->grade_grades_final[1]->gradescale);
$this->assertEqual(1, $grade_item->grade_grades_final[1]->userid);
}
function test_grade_item_toggle_locking() {
@ -353,5 +361,48 @@ class grade_item_test extends gradelib_test {
$this->assertTrue($grade_item->grade_grades_final[2]->hidden);
$this->assertTrue($grade_item->grade_grades_final[3]->hidden);
}
function test_grade_item_generate_final() {
$grade_item = new grade_item();
$grade_item->courseid = $this->courseid;
$grade_item->categoryid = $this->grade_categories[1]->id;
$grade_item->itemname = 'unittestgradeitem4';
$grade_item->itemtype = 'mod';
$grade_item->itemmodule = 'quiz';
$grade_item->iteminfo = 'Grade item used for unit testing';
$grade_item->insert();
$grade_grades_raw = new grade_grades_raw();
$grade_grades_raw->itemid = $grade_item->id;
$grade_grades_raw->userid = 1;
$grade_grades_raw->gradevalue = 88;
$grade_grades_raw->grademax = 110;
$grade_grades_raw->grademin = 18;
$grade_grades_raw->insert();
$grade_grades_raw = new grade_grades_raw();
$grade_grades_raw->itemid = $grade_item->id;
$grade_grades_raw->userid = 2;
$grade_grades_raw->gradevalue = 68;
$grade_grades_raw->grademax = 110;
$grade_grades_raw->grademin = 18;
$grade_grades_raw->insert();
$grade_grades_raw = new grade_grades_raw();
$grade_grades_raw->itemid = $grade_item->id;
$grade_grades_raw->userid = 3;
$grade_grades_raw->gradevalue = 81;
$grade_grades_raw->grademax = 110;
$grade_grades_raw->grademin = 18;
$grade_grades_raw->insert();
$grade_item->load_raw();
$this->assertEqual(3, count($grade_item->grade_grades_raw));
$grade_item->generate_final();
$this->assertEqual(3, count($grade_item->grade_grades_final));
}
}
?>

View File

@ -61,7 +61,6 @@ class grade_outcome_test extends gradelib_test {
$this->assertEqual($grade_outcome->id, $last_grade_outcome->id + 1);
$this->assertFalse(empty($grade_outcome->timecreated));
$this->assertFalse(empty($grade_outcome->timemodified));
$this->grade_outcomes[] = $grade_outcome;
}
function test_grade_outcome_update() {

View File

@ -67,8 +67,6 @@ class grade_raw_test extends gradelib_test {
$this->assertEqual($grade_grades_raw->id, $last_grade_grades_raw->id + 1);
$this->assertFalse(empty($grade_grades_raw->timecreated));
$this->assertFalse(empty($grade_grades_raw->timemodified));
$this->grade_grades_raw[] = $grade_grades_raw;
}
function test_grade_grades_raw_update() {

View File

@ -71,8 +71,6 @@ class grade_scale_test extends gradelib_test {
$this->assertEqual($grade_scale->id, $last_grade_scale->id + 1);
$this->assertTrue(!empty($grade_scale->timecreated));
$this->assertTrue(!empty($grade_scale->timemodified));
$this->scale[] = $grade_scale;
}
function test_grade_scale_update() {

View File

@ -73,8 +73,6 @@ class grade_text_test extends gradelib_test {
$this->assertFalse(empty($grade_grades_text->timecreated));
$this->assertFalse(empty($grade_grades_text->timemodified));
$this->assertEqual($USER->id, $grade_grades_text->usermodified);
$this->grade_grades_text[] = $grade_grades_text;
}
function test_grade_grades_text_update() {