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.
This commit is contained in:
skodak 2007-06-23 16:33:17 +00:00
parent d0c92410ef
commit 61c33818d5
17 changed files with 405 additions and 716 deletions

View File

@ -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) {

View File

@ -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 "<br />";
}
}
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'])) {

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="lib/db" VERSION="20070619" COMMENT="XMLDB file for core Moodle tables"
<XMLDB PATH="lib/db" VERSION="20070623" COMMENT="XMLDB file for core Moodle tables"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
>
@ -1247,8 +1247,9 @@
<FIELD NAME="iteminstance" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" ENUM="false" COMMENT="id of the item module" PREVIOUS="itemmodule" NEXT="itemnumber"/>
<FIELD NAME="itemnumber" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" ENUM="false" COMMENT="Can be used to distinguish multiple grades for an activity" PREVIOUS="iteminstance" NEXT="iteminfo"/>
<FIELD NAME="iteminfo" TYPE="text" LENGTH="medium" NOTNULL="true" SEQUENCE="false" ENUM="false" COMMENT="Info and notes about this item XXX" PREVIOUS="itemnumber" NEXT="idnumber"/>
<FIELD NAME="idnumber" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" ENUM="false" COMMENT="Arbitrary idnumber provided by the module responsible" PREVIOUS="iteminfo" NEXT="gradetype"/>
<FIELD NAME="gradetype" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="1" SEQUENCE="false" ENUM="false" COMMENT="0 = none, 1 = value, 2 = scale, 3 = text" PREVIOUS="idnumber" NEXT="grademax"/>
<FIELD NAME="idnumber" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" ENUM="false" COMMENT="Arbitrary idnumber provided by the module responsible" PREVIOUS="iteminfo" NEXT="calculation"/>
<FIELD NAME="calculation" TYPE="text" LENGTH="medium" NOTNULL="false" SEQUENCE="false" ENUM="false" COMMENT="Formula describing how to derive this grade from other items, referring to them using giXXX where XXX is grade item id ... eg something like: =sin(square([#gi20#])) + [#gi30#]" PREVIOUS="idnumber" NEXT="gradetype"/>
<FIELD NAME="gradetype" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="1" SEQUENCE="false" ENUM="false" COMMENT="0 = none, 1 = value, 2 = scale, 3 = text" PREVIOUS="calculation" NEXT="grademax"/>
<FIELD NAME="grademax" TYPE="number" LENGTH="10" NOTNULL="true" UNSIGNED="false" DEFAULT="100" SEQUENCE="false" ENUM="false" DECIMALS="5" COMMENT="What is the maximum allowable grade?" PREVIOUS="gradetype" NEXT="grademin"/>
<FIELD NAME="grademin" TYPE="number" LENGTH="10" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" ENUM="false" DECIMALS="5" COMMENT="What is the minimum allowable grade?" PREVIOUS="grademax" NEXT="scaleid"/>
<FIELD NAME="scaleid" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" ENUM="false" COMMENT="If this grade is based on a scale, which one is it?" PREVIOUS="grademin" NEXT="outcomeid"/>
@ -1273,7 +1274,7 @@
<KEY NAME="outcomeid" TYPE="foreign" FIELDS="outcomeid" REFTABLE="grade_outcomes" REFFIELDS="id" PREVIOUS="scaleid"/>
</KEYS>
</TABLE>
<TABLE NAME="grade_categories" COMMENT="This table keeps information about categories, used for grouping items." PREVIOUS="grade_items" NEXT="grade_calculations">
<TABLE NAME="grade_categories" COMMENT="This table keeps information about categories, used for grouping items." PREVIOUS="grade_items" NEXT="grade_grades">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="false" SEQUENCE="true" ENUM="false" COMMENT="id of the table, please edit me" NEXT="courseid"/>
<FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" ENUM="false" COMMENT="The course this grade category is part of" PREVIOUS="id" NEXT="parent"/>
@ -1293,22 +1294,7 @@
<KEY NAME="parent" TYPE="foreign" FIELDS="parent" REFTABLE="grade_categories" REFFIELDS="id" PREVIOUS="courseid"/>
</KEYS>
</TABLE>
<TABLE NAME="grade_calculations" COMMENT="This table describes the calculated grade_items in more details." PREVIOUS="grade_categories" NEXT="grade_grades">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="false" SEQUENCE="true" ENUM="false" COMMENT="id of the table, please edit me" NEXT="itemid"/>
<FIELD NAME="itemid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" ENUM="false" COMMENT="The grade_item this relates to" PREVIOUS="id" NEXT="calculation"/>
<FIELD NAME="calculation" TYPE="text" LENGTH="medium" NOTNULL="false" SEQUENCE="false" ENUM="false" COMMENT="Formula describing how to derive this grade from other items, referring to them using [idnumber] ... eg something like:sin(square([XXXXX])) + [YYYY]" PREVIOUS="itemid" NEXT="timecreated"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" ENUM="false" COMMENT="the time this calculation was first created" PREVIOUS="calculation" NEXT="timemodified"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" ENUM="false" COMMENT="the time this calculation was last modified" PREVIOUS="timecreated" NEXT="usermodified"/>
<FIELD NAME="usermodified" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" ENUM="false" COMMENT="the userid of the person who last modified this calculation" PREVIOUS="timemodified"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="primary key of the table, please edit me" NEXT="itemid"/>
<KEY NAME="itemid" TYPE="foreign" FIELDS="itemid" REFTABLE="grade_items" REFFIELDS="id" PREVIOUS="primary" NEXT="usermodified"/>
<KEY NAME="usermodified" TYPE="foreign" FIELDS="usermodified" REFTABLE="user" REFFIELDS="id" PREVIOUS="itemid"/>
</KEYS>
</TABLE>
<TABLE NAME="grade_grades" COMMENT="grade_grades This table keeps individual grades for each user and each item, exactly as imported or submitted by modules. The rawgrademax/min and rawscaleid are stored here to record the values at the time the grade was stored, because teachers might change this for an activity! All the results are normalised/resampled for the final grade value." PREVIOUS="grade_calculations" NEXT="grade_grades_text">
<TABLE NAME="grade_grades" COMMENT="grade_grades This table keeps individual grades for each user and each item, exactly as imported or submitted by modules. The rawgrademax/min and rawscaleid are stored here to record the values at the time the grade was stored, because teachers might change this for an activity! All the results are normalised/resampled for the final grade value." PREVIOUS="grade_categories" NEXT="grade_grades_text">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="false" SEQUENCE="true" ENUM="false" COMMENT="id of the table, please edit me" NEXT="itemid"/>
<FIELD NAME="itemid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" ENUM="false" COMMENT="The item this grade belongs to" PREVIOUS="id" NEXT="userid"/>

