|
Moodle
2.2.1
http://www.collinsharper.com
|
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.'–'. $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 }