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:
+121
-30
@@ -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
@@ -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
|
||||
|
||||
@@ -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,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
|
||||
|
||||
Reference in New Issue
Block a user