MDL-9506 Upgraded grade_item::adjust_grade with Darlene's more elegant formula. Completed grade_category::get_children($depth, $arraytype). Added 4 constants to help that method. Added and passed all unit tests (111 now).

This commit is contained in:
nicolasconnault
2007-05-03 07:10:22 +00:00
parent a907c54043
commit 27f95e9b47
4 changed files with 180 additions and 59 deletions
+121 -30
View File
@@ -49,7 +49,7 @@ class grade_category extends grade_object {
* @var int $parent
*/
var $parent;
/**
* The number of parents this category has.
* @var int $depth
@@ -148,8 +148,7 @@ class grade_category extends grade_object {
* @param string $fields
* @return object grade_category object or false if none found.
*/
function fetch($field1, $value1, $field2='', $value2='', $field3='', $value3='', $fields="*")
{
function fetch($field1, $value1, $field2='', $value2='', $field3='', $value3='', $fields="*") {
if ($grade_category = get_record('grade_categories', $field1, $value1, $field2, $value2, $field3, $value3, $fields)) {
if (isset($this) && get_class($this) == 'grade_category') {
foreach ($grade_category as $param => $value) {
@@ -169,7 +168,7 @@ class grade_category extends grade_object {
* In addition to the normal insert() defined in grade_object, this method sets the depth
* and path for this object, and update the record accordingly. The reason why this must
* be done here instead of in the constructor, is that they both need to know the record's
* id number, which only gets created at insertion time.
* id number, which only gets created at insertion time.
*/
function insert() {
$result = parent::insert();
@@ -212,33 +211,125 @@ class grade_category extends grade_object {
* @return array Array of child objects (grade_category and grade_item).
*/
function get_children($depth=1, $arraytype='nested') {
$children = array();
if ($depth == 1) {
if (!empty($this->children)) {
return $this->children;
} else {
$cat = new grade_category();
$cat->parent = $this->id;
$children = $cat->fetch_all_using_this();
$item = new grade_item();
$item->categoryid = $this->id;
$item_children = $item->fetch_all_using_this();
if (!empty($children)) {
$children = array_merge($children, $item_children);
} else {
$children = $item_children;
}
$this->children = $children;
}
} elseif ($depth > 1) {
// TODO implement
} elseif ($depth == 0) {
// TODO implement
$children_array = array();
// Set up $depth for recursion
$newdepth = $depth;
if ($depth > 1) {
$newdepth--;
}
return $children;
$childrentype = $this->get_childrentype();
if ($childrentype == 'grade_item') {
$children = get_records('grade_items', 'categoryid', $this->id, 'id');
// No need to proceed with recursion
$children_array = $this->children_to_array($children, $arraytype, 'grade_item');
$this->children = $this->children_to_array($children, 'flat', 'grade_item');
} elseif ($childrentype == 'grade_category') {
$children = get_records('grade_categories', 'parent', $this->id, 'id');
if ($depth == 1) {
$children_array = $this->children_to_array($children, $arraytype, 'grade_category');
$this->children = $this->children_to_array($children, 'flat', 'grade_category');
} else {
foreach ($children as $id => $child) {
$cat = new grade_category($child, false);
if ($cat->has_children()) {
if ($arraytype == 'nested') {
$children_array[] = array('object' => $cat, 'children' => $cat->get_children($newdepth, $arraytype));
} else {
$children_array[] = $cat;
$cat_children = $cat->get_children($newdepth, $arraytype);
foreach ($cat_children as $id => $cat_child) {
$children_array[] = new grade_category($cat_child, false);
}
}
} else {
if ($arraytype == 'nested') {
$children_array[] = array('object' => $cat);
} else {
$children_array[] = $cat;
}
}
}
}
} else {
return null;
}
return $children_array;
}
/**
* Given an array of stdClass children of a certain $object_type, returns a flat or nested
* array of these children, ready for appending to a tree built by get_children.
* @static
* @param array $children
* @param string $arraytype
* @param string $object_type
* @return array
*/
function children_to_array($children, $arraytype='nested', $object_type='grade_item') {
$children_array = array();
foreach ($children as $id => $child) {
if ($arraytype == 'nested') {
$children_array[] = array('object' => new $object_type($child, false));
} else {
$children_array[] = new $object_type($child);
}
}
return $children_array;
}
/**
* Returns true if this category has any child grade_category or grade_item.
* @return int number of direct children, or false if none found.
*/
function has_children() {
return count_records('grade_categories', 'parent', $this->id) + count_records('grade_items', 'categoryid', $this->id);
}
/**
* This method checks whether an existing child exists for this
* category. If the new child is of a different type, the method will return false (not allowed).
* Otherwise it will return true.
* @param object $child This must be a complete object, not a stdClass
* @return boolean Success or failure
*/
function can_add_child($child) {
if ($this->has_children()) {
if (get_class($child) != $this->get_childrentype()) {
return false;
} else {
return true;
}
} else {
return true;
}
}
/**
* Check the type of the first child of this category, to see whether it is a
* grade_category or a grade_item, and returns that type as a string (get_class).
* @return string
*/
function get_childrentype() {
$children = $this->children;
if (empty($this->children)) {
$count_item_children = count_records('grade_items', 'categoryid', $this->id);
$count_cat_children = count_records('grade_categories', 'parent', $this->id);
if ($count_item_children > 0) {
return 'grade_item';
} elseif ($count_cat_children > 0) {
return 'grade_category';
} else {
return null;
}
}
return get_class($children[0]);
}
}
+10 -16
View File
@@ -227,7 +227,6 @@ class grade_item extends grade_object {
}
}
/**
* Returns the raw values for this grade item (as imported by module or other source).
* @param int $userid Optional: to retrieve a single raw grade
@@ -458,33 +457,28 @@ class grade_item extends grade_object {
if (empty($gradevalue)) {
$gradevalue = $grade_raw->gradevalue;
}
// Adjust both scales to 0-? and store offsets
$raw_offset = -(0 - $grade_raw->grademin);
$item_offset = -(0 - $this->grademin);
$raw_adjusted_max = $grade_raw->grademax - $raw_offset;
$item_adjusted_max = $this->grademax - $item_offset;
} elseif(!empty($grade_raw->gradescale)) { // Dealing with a scale value
if (empty($gradevalue)) {
$gradevalue = $grade_raw->gradescale;
}
$raw_adjusted_max = count($grade_raw->scale->scale_items) - 1;
$item_adjusted_max = count($this->scale->scale_items) - 1;
$grade_raw->grademax = count($grade_raw->scale->scale_items) - 1;
$this->grademax = count($this->scale->scale_items) - 1;
$grade_raw->grademin = 0;
$this->grademin = 0;
} else { // Something's wrong, the raw grade has no value!?
return false;
}
// Compute factor from grademax of adjusted scales
$factor = ($item_adjusted_max) / ($raw_adjusted_max);
// Multiply adjusted grade value (using source offset) by factor
$gradevalue = ($gradevalue - $raw_offset) * $factor;
/**
* Darlene's formula
*/
$factor = ($gradevalue - $grade_raw->grademin) / ($grade_raw->grademax - $grade_raw->grademin);
$diff = $this->grademax - $this->grademin;
$gradevalue = $factor * $diff + $this->grademin;
// Add target offset to resulting grade value
$gradevalue += $item_offset;
// Apply rounding or factors, depending on whether it's a scale or value
if (!empty($grade_raw->gradevalue)) {
// Apply other grade_item factors
+4
View File
@@ -36,6 +36,10 @@ define('GRADE_AGGREGATE_MEAN', 0);
define('GRADE_AGGREGATE_MEDIAN', 1);
define('GRADE_AGGREGATE_SUM', 2);
define('GRADE_AGGREGATE_MODE', 3);
define('GRADE_CHILDTYPE_ITEM', 0);
define('GRADE_CHILDTYPE_CAT', 1);
define('GRADE_ITEM', 0); // Used to compare class names with CHILDTYPE values
define('GRADE_CATEGORY', 1); // Used to compare class names with CHILDTYPE values
require_once($CFG->libdir . '/grade/grade_category.php');
require_once($CFG->libdir . '/grade/grade_item.php');
+45 -13
View File
@@ -45,16 +45,14 @@ require_once($CFG->libdir . '/dmllib.php');
* this search (%unittest%) will be deleted! Maybe a good idea to switch this off in
* production environment.
*/
/*
delete_records_select('grade_categories', 'fullname LIKE "%unittest%"');
delete_records_select('grade_items', 'itemname LIKE "%unittest%"');
delete_records_select('grade_calculation', 'calculation LIKE "%unittest%"');
delete_records_select('scale', 'name LIKE "%unittest%"');
*/
/**
* Here is a brief explanation of the test data set up in these unit tests.
* category1 => array(grade_item1, grade_item3, category2 => array(grade_item2))
* category1 => array(category2 => array(grade_item1, grade_item2), category3 => array(grade_item3))
* 3 users for 3 grade_items
*/
class gradelib_test extends UnitTestCase {
@@ -151,6 +149,22 @@ class gradelib_test extends UnitTestCase {
if ($grade_category->id = insert_record('grade_categories', $grade_category)) {
$this->grade_categories[] = $grade_category;
}
$grade_category = new stdClass();
$grade_category->fullname = 'unittestcategory3';
$grade_category->courseid = $this->courseid;
$grade_category->aggregation = GRADE_AGGREGATE_MODE;
$grade_category->keephigh = 100;
$grade_category->droplow = 10;
$grade_category->hidden = 0;
$grade_category->parent = $this->grade_categories[0]->id;
$grade_category->timecreated = mktime();
$grade_category->timemodified = mktime();
if ($grade_category->id = insert_record('grade_categories', $grade_category)) {
$this->grade_categories[] = $grade_category;
}
}
/**
@@ -160,7 +174,7 @@ class gradelib_test extends UnitTestCase {
$grade_item = new stdClass();
$grade_item->courseid = $this->courseid;
$grade_item->categoryid = $this->grade_categories[0]->id;
$grade_item->categoryid = $this->grade_categories[1]->id;
$grade_item->itemname = 'unittestgradeitem1';
$grade_item->itemtype = 'mod';
$grade_item->itemmodule = 'quiz';
@@ -196,7 +210,7 @@ class gradelib_test extends UnitTestCase {
$grade_item = new stdClass();
$grade_item->courseid = $this->courseid;
$grade_item->categoryid = $this->grade_categories[0]->id;
$grade_item->categoryid = $this->grade_categories[2]->id;
$grade_item->itemname = 'unittestgradeitem3';
$grade_item->itemtype = 'mod';
$grade_item->itemmodule = 'forum';
@@ -569,7 +583,7 @@ class gradelib_test extends UnitTestCase {
$grade_category->timecreated = mktime();
$grade_category->timemodified = mktime();
$grade_category->id = grade_create_category($this->courseid, 'unittestcategory2', $this->grade_items, GRADE_AGGREGATE_MEAN);
$grade_category->id = grade_create_category($this->courseid, 'unittestcategory4', $this->grade_items, GRADE_AGGREGATE_MEAN);
$last_grade_category = end($this->grade_categories);
$this->assertEqual($grade_category->id, $last_grade_category->id + 1);
@@ -750,7 +764,7 @@ class gradelib_test extends UnitTestCase {
$this->assertTrue(method_exists($grade_item, 'get_category'));
$category = $grade_item->get_category();
$this->assertEqual($this->grade_categories[0]->fullname, $category->fullname);
$this->assertEqual($this->grade_categories[1]->fullname, $category->fullname);
}
/**
@@ -955,13 +969,31 @@ class gradelib_test extends UnitTestCase {
function test_grade_category_get_children() {
$category = new grade_category($this->grade_categories[0]);
$this->assertTrue(method_exists($category, 'get_children'));
$children_array = $category->get_children(0);
$this->assertTrue(is_array($children_array));
$this->assertTrue(!empty($children_array[0]));
$this->assertTrue(!empty($children_array[0]['object']));
$this->assertTrue(!empty($children_array[0]['children']));
$this->assertEqual($this->grade_categories[1]->id, $children_array[0]['object']->id);
$this->assertEqual($this->grade_categories[2]->id, $children_array[1]['object']->id);
$this->assertEqual($this->grade_items[0]->id, $children_array[0]['children'][0]['object']->id);
$this->assertEqual($this->grade_items[1]->id, $children_array[0]['children'][1]['object']->id);
$this->assertEqual($this->grade_items[2]->id, $children_array[1]['children'][0]['object']->id);
$children_array = $category->get_children(0, 'flat');
$this->assertEqual(5, count($children_array));
$this->assertEqual(3, count($category->get_children()));
$this->assertEqual(5, count($category->get_children(0)));
$category = new grade_category($this->grade_categories[1]);
$this->assertEqual(1, count($category->get_children()));
$children_array = $category->get_children(1, 'flat');
$this->assertEqual(2, count($children_array));
}
function test_grade_category_children_to_array() {
$children = get_records('grade_items', 'categoryid', $this->grade_categories[1]->id);
$children_array = grade_category::children_to_array($children, 'nested', 'grade_item');
$this->assertTrue(is_array($children_array));
$this->assertTrue(isset($children_array[0]));
$this->assertTrue(isset($children_array[0]['object']));
$this->assertEqual($this->grade_items[0]->id, $children_array[0]['object']->id);
}
// GRADE_CALCULATION OBJECT