View File

@ -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;
}

View File

@ -1,263 +0,0 @@
<?php // $Id$
///////////////////////////////////////////////////////////////////////////
// //
// NOTICE OF COPYRIGHT //
// //
// Moodle - Modular Object-Oriented Dynamic Learning Environment //
// http://moodle.com //
// //
// Copyright (C) 2001-2007 Martin Dougiamas http://dougiamas.com //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation; either version 2 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License for more details: //
// //
// http://www.gnu.org/copyleft/gpl.html //
// //
///////////////////////////////////////////////////////////////////////////
require_once('grade_object.php');
/**
* A calculation string used to compute the value displayed by a grade_item.
* There can be only one grade_calculation per grade_item (one-to-one).
*
* Calculation formula may use final grades of other grade items (giXXX
* where XXX is the id number of grade item). The result is stored in
* finalgrade field. The rawgrade is not used at all.
*/
class grade_calculation extends grade_object {
/**
* DB Table (used by grade_object).
* @var string $table
*/
var $table = 'grade_calculations';
/**
* Array of class variables that are not part of the DB table fields
* @var array $nonfields
*/
var $nonfields = array('table', 'nonfields', 'formula', 'grade_item');
/**
* A reference to the grade_item this calculation belongs to.
* @var int $itemid
*/
var $itemid;
/**
* The string representation of the calculation.
* @var string $calculation
*/
var $calculation;
/**
* The userid of the person who last modified this calculation.
* @var int $usermodified
*/
var $usermodified;
/**
* Grade item object
*/
var $grade_item;
/**
* Math evaluation object
*/
var $formula;
/**
* Loads the grade_item object referenced by $this->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;
}
}
}
?>

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}
return !empty($this->calculation);
/*
* 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 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->calculation = $formula;
}
$this->force_regrading();
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;
}
}
}
?>

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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.
*/

View File

@ -1,120 +0,0 @@
<?php // $Id$
///////////////////////////////////////////////////////////////////////////
// //
// NOTICE OF COPYRIGHT //
// //
// Moodle - Modular Object-Oriented Dynamic Learning Environment //
// http://moodle.org //
// //
// Copyright (C) 1999-2004 Martin Dougiamas http://dougiamas.com //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation; either version 2 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License for more details: //
// //
// http://www.gnu.org/copyleft/gpl.html //
// //
///////////////////////////////////////////////////////////////////////////
/**
* Unit tests for grade_calculation object.
*
* @author nicolas@moodle.com
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package moodlecore
*/
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
}
require_once($CFG->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);
}
}
?>

View File

@ -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);
}
}
?>

View File

@ -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