/** * Generates and saves final grades in associated category grade item. * These immediate children must already have their own final grades. * The category's aggregation method is used to generate final grades. * * Please note that category grade is either calculated or aggregated, not both at the same time. * * This method must be used ONLY from grade_item::regrade_final_grades(), * because the calculation must be done in correct order! * * Steps to follow: * 1. Get final grades from immediate children * 3. Aggregate these grades * 4. Save them in final grades of associated category grade item * * @param int $userid The user ID if final grade generation should be limited to a single user * @return bool */ public function generate_grades($userid = null) { global $CFG, $DB; $this->load_grade_item(); if ($this->grade_item->is_locked()) { return true; // no need to recalculate locked items } // find grade items of immediate children (category or grade items) and force site settings $depends_on = $this->grade_item->depends_on(); if (empty($depends_on)) { $items = false; } else { list($usql, $params) = $DB->get_in_or_equal($depends_on); $sql = "SELECT *\n FROM {grade_items}\n WHERE id {$usql}"; $items = $DB->get_records_sql($sql, $params); } // needed mostly for SUM agg type $this->auto_update_max($items); $grade_inst = new grade_grade(); $fields = 'g.' . implode(',g.', $grade_inst->required_fields); // where to look for final grades - include grade of this item too, we will store the results there $gis = array_merge($depends_on, array($this->grade_item->id)); list($usql, $params) = $DB->get_in_or_equal($gis); if ($userid) { $usersql = "AND g.userid=?"; $params[] = $userid; } else { $usersql = ""; } $sql = "SELECT {$fields}\n FROM {grade_grades} g, {grade_items} gi\n WHERE gi.id = g.itemid AND gi.id {$usql} {$usersql}\n ORDER BY g.userid"; // group the results by userid and aggregate the grades for this user $rs = $DB->get_recordset_sql($sql, $params); if ($rs->valid()) { $prevuser = 0; $grade_values = array(); $excluded = array(); $oldgrade = null; foreach ($rs as $used) { if ($used->userid != $prevuser) { $this->aggregate_grades($prevuser, $items, $grade_values, $oldgrade, $excluded); $prevuser = $used->userid; $grade_values = array(); $excluded = array(); $oldgrade = null; } $grade_values[$used->itemid] = $used->finalgrade; if ($used->excluded) { $excluded[] = $used->itemid; } if ($this->grade_item->id == $used->itemid) { $oldgrade = $used; } } $this->aggregate_grades($prevuser, $items, $grade_values, $oldgrade, $excluded); //the last one } $rs->close(); return true; }
protected function sub_test_grade_item_depends_on() { global $CFG; $origenableoutcomes = $CFG->enableoutcomes; $CFG->enableoutcomes = 0; $grade_item = new grade_item($this->grade_items[1], false); // Calculated grade dependency. $deps = $grade_item->depends_on(); sort($deps, SORT_NUMERIC); // For comparison. $this->assertEquals(array($this->grade_items[0]->id), $deps); // Simulate depends on returns none when locked. $grade_item->locked = time(); $grade_item->update(); $deps = $grade_item->depends_on(); sort($deps, SORT_NUMERIC); // For comparison. $this->assertEquals(array(), $deps); // Category dependency. $grade_item = new grade_item($this->grade_items[3], false); $deps = $grade_item->depends_on(); sort($deps, SORT_NUMERIC); // For comparison. $res = array($this->grade_items[4]->id, $this->grade_items[5]->id); $this->assertEquals($res, $deps); $CFG->enableoutcomes = 1; $origgradeincludescalesinaggregation = $CFG->grade_includescalesinaggregation; $CFG->grade_includescalesinaggregation = 1; // Item in category with aggregate sub categories + $CFG->grade_includescalesinaggregation = 1. $grade_item = new grade_item($this->grade_items[12], false); $deps = $grade_item->depends_on(); sort($deps, SORT_NUMERIC); $res = array($this->grade_items[15]->id, $this->grade_items[16]->id); $this->assertEquals($res, $deps); // Item in category with aggregate sub categories + $CFG->grade_includescalesinaggregation = 0. $CFG->grade_includescalesinaggregation = 0; $grade_item = new grade_item($this->grade_items[12], false); $deps = $grade_item->depends_on(); sort($deps, SORT_NUMERIC); $res = array($this->grade_items[15]->id); $this->assertEquals($res, $deps); $CFG->grade_includescalesinaggregation = 1; // Outcome item in category with with aggregate sub categories. $CFG->enableoutcomes = 0; $grade_item = new grade_item($this->grade_items[12], false); $deps = $grade_item->depends_on(); sort($deps, SORT_NUMERIC); $res = array($this->grade_items[15]->id, $this->grade_items[16]->id, $this->grade_items[17]->id); $this->assertEquals($res, $deps); $CFG->enableoutcomes = $origenableoutcomes; $CFG->grade_includescalesinaggregation = $origgradeincludescalesinaggregation; }
/** * Some aggregation types may need to update their max grade. * * This must be executed after updating the weights as it relies on them. * * @return void */ private function auto_update_max() { global $DB; if ($this->aggregation != GRADE_AGGREGATE_SUM) { // not needed at all return; } // Find grade items of immediate children (category or grade items) and force site settings. $this->load_grade_item(); $depends_on = $this->grade_item->depends_on(); $items = false; if (!empty($depends_on)) { list($usql, $params) = $DB->get_in_or_equal($depends_on); $sql = "SELECT *\n FROM {grade_items}\n WHERE id {$usql}"; $items = $DB->get_records_sql($sql, $params); } if (!$items) { if ($this->grade_item->grademax != 0 or $this->grade_item->gradetype != GRADE_TYPE_VALUE) { $this->grade_item->grademax = 0; $this->grade_item->grademin = 0; $this->grade_item->gradetype = GRADE_TYPE_VALUE; $this->grade_item->update('aggregation'); } return; } //find max grade possible $maxes = array(); foreach ($items as $item) { if ($item->aggregationcoef > 0) { // extra credit from this activity - does not affect total continue; } else { if ($item->aggregationcoef2 <= 0) { // Items with a weight of 0 do not affect the total. continue; } } if ($item->gradetype == GRADE_TYPE_VALUE) { $maxes[$item->id] = $item->grademax; } else { if ($item->gradetype == GRADE_TYPE_SCALE) { $maxes[$item->id] = $item->grademax; // 0 = nograde, 1 = first scale item, 2 = second scale item } } } if ($this->can_apply_limit_rules()) { // Apply droplow and keephigh. $this->apply_limit_rules($maxes, $items); } $max = array_sum($maxes); // update db if anything changed if ($this->grade_item->grademax != $max or $this->grade_item->grademin != 0 or $this->grade_item->gradetype != GRADE_TYPE_VALUE) { $this->grade_item->grademax = $max; $this->grade_item->grademin = 0; $this->grade_item->gradetype = GRADE_TYPE_VALUE; $this->grade_item->update('aggregation'); } }
protected function sub_test_grade_item_depends_on() { $grade_item = new grade_item($this->grade_items[1], false); // calculated grade dependency $deps = $grade_item->depends_on(); sort($deps, SORT_NUMERIC); // for comparison $this->assertEquals(array($this->grade_items[0]->id), $deps); // simulate depends on returns none when locked $grade_item->locked = time(); $grade_item->update(); $deps = $grade_item->depends_on(); sort($deps, SORT_NUMERIC); // for comparison $this->assertEquals(array(), $deps); // category dependency $grade_item = new grade_item($this->grade_items[3], false); $deps = $grade_item->depends_on(); sort($deps, SORT_NUMERIC); // for comparison $res = array($this->grade_items[4]->id, $this->grade_items[5]->id); $this->assertEquals($res, $deps); }
/** * Some aggregation types may need to update their max grade. * * This must be executed after updating the weights as it relies on them. * * @return void */ private function auto_update_max() { global $DB; if ($this->aggregation != GRADE_AGGREGATE_SUM) { // not needed at all return; } // Find grade items of immediate children (category or grade items) and force site settings. $this->load_grade_item(); $depends_on = $this->grade_item->depends_on(); // Check to see if the gradebook is frozen. This allows grades to not be altered at all until a user verifies that they // wish to update the grades. $gradebookcalculationsfreeze = get_config('core', 'gradebook_calculations_freeze_' . $this->courseid); // Only run if the gradebook isn't frozen. if ($gradebookcalculationsfreeze && (int) $gradebookcalculationsfreeze <= 20150627) { // Do nothing. } else { // Don't automatically update the max for calculated items. if ($this->grade_item->is_calculated()) { return; } } $items = false; if (!empty($depends_on)) { list($usql, $params) = $DB->get_in_or_equal($depends_on); $sql = "SELECT *\n FROM {grade_items}\n WHERE id {$usql}"; $items = $DB->get_records_sql($sql, $params); } if (!$items) { if ($this->grade_item->grademax != 0 or $this->grade_item->gradetype != GRADE_TYPE_VALUE) { $this->grade_item->grademax = 0; $this->grade_item->grademin = 0; $this->grade_item->gradetype = GRADE_TYPE_VALUE; $this->grade_item->update('aggregation'); } return; } //find max grade possible $maxes = array(); foreach ($items as $item) { if ($item->aggregationcoef > 0) { // extra credit from this activity - does not affect total continue; } else { if ($item->aggregationcoef2 <= 0) { // Items with a weight of 0 do not affect the total. continue; } } if ($item->gradetype == GRADE_TYPE_VALUE) { $maxes[$item->id] = $item->grademax; } else { if ($item->gradetype == GRADE_TYPE_SCALE) { $maxes[$item->id] = $item->grademax; // 0 = nograde, 1 = first scale item, 2 = second scale item } } } if ($this->can_apply_limit_rules()) { // Apply droplow and keephigh. $this->apply_limit_rules($maxes, $items); } $max = array_sum($maxes); // update db if anything changed if ($this->grade_item->grademax != $max or $this->grade_item->grademin != 0 or $this->grade_item->gradetype != GRADE_TYPE_VALUE) { $this->grade_item->grademax = $max; $this->grade_item->grademin = 0; $this->grade_item->gradetype = GRADE_TYPE_VALUE; $this->grade_item->update('aggregation'); } }
protected function scales_outcomes_test_grade_item_depends_on() { $CFG->enableoutcomes = 1; $origgradeincludescalesinaggregation = $CFG->grade_includescalesinaggregation; $CFG->grade_includescalesinaggregation = 1; // Scale item in category with $CFG->grade_includescalesinaggregation = 1. $grade_item = new grade_item($this->grade_items[14], false); $deps = $grade_item->depends_on(); sort($deps, SORT_NUMERIC); $res = array($this->grade_items[16]->id); $this->assertEquals($res, $deps); // Scale item in category with $CFG->grade_includescalesinaggregation = 0. $CFG->grade_includescalesinaggregation = 0; $grade_item = new grade_item($this->grade_items[14], false); $deps = $grade_item->depends_on(); $res = array(); $this->assertEquals($res, $deps); $CFG->grade_includescalesinaggregation = 1; // Outcome item in category with outcomes disabled. $CFG->enableoutcomes = 0; $grade_item = new grade_item($this->grade_items[14], false); $deps = $grade_item->depends_on(); sort($deps, SORT_NUMERIC); $res = array($this->grade_items[16]->id, $this->grade_items[17]->id); $this->assertEquals($res, $deps); $CFG->enableoutcomes = $origenableoutcomes; $CFG->grade_includescalesinaggregation = $origgradeincludescalesinaggregation; }