Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/lib/grade/grade_item.php
Go to the documentation of this file.
00001 <?php
00002 
00003 // This file is part of Moodle - http://moodle.org/
00004 //
00005 // Moodle is free software: you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License as published by
00007 // the Free Software Foundation, either version 3 of the License, or
00008 // (at your option) any later version.
00009 //
00010 // Moodle is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 // GNU General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License
00016 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
00026 defined('MOODLE_INTERNAL') || die();
00027 require_once('grade_object.php');
00028 
00033 class grade_item extends grade_object {
00038     public $table = 'grade_items';
00039 
00044     public $required_fields = array('id', 'courseid', 'categoryid', 'itemname', 'itemtype', 'itemmodule', 'iteminstance',
00045                                  'itemnumber', 'iteminfo', 'idnumber', 'calculation', 'gradetype', 'grademax', 'grademin',
00046                                  'scaleid', 'outcomeid', 'gradepass', 'multfactor', 'plusfactor', 'aggregationcoef',
00047                                  'sortorder', 'display', 'decimals', 'hidden', 'locked', 'locktime', 'needsupdate', 'timecreated',
00048                                  'timemodified');
00049 
00054     public $courseid;
00055 
00060     public $categoryid;
00061 
00066     public $item_category;
00067 
00072     public $parent_category;
00073 
00074 
00079     public $itemname;
00080 
00085     public $itemtype;
00086 
00091     public $itemmodule;
00092 
00097     public $iteminstance;
00098 
00103     public $itemnumber;
00104 
00109     public $iteminfo;
00110 
00115     public $idnumber;
00116 
00121     public $calculation;
00122 
00128     public $calculation_normalized;
00132     public $formula;
00133 
00138     public $gradetype = GRADE_TYPE_VALUE;
00139 
00144     public $grademax = 100;
00145 
00150     public $grademin = 0;
00151 
00156     public $scaleid;
00157 
00162     public $scale;
00163 
00168     public $outcomeid;
00169 
00174     public $outcome;
00175 
00180     public $gradepass = 0;
00181 
00186     public $multfactor = 1.0;
00187 
00192     public $plusfactor = 0;
00193 
00198     public $aggregationcoef = 0;
00199 
00204     public $sortorder = 0;
00205 
00210     public $display = GRADE_DISPLAY_TYPE_DEFAULT;
00211 
00216     public $decimals = null;
00217 
00222     public $locked = 0;
00223 
00228     public $locktime = 0;
00229 
00234     public $needsupdate = 1;
00235 
00239     public $dependson_cache = null;
00240 
00248     public function update($source=null) {
00249         // reset caches
00250         $this->dependson_cache = null;
00251 
00252         // Retrieve scale and infer grademax/min from it if needed
00253         $this->load_scale();
00254 
00255         // make sure there is not 0 in outcomeid
00256         if (empty($this->outcomeid)) {
00257             $this->outcomeid = null;
00258         }
00259 
00260         if ($this->qualifies_for_regrading()) {
00261             $this->force_regrading();
00262         }
00263 
00264         $this->timemodified = time();
00265 
00266         $this->grademin        = grade_floatval($this->grademin);
00267         $this->grademax        = grade_floatval($this->grademax);
00268         $this->multfactor      = grade_floatval($this->multfactor);
00269         $this->plusfactor      = grade_floatval($this->plusfactor);
00270         $this->aggregationcoef = grade_floatval($this->aggregationcoef);
00271 
00272         return parent::update($source);
00273     }
00274 
00281     public function qualifies_for_regrading() {
00282         if (empty($this->id)) {
00283             return false;
00284         }
00285 
00286         $db_item = new grade_item(array('id' => $this->id));
00287 
00288         $calculationdiff = $db_item->calculation != $this->calculation;
00289         $categorydiff    = $db_item->categoryid  != $this->categoryid;
00290         $gradetypediff   = $db_item->gradetype   != $this->gradetype;
00291         $scaleiddiff     = $db_item->scaleid     != $this->scaleid;
00292         $outcomeiddiff   = $db_item->outcomeid   != $this->outcomeid;
00293         $locktimediff    = $db_item->locktime    != $this->locktime;
00294         $grademindiff    = grade_floats_different($db_item->grademin,        $this->grademin);
00295         $grademaxdiff    = grade_floats_different($db_item->grademax,        $this->grademax);
00296         $multfactordiff  = grade_floats_different($db_item->multfactor,      $this->multfactor);
00297         $plusfactordiff  = grade_floats_different($db_item->plusfactor,      $this->plusfactor);
00298         $acoefdiff       = grade_floats_different($db_item->aggregationcoef, $this->aggregationcoef);
00299 
00300         $needsupdatediff = !$db_item->needsupdate &&  $this->needsupdate;    // force regrading only if setting the flag first time
00301         $lockeddiff      = !empty($db_item->locked) && empty($this->locked); // force regrading only when unlocking
00302 
00303         return ($calculationdiff || $categorydiff || $gradetypediff || $grademaxdiff || $grademindiff || $scaleiddiff
00304              || $outcomeiddiff || $multfactordiff || $plusfactordiff || $needsupdatediff
00305              || $lockeddiff || $acoefdiff || $locktimediff);
00306     }
00307 
00315     public static function fetch($params) {
00316         return grade_object::fetch_helper('grade_items', 'grade_item', $params);
00317     }
00318 
00326     public static function fetch_all($params) {
00327         return grade_object::fetch_all_helper('grade_items', 'grade_item', $params);
00328     }
00329 
00335     public function delete($source=null) {
00336         $this->delete_all_grades($source);
00337         return parent::delete($source);
00338     }
00339 
00345     public function delete_all_grades($source=null) {
00346         if (!$this->is_course_item()) {
00347             $this->force_regrading();
00348         }
00349 
00350         if ($grades = grade_grade::fetch_all(array('itemid'=>$this->id))) {
00351             foreach ($grades as $grade) {
00352                 $grade->delete($source);
00353             }
00354         }
00355 
00356         return true;
00357     }
00358 
00364     public function insert($source=null) {
00365         global $CFG, $DB;
00366 
00367         if (empty($this->courseid)) {
00368             print_error('cannotinsertgrade');
00369         }
00370 
00371         // load scale if needed
00372         $this->load_scale();
00373 
00374         // add parent category if needed
00375         if (empty($this->categoryid) and !$this->is_course_item() and !$this->is_category_item()) {
00376             $course_category = grade_category::fetch_course_category($this->courseid);
00377             $this->categoryid = $course_category->id;
00378 
00379         }
00380 
00381         // always place the new items at the end, move them after insert if needed
00382         $last_sortorder = $DB->get_field_select('grade_items', 'MAX(sortorder)', "courseid = ?", array($this->courseid));
00383         if (!empty($last_sortorder)) {
00384             $this->sortorder = $last_sortorder + 1;
00385         } else {
00386             $this->sortorder = 1;
00387         }
00388 
00389         // add proper item numbers to manual items
00390         if ($this->itemtype == 'manual') {
00391             if (empty($this->itemnumber)) {
00392                 $this->itemnumber = 0;
00393             }
00394         }
00395 
00396         // make sure there is not 0 in outcomeid
00397         if (empty($this->outcomeid)) {
00398             $this->outcomeid = null;
00399         }
00400 
00401         $this->timecreated = $this->timemodified = time();
00402 
00403         if (parent::insert($source)) {
00404             // force regrading of items if needed
00405             $this->force_regrading();
00406             return $this->id;
00407 
00408         } else {
00409             debugging("Could not insert this grade_item in the database!");
00410             return false;
00411         }
00412     }
00413 
00419     public function add_idnumber($idnumber) {
00420         global $DB;
00421         if (!empty($this->idnumber)) {
00422             return false;
00423         }
00424 
00425         if ($this->itemtype == 'mod' and !$this->is_outcome_item()) {
00426             if ($this->itemnumber === 0) {
00427                 // for activity modules, itemnumber 0 is synced with the course_modules
00428                 if (!$cm = get_coursemodule_from_instance($this->itemmodule, $this->iteminstance, $this->courseid)) {
00429                     return false;
00430                 }
00431                 if (!empty($cm->idnumber)) {
00432                     return false;
00433                 }
00434                 $DB->set_field('course_modules', 'idnumber', $idnumber, array('id' => $cm->id));
00435                 $this->idnumber = $idnumber;
00436                 return $this->update();
00437             } else {
00438                 $this->idnumber = $idnumber;
00439                 return $this->update();
00440             }
00441 
00442         } else {
00443             $this->idnumber = $idnumber;
00444             return $this->update();
00445         }
00446     }
00447 
00456     public function is_locked($userid=NULL) {
00457         if (!empty($this->locked)) {
00458             return true;
00459         }
00460 
00461         if (!empty($userid)) {
00462             if ($grade = grade_grade::fetch(array('itemid'=>$this->id, 'userid'=>$userid))) {
00463                 $grade->grade_item =& $this; // prevent db fetching of cached grade_item
00464                 return $grade->is_locked();
00465             }
00466         }
00467 
00468         return false;
00469     }
00470 
00478     public function set_locked($lockedstate, $cascade=false, $refresh=true) {
00479         if ($lockedstate) {
00481             if ($this->needsupdate) {
00482                 return false; // can not lock grade without first having final grade
00483             }
00484 
00485             $this->locked = time();
00486             $this->update();
00487 
00488             if ($cascade) {
00489                 $grades = $this->get_final();
00490                 foreach($grades as $g) {
00491                     $grade = new grade_grade($g, false);
00492                     $grade->grade_item =& $this;
00493                     $grade->set_locked(1, null, false);
00494                 }
00495             }
00496 
00497             return true;
00498 
00499         } else {
00501             if (!empty($this->locked) and $this->locktime < time()) {
00502                 //we have to reset locktime or else it would lock up again
00503                 $this->locktime = 0;
00504             }
00505 
00506             $this->locked = 0;
00507             $this->update();
00508 
00509             if ($cascade) {
00510                 if ($grades = grade_grade::fetch_all(array('itemid'=>$this->id))) {
00511                     foreach($grades as $grade) {
00512                         $grade->grade_item =& $this;
00513                         $grade->set_locked(0, null, false);
00514                     }
00515                 }
00516             }
00517 
00518             if ($refresh) {
00519                 //refresh when unlocking
00520                 $this->refresh_grades();
00521             }
00522 
00523             return true;
00524         }
00525     }
00526 
00530     public function check_locktime() {
00531         if (!empty($this->locked)) {
00532             return; // already locked
00533         }
00534 
00535         if ($this->locktime and $this->locktime < time()) {
00536             $this->locked = time();
00537             $this->update('locktime');
00538         }
00539     }
00540 
00547     public function set_locktime($locktime) {
00548         $this->locktime = $locktime;
00549         $this->update();
00550     }
00551 
00557     public function get_locktime() {
00558         return $this->locktime;
00559     }
00560 
00567     public function set_hidden($hidden, $cascade=false) {
00568         parent::set_hidden($hidden, $cascade);
00569 
00570         if ($cascade) {
00571             if ($grades = grade_grade::fetch_all(array('itemid'=>$this->id))) {
00572                 foreach($grades as $grade) {
00573                     $grade->grade_item =& $this;
00574                     $grade->set_hidden($hidden, $cascade);
00575                 }
00576             }
00577         }
00578 
00579         //if marking item visible make sure category is visible MDL-21367
00580         if( !$hidden ) {
00581             $category_array = grade_category::fetch_all(array('id'=>$this->categoryid));
00582             if ($category_array && array_key_exists($this->categoryid, $category_array)) {
00583                 $category = $category_array[$this->categoryid];
00584                 //call set_hidden on the category regardless of whether it is hidden as its parent might be hidden
00585                 //if($category->is_hidden()) {
00586                     $category->set_hidden($hidden, false);
00587                 //}
00588             }
00589         }
00590     }
00591 
00599     public function has_hidden_grades($groupsql="", array $params=null, $groupwheresql="") {
00600         global $DB;
00601         $params = (array)$params;
00602         $params['itemid'] = $this->id;
00603 
00604         return $DB->get_field_sql("SELECT COUNT(*) FROM {grade_grades} g LEFT JOIN "
00605                             ."{user} u ON g.userid = u.id $groupsql WHERE itemid = :itemid AND hidden = 1 $groupwheresql", $params);
00606     }
00607 
00611     public function regrading_finished() {
00612         global $DB;
00613         $this->needsupdate = 0;
00614         //do not use $this->update() because we do not want this logged in grade_item_history
00615         $DB->set_field('grade_items', 'needsupdate', 0, array('id' => $this->id));
00616     }
00617 
00627     public function regrade_final_grades($userid=null) {
00628         global $CFG, $DB;
00629 
00630         // locked grade items already have correct final grades
00631         if ($this->is_locked()) {
00632             return true;
00633         }
00634 
00635         // calculation produces final value using formula from other final values
00636         if ($this->is_calculated()) {
00637             if ($this->compute($userid)) {
00638                 return true;
00639             } else {
00640                 return "Could not calculate grades for grade item"; // TODO: improve and localize
00641             }
00642 
00643         // noncalculated outcomes already have final values - raw grades not used
00644         } else if ($this->is_outcome_item()) {
00645             return true;
00646 
00647         // aggregate the category grade
00648         } else if ($this->is_category_item() or $this->is_course_item()) {
00649             // aggregate category grade item
00650             $category = $this->get_item_category();
00651             $category->grade_item =& $this;
00652             if ($category->generate_grades($userid)) {
00653                 return true;
00654             } else {
00655                 return "Could not aggregate final grades for category:".$this->id; // TODO: improve and localize
00656             }
00657 
00658         } else if ($this->is_manual_item()) {
00659             // manual items track only final grades, no raw grades
00660             return true;
00661 
00662         } else if (!$this->is_raw_used()) {
00663             // hmm - raw grades are not used- nothing to regrade
00664             return true;
00665         }
00666 
00667         // normal grade item - just new final grades
00668         $result = true;
00669         $grade_inst = new grade_grade();
00670         $fields = implode(',', $grade_inst->required_fields);
00671         if ($userid) {
00672             $params = array($this->id, $userid);
00673             $rs = $DB->get_recordset_select('grade_grades', "itemid=? AND userid=?", $params, '', $fields);
00674         } else {
00675             $rs = $DB->get_recordset('grade_grades', array('itemid' => $this->id), '', $fields);
00676         }
00677         if ($rs) {
00678             foreach ($rs as $grade_record) {
00679                 $grade = new grade_grade($grade_record, false);
00680 
00681                 if (!empty($grade_record->locked) or !empty($grade_record->overridden)) {
00682                     // this grade is locked - final grade must be ok
00683                     continue;
00684                 }
00685 
00686                 $grade->finalgrade = $this->adjust_raw_grade($grade->rawgrade, $grade->rawgrademin, $grade->rawgrademax);
00687 
00688                 if (grade_floats_different($grade_record->finalgrade, $grade->finalgrade)) {
00689                     if (!$grade->update('system')) {
00690                         $result = "Internal error updating final grade";
00691                     }
00692                 }
00693             }
00694             $rs->close();
00695         }
00696 
00697         return $result;
00698     }
00699 
00708     public function adjust_raw_grade($rawgrade, $rawmin, $rawmax) {
00709         if (is_null($rawgrade)) {
00710             return null;
00711         }
00712 
00713         if ($this->gradetype == GRADE_TYPE_VALUE) { // Dealing with numerical grade
00714 
00715             if ($this->grademax < $this->grademin) {
00716                 return null;
00717             }
00718 
00719             if ($this->grademax == $this->grademin) {
00720                 return $this->grademax; // no range
00721             }
00722 
00723             // Standardise score to the new grade range
00724             // NOTE: this is not compatible with current assignment grading
00725             if ($this->itemmodule != 'assignment' and ($rawmin != $this->grademin or $rawmax != $this->grademax)) {
00726                 $rawgrade = grade_grade::standardise_score($rawgrade, $rawmin, $rawmax, $this->grademin, $this->grademax);
00727             }
00728 
00729             // Apply other grade_item factors
00730             $rawgrade *= $this->multfactor;
00731             $rawgrade += $this->plusfactor;
00732 
00733             return $this->bounded_grade($rawgrade);
00734 
00735         } else if ($this->gradetype == GRADE_TYPE_SCALE) { // Dealing with a scale value
00736             if (empty($this->scale)) {
00737                 $this->load_scale();
00738             }
00739 
00740             if ($this->grademax < 0) {
00741                 return null; // scale not present - no grade
00742             }
00743 
00744             if ($this->grademax == 0) {
00745                 return $this->grademax; // only one option
00746             }
00747 
00748             // Convert scale if needed
00749             // NOTE: this is not compatible with current assignment grading
00750             if ($this->itemmodule != 'assignment' and ($rawmin != $this->grademin or $rawmax != $this->grademax)) {
00751                 $rawgrade = grade_grade::standardise_score($rawgrade, $rawmin, $rawmax, $this->grademin, $this->grademax);
00752             }
00753 
00754             return $this->bounded_grade($rawgrade);
00755 
00756 
00757         } else if ($this->gradetype == GRADE_TYPE_TEXT or $this->gradetype == GRADE_TYPE_NONE) { // no value
00758             // somebody changed the grading type when grades already existed
00759             return null;
00760 
00761         } else {
00762             debugging("Unknown grade type");
00763             return null;
00764         }
00765     }
00766 
00771     public function force_regrading() {
00772         global $DB;
00773         $this->needsupdate = 1;
00774         //mark this item and course item only - categories and calculated items are always regraded
00775         $wheresql = "(itemtype='course' OR id=?) AND courseid=?";
00776         $params   = array($this->id, $this->courseid);
00777         $DB->set_field_select('grade_items', 'needsupdate', 1, $wheresql, $params);
00778     }
00779 
00785     public function load_scale() {
00786         if ($this->gradetype != GRADE_TYPE_SCALE) {
00787             $this->scaleid = null;
00788         }
00789 
00790         if (!empty($this->scaleid)) {
00791             //do not load scale if already present
00792             if (empty($this->scale->id) or $this->scale->id != $this->scaleid) {
00793                 $this->scale = grade_scale::fetch(array('id'=>$this->scaleid));
00794                 if (!$this->scale) {
00795                     debugging('Incorrect scale id: '.$this->scaleid);
00796                     $this->scale = null;
00797                     return null;
00798                 }
00799                 $this->scale->load_items();
00800             }
00801 
00802             // Until scales are uniformly set to min=0 max=count(scaleitems)-1 throughout Moodle, we
00803             // stay with the current min=1 max=count(scaleitems)
00804             $this->grademax = count($this->scale->scale_items);
00805             $this->grademin = 1;
00806 
00807         } else {
00808             $this->scale = null;
00809         }
00810 
00811         return $this->scale;
00812     }
00813 
00819     public function load_outcome() {
00820         if (!empty($this->outcomeid)) {
00821             $this->outcome = grade_outcome::fetch(array('id'=>$this->outcomeid));
00822         }
00823         return $this->outcome;
00824     }
00825 
00832     public function get_parent_category() {
00833         if ($this->is_category_item() or $this->is_course_item()) {
00834             return $this->get_item_category();
00835 
00836         } else {
00837             return grade_category::fetch(array('id'=>$this->categoryid));
00838         }
00839     }
00840 
00846     public function load_parent_category() {
00847         if (empty($this->parent_category->id)) {
00848             $this->parent_category = $this->get_parent_category();
00849         }
00850         return $this->parent_category;
00851     }
00852 
00858     public function get_item_category() {
00859         if (!$this->is_course_item() and !$this->is_category_item()) {
00860             return false;
00861         }
00862         return grade_category::fetch(array('id'=>$this->iteminstance));
00863     }
00864 
00870     public function load_item_category() {
00871         if (empty($this->item_category->id)) {
00872             $this->item_category = $this->get_item_category();
00873         }
00874         return $this->item_category;
00875     }
00876 
00881     public function is_category_item() {
00882         return ($this->itemtype == 'category');
00883     }
00884 
00889     public function is_course_item() {
00890         return ($this->itemtype == 'course');
00891     }
00892 
00897     public function is_manual_item() {
00898         return ($this->itemtype == 'manual');
00899     }
00900 
00905     public function is_outcome_item() {
00906         return !empty($this->outcomeid);
00907     }
00908 
00913     public function is_external_item() {
00914         return ($this->itemtype == 'mod');
00915     }
00916 
00921     public function is_overridable_item() {
00922         return !$this->is_outcome_item() and ($this->is_external_item() or $this->is_calculated() or $this->is_course_item() or $this->is_category_item());
00923     }
00924 
00929     public function is_overridable_item_feedback() {
00930         return !$this->is_outcome_item() and $this->is_external_item();
00931     }
00932 
00937     public function is_raw_used() {
00938         return ($this->is_external_item() and !$this->is_calculated() and !$this->is_outcome_item());
00939     }
00940 
00946     public static function fetch_course_item($courseid) {
00947         if ($course_item = grade_item::fetch(array('courseid'=>$courseid, 'itemtype'=>'course'))) {
00948             return $course_item;
00949         }
00950 
00951         // first get category - it creates the associated grade item
00952         $course_category = grade_category::fetch_course_category($courseid);
00953         return $course_category->get_grade_item();
00954     }
00955 
00960     public function is_editable() {
00961         return true;
00962     }
00963 
00968     public function is_calculated() {
00969         if (empty($this->calculation)) {
00970             return false;
00971         }
00972 
00973         /*
00974          * The main reason why we use the ##gixxx## instead of [[idnumber]] is speed of depends_on(),
00975          * we would have to fetch all course grade items to find out the ids.
00976          * Also if user changes the idnumber the formula does not need to be updated.
00977          */
00978 
00979         // first detect if we need to change calculation formula from [[idnumber]] to ##giXXX## (after backup, etc.)
00980         if (!$this->calculation_normalized and strpos($this->calculation, '[[') !== false) {
00981             $this->set_calculation($this->calculation);
00982         }
00983 
00984         return !empty($this->calculation);
00985     }
00986 
00991     public function get_calculation() {
00992         if ($this->is_calculated()) {
00993             return grade_item::denormalize_formula($this->calculation, $this->courseid);
00994 
00995         } else {
00996             return NULL;
00997         }
00998     }
00999 
01007     public function set_calculation($formula) {
01008         $this->calculation = grade_item::normalize_formula($formula, $this->courseid);
01009         $this->calculation_normalized = true;
01010         return $this->update();
01011     }
01012 
01019     public static function denormalize_formula($formula, $courseid) {
01020         if (empty($formula)) {
01021             return '';
01022         }
01023 
01024         // denormalize formula - convert ##giXX## to [[idnumber]]
01025         if (preg_match_all('/##gi(\d+)##/', $formula, $matches)) {
01026             foreach ($matches[1] as $id) {
01027                 if ($grade_item = grade_item::fetch(array('id'=>$id, 'courseid'=>$courseid))) {
01028                     if (!empty($grade_item->idnumber)) {
01029                         $formula = str_replace('##gi'.$grade_item->id.'##', '[['.$grade_item->idnumber.']]', $formula);
01030                     }
01031                 }
01032             }
01033         }
01034 
01035         return $formula;
01036 
01037     }
01038 
01045     public static function normalize_formula($formula, $courseid) {
01046         $formula = trim($formula);
01047 
01048         if (empty($formula)) {
01049             return NULL;
01050 
01051         }
01052 
01053         // normalize formula - we want grade item ids ##giXXX## instead of [[idnumber]]
01054         if ($grade_items = grade_item::fetch_all(array('courseid'=>$courseid))) {
01055             foreach ($grade_items as $grade_item) {
01056                 $formula = str_replace('[['.$grade_item->idnumber.']]', '##gi'.$grade_item->id.'##', $formula);
01057             }
01058         }
01059 
01060         return $formula;
01061     }
01062 
01068     public function get_final($userid=NULL) {
01069         global $DB;
01070         if ($userid) {
01071             if ($user = $DB->get_record('grade_grades', array('itemid' => $this->id, 'userid' => $userid))) {
01072                 return $user;
01073             }
01074 
01075         } else {
01076             if ($grades = $DB->get_records('grade_grades', array('itemid' => $this->id))) {
01077                 //TODO: speed up with better SQL
01078                 $result = array();
01079                 foreach ($grades as $grade) {
01080                     $result[$grade->userid] = $grade;
01081                 }
01082                 return $result;
01083             } else {
01084                 return array();
01085             }
01086         }
01087     }
01088 
01094     public function get_grade($userid, $create=true) {
01095         if (empty($this->id)) {
01096             debugging('Can not use before insert');
01097             return false;
01098         }
01099 
01100         $grade = new grade_grade(array('userid'=>$userid, 'itemid'=>$this->id));
01101         if (empty($grade->id) and $create) {
01102             $grade->insert();
01103         }
01104 
01105         return $grade;
01106     }
01107 
01113     public function get_sortorder() {
01114         return $this->sortorder;
01115     }
01116 
01122     public function get_idnumber() {
01123         return $this->idnumber;
01124     }
01125 
01131     public function get_grade_item() {
01132         return $this;
01133     }
01134 
01141     public function set_sortorder($sortorder) {
01142         if ($this->sortorder == $sortorder) {
01143             return;
01144         }
01145         $this->sortorder = $sortorder;
01146         $this->update();
01147     }
01148 
01149     public function move_after_sortorder($sortorder) {
01150         global $CFG, $DB;
01151 
01152         //make some room first
01153         $params = array($sortorder, $this->courseid);
01154         $sql = "UPDATE {grade_items}
01155                    SET sortorder = sortorder + 1
01156                  WHERE sortorder > ? AND courseid = ?";
01157         $DB->execute($sql, $params);
01158 
01159         $this->set_sortorder($sortorder + 1);
01160     }
01161 
01168     public function get_name($fulltotal=false) {
01169         if (!empty($this->itemname)) {
01170             // MDL-10557
01171             return format_string($this->itemname);
01172 
01173         } else if ($this->is_course_item()) {
01174             return get_string('coursetotal', 'grades');
01175 
01176         } else if ($this->is_category_item()) {
01177             if ($fulltotal) {
01178                 $category = $this->load_parent_category();
01179                 $a = new stdClass();
01180                 $a->category = $category->get_name();
01181                 return get_string('categorytotalfull', 'grades', $a);
01182             } else {
01183             return get_string('categorytotal', 'grades');
01184             }
01185 
01186         } else {
01187             return get_string('grade');
01188         }
01189     }
01190 
01196     public function set_parent($parentid) {
01197         if ($this->is_course_item() or $this->is_category_item()) {
01198             print_error('cannotsetparentforcatoritem');
01199         }
01200 
01201         if ($this->categoryid == $parentid) {
01202             return true;
01203         }
01204 
01205         // find parent and check course id
01206         if (!$parent_category = grade_category::fetch(array('id'=>$parentid, 'courseid'=>$this->courseid))) {
01207             return false;
01208         }
01209 
01210         // MDL-19407 If moving from a non-SWM category to a SWM category, convert aggregationcoef to 0
01211         $currentparent = $this->load_parent_category();
01212 
01213         if ($currentparent->aggregation != GRADE_AGGREGATE_WEIGHTED_MEAN2 && $parent_category->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN2) {
01214             $this->aggregationcoef = 0;
01215         }
01216 
01217         $this->force_regrading();
01218 
01219         // set new parent
01220         $this->categoryid = $parent_category->id;
01221         $this->parent_category =& $parent_category;
01222 
01223         return $this->update();
01224     }
01225 
01231     public function bounded_grade($gradevalue) {
01232         global $CFG;
01233 
01234         if (is_null($gradevalue)) {
01235             return null;
01236         }
01237 
01238         if ($this->gradetype == GRADE_TYPE_SCALE) {
01239             // no >100% grades hack for scale grades!
01240             // 1.5 is rounded to 2 ;-)
01241             return (int)bounded_number($this->grademin, round($gradevalue+0.00001), $this->grademax);
01242         }
01243 
01244         $grademax = $this->grademax;
01245 
01246         // NOTE: if you change this value you must manually reset the needsupdate flag in all grade items
01247         $maxcoef = isset($CFG->gradeoverhundredprocentmax) ? $CFG->gradeoverhundredprocentmax : 10; // 1000% max by default
01248 
01249         if (!empty($CFG->unlimitedgrades)) {
01250             // NOTE: if you change this value you must manually reset the needsupdate flag in all grade items
01251             $grademax = $grademax * $maxcoef;
01252         } else if ($this->is_category_item() or $this->is_course_item()) {
01253             $category = $this->load_item_category();
01254             if ($category->aggregation >= 100) {
01255                 // grade >100% hack
01256                 $grademax = $grademax * $maxcoef;
01257             }
01258         }
01259 
01260         return (float)bounded_number($this->grademin, $gradevalue, $grademax);
01261     }
01262 
01268     public function depends_on($reset_cache=false) {
01269         global $CFG, $DB;
01270 
01271         if ($reset_cache) {
01272             $this->dependson_cache = null;
01273         } else if (isset($this->dependson_cache)) {
01274             return $this->dependson_cache;
01275         }
01276 
01277         if ($this->is_locked()) {
01278             // locked items do not need to be regraded
01279             $this->dependson_cache = array();
01280             return $this->dependson_cache;
01281         }
01282 
01283         if ($this->is_calculated()) {
01284             if (preg_match_all('/##gi(\d+)##/', $this->calculation, $matches)) {
01285                 $this->dependson_cache = array_unique($matches[1]); // remove duplicates
01286                 return $this->dependson_cache;
01287             } else {
01288                 $this->dependson_cache = array();
01289                 return $this->dependson_cache;
01290             }
01291 
01292         } else if ($grade_category = $this->load_item_category()) {
01293             $params = array();
01294 
01295             //only items with numeric or scale values can be aggregated
01296             if ($this->gradetype != GRADE_TYPE_VALUE and $this->gradetype != GRADE_TYPE_SCALE) {
01297                 $this->dependson_cache = array();
01298                 return $this->dependson_cache;
01299             }
01300 
01301             $grade_category->apply_forced_settings();
01302 
01303             if (empty($CFG->enableoutcomes) or $grade_category->aggregateoutcomes) {
01304                 $outcomes_sql = "";
01305             } else {
01306                 $outcomes_sql = "AND gi.outcomeid IS NULL";
01307             }
01308 
01309             if (empty($CFG->grade_includescalesinaggregation)) {
01310                 $gtypes = "gi.gradetype = ?";
01311                 $params[] = GRADE_TYPE_VALUE;
01312             } else {
01313                 $gtypes = "(gi.gradetype = ? OR gi.gradetype = ?)";
01314                 $params[] = GRADE_TYPE_VALUE;
01315                 $params[] = GRADE_TYPE_SCALE;
01316             }
01317 
01318             if ($grade_category->aggregatesubcats) {
01319                 // return all children excluding category items
01320                 $params[] = '%/' . $grade_category->id . '/%';
01321                 $sql = "SELECT gi.id
01322                           FROM {grade_items} gi
01323                          WHERE $gtypes
01324                                $outcomes_sql
01325                                AND gi.categoryid IN (
01326                                   SELECT gc.id
01327                                     FROM {grade_categories} gc
01328                                    WHERE gc.path LIKE ?)";
01329             } else {
01330                 $params[] = $grade_category->id;
01331                 $params[] = $grade_category->id;
01332                 if (empty($CFG->grade_includescalesinaggregation)) {
01333                     $params[] = GRADE_TYPE_VALUE;
01334                 } else {
01335                     $params[] = GRADE_TYPE_VALUE;
01336                     $params[] = GRADE_TYPE_SCALE;
01337                 }
01338                 $sql = "SELECT gi.id
01339                           FROM {grade_items} gi
01340                          WHERE $gtypes
01341                                AND gi.categoryid = ?
01342                                $outcomes_sql
01343                         UNION
01344 
01345                         SELECT gi.id
01346                           FROM {grade_items} gi, {grade_categories} gc
01347                          WHERE (gi.itemtype = 'category' OR gi.itemtype = 'course') AND gi.iteminstance=gc.id
01348                                AND gc.parent = ?
01349                                AND $gtypes
01350                                $outcomes_sql";
01351             }
01352 
01353             if ($children = $DB->get_records_sql($sql, $params)) {
01354                 $this->dependson_cache = array_keys($children);
01355                 return $this->dependson_cache;
01356             } else {
01357                 $this->dependson_cache = array();
01358                 return $this->dependson_cache;
01359             }
01360 
01361         } else {
01362             $this->dependson_cache = array();
01363             return $this->dependson_cache;
01364         }
01365     }
01366 
01371     public function refresh_grades($userid=0) {
01372         global $DB;
01373         if ($this->itemtype == 'mod') {
01374             if ($this->is_outcome_item()) {
01375                 //nothing to do
01376                 return;
01377             }
01378 
01379             if (!$activity = $DB->get_record($this->itemmodule, array('id' => $this->iteminstance))) {
01380                 debugging("Can not find $this->itemmodule activity with id $this->iteminstance");
01381                 return;
01382             }
01383 
01384             if (!$cm = get_coursemodule_from_instance($this->itemmodule, $activity->id, $this->courseid)) {
01385                 debugging('Can not find course module');
01386                 return;
01387             }
01388 
01389             $activity->modname    = $this->itemmodule;
01390             $activity->cmidnumber = $cm->idnumber;
01391 
01392             grade_update_mod_grades($activity);
01393         }
01394     }
01395 
01410     public function update_final_grade($userid, $finalgrade=false, $source=NULL, $feedback=false, $feedbackformat=FORMAT_MOODLE, $usermodified=null) {
01411         global $USER, $CFG;
01412 
01413         $result = true;
01414 
01415         // no grading used or locked
01416         if ($this->gradetype == GRADE_TYPE_NONE or $this->is_locked()) {
01417             return false;
01418         }
01419 
01420         $grade = new grade_grade(array('itemid'=>$this->id, 'userid'=>$userid));
01421         $grade->grade_item =& $this; // prevent db fetching of this grade_item
01422 
01423         if (empty($usermodified)) {
01424             $grade->usermodified = $USER->id;
01425         } else {
01426             $grade->usermodified = $usermodified;
01427         }
01428 
01429         if ($grade->is_locked()) {
01430             // do not update locked grades at all
01431             return false;
01432         }
01433 
01434         $locktime = $grade->get_locktime();
01435         if ($locktime and $locktime < time()) {
01436             // do not update grades that should be already locked, force regrade instead
01437             $this->force_regrading();
01438             return false;
01439         }
01440 
01441         $oldgrade = new stdClass();
01442         $oldgrade->finalgrade     = $grade->finalgrade;
01443         $oldgrade->overridden     = $grade->overridden;
01444         $oldgrade->feedback       = $grade->feedback;
01445         $oldgrade->feedbackformat = $grade->feedbackformat;
01446 
01447         // changed grade?
01448         if ($finalgrade !== false) {
01449             if ($this->is_overridable_item()) {
01450                 $grade->overridden = time();
01451             }
01452 
01453             $grade->finalgrade = $this->bounded_grade($finalgrade);
01454         }
01455 
01456         // do we have comment from teacher?
01457         if ($feedback !== false) {
01458             if ($this->is_overridable_item_feedback()) {
01459                 // external items (modules, plugins) may have own feedback
01460                 $grade->overridden = time();
01461             }
01462 
01463             $grade->feedback       = $feedback;
01464             $grade->feedbackformat = $feedbackformat;
01465         }
01466 
01467         if (empty($grade->id)) {
01468             $grade->timecreated  = null;   // hack alert - date submitted - no submission yet
01469             $grade->timemodified = time(); // hack alert - date graded
01470             $result = (boolean)$grade->insert($source);
01471 
01472         } else if (grade_floats_different($grade->finalgrade, $oldgrade->finalgrade)
01473                 or $grade->feedback       !== $oldgrade->feedback
01474                 or $grade->feedbackformat != $oldgrade->feedbackformat
01475                 or ($oldgrade->overridden == 0 and $grade->overridden > 0)) {
01476             $grade->timemodified = time(); // hack alert - date graded
01477             $result = $grade->update($source);
01478         } else {
01479             // no grade change
01480             return $result;
01481         }
01482 
01483         if (!$result) {
01484             // something went wrong - better force final grade recalculation
01485             $this->force_regrading();
01486 
01487         } else if ($this->is_course_item() and !$this->needsupdate) {
01488             if (grade_regrade_final_grades($this->courseid, $userid, $this) !== true) {
01489                 $this->force_regrading();
01490             }
01491 
01492         } else if (!$this->needsupdate) {
01493             $course_item = grade_item::fetch_course_item($this->courseid);
01494             if (!$course_item->needsupdate) {
01495                 if (grade_regrade_final_grades($this->courseid, $userid, $this) !== true) {
01496                     $this->force_regrading();
01497                 }
01498             } else {
01499                 $this->force_regrading();
01500             }
01501         }
01502 
01503         return $result;
01504     }
01505 
01506 
01524     public function update_raw_grade($userid, $rawgrade=false, $source=NULL, $feedback=false, $feedbackformat=FORMAT_MOODLE, $usermodified=null, $dategraded=null, $datesubmitted=null, $grade=null) {
01525         global $USER;
01526 
01527         $result = true;
01528 
01529         // calculated grades can not be updated; course and category can not be updated  because they are aggregated
01530         if (!$this->is_raw_used() or $this->gradetype == GRADE_TYPE_NONE or $this->is_locked()) {
01531             return false;
01532         }
01533 
01534         if (is_null($grade)) {
01535             //fetch from db
01536             $grade = new grade_grade(array('itemid'=>$this->id, 'userid'=>$userid));
01537         }
01538         $grade->grade_item =& $this; // prevent db fetching of this grade_item
01539 
01540         if (empty($usermodified)) {
01541             $grade->usermodified = $USER->id;
01542         } else {
01543             $grade->usermodified = $usermodified;
01544         }
01545 
01546         if ($grade->is_locked()) {
01547             // do not update locked grades at all
01548             return false;
01549         }
01550 
01551         $locktime = $grade->get_locktime();
01552         if ($locktime and $locktime < time()) {
01553             // do not update grades that should be already locked and force regrade
01554             $this->force_regrading();
01555             return false;
01556         }
01557 
01558         $oldgrade = new stdClass();
01559         $oldgrade->finalgrade     = $grade->finalgrade;
01560         $oldgrade->rawgrade       = $grade->rawgrade;
01561         $oldgrade->rawgrademin    = $grade->rawgrademin;
01562         $oldgrade->rawgrademax    = $grade->rawgrademax;
01563         $oldgrade->rawscaleid     = $grade->rawscaleid;
01564         $oldgrade->feedback       = $grade->feedback;
01565         $oldgrade->feedbackformat = $grade->feedbackformat;
01566 
01567         // use new min and max
01568         $grade->rawgrade    = $grade->rawgrade;
01569         $grade->rawgrademin = $this->grademin;
01570         $grade->rawgrademax = $this->grademax;
01571         $grade->rawscaleid  = $this->scaleid;
01572 
01573         // change raw grade?
01574         if ($rawgrade !== false) {
01575             $grade->rawgrade = $rawgrade;
01576         }
01577 
01578         // empty feedback means no feedback at all
01579         if ($feedback === '') {
01580             $feedback = null;
01581         }
01582 
01583         // do we have comment from teacher?
01584         if ($feedback !== false and !$grade->is_overridden()) {
01585             $grade->feedback       = $feedback;
01586             $grade->feedbackformat = $feedbackformat;
01587         }
01588 
01589         // update final grade if possible
01590         if (!$grade->is_locked() and !$grade->is_overridden()) {
01591             $grade->finalgrade = $this->adjust_raw_grade($grade->rawgrade, $grade->rawgrademin, $grade->rawgrademax);
01592         }
01593 
01594         // TODO: hack alert - create new fields for these in 2.0
01595         $oldgrade->timecreated  = $grade->timecreated;
01596         $oldgrade->timemodified = $grade->timemodified;
01597 
01598         $grade->timecreated = $datesubmitted;
01599 
01600         if ($grade->is_overridden()) {
01601             // keep original graded date - update_final_grade() sets this for overridden grades
01602 
01603         } else if (is_null($grade->rawgrade) and is_null($grade->feedback)) {
01604             // no grade and feedback means no grading yet
01605             $grade->timemodified = null;
01606 
01607         } else if (!empty($dategraded)) {
01608             // fine - module sends info when graded (yay!)
01609             $grade->timemodified = $dategraded;
01610 
01611         } else if (grade_floats_different($grade->finalgrade, $oldgrade->finalgrade)
01612                    or $grade->feedback !== $oldgrade->feedback) {
01613             // guess - if either grade or feedback changed set new graded date
01614             $grade->timemodified = time();
01615 
01616         } else {
01617             //keep original graded date
01618         }
01619         // end of hack alert
01620 
01621         if (empty($grade->id)) {
01622             $result = (boolean)$grade->insert($source);
01623 
01624         } else if (grade_floats_different($grade->finalgrade,  $oldgrade->finalgrade)
01625                 or grade_floats_different($grade->rawgrade,    $oldgrade->rawgrade)
01626                 or grade_floats_different($grade->rawgrademin, $oldgrade->rawgrademin)
01627                 or grade_floats_different($grade->rawgrademax, $oldgrade->rawgrademax)
01628                 or $grade->rawscaleid     != $oldgrade->rawscaleid
01629                 or $grade->feedback       !== $oldgrade->feedback
01630                 or $grade->feedbackformat != $oldgrade->feedbackformat
01631                 or $grade->timecreated    != $oldgrade->timecreated  // part of hack above
01632                 or $grade->timemodified   != $oldgrade->timemodified // part of hack above
01633                 ) {
01634             $result = $grade->update($source);
01635         } else {
01636             return $result;
01637         }
01638 
01639         if (!$result) {
01640             // something went wrong - better force final grade recalculation
01641             $this->force_regrading();
01642 
01643         } else if (!$this->needsupdate) {
01644             $course_item = grade_item::fetch_course_item($this->courseid);
01645             if (!$course_item->needsupdate) {
01646                 if (grade_regrade_final_grades($this->courseid, $userid, $this) !== true) {
01647                     $this->force_regrading();
01648                 }
01649             }
01650         }
01651 
01652         return $result;
01653     }
01654 
01660     public function compute($userid=null) {
01661         global $CFG, $DB;
01662 
01663         if (!$this->is_calculated()) {
01664             return false;
01665         }
01666 
01667         require_once($CFG->libdir.'/mathslib.php');
01668 
01669         if ($this->is_locked()) {
01670             return true; // no need to recalculate locked items
01671         }
01672 
01673         // precreate grades - we need them to exist
01674         $params = array($this->courseid, $this->id, $this->id);
01675         $sql = "SELECT DISTINCT go.userid
01676                   FROM {grade_grades} go
01677                        JOIN {grade_items} gi
01678                        ON (gi.id = go.itemid AND gi.courseid = ?)
01679                        LEFT OUTER JOIN {grade_grades} g
01680                        ON (g.userid = go.userid AND g.itemid = ?)
01681                  WHERE gi.id <> ? AND g.id IS NULL";
01682         if ($missing = $DB->get_records_sql($sql, $params)) {
01683             foreach ($missing as $m) {
01684                 $grade = new grade_grade(array('itemid'=>$this->id, 'userid'=>$m->userid), false);
01685                 $grade->grade_item =& $this;
01686                 $grade->insert('system');
01687             }
01688         }
01689 
01690         // get used items
01691         $useditems = $this->depends_on();
01692 
01693         // prepare formula and init maths library
01694         $formula = preg_replace('/##(gi\d+)##/', '\1', $this->calculation);
01695         if (strpos($formula, '[[') !== false) {
01696             // missing item
01697             return false;
01698         }
01699         $this->formula = new calc_formula($formula);
01700 
01701         // where to look for final grades?
01702         // this itemid is added so that we use only one query for source and final grades
01703         $gis = array_merge($useditems, array($this->id));
01704         list($usql, $params) = $DB->get_in_or_equal($gis);
01705 
01706         if ($userid) {
01707             $usersql = "AND g.userid=?";
01708             $params[] = $userid;
01709         } else {
01710             $usersql = "";
01711         }
01712 
01713         $grade_inst = new grade_grade();
01714         $fields = 'g.'.implode(',g.', $grade_inst->required_fields);
01715 
01716         $params[] = $this->courseid;
01717         $sql = "SELECT $fields
01718                   FROM {grade_grades} g, {grade_items} gi
01719                  WHERE gi.id = g.itemid AND gi.id $usql $usersql AND gi.courseid=?
01720                  ORDER BY g.userid";
01721 
01722         $return = true;
01723 
01724         // group the grades by userid and use formula on the group
01725         $rs = $DB->get_recordset_sql($sql, $params);
01726         if ($rs->valid()) {
01727             $prevuser = 0;
01728             $grade_records   = array();
01729             $oldgrade    = null;
01730             foreach ($rs as $used) {
01731                 if ($used->userid != $prevuser) {
01732                     if (!$this->use_formula($prevuser, $grade_records, $useditems, $oldgrade)) {
01733                         $return = false;
01734                     }
01735                     $prevuser = $used->userid;
01736                     $grade_records   = array();
01737                     $oldgrade    = null;
01738                 }
01739                 if ($used->itemid == $this->id) {
01740                     $oldgrade = $used;
01741                 }
01742                 $grade_records['gi'.$used->itemid] = $used->finalgrade;
01743             }
01744             if (!$this->use_formula($prevuser, $grade_records, $useditems, $oldgrade)) {
01745                 $return = false;
01746             }
01747         }
01748         $rs->close();
01749 
01750         return $return;
01751     }
01752 
01756     public function use_formula($userid, $params, $useditems, $oldgrade) {
01757         if (empty($userid)) {
01758             return true;
01759         }
01760 
01761         // add missing final grade values
01762         // not graded (null) is counted as 0 - the spreadsheet way
01763         $allinputsnull = true;
01764         foreach($useditems as $gi) {
01765             if (!array_key_exists('gi'.$gi, $params) || is_null($params['gi'.$gi])) {
01766                 $params['gi'.$gi] = 0;
01767             } else {
01768                 $params['gi'.$gi] = (float)$params['gi'.$gi];
01769                 if ($gi != $this->id) {
01770                     $allinputsnull = false;
01771                 }
01772             }
01773         }
01774 
01775         // can not use own final grade during calculation
01776         unset($params['gi'.$this->id]);
01777 
01778         // insert final grade - will be needed later anyway
01779         if ($oldgrade) {
01780             $oldfinalgrade = $oldgrade->finalgrade;
01781             $grade = new grade_grade($oldgrade, false); // fetching from db is not needed
01782             $grade->grade_item =& $this;
01783 
01784         } else {
01785             $grade = new grade_grade(array('itemid'=>$this->id, 'userid'=>$userid), false);
01786             $grade->grade_item =& $this;
01787             $grade->insert('system');
01788             $oldfinalgrade = null;
01789         }
01790 
01791         // no need to recalculate locked or overridden grades
01792         if ($grade->is_locked() or $grade->is_overridden()) {
01793             return true;
01794         }
01795 
01796         if ($allinputsnull) {
01797             $grade->finalgrade = null;
01798             $result = true;
01799 
01800         } else {
01801 
01802             // do the calculation
01803             $this->formula->set_params($params);
01804             $result = $this->formula->evaluate();
01805 
01806             if ($result === false) {
01807                 $grade->finalgrade = null;
01808 
01809             } else {
01810                 // normalize
01811                 $grade->finalgrade = $this->bounded_grade($result);
01812             }
01813 
01814         }
01815 
01816         // update in db if changed
01817         if (grade_floats_different($grade->finalgrade, $oldfinalgrade)) {
01818             $grade->timemodified = time();
01819             $grade->update('compute');
01820         }
01821 
01822         if ($result !== false) {
01823             //lock grade if needed
01824         }
01825 
01826         if ($result === false) {
01827             return false;
01828         } else {
01829             return true;
01830         }
01831 
01832     }
01833 
01839     public function validate_formula($formulastr) {
01840         global $CFG, $DB;
01841         require_once($CFG->libdir.'/mathslib.php');
01842 
01843         $formulastr = grade_item::normalize_formula($formulastr, $this->courseid);
01844 
01845         if (empty($formulastr)) {
01846             return true;
01847         }
01848 
01849         if (strpos($formulastr, '=') !== 0) {
01850             return get_string('errorcalculationnoequal', 'grades');
01851         }
01852 
01853         // get used items
01854         if (preg_match_all('/##gi(\d+)##/', $formulastr, $matches)) {
01855             $useditems = array_unique($matches[1]); // remove duplicates
01856         } else {
01857             $useditems = array();
01858         }
01859 
01860         // MDL-11902
01861         // unset the value if formula is trying to reference to itself
01862         // but array keys does not match itemid
01863         if (!empty($this->id)) {
01864             $useditems = array_diff($useditems, array($this->id));
01865             //unset($useditems[$this->id]);
01866         }
01867 
01868         // prepare formula and init maths library
01869         $formula = preg_replace('/##(gi\d+)##/', '\1', $formulastr);
01870         $formula = new calc_formula($formula);
01871 
01872 
01873         if (empty($useditems)) {
01874             $grade_items = array();
01875 
01876         } else {
01877             list($usql, $params) = $DB->get_in_or_equal($useditems);
01878             $params[] = $this->courseid;
01879             $sql = "SELECT gi.*
01880                       FROM {grade_items} gi
01881                      WHERE gi.id $usql and gi.courseid=?"; // from the same course only!
01882 
01883             if (!$grade_items = $DB->get_records_sql($sql, $params)) {
01884                 $grade_items = array();
01885             }
01886         }
01887 
01888         $params = array();
01889         foreach ($useditems as $itemid) {
01890             // make sure all grade items exist in this course
01891             if (!array_key_exists($itemid, $grade_items)) {
01892                 return false;
01893             }
01894             // use max grade when testing formula, this should be ok in 99.9%
01895             // division by 0 is one of possible problems
01896             $params['gi'.$grade_items[$itemid]->id] = $grade_items[$itemid]->grademax;
01897         }
01898 
01899         // do the calculation
01900         $formula->set_params($params);
01901         $result = $formula->evaluate();
01902 
01903         // false as result indicates some problem
01904         if ($result === false) {
01905             // TODO: add more error hints
01906             return get_string('errorcalculationunknown', 'grades');
01907         } else {
01908             return true;
01909         }
01910     }
01911 
01916     public function get_displaytype() {
01917         global $CFG;
01918 
01919         if ($this->display == GRADE_DISPLAY_TYPE_DEFAULT) {
01920             return grade_get_setting($this->courseid, 'displaytype', $CFG->grade_displaytype);
01921 
01922         } else {
01923             return $this->display;
01924         }
01925     }
01926 
01931     public function get_decimals() {
01932         global $CFG;
01933 
01934         if (is_null($this->decimals)) {
01935             return grade_get_setting($this->courseid, 'decimalpoints', $CFG->grade_decimalpoints);
01936 
01937         } else {
01938             return $this->decimals;
01939         }
01940     }
01941 
01948     function get_formatted_range($rangesdisplaytype=null, $rangesdecimalpoints=null) {
01949 
01950         global $USER;
01951 
01952         // Determine which display type to use for this average
01953         if (isset($USER->gradeediting) && array_key_exists($this->courseid, $USER->gradeediting) && $USER->gradeediting[$this->courseid]) {
01954             $displaytype = GRADE_DISPLAY_TYPE_REAL;
01955 
01956         } else if ($rangesdisplaytype == GRADE_REPORT_PREFERENCE_INHERIT) { // no ==0 here, please resave report and user prefs
01957             $displaytype = $this->get_displaytype();
01958 
01959         } else {
01960             $displaytype = $rangesdisplaytype;
01961         }
01962 
01963         // Override grade_item setting if a display preference (not default) was set for the averages
01964         if ($rangesdecimalpoints == GRADE_REPORT_PREFERENCE_INHERIT) {
01965             $decimalpoints = $this->get_decimals();
01966 
01967         } else {
01968             $decimalpoints = $rangesdecimalpoints;
01969         }
01970 
01971         if ($displaytype == GRADE_DISPLAY_TYPE_PERCENTAGE) {
01972             $grademin = "0 %";
01973             $grademax = "100 %";
01974 
01975         } else {
01976             $grademin = grade_format_gradevalue($this->grademin, $this, true, $displaytype, $decimalpoints);
01977             $grademax = grade_format_gradevalue($this->grademax, $this, true, $displaytype, $decimalpoints);
01978         }
01979 
01980         return $grademin.'&ndash;'. $grademax;
01981     }
01982 
01987     public function get_coefstring() {
01988         $parent_category = $this->load_parent_category();
01989         if ($this->is_category_item()) {
01990             $parent_category = $parent_category->load_parent_category();
01991         }
01992 
01993         if ($parent_category->is_aggregationcoef_used()) {
01994             return $parent_category->get_coefstring();
01995         } else {
01996             return false;
01997         }
01998     }
01999 }
 All Data Structures Namespaces Files Functions Variables Enumerations