* @var int $parent
*/
var $parent;
-
+
/**
* The number of parents this category has.
* @var int $depth
* @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) {
* 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();
* @return array Array of child objects (grade_category and grade_item).
*/
function get_children($depth=1, $arraytype='nested') {
- $children = array();
+ $children_array = array();
+
+ // Set up $depth for recursion
+ $newdepth = $depth;
+ if ($depth > 1) {
+ $newdepth--;
+ }
+
+ $childrentype = $this->get_childrentype();
- if ($depth == 1) {
- if (!empty($this->children)) {
- return $this->children;
+ 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 {
- $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;
+ 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);
+ }
+ }
- $this->children = $children;
+ 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;
}
- } elseif ($depth > 1) {
- // TODO implement
- } elseif ($depth == 0) {
- // TODO implement
}
- return $children;
+ return get_class($children[0]);
}
}
}
}
-
/**
* 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
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
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');
* 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 {
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;
+ }
}
/**
$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';
$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';
$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);
$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);
}
/**
function test_grade_category_get_children() {
$category = new grade_category($this->grade_categories[0]);
- $this->assertTrue(method_exists($category, 'get_children'));
-
- $this->assertEqual(3, count($category->get_children()));
- $this->assertEqual(5, count($category->get_children(0)));
+ $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));
- $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