From 61c33818d5bf6e944ef782f2cce5b560fc8afa5c Mon Sep 17 00:00:00 2001 From: skodak Date: Sat, 23 Jun 2007 16:33:17 +0000 Subject: [PATCH] MDL-10231 merge grade_calculations and grade_items table + calculation improvements MDL-10233 fixed grade_*::fetch() - does not modify $this anymore, we can now use it from normal methods to fetch other objects of the same class. --- backup/backuplib.php | 50 +-- backup/restorelib.php | 34 +- lib/db/install.xml | 26 +- lib/db/upgrade.php | 90 +++--- lib/grade/grade_calculation.php | 263 ---------------- lib/grade/grade_category.php | 27 +- lib/grade/grade_grades.php | 12 +- lib/grade/grade_grades_text.php | 13 +- lib/grade/grade_history.php | 13 +- lib/grade/grade_item.php | 292 ++++++++++++++---- lib/grade/grade_outcome.php | 14 +- lib/grade/grade_scale.php | 13 +- lib/gradelib.php | 5 +- lib/simpletest/fixtures/gradetest.php | 44 +-- .../grade/simpletest/testgradecalculation.php | 120 ------- .../grade/simpletest/testgradeitem.php | 103 ++++-- version.php | 2 +- 17 files changed, 405 insertions(+), 716 deletions(-) delete mode 100644 lib/grade/grade_calculation.php delete mode 100644 lib/simpletest/grade/simpletest/testgradecalculation.php diff --git a/backup/backuplib.php b/backup/backuplib.php index a3aac290a5..885de7b364 100644 --- a/backup/backuplib.php +++ b/backup/backuplib.php @@ -1492,6 +1492,8 @@ fwrite ($bf,full_tag("ITEMNUMBER",5,false,$grade_item->itemnumber)); fwrite ($bf,full_tag("ITEMINFO",5,false,$grade_item->iteminfo)); fwrite ($bf,full_tag("IDNUMBER",5,false,$grade_item->idnumber)); + // use [idnumber] in calculation instead of [#giXXX#] + fwrite ($bf,full_tag("CALCULATION",5,false,$grade_item->get_caclulation())); fwrite ($bf,full_tag("GRADEMAX",5,false,$grade_item->grademax)); fwrite ($bf,full_tag("GRADEMIN",5,false,$grade_item->grademin)); fwrite ($bf,full_tag("SCALEID",5,false,$grade_item->scaleid)); @@ -1504,7 +1506,6 @@ fwrite ($bf,full_tag("LOCKTIME",5,false,$grade_item->locktime)); // back up the other stuff here - $status = backup_gradebook_calculations_info($bf,$preferences,$grade_item->id); $status = backup_gradebook_grades_info($bf,$preferences,$grade_item->id); $status = backup_gradebook_grades_history_info($bf,$preferences,$grade_item->id); $status = backup_gradebook_grades_text_info($bf,$preferences,$grade_item->id); @@ -1553,27 +1554,6 @@ return $status; } - //Backup gradebook_calculations(called from backup_gradebook_item_info - function backup_gradebook_calculations_info($bf,$preferences, $itemid) { - - global $CFG; - - $status = true; - - // find all calculations belonging to this item - if ($calculations = get_records('grade_calculations', 'itemid', $itemid)) { - fwrite ($bf,start_tag("GRADE_CALCULATIONS",5,true)); - foreach ($calculations as $calculation) { - fwrite ($bf,start_tag("GRADE_CALCULATION",6,true)); - fwrite ($bf,full_tag("ID",7,false,$calculation->id)); - fwrite ($bf,full_tag("CALCULATION",7,false,$calculation->calculation)); - fwrite ($bf,full_tag("USERMODIFIED",7,false,$calculation->usermodified)); - fwrite ($bf,end_tag("GRADE_CALCULATION",6,true)); - } - $status = fwrite ($bf,end_tag("GRADE_CALCULATIONS",5,true)); - } - return $status; - } function backup_gradebook_grades_info($bf,$preferences, $itemid) { @@ -1581,19 +1561,19 @@ $status = true; - // find all calculations belonging to this item - if ($raws = get_records('grade_grades', 'itemid', $itemid)) { + // find all grades belonging to this item + if ($grades = get_records('grade_grades', 'itemid', $itemid)) { fwrite ($bf,start_tag("GRADE_GRADES",5,true)); - foreach ($raws as $raw) { + foreach ($grades as $grade) { fwrite ($bf,start_tag("GRADE",6,true)); - fwrite ($bf,full_tag("ID",7,false,$raw->id)); - fwrite ($bf,full_tag("USERID",7,false,$raw->userid)); - fwrite ($bf,full_tag("RAWGRADE",7,false,$raw->rawgrade)); - fwrite ($bf,full_tag("RAWGRADEMAX",7,false,$raw->rawgrademax)); - fwrite ($bf,full_tag("RAWGRADEMIN",7,false,$raw->rawgrademin)); - fwrite ($bf,full_tag("RAWSCALEID",7,false,$raw->rawscaleid)); - fwrite ($bf,full_tag("USERMODIFIED",7,false,$raw->usermodified)); - fwrite ($bf,full_tag("FINALGRADE",7,false,$raw->finalgrade)); + fwrite ($bf,full_tag("ID",7,false,$grade->id)); + fwrite ($bf,full_tag("USERID",7,false,$grade->userid)); + fwrite ($bf,full_tag("RAWGRADE",7,false,$grade->rawgrade)); + fwrite ($bf,full_tag("RAWGRADEMAX",7,false,$grade->rawgrademax)); + fwrite ($bf,full_tag("RAWGRADEMIN",7,false,$grade->rawgrademin)); + fwrite ($bf,full_tag("RAWSCALEID",7,false,$grade->rawscaleid)); + fwrite ($bf,full_tag("USERMODIFIED",7,false,$grade->usermodified)); + fwrite ($bf,full_tag("FINALGRADE",7,false,$grade->finalgrade)); fwrite ($bf,full_tag("HIDDEN",7,false,$final->hidden)); fwrite ($bf,full_tag("LOCKED",7,false,$final->locked)); fwrite ($bf,full_tag("LOCKTIME",7,false,$final->locktime)); @@ -1611,7 +1591,7 @@ $status = true; - // find all calculations belonging to this item + // find all grade texts belonging to this item if ($texts = get_records('grade_grades_text', 'itemid', $itemid)) { fwrite ($bf,start_tag("GRADE_GRADES_TEXT",5,true)); foreach ($texts as $text) { @@ -1635,7 +1615,7 @@ $status = true; - // find all calculations belonging to this item + // find all grade history belonging to this item if ($histories = get_records('grade_history', 'itemid', $itemid)) { fwrite ($bf,start_tag("GRADE_GRADES_HISTORY",5,true)); foreach ($histories as $history) { diff --git a/backup/restorelib.php b/backup/restorelib.php index e3cb459aa5..f9f5c439db 100644 --- a/backup/restorelib.php +++ b/backup/restorelib.php @@ -1445,6 +1445,7 @@ $dbrec->itemnumber = backup_todb($info['GRADE_ITEM']['#']['ITEMNUMBER']['0']['#']); $dbrec->iteminfo = backup_todb($info['GRADE_ITEM']['#']['ITEMINFO']['0']['#']); $dbrec->idnumber = backup_todb($info['GRADE_ITEM']['#']['IDNUMBER']['0']['#']); + $dbrec->calculation = backup_todb($info['GRADE_ITEM']['#']['CALCULATION']['0']['#']); $dbrec->grademax = backup_todb($info['GRADE_ITEM']['#']['GRADEMAX']['0']['#']); $dbrec->grademin = backup_todb($info['GRADE_ITEM']['#']['GRADEMIN']['0']['#']); /// needs to be restored first @@ -1499,7 +1500,7 @@ $itemid = insert_record('grade_items',$dbrec); - /// now, restore grade_calculations, grade_raw, grade_final, grade_text, and grade_history + /// now, restore grade_grades, grade_text, and grade_history if (!empty($info['GRADE_ITEM']['#']['GRADE_GRADES']['0']['#']) && ($grades = $info['GRADE_ITEM']['#']['GRADE_GRADES']['0']['#']['GRADE'])) { //Iterate over items for($i = 0; $i < sizeof($grades); $i++) { @@ -1542,37 +1543,6 @@ } } - /// processing grade_calculations - if (!empty($info['GRADE_ITEM']['#']['GRADE_CALCULATIONS']['0']['#']) && ($calcs = $info['GRADE_ITEM']['#']['GRADE_CALCULATIONS']['0']['#']['GRADE_CALCULATION'])) { - //Iterate over items - for($i = 0; $i < sizeof($calcs); $i++) { - $ite_info = $calcs[$i]; - //traverse_xmlize($ite_info); -//Debug - //print_object ($GLOBALS['traverse_array']); -//Debug - //$GLOBALS['traverse_array']=""; //Debug - $calc->itemid = $itemid; - - $calc->calculation = backup_todb($ite_info['#']['CALCULATION']['0']['#']); - - $modifier = backup_getid($restore->backup_unique_code,"user", backup_todb($ite_info['#']['USERMODIFIED']['0']['#'])); - $calc->usermodified = $modifier->new_id; - - insert_record('grade_calculations', $calc); - - $counter++; - if ($counter % 20 == 0) { - if (!defined('RESTORE_SILENTLY')) { - echo "."; - if ($counter % 400 == 0) { - echo "
"; - } - } - backup_flush(300); - } - } - } /// processing grade_grades_text if (!empty($info['GRADE_ITEM']['#']['GRADE_GRADES_TEXT']['0']['#']) && ($texts = $info['GRADE_ITEM']['#']['GRADE_GRADES_TEXT']['0']['#']['GRADE_TEXT'])) { diff --git a/lib/db/install.xml b/lib/db/install.xml index 841978717b..9d2717c703 100644 --- a/lib/db/install.xml +++ b/lib/db/install.xml @@ -1,5 +1,5 @@ - @@ -1247,8 +1247,9 @@ - - + + + @@ -1273,7 +1274,7 @@ - +
@@ -1293,22 +1294,7 @@
- - - - - - - - - - - - - - -
- +
diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index 7e63cf4844..694bb843a9 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -819,6 +819,7 @@ function xmldb_main_upgrade($oldversion=0) { $table->addFieldInfo('itemnumber', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null); $table->addFieldInfo('iteminfo', XMLDB_TYPE_TEXT, 'medium', null, XMLDB_NOTNULL, null, null, null, null); $table->addFieldInfo('idnumber', XMLDB_TYPE_CHAR, '255', null, null, null, null, null, null); + $table->addFieldInfo('calculation', XMLDB_TYPE_TEXT, 'medium', null, null, null, null, null, null); $table->addFieldInfo('gradetype', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, null, null, '0'); $table->addFieldInfo('grademax', XMLDB_TYPE_NUMBER, '10, 5', null, XMLDB_NOTNULL, null, null, null, '100'); $table->addFieldInfo('grademin', XMLDB_TYPE_NUMBER, '10, 5', null, XMLDB_NOTNULL, null, null, null, '0'); @@ -865,25 +866,6 @@ function xmldb_main_upgrade($oldversion=0) { /// Launch create table for grade_categories $result = $result && create_table($table); - /// Define table grade_calculations to be created - $table = new XMLDBTable('grade_calculations'); - - /// Adding fields to table grade_calculations - $table->addFieldInfo('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null, null); - $table->addFieldInfo('itemid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, null); - $table->addFieldInfo('calculation', XMLDB_TYPE_TEXT, 'medium', null, null, null, null, null, null); - $table->addFieldInfo('timecreated', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null); - $table->addFieldInfo('timemodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null); - $table->addFieldInfo('usermodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, null); - - /// Adding keys to table grade_calculations - $table->addKeyInfo('primary', XMLDB_KEY_PRIMARY, array('id')); - $table->addKeyInfo('itemid', XMLDB_KEY_FOREIGN, array('itemid'), 'grade_items', array('id')); - $table->addKeyInfo('usermodified', XMLDB_KEY_FOREIGN, array('usermodified'), 'user', array('id')); - - /// Launch create table for grade_calculations - $result = $result && create_table($table); - /// Define table grade_grades to be created $table = new XMLDBTable('grade_grades'); @@ -1006,17 +988,6 @@ function xmldb_main_upgrade($oldversion=0) { $result = $result && add_field($table, $field); } - if ($result && $oldversion < 2007042601) { - - /// Changing nullability of field usermodified on table grade_calculations to null - $table = new XMLDBTable('grade_calculations'); - $field = new XMLDBField('usermodified'); - $field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null, 'timemodified'); - - /// Launch change of nullability for field usermodified - $result = $result && change_field_notnull($table, $field); - } - if ($result && $oldversion < 2007042701) { /// Define key categoryid (foreign) to be dropped form grade_categories @@ -1288,25 +1259,6 @@ function xmldb_main_upgrade($oldversion=0) { $result = $result && create_table($table); } - /// Remove the obsoleted unitttests tables - they will be recreated automatically - $tables = array('grade_categories', - 'scale', - 'grade_items', - 'grade_calculations', - 'grade_grades_raw', - 'grade_grades_final', - 'grade_grades_text', - 'grade_outcomes', - 'grade_history'); - - foreach ($tables as $table) { - $table = new XMLDBTable('unittest_'.$table); - if (table_exists($table)) { - drop_table($table); - } - - } - /// Define table grade_import_values to be created $table = new XMLDBTable('grade_import_values'); if (table_exists($table)) { @@ -1362,6 +1314,46 @@ function xmldb_main_upgrade($oldversion=0) { } } + +/// merge calculation formula into grade_item + if ($result && $oldversion < 2007062301) { + + /// Delete obsoleted calculations table - we did not need the data yet + $table = new XMLDBTable('grade_calculations'); + if (table_exists($table)) { + drop_table($table); + } + + /// Define field calculation to be added to grade_items + $table = new XMLDBTable('grade_items'); + $field = new XMLDBField('calculation'); + + if (!field_exists($table, $field)) { + $field->setAttributes(XMLDB_TYPE_TEXT, 'medium', null, null, null, null, null, null, 'idnumber'); + /// Launch add field calculation + $result = $result && add_field($table, $field); + } + + /// Remove the obsoleted unitttests tables - they will be recreated automatically + $tables = array('grade_categories', + 'scale', + 'grade_items', + 'grade_calculations', + 'grade_grades_raw', + 'grade_grades_final', + 'grade_grades_text', + 'grade_outcomes', + 'grade_history'); + + foreach ($tables as $table) { + $table = new XMLDBTable('unittest_'.$table); + if (table_exists($table)) { + drop_table($table); + } + } + + } + return $result; } diff --git a/lib/grade/grade_calculation.php b/lib/grade/grade_calculation.php deleted file mode 100644 index c0c7a7a605..0000000000 --- a/lib/grade/grade_calculation.php +++ /dev/null @@ -1,263 +0,0 @@ -itemid and saves it as $this->grade_item for easy access. - * @return object grade_item. - */ - function load_grade_item() { - if (empty($this->grade_item) && !empty($this->itemid)) { - $this->grade_item = grade_item::fetch('id', $this->itemid); - } - return $this->grade_item; - } - - /** - * Applies the formula represented by this object. The parameteres are taken from final - * grades of grade items in current course only. - * @return boolean false if error - */ - function compute() { - global $CFG; - require_once($CFG->libdir.'/mathslib.php'); - - if (empty($this->id) or empty($this->itemid)) { - debugging('Can not initialize calculation!'); - return false; - } - - // init grade_item - $this->load_grade_item(); - - if ($this->grade_item->is_locked()) { - return true; // no need to recalculate locked items - } - - //get used items - $useditems = $this->dependson(); - - // init maths library - $this->formula = new calc_formula($this->calculation); - - // where to look for final grades? - // this itemid is added so that we use only one query for source and final grades - $gis = implode(',', array_merge($useditems, array($this->itemid))); - - $sql = "SELECT g.* - FROM {$CFG->prefix}grade_grades g, {$CFG->prefix}grade_items gi - WHERE gi.id = g.itemid AND gi.courseid={$this->grade_item->courseid} AND gi.id IN ($gis) - ORDER BY g.userid"; - - $return = true; - - // group the grades by userid and use formula on the group - if ($rs = get_recordset_sql($sql)) { - if ($rs->RecordCount() > 0) { - $prevuser = 0; - $grades = array(); - $final = null; - while ($used = rs_fetch_next_record($rs)) { - if ($used->userid != $prevuser) { - if (!$this->use_formula($prevuser, $grades, $useditems, $final)) { - $return = false; - } - $prevuser = $used->userid; - $grades = array(); - $final = null; - } - if ($used->itemid == $this->grade_item->id) { - $final = new grade_grades($used, false); // fetching from db is not needed - $final->grade_item =& $this->grade_item; - } - $grades['gi'.$used->itemid] = $used->finalgrade; - } - if (!$this->use_formula($prevuser, $grades, $useditems, $final)) { - $return = false; - } - } - } - - //TODO: we could return array of errors here - return $return; - } - - /** - * internal function - does the final grade calculation - */ - function use_formula($userid, $params, $useditems, $final) { - if (empty($userid)) { - return true; - } - - // add missing final grade values - // not graded (null) is counted as 0 - the spreadsheet way - foreach($useditems as $gi) { - if (!array_key_exists('gi'.$gi, $params)) { - $params['gi'.$gi] = 0; - } else { - $params['gi'.$gi] = (float)$params['gi'.$gi]; - } - } - - // can not use own final grade during calculation - unset($params['gi'.$this->grade_item->id]); - - // insert final grade - will be needed later anyway - if (empty($final)) { - $final = new grade_grades(array('itemid'=>$this->grade_item->id, 'userid'=>$userid), false); - $final->insert(); - $final->grade_item =& $this->grade_item; - - } else if ($final->is_locked()) { - // no need to recalculate locked grades - return; - } - - - // do the calculation - $this->formula->set_params($params); - $result = $this->formula->evaluate(); - - // store the result - if ($result === false) { - // error during calculation - if (!is_null($final->finalgrade) or !is_null($final->rawgrade)) { - $final->finalgrade = null; - $final->rawgrade = null; - $final->update(); - } - return false; - - } else { - // normalize - $result = bounded_number($this->grade_item->grademin, $result, $this->grade_item->grademax); - if ($this->grade_item->gradetype == GRADE_TYPE_SCALE) { - $result = round($result+0.00001); // round scales upwards - } - - // store only if final grade changed, remove raw grade because we do not need it - if ($final->finalgrade != $result or !is_null($final->rawgrade)) { - $final->finalgrade = $result; - $final->rawgrade = null; - $final->update(); - } - return true; - } - } - - /** - * Finds out on which other items does this calculation depend - * @return array of grade_item ids this one depends on - */ - function dependson() { - if (preg_match_all('/gi([0-9]+)/i', $this->calculation, $matches)) { - return ($matches[1]); - } else { - return array(); - } - } - - /** - * Finds and returns a grade_calculation object based on 1-3 field values. - * - * @param boolean $static Unless set to true, this method will also set $this object with the returned values. - * @param string $field1 - * @param string $value1 - * @param string $field2 - * @param string $value2 - * @param string $field3 - * @param string $value3 - * @param string $fields - * @return object grade_calculation object or false if none found. - */ - function fetch($field1, $value1, $field2='', $value2='', $field3='', $value3='', $fields="*") { - if ($grade_calculation = get_record('grade_calculations', $field1, $value1, $field2, $value2, $field3, $value3, $fields)) { - if (isset($this) && get_class($this) == 'grade_calculation') { - print_object($this); - foreach ($grade_calculation as $param => $value) { - $this->$param = $value; - } - return $this; - } else { - $grade_calculation = new grade_calculation($grade_calculation); - return $grade_calculation; - } - } else { - return false; - } - } -} -?> diff --git a/lib/grade/grade_category.php b/lib/grade/grade_category.php index f5f7e82f6f..07d0718f82 100644 --- a/lib/grade/grade_category.php +++ b/lib/grade/grade_category.php @@ -153,6 +153,7 @@ class grade_category extends grade_object { /** * Finds and returns a grade_category object based on 1-3 field values. + * @static * * @param string $field1 * @param string $value1 @@ -165,15 +166,9 @@ class grade_category extends grade_object { */ 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) { - $this->$param = $value; - } - return $this; - } else { - $grade_category = new grade_category($grade_category); - return $grade_category; - } + $grade_category = new grade_category($grade_category); + return $grade_category; + } else { return false; } @@ -361,15 +356,15 @@ class grade_category extends grade_object { // find grde items of immediate children (category or grade items) - $dependson = $this->grade_item->dependson(); + $depends_on = $this->grade_item->depends_on(); $items = array(); - foreach($dependson as $dep) { + foreach($depends_on as $dep) { $items[$dep] = grade_item::fetch('id', $dep); } // where to look for final grades - include or grade item too - $gis = implode(',', array_merge($dependson, array($this->grade_item->id))); + $gis = implode(',', array_merge($depends_on, array($this->grade_item->id))); $sql = "SELECT g.* FROM {$CFG->prefix}grade_grades g, {$CFG->prefix}grade_items gi @@ -384,7 +379,7 @@ class grade_category extends grade_object { $final = null; while ($used = rs_fetch_next_record($rs)) { if ($used->userid != $prevuser) { - $this->aggregate_grades($prevuser, $items, $grades, $dependson, $final); + $this->aggregate_grades($prevuser, $items, $grades, $depends_on, $final); $prevuser = $used->userid; $grades = array(); $final = null; @@ -395,7 +390,7 @@ class grade_category extends grade_object { } $grades[$used->itemid] = $used->finalgrade; } - $this->aggregate_grades($prevuser, $items, $grades, $dependson, $final); + $this->aggregate_grades($prevuser, $items, $grades, $depends_on, $final); } } @@ -405,7 +400,7 @@ class grade_category extends grade_object { /** * internal function for category grades aggregation */ - function aggregate_grades($userid, $items, $grades, $dependson, $final) { + function aggregate_grades($userid, $items, $grades, $depends_on, $final) { if (empty($userid)) { //ignore first run return; @@ -480,7 +475,7 @@ class grade_category extends grade_object { break; case GRADE_AGGREGATE_MEAN_ALL: // Arithmetic average of all grade items including even NULLs; NULL grade caunted as minimum - $num = count($dependson); // you can calculate sum from this one if you multiply it with count($this->dependson() ;-) + $num = count($depends_on); // you can calculate sum from this one if you multiply it with count($this->depends_on() ;-) $sum = array_sum($grades); $rawgrade = $sum / $num; break; diff --git a/lib/grade/grade_grades.php b/lib/grade/grade_grades.php index 4362df57ec..7d05b5d5a4 100644 --- a/lib/grade/grade_grades.php +++ b/lib/grade/grade_grades.php @@ -216,6 +216,7 @@ class grade_grades extends grade_object { /** * Finds and returns a grade_grades object based on 1-3 field values. * @static + * * @param string $field1 * @param string $value1 * @param string $field2 @@ -227,16 +228,9 @@ class grade_grades extends grade_object { */ function fetch($field1, $value1, $field2='', $value2='', $field3='', $value3='', $fields="*") { if ($object = get_record('grade_grades', $field1, $value1, $field2, $value2, $field3, $value3, $fields)) { - if (isset($this) && get_class($this) == 'grade_grades') { - foreach ($object as $param => $value) { - $this->$param = $value; - } + $object = new grade_grades($object); + return $object; - return $this; - } else { - $object = new grade_grades($object); - return $object; - } } else { return false; } diff --git a/lib/grade/grade_grades_text.php b/lib/grade/grade_grades_text.php index ca88398b7f..3700232ffe 100644 --- a/lib/grade/grade_grades_text.php +++ b/lib/grade/grade_grades_text.php @@ -92,6 +92,7 @@ class grade_grades_text extends grade_object { /** * Finds and returns a grade_text object based on 1-3 field values. + * @static * * @param boolean $static Unless set to true, this method will also set $this object with the returned values. * @param string $field1 @@ -105,15 +106,9 @@ class grade_grades_text extends grade_object { */ function fetch($field1, $value1, $field2='', $value2='', $field3='', $value3='', $fields="*") { if ($grade_text = get_record('grade_grades_text', $field1, $value1, $field2, $value2, $field3, $value3, $fields)) { - if (isset($this) && get_class($this) == 'grade_grades_text') { - foreach ($grade_text as $param => $value) { - $this->$param = $value; - } - return $this; - } else { - $grade_text = new grade_grades_text($grade_text); - return $grade_text; - } + $grade_text = new grade_grades_text($grade_text); + return $grade_text; + } else { return false; } diff --git a/lib/grade/grade_history.php b/lib/grade/grade_history.php index bb5400da1b..9c8da938af 100644 --- a/lib/grade/grade_history.php +++ b/lib/grade/grade_history.php @@ -80,6 +80,7 @@ class grade_history extends grade_object { /** * Finds and returns a grade_history object based on 1-3 field values. + * @static * * @param string $field1 * @param string $value1 @@ -92,15 +93,9 @@ class grade_history extends grade_object { */ function fetch($field1, $value1, $field2='', $value2='', $field3='', $value3='', $fields="*") { if ($grade_history = get_record('grade_history', $field1, $value1, $field2, $value2, $field3, $value3, $fields)) { - if (isset($this) && get_class($this) == 'grade_history') { - foreach ($grade_history as $param => $value) { - $this->$param = $value; - } - return $this; - } else { - $grade_history = new grade_history($grade_history); - return $grade_history; - } + $grade_history = new grade_history($grade_history); + return $grade_history; + } else { return false; } diff --git a/lib/grade/grade_item.php b/lib/grade/grade_item.php index 141213980c..7b26cd560a 100644 --- a/lib/grade/grade_item.php +++ b/lib/grade/grade_item.php @@ -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', 'scale', 'category', 'outcome'); + var $nonfields = array('table', 'nonfields', 'formula', 'calculation_normalized', 'scale', 'category', 'outcome'); /** * The course this grade_item belongs to. @@ -108,6 +108,23 @@ class grade_item extends grade_object { */ var $idnumber; + /** + * Calculation string used for this item. + * @var string $calculation + */ + var $calculation; + + /** + * Indicates if we already tried to normalize the grade calculation formula. + * This flag helps to minimize db access when broken formulas used in calculation. + * @var boolean + */ + var $calculation_normalized; + /** + * Math evaluation object + */ + var $formula; + /** * The type of grade (0 = none, 1 = value, 2 = scale, 3 = text) * @var int $gradetype @@ -198,12 +215,6 @@ class grade_item extends grade_object { */ var $needsupdate; - /** - * Calculation string used for this item. - * @var string $calculation - */ - var $calculation; - /** * Constructor. Extends the basic functionality defined in grade_object. * @param array $params Can also be a standard object. @@ -263,6 +274,7 @@ class grade_item extends grade_object { $db_item = new grade_item(array('id' => $this->id)); + $calculationdiff = $db_item->calculation != $this->calculation; $gradetypediff = $db_item->gradetype != $this->gradetype; $grademaxdiff = $db_item->grademax != $this->grademax; $grademindiff = $db_item->grademin != $this->grademin; @@ -275,12 +287,14 @@ class grade_item extends grade_object { $needsupdatediff = !$db_item->needsupdate && $this->needsupdate; // force regrading only if setting the flag first time $lockeddiff = !empty($db_item->locked) && empty($this->locked); // force regrading only when unlocking - return ($gradetypediff || $grademaxdiff || $grademindiff || $scaleiddiff || $outcomeiddiff || - $multfactordiff || $plusfactordiff || $deleteddiff || $needsupdatediff || $lockeddiff); + return ($calculationdiff || $gradetypediff || $grademaxdiff || $grademindiff || $scaleiddiff + || $outcomeiddiff || $multfactordiff || $plusfactordiff || $deleteddiff || $needsupdatediff + || $lockeddiff); } /** * Finds and returns a grade_item object based on 1-3 field values. + * @static * * @param string $field1 * @param string $value1 @@ -293,16 +307,9 @@ class grade_item extends grade_object { */ function fetch($field1, $value1, $field2='', $value2='', $field3='', $value3='', $fields="*") { if ($grade_item = get_record('grade_items', $field1, $value1, $field2, $value2, $field3, $value3, $fields)) { - if (isset($this) && get_class($this) == 'grade_item') { - foreach ($grade_item as $param => $value) { - $this->$param = $value; - } + $grade_item = new grade_item($grade_item); + return $grade_item; - return $this; - } else { - $grade_item = new grade_item($grade_item); - return $grade_item; - } } else { return false; } @@ -410,11 +417,11 @@ class grade_item extends grade_object { * @return boolean Locked state */ function is_locked($userid=NULL) { - if (!empty($this->locked)) { + if (!empty($this->locked)) { return true; - } + } - if (!empty($userid)) { + if (!empty($userid)) { $grade = new grade_grades(array('itemid'=>$this->id, 'userid'=>$userid)); $grade->grade_item =& $this; // prevent db fetching of cached grade_item @@ -527,7 +534,7 @@ class grade_item extends grade_object { } if ($this->is_calculated()) { - if ($this->calculation->compute()) { + if ($this->compute()) { $this->needsupdate = false; $this->update(); return true; @@ -775,14 +782,32 @@ class grade_item extends grade_object { /** * Checks if grade calculated. Returns this object's calculation. - * @return mixed $calculation Calculation object if exists, false otherwise. + * @return boolean true if grade item calculated. */ function is_calculated() { - if (is_null($this->calculation)) { - $this->calculation = grade_calculation::fetch('itemid', $this->id); + if (empty($this->calculation)) { + return false; + } + + /* + * The main reason why we use the [#gixxx#] instead of [idnumber] is speed of depends_on(), + * we would have to fetch all course grade items to find out the ids. + * Also if user changes the idnumber the formula does not need to be updated. + */ + + // first detect if we need to update calculation formula from [idnumber] to [#giXXX#] (after backup, etc.) + if (!$this->calculation_normalized and preg_match_all('/\[(?!#gi)(.*?)\]/', $this->calculation, $matches)) { + foreach ($matches[1] as $idnumber) { + if ($grade_item = grade_item::fetch('courseid', $this->courseid, 'idnumber', $idnumber)) { + $this->calculation = str_replace('['.$grade_item->idnumber.']', '[#gi'.$grade_item->id.'#]', $this->calculation); + } + } + $this->update(); // update in db if needed + $this->calculation_normalized = true; + return !empty($this->calculation); } - return !empty($this->calculation); + return true; } /** @@ -791,7 +816,20 @@ class grade_item extends grade_object { */ function get_calculation() { if ($this->is_calculated()) { - return $this->calculation->calculation; + $formula = $this->calculation; + // denormalize formula - convert [#giXX#] to [idnumber] + if (preg_match_all('/\[#gi([0-9]+)#\]/', $formula, $matches)) { + foreach ($matches[1] as $id) { + if ($grade_item = grade_item::fetch('id', $id)) { + if (!empty($grade_item->idnumber)) { + $formula = str_replace('[#gi'.$grade_item->id.'#]', '['.$grade_item->idnumber.']', $formula); + } + } + } + } + + return $formula; + } else { return NULL; } @@ -805,40 +843,29 @@ class grade_item extends grade_object { * @return boolean success */ function set_calculation($formula) { - // refresh cached calculation object - $this->calculation = null; + $formula = trim($formula); - $result = true; + if (empty($formula)) { + $this->calculation = NULL; - if (empty($formula)) { // We are removing this calculation - if (!empty($this->id) and $this->is_calculated()) { - $this->calculation->delete(); - $this->calculation = null; // remove cache + } else { + if (strpos($formula, '=') !== 0) { + $formula = '='.$formula; } - } else { // We are updating or creating the calculation entry in the DB - if ($this->is_calculated()) { - $this->calculation->calculation = $formula; - if (!$this->calculation->update()) { - $this->calculation = null; // remove cache - $result = false; - debugging("Could not save the calculation in the database for this grade_item."); + // normalize formula - we want grade item ids [#giXXX#] instead of [idnumber] + $grade_item = new grade_item(array('courseid'=>$this->courseid), false); + if ($grade_items = $grade_item->fetch_all_using_this()) { + foreach ($grade_items as $grade_item) { + $formula = str_replace('['.$grade_item->idnumber.']', '[#gi'.$grade_item->id.'#]', $formula); } - - } else { - $grade_calculation = new grade_calculation(array('calculation'=>$formula, 'itemid'=>$this->id), false); - if (!$grade_calculation->insert()) { - $this->calculation = null; // remove cache - $result = false; - debugging("Could not save the calculation in the database for this grade_item."); - } - $this->calculation = $grade_calculation; } - } - $this->force_regrading(); + $this->calculation = $formula; + } - return $result; + $this->calculation_normalized = true; + return $this->update(); } /** @@ -959,18 +986,21 @@ class grade_item extends grade_object { * Finds out on which other items does this depend directly when doing calculation or category agregation * @return array of grade_item ids this one depends on */ - function dependson() { + function depends_on() { if ($this->is_locked()) { // locked items do not need to be regraded return array(); } - if ($calculation = $this->is_calculated()) { - return $calculation->dependson(); + if ($this->is_calculated()) { + if (preg_match_all('/\[#gi([0-9]+)#\]/', $this->calculation, $matches)) { + return array_unique($matches[1]); // remove duplicates + } else { + return array(); + } - } else if ($this->itemtype == 'category') { - $grade_category = grade_category::fetch('id', $this->iteminstance); + } else if ($grade_category = $this->load_category()) { $children = $grade_category->get_children(1, 'flat'); if (empty($children)) { @@ -979,15 +1009,13 @@ class grade_item extends grade_object { $result = array(); - $childrentype = get_class(reset($children)); - if ($childrentype == 'grade_category') { - foreach ($children as $id => $category) { - $grade_item = $category->get_grade_item(); - $result[] = $grade_item->id; - } - } elseif ($childrentype == 'grade_item') { - foreach ($children as $id => $grade_item) { + foreach ($children as $id => $child) { + if (get_class($child) == 'grade_category') { + $grade_item = $child->get_grade_item(); $result[] = $grade_item->id; + + } else if (get_class($child) == 'grade_item') { + $result[] = $child->id; } } @@ -1086,5 +1114,137 @@ class grade_item extends grade_object { return false; } } + + /** + * Calculates final grade values useing the formula in calculation property. + * The parameteres are taken from final grades of grade items in current course only. + * @return boolean false if error + */ + function compute() { + global $CFG; + + if (!$this->is_calculated()) { + return false; + } + + require_once($CFG->libdir.'/mathslib.php'); + + if ($this->is_locked()) { + return true; // no need to recalculate locked items + } + + // get used items + $useditems = $this->depends_on(); + + // prepare formula and init maths library + $formula = preg_replace('/\[#(gi[0-9]+)#\]/', '\1', $this->calculation); + $this->formula = new calc_formula($formula); + + // where to look for final grades? + // this itemid is added so that we use only one query for source and final grades + $gis = implode(',', array_merge($useditems, array($this->id))); + + $sql = "SELECT g.* + FROM {$CFG->prefix}grade_grades g, {$CFG->prefix}grade_items gi + WHERE gi.id = g.itemid AND gi.courseid={$this->courseid} AND gi.id IN ($gis) + ORDER BY g.userid"; + + $return = true; + + // group the grades by userid and use formula on the group + if ($rs = get_recordset_sql($sql)) { + if ($rs->RecordCount() > 0) { + $prevuser = 0; + $grades = array(); + $final = null; + while ($used = rs_fetch_next_record($rs)) { + if ($used->userid != $prevuser) { + if (!$this->use_formula($prevuser, $grades, $useditems, $final)) { + $return = false; + } + $prevuser = $used->userid; + $grades = array(); + $final = null; + } + if ($used->itemid == $this->id) { + $final = new grade_grades($used, false); // fetching from db is not needed + $final->grade_item =& $this; + } + $grades['gi'.$used->itemid] = $used->finalgrade; + } + if (!$this->use_formula($prevuser, $grades, $useditems, $final)) { + $return = false; + } + } + } + + //TODO: we could return array of errors here + return $return; + } + + /** + * internal function - does the final grade calculation + */ + function use_formula($userid, $params, $useditems, $final) { + if (empty($userid)) { + return true; + } + + // add missing final grade values + // not graded (null) is counted as 0 - the spreadsheet way + foreach($useditems as $gi) { + if (!array_key_exists('gi'.$gi, $params)) { + $params['gi'.$gi] = 0; + } else { + $params['gi'.$gi] = (float)$params['gi'.$gi]; + } + } + + // can not use own final grade during calculation + unset($params['gi'.$this->id]); + + // insert final grade - will be needed later anyway + if (empty($final)) { + $final = new grade_grades(array('itemid'=>$this->id, 'userid'=>$userid), false); + $final->insert(); + $final->grade_item =& $this; + + } else if ($final->is_locked()) { + // no need to recalculate locked grades + return; + } + + + // do the calculation + $this->formula->set_params($params); + $result = $this->formula->evaluate(); + + // store the result + if ($result === false) { + // error during calculation + if (!is_null($final->finalgrade) or !is_null($final->rawgrade)) { + $final->finalgrade = null; + $final->rawgrade = null; + $final->update(); + } + return false; + + } else { + // normalize + $result = bounded_number($this->grademin, $result, $this->grademax); + if ($this->gradetype == GRADE_TYPE_SCALE) { + $result = round($result+0.00001); // round scales upwards + } + + // store only if final grade changed, remove raw grade because we do not need it + if ($final->finalgrade != $result or !is_null($final->rawgrade)) { + $final->finalgrade = $result; + $final->rawgrade = null; + $final->update(); + } + return true; + } + } + } ?> diff --git a/lib/grade/grade_outcome.php b/lib/grade/grade_outcome.php index 6e90a0e8ff..c61e680e84 100644 --- a/lib/grade/grade_outcome.php +++ b/lib/grade/grade_outcome.php @@ -93,6 +93,7 @@ class grade_outcome extends grade_object { /** * Finds and returns a grade_outcome object based on 1-3 field values. + * @static * * @param boolean $static Unless set to true, this method will also set $this object with the returned values. * @param string $field1 @@ -106,16 +107,9 @@ class grade_outcome extends grade_object { */ function fetch($field1, $value1, $field2='', $value2='', $field3='', $value3='', $fields="*") { if ($grade_outcome = get_record('grade_outcomes', $field1, $value1, $field2, $value2, $field3, $value3, $fields)) { - if (isset($this) && get_class($this) == 'grade_outcome') { - print_object($this); - foreach ($grade_outcome as $param => $value) { - $this->$param = $value; - } - return $this; - } else { - $grade_outcome = new grade_outcome($grade_outcome); - return $grade_outcome; - } + $grade_outcome = new grade_outcome($grade_outcome); + return $grade_outcome; + } else { return false; } diff --git a/lib/grade/grade_scale.php b/lib/grade/grade_scale.php index 38264bc492..82a1d22717 100644 --- a/lib/grade/grade_scale.php +++ b/lib/grade/grade_scale.php @@ -74,6 +74,7 @@ class grade_scale extends grade_object { /** * Finds and returns a grade_scale object based on 1-3 field values. + * @static * * @param string $field1 * @param string $value1 @@ -86,15 +87,9 @@ class grade_scale extends grade_object { */ function fetch($field1, $value1, $field2='', $value2='', $field3='', $value3='', $fields="*") { if ($grade_scale = get_record('scale', $field1, $value1, $field2, $value2, $field3, $value3, $fields)) { - if (isset($this) && get_class($this) == 'grade_scale') { - foreach ($grade_scale as $param => $value) { - $this->$param = $value; - } - return $this; - } else { - $grade_scale = new grade_scale($grade_scale); - return $grade_scale; - } + $grade_scale = new grade_scale($grade_scale); + return $grade_scale; + } else { return false; } diff --git a/lib/gradelib.php b/lib/gradelib.php index 13ebbc867e..6302ecd104 100644 --- a/lib/gradelib.php +++ b/lib/gradelib.php @@ -59,7 +59,6 @@ define('GRADE_UPDATE_ITEM_LOCKED', 4); require_once($CFG->libdir . '/grade/grade_category.php'); require_once($CFG->libdir . '/grade/grade_item.php'); -require_once($CFG->libdir . '/grade/grade_calculation.php'); require_once($CFG->libdir . '/grade/grade_grades.php'); require_once($CFG->libdir . '/grade/grade_scale.php'); require_once($CFG->libdir . '/grade/grade_outcome.php'); @@ -404,10 +403,10 @@ function grade_update_final_grades($courseid, $regradeall=false) { } //do we have all data for finalizing of this item? - $dependson = $grade_item->dependson(); + $depends_on = $grade_item->depends_on(); $doupdate = true; - foreach ($dependson as $did) { + foreach ($depends_on as $did) { if (!in_array($did, $finalids)) { $doupdate = false; } diff --git a/lib/simpletest/fixtures/gradetest.php b/lib/simpletest/fixtures/gradetest.php index ca32968102..553cb1aecb 100644 --- a/lib/simpletest/fixtures/gradetest.php +++ b/lib/simpletest/fixtures/gradetest.php @@ -54,7 +54,6 @@ class grade_test extends UnitTestCase { var $tables = array('grade_categories', 'scale', 'grade_items', - 'grade_calculations', 'grade_grades', 'grade_grades_text', 'grade_outcomes', @@ -62,7 +61,6 @@ class grade_test extends UnitTestCase { var $grade_items = array(); var $grade_categories = array(); - var $grade_calculations = array(); var $grade_grades = array(); var $grade_grades_text = array(); var $grade_outcomes = array(); @@ -108,6 +106,7 @@ class grade_test extends UnitTestCase { $table->addFieldInfo('itemnumber', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null); $table->addFieldInfo('iteminfo', XMLDB_TYPE_TEXT, 'medium', null, XMLDB_NOTNULL, null, null, null, null); $table->addFieldInfo('idnumber', XMLDB_TYPE_CHAR, '255', null, null, null, null, null, null); + $table->addFieldInfo('calculation', XMLDB_TYPE_TEXT, 'medium', null, null, null, null, null, null); $table->addFieldInfo('gradetype', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, null, null, '1'); $table->addFieldInfo('grademax', XMLDB_TYPE_NUMBER, '10, 5', null, XMLDB_NOTNULL, null, null, null, '100'); $table->addFieldInfo('grademin', XMLDB_TYPE_NUMBER, '10, 5', null, XMLDB_NOTNULL, null, null, null, '0'); @@ -158,28 +157,6 @@ class grade_test extends UnitTestCase { $result = $result && create_table($table, true, false); } - /// Define table grade_calculations to be created - $table = new XMLDBTable('grade_calculations'); - - if ($result && !table_exists($table)) { - - /// Adding fields to table grade_calculations - $table->addFieldInfo('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null, null); - $table->addFieldInfo('itemid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, null); - $table->addFieldInfo('calculation', XMLDB_TYPE_TEXT, 'medium', null, null, null, null, null, null); - $table->addFieldInfo('timecreated', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null); - $table->addFieldInfo('timemodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null); - $table->addFieldInfo('usermodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null); - - /// Adding keys to table grade_calculations - $table->addKeyInfo('primary', XMLDB_KEY_PRIMARY, array('id')); - $table->addKeyInfo('itemid', XMLDB_KEY_FOREIGN, array('itemid'), 'grade_items', array('id')); - $table->addKeyInfo('usermodified', XMLDB_KEY_FOREIGN, array('usermodified'), 'user', array('id')); - - /// Launch create table for grade_calculations - $result = $result && create_table($table, true, false); - } - /// Define table grade_grades_text to be created $table = new XMLDBTable('grade_grades_text'); @@ -493,6 +470,7 @@ class grade_test extends UnitTestCase { $grade_item->grademin = 30; $grade_item->grademax = 110; $grade_item->itemnumber = 1; + $grade_item->idnumber = 'item id 0'; $grade_item->iteminfo = 'Grade item used for unit testing'; $grade_item->timecreated = mktime(); $grade_item->timemodified = mktime(); @@ -510,6 +488,7 @@ class grade_test extends UnitTestCase { $grade_item->itemname = 'unittestgradeitem2'; $grade_item->itemtype = 'import'; $grade_item->itemmodule = 'assignment'; + $grade_item->calculation = '= [#gi'.$this->grade_items[0]->id.'#] + 30 + [item id 0] - [item id 0]'; $grade_item->gradetype = GRADE_TYPE_VALUE; $grade_item->iteminstance = 2; $grade_item->itemnumber = null; @@ -698,23 +677,6 @@ class grade_test extends UnitTestCase { } - /** - * Load grade_calculation data into the database, and adds the corresponding objects to this class' variable. - */ - function load_grade_calculations() { - // Calculation for grade_item 2 - $grade_calculation = new stdClass(); - $grade_calculation->itemid = $this->grade_items[1]->id; - $grade_calculation->calculation = '= gi'.$this->grade_items[0]->id.' + 30 '; - $grade_calculation->timecreated = mktime(); - $grade_calculation->timemodified = mktime(); - - if ($grade_calculation->id = insert_record('grade_calculations', $grade_calculation)) { - $this->grade_calculations[0] = $grade_calculation; - $this->grade_items[1]->calculation = $grade_calculation; - } - } - /** * Load grade_grades data into the database, and adds the corresponding objects to this class' variable. */ diff --git a/lib/simpletest/grade/simpletest/testgradecalculation.php b/lib/simpletest/grade/simpletest/testgradecalculation.php deleted file mode 100644 index 5832d8ffee..0000000000 --- a/lib/simpletest/grade/simpletest/testgradecalculation.php +++ /dev/null @@ -1,120 +0,0 @@ -libdir.'/simpletest/fixtures/gradetest.php'); - -class grade_calculation_test extends grade_test { - - function test_grade_calculation_construct() { - $params = new stdClass(); - - $params->itemid = $this->grade_items[0]->id; - $params->calculation = '=MEAN(1, 2)'; - - $grade_calculation = new grade_calculation($params, false); - $this->assertEqual($params->itemid, $grade_calculation->itemid); - $this->assertEqual($params->calculation, $grade_calculation->calculation); - } - - function test_grade_calculation_insert() { - $grade_calculation = new grade_calculation(); - $this->assertTrue(method_exists($grade_calculation, 'insert')); - - $grade_calculation->itemid = $this->grade_items[0]->id; - $grade_calculation->calculation = '=MEAN(1, 2)'; - - $grade_calculation->insert(); - - $last_grade_calculation = end($this->grade_calculations); - - $this->assertEqual($grade_calculation->id, $last_grade_calculation->id + 1); - $this->assertFalse(empty($grade_calculation->timecreated)); - $this->assertFalse(empty($grade_calculation->timemodified)); - - } - - function test_grade_calculation_update() { - $grade_calculation = new grade_calculation($this->grade_calculations[0]); - $this->assertTrue(method_exists($grade_calculation, 'update')); - $grade_calculation->calculation = '=MEAN(1, 2)'; - $this->assertTrue($grade_calculation->update()); - $calculation = get_field('grade_calculations', 'calculation', 'id', $this->grade_calculations[0]->id); - $this->assertEqual($grade_calculation->calculation, $calculation); - } - - function test_grade_calculation_delete() { - $grade_calculation = new grade_calculation($this->grade_calculations[0]); - $this->assertTrue(method_exists($grade_calculation, 'delete')); - - $this->assertTrue($grade_calculation->delete()); - $this->assertFalse(get_record('grade_calculations', 'id', $grade_calculation->id)); - } - - function test_grade_calculation_fetch() { - $grade_calculation = new grade_calculation(); - $this->assertTrue(method_exists($grade_calculation, 'fetch')); - - $grade_calculation = grade_calculation::fetch('id', $this->grade_calculations[0]->id); - $this->assertEqual($this->grade_calculations[0]->id, $grade_calculation->id); - $this->assertEqual($this->grade_calculations[0]->calculation, $grade_calculation->calculation); - } - - function test_grade_calculation_compute() { - $grade_calculation = new grade_calculation($this->grade_calculations[0]); - $this->assertTrue(method_exists($grade_calculation, 'compute')); - - $grade_item = $grade_calculation->load_grade_item(); - - $grade_grades = grade_grades::fetch('id', $this->grade_grades[3]->id); - $grade_grades->delete(); - $grade_grades = grade_grades::fetch('id', $this->grade_grades[4]->id); - $grade_grades->delete(); - $grade_grades = grade_grades::fetch('id', $this->grade_grades[5]->id); - $grade_grades->delete(); - - $grade_calculation->compute(); - - $grade_grades = grade_grades::fetch('userid', $this->grade_grades[3]->userid, 'itemid', $this->grade_grades[3]->itemid); - $this->assertEqual($this->grade_grades[3]->finalgrade, $grade_grades->finalgrade); - $grade_grades = grade_grades::fetch('userid', $this->grade_grades[4]->userid, 'itemid', $this->grade_grades[4]->itemid); - $this->assertEqual($this->grade_grades[4]->finalgrade, $grade_grades->finalgrade); - $grade_grades = grade_grades::fetch('userid', $this->grade_grades[5]->userid, 'itemid', $this->grade_grades[5]->itemid); - $this->assertEqual($this->grade_grades[5]->finalgrade, $grade_grades->finalgrade); - } - -} -?> diff --git a/lib/simpletest/grade/simpletest/testgradeitem.php b/lib/simpletest/grade/simpletest/testgradeitem.php index f8cc1e6155..f61f68ad1c 100755 --- a/lib/simpletest/grade/simpletest/testgradeitem.php +++ b/lib/simpletest/grade/simpletest/testgradeitem.php @@ -232,24 +232,6 @@ class grade_item_test extends grade_test { $this->assertEqual($this->grade_grades[0]->finalgrade, $final_grade->finalgrade); } - function test_grade_item_is_calculated() { - $grade_item = new grade_item($this->grade_items[1]); - $this->assertTrue(method_exists($grade_item, 'is_calculated')); - $this->assertTrue($grade_item->is_calculated()); - } - - function test_grade_item_set_calculation() { -/* $grade_item = new grade_item($this->grade_items[1]); - $this->assertTrue(method_exists($grade_item, 'set_calculation')); - $this->assertTrue(method_exists($grade_item, 'is_calculated')); - - $calculation = '=SUM([unittestgradeitem1], [unittestgradeitem3])'; - $grade_item->set_calculation($calculation); - $new_calculation = $grade_item->is_calculated(); - - $this->assertEqual($calculation, $new_calculation->calculation); -*/ } - function test_grade_item_get_category() { $grade_item = new grade_item($this->grade_items[0]); $this->assertTrue(method_exists($grade_item, 'get_category')); @@ -330,6 +312,9 @@ class grade_item_test extends grade_test { $this->assertEqual(round(1.6), round($grade_item->adjust_grade($grade_raw->rawgrade, $grade_raw->grademin, $grade_raw->grademax))); } + /** + * Test locking of grade items + */ function test_grade_item_set_locked() { $grade_item = new grade_item($this->grade_items[0]); $this->assertTrue(method_exists($grade_item, 'set_locked')); @@ -362,11 +347,28 @@ class grade_item_test extends grade_test { $this->assertTrue($grade_item->is_locked(1)); } - function test_grade_item_dependson() { - $grade_item = new grade_item($this->grade_items[0]); - //TODO - } + function test_grade_item_depends_on() { + $grade_item = new grade_item($this->grade_items[1]); + // calculated grade dependency + $deps = $grade_item->depends_on(); + sort($deps, SORT_NUMERIC); // for comparison + $this->assertEqual(array($this->grade_items[0]->id), $deps); + + // simulate depends on returns none when locked + $grade_item->locked = time(); + $grade_item->update(); + $deps = $grade_item->depends_on(); + sort($deps, SORT_NUMERIC); // for comparison + $this->assertEqual(array(), $deps); + + // category dependency + $grade_item = new grade_item($this->grade_items[3]); + $deps = $grade_item->depends_on(); + sort($deps, SORT_NUMERIC); // for comparison + $res = array($this->grade_items[4]->id, $this->grade_items[5]->id); + $this->assertEqual($res, $deps); + } /* function test_grade_item_toggle_hiding() { @@ -383,12 +385,65 @@ class grade_item_test extends grade_test { $this->assertEqual(3, $grade_item->toggle_hiding(true)); $this->assertTrue($grade_item->hidden); $this->assertTrue($grade_item->grade_grades[1]->hidden); - $this->assertTrue($grade_item->grade_grades[2]->hidden); + $this->assertTrue($grade_item->grade_gra + des[2]->hidden); $this->assertTrue($grade_item->grade_grades[3]->hidden); } */ - function test_float_keys() { + function test_grade_item_is_calculated() { + $grade_item = new grade_item($this->grade_items[1]); + $this->assertTrue(method_exists($grade_item, 'is_calculated')); + $grade_itemsource = new grade_item($this->grade_items[0]); + $normalizedformula = str_replace('['.$grade_itemsource->idnumber.']', '[#gi'.$grade_itemsource->id.'#]', $this->grade_items[1]->calculation); + + $this->assertTrue($grade_item->is_calculated()); + $this->assertEqual($normalizedformula, $grade_item->calculation); + } + + function test_grade_item_set_calculation() { + $grade_item = new grade_item($this->grade_items[1]); + $this->assertTrue(method_exists($grade_item, 'set_calculation')); + $grade_itemsource = new grade_item($this->grade_items[0]); + + $grade_item->set_calculation('=['.$grade_itemsource->idnumber.']'); + + $this->assertTrue(!empty($grade_item->needsupdate)); + $this->assertEqual('=[#gi'.$grade_itemsource->id.'#]', $grade_item->calculation); } + + function test_grade_item_get_calculation() { + $grade_item = new grade_item($this->grade_items[1]); + $this->assertTrue(method_exists($grade_item, 'get_calculation')); + $grade_itemsource = new grade_item($this->grade_items[0]); + + $denormalizedformula = str_replace('[#gi'.$grade_itemsource->id.'#]', '['.$grade_itemsource->idnumber.']', $this->grade_items[1]->calculation); + + $formula = $grade_item->get_calculation(); + $this->assertTrue(!empty($grade_item->needsupdate)); + $this->assertEqual($denormalizedformula, $formula); + } + + function test_grade_item_compute() { + $grade_item = new grade_item($this->grade_items[1]); + $this->assertTrue(method_exists($grade_item, 'compute')); + + $grade_grades = grade_grades::fetch('id', $this->grade_grades[3]->id); + $grade_grades->delete(); + $grade_grades = grade_grades::fetch('id', $this->grade_grades[4]->id); + $grade_grades->delete(); + $grade_grades = grade_grades::fetch('id', $this->grade_grades[5]->id); + $grade_grades->delete(); + + $grade_item->compute(); + + $grade_grades = grade_grades::fetch('userid', $this->grade_grades[3]->userid, 'itemid', $this->grade_grades[3]->itemid); + $this->assertEqual($this->grade_grades[3]->finalgrade, $grade_grades->finalgrade); + $grade_grades = grade_grades::fetch('userid', $this->grade_grades[4]->userid, 'itemid', $this->grade_grades[4]->itemid); + $this->assertEqual($this->grade_grades[4]->finalgrade, $grade_grades->finalgrade); + $grade_grades = grade_grades::fetch('userid', $this->grade_grades[5]->userid, 'itemid', $this->grade_grades[5]->itemid); + $this->assertEqual($this->grade_grades[5]->finalgrade, $grade_grades->finalgrade); + } + } ?> diff --git a/version.php b/version.php index b005215a53..5c17d34428 100644 --- a/version.php +++ b/version.php @@ -6,7 +6,7 @@ // This is compared against the values stored in the database to determine // whether upgrades should be performed (see lib/db/*.php) - $version = 2007062008; // YYYYMMDD = date + $version = 2007062301; // YYYYMMDD = date // XY = increments within a single day $release = '1.9 dev'; // Human-friendly version name -- 2.39.5