MDL-9506 Finished grade_item::adjust_grade, which now also adjusts scale grades correctly. Added grade_scale object to wrap around the current non-normalized scale DB table. When/if we upgrade to grade_scales and grade_scale_items DB tables, changing the grade_scale class will be easy.
This commit is contained in:
@@ -36,7 +36,7 @@ class grade_grades_raw extends grade_object {
|
||||
* Array of class variables that are not part of the DB table fields
|
||||
* @var array $nonfields
|
||||
*/
|
||||
var $nonfields = array('table', 'nonfields');
|
||||
var $nonfields = array('table', 'nonfields', 'scale');
|
||||
|
||||
/**
|
||||
* The id of the grade_item this raw grade belongs to.
|
||||
@@ -79,7 +79,13 @@ class grade_grades_raw extends grade_object {
|
||||
* @var int $scaleid
|
||||
*/
|
||||
var $scaleid;
|
||||
|
||||
|
||||
/**
|
||||
* A grade_scale object (referenced by $this->scaleid).
|
||||
* @var object $scale
|
||||
*/
|
||||
var $scale;
|
||||
|
||||
/**
|
||||
* The userid of the person who last modified this grade.
|
||||
* @var int $usermodified
|
||||
@@ -93,6 +99,10 @@ class grade_grades_raw extends grade_object {
|
||||
*/
|
||||
function grade_grades_raw($params=NULL, $fetch=true) {
|
||||
$this->grade_object($params, $fetch);
|
||||
if (!empty($this->scaleid)) {
|
||||
$this->scale = new grade_scale(array('id' => $this->scaleid));
|
||||
$this->scale->load_items();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -138,6 +148,11 @@ class grade_grades_raw extends grade_object {
|
||||
global $USER;
|
||||
$oldgrade = $this->gradevalue;
|
||||
$this->gradevalue = $newgrade;
|
||||
|
||||
// Update this scaleid if it has changed (use the largest integer (most recent))
|
||||
if ($this->scale->id != $this->scaleid) {
|
||||
$this->scaleid = max($this->scale->id, $this->scaleid);
|
||||
}
|
||||
|
||||
$result = parent::update();
|
||||
|
||||
|
||||
+50
-16
@@ -40,7 +40,7 @@ class grade_item extends grade_object {
|
||||
* Array of class variables that are not part of the DB table fields
|
||||
* @var array $nonfields
|
||||
*/
|
||||
var $nonfields = array('table', 'nonfields', 'calculation', 'grade_grades_raw');
|
||||
var $nonfields = array('table', 'nonfields', 'calculation', 'grade_grades_raw', 'scale');
|
||||
|
||||
/**
|
||||
* The course this grade_item belongs to.
|
||||
@@ -109,11 +109,17 @@ class grade_item extends grade_object {
|
||||
var $grademin;
|
||||
|
||||
/**
|
||||
* The scale this grade is based on, if applicable.
|
||||
* id of the scale, if this grade is based on a scale.
|
||||
* @var int $scaleid
|
||||
*/
|
||||
var $scaleid;
|
||||
|
||||
/**
|
||||
* A grade_scale object (referenced by $this->scaleid).
|
||||
* @var object $scale
|
||||
*/
|
||||
var $scale;
|
||||
|
||||
|
||||
/**
|
||||
* The Outcome this grade is associated with, if applicable.
|
||||
* @var object $outcome
|
||||
@@ -180,6 +186,19 @@ class grade_item extends grade_object {
|
||||
*/
|
||||
var $grade_grades_final = array();
|
||||
|
||||
/**
|
||||
* Constructor. Extends the basic functionality defined in grade_object.
|
||||
* @param array $params Can also be a standard object.
|
||||
* @param boolean $fetch Wether or not to fetch the corresponding row from the DB.
|
||||
*/
|
||||
function grade_item($params=NULL, $fetch=true) {
|
||||
$this->grade_object($params, $fetch);
|
||||
if (!empty($this->scaleid)) {
|
||||
$this->scale = new grade_scale(array('id' => $this->scaleid));
|
||||
$this->scale->load_items();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns a grade_item object based on 1-3 field values.
|
||||
*
|
||||
@@ -433,33 +452,48 @@ class grade_item extends grade_object {
|
||||
* @return mixed
|
||||
*/
|
||||
function adjust_grade($grade_raw, $gradevalue=NULL) {
|
||||
$raw_offset = 0;
|
||||
$item_offset = 0;
|
||||
|
||||
if (!empty($grade_raw->gradevalue)) { // Dealing with numerical 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;
|
||||
|
||||
} else { // Something's wrong, the raw grade has no value!?
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// Compute factor from adjusted scales
|
||||
// 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;
|
||||
$gradevalue += $item_offset;
|
||||
|
||||
// Apply factors
|
||||
$gradevalue *= $this->multfactor;
|
||||
$gradevalue += $this->plusfactor;
|
||||
// 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
|
||||
$gradevalue *= $this->multfactor;
|
||||
$gradevalue += $this->plusfactor;
|
||||
} elseif (!empty($grade_raw->gradescale)) {
|
||||
$gradevalue = (int) round($gradevalue);
|
||||
}
|
||||
|
||||
return $gradevalue;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ require_once($CFG->libdir . '/grade/grade_item.php');
|
||||
require_once($CFG->libdir . '/grade/grade_calculation.php');
|
||||
require_once($CFG->libdir . '/grade/grade_grades_raw.php');
|
||||
require_once($CFG->libdir . '/grade/grade_grades_final.php');
|
||||
require_once($CFG->libdir . '/grade/grade_scale.php');
|
||||
|
||||
/**
|
||||
* Extracts from the gradebook all the grade items attached to the calling object.
|
||||
|
||||
@@ -41,11 +41,14 @@ require_once($CFG->libdir . '/dmllib.php');
|
||||
|
||||
/**
|
||||
* A cleanup of the tables is a good idea before we start, in case the last unit test
|
||||
* crashed before running its tearDown method.
|
||||
* crashed before running its tearDown method. Be careful because ANY record matching
|
||||
* 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%"');
|
||||
|
||||
class gradelib_test extends UnitTestCase {
|
||||
|
||||
@@ -62,7 +65,8 @@ class gradelib_test extends UnitTestCase {
|
||||
'grade_grades_final',
|
||||
'grade_grades_text',
|
||||
'grade_outcomes',
|
||||
'grade_history');
|
||||
'grade_history',
|
||||
'scale');
|
||||
|
||||
var $grade_items = array();
|
||||
var $grade_categories = array();
|
||||
@@ -72,6 +76,7 @@ class gradelib_test extends UnitTestCase {
|
||||
var $grade_grades_text = array();
|
||||
var $grade_outcomes = array();
|
||||
var $grade_history = array();
|
||||
var $scale = array();
|
||||
|
||||
var $courseid = 1;
|
||||
var $userid = 1;
|
||||
@@ -472,6 +477,36 @@ class gradelib_test extends UnitTestCase {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Load scale data into the database, and adds the corresponding objects to this class' variable.
|
||||
*/
|
||||
function load_scale() {
|
||||
$scale = new stdClass();
|
||||
|
||||
$scale->name = 'unittestscale1';
|
||||
$scale->courseid = $this->courseid;
|
||||
$scale->userid = $this->userid;
|
||||
$scale->scale = 'Way off topic, Not very helpful, Fairly neutral, Fairly helpful, Supportive, Some good information, Perfect answer!';
|
||||
$scale->description = 'This scale defines some of qualities that make posts helpful within the Moodle help forums.\n Your feedback will help others see how their posts are being received.';
|
||||
$scale->timemodified = mktime();
|
||||
|
||||
if ($scale->id = insert_record('scale', $scale)) {
|
||||
$this->scale[] = $scale;
|
||||
}
|
||||
|
||||
$scale = new stdClass();
|
||||
|
||||
$scale->name = 'unittestscale2';
|
||||
$scale->courseid = $this->courseid;
|
||||
$scale->userid = $this->userid;
|
||||
$scale->scale = 'Distinction, Very Good, Good, Pass, Fail';
|
||||
$scale->description = 'This scale is used to mark standard assignments.';
|
||||
$scale->timemodified = mktime();
|
||||
|
||||
if ($scale->id = insert_record('scale', $scale)) {
|
||||
$this->scale[] = $scale;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* TESTS BEGIN HERE
|
||||
*/
|
||||
@@ -537,7 +572,7 @@ class gradelib_test extends UnitTestCase {
|
||||
$params->iteminstance = 4;
|
||||
$params->iteminfo = 'Grade item used for unit testing';
|
||||
|
||||
$grade_item = new grade_item($params);
|
||||
$grade_item = new grade_item($params, false);
|
||||
|
||||
$this->assertEqual($params->courseid, $grade_item->courseid);
|
||||
$this->assertEqual($params->categoryid, $grade_item->categoryid);
|
||||
@@ -752,7 +787,6 @@ class gradelib_test extends UnitTestCase {
|
||||
// Try a larger maximum grade
|
||||
$grade_item->grademax = 150;
|
||||
$grade_item->grademin = 0;
|
||||
|
||||
$this->assertEqual(60, $grade_item->adjust_grade($grade_raw));
|
||||
|
||||
// Try larger minimum grade
|
||||
@@ -791,6 +825,22 @@ class gradelib_test extends UnitTestCase {
|
||||
$this->assertEqual(round(1.6), round($grade_item->adjust_grade($grade_raw)));
|
||||
}
|
||||
|
||||
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;
|
||||
$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));
|
||||
$this->assertEqual('Very Good', $grade_item->scale->scale_items[1]);
|
||||
|
||||
// Test grade_item::adjust_scale
|
||||
$this->assertEqual(3, $grade_item->adjust_grade($grade_raw));
|
||||
$grade_raw->gradescale = 6;
|
||||
$this->assertEqual(4, $grade_item->adjust_grade($grade_raw));
|
||||
}
|
||||
|
||||
// GRADE_CATEGORY OBJECT
|
||||
|
||||
function test_grade_category_construct() {
|
||||
@@ -815,6 +865,46 @@ class gradelib_test extends UnitTestCase {
|
||||
|
||||
// GRADE_CALCULATION OBJECT
|
||||
|
||||
// SCALE OBJECT
|
||||
|
||||
function test_scale_constructor() {
|
||||
$params = new stdClass();
|
||||
|
||||
$params->name = 'unittestscale3';
|
||||
$params->courseid = $this->courseid;
|
||||
$params->userid = $this->userid;
|
||||
$params->scale = 'Distinction, Very Good, Good, Pass, Fail';
|
||||
$params->description = 'This scale is used to mark standard assignments.';
|
||||
$params->timemodified = mktime();
|
||||
|
||||
$scale = new grade_scale($params, false);
|
||||
|
||||
$this->assertEqual($params->name, $scale->name);
|
||||
$this->assertEqual($params->scale, $scale->scale);
|
||||
$this->assertEqual($params->description, $scale->description);
|
||||
|
||||
}
|
||||
|
||||
function test_scale_load_items() {
|
||||
$scale = new grade_scale($this->scale[0]);
|
||||
$this->assertTrue(method_exists($scale, 'load_items'));
|
||||
|
||||
$scale->load_items();
|
||||
$this->assertEqual(7, count($scale->scale_items));
|
||||
$this->assertEqual('Fairly neutral', $scale->scale_items[2]);
|
||||
}
|
||||
|
||||
function test_scale_compact_items() {
|
||||
$scale = new grade_scale($this->scale[0]);
|
||||
$this->assertTrue(method_exists($scale, 'compact_items'));
|
||||
|
||||
$scale->load_items();
|
||||
$scale->scale = null;
|
||||
$scale->compact_items();
|
||||
|
||||
// The original string and the new string may have differences in whitespace around the delimiter, and that's OK
|
||||
$this->assertEqual(preg_replace('/\s*,\s*/', ',', $this->scale[0]->scale), $scale->scale);
|
||||
}
|
||||
// SCENARIOS: define use cases here by defining tests and implementing code until the tests pass.
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user