コード例 #1
0
 public function test_task_timefilter()
 {
     $task = new \local_gradelock\task\lock_grades();
     $grade_grade = new grade_grade();
     $grade_grade->itemid = $this->grade_items[0]->id;
     $grade_grade->userid = 10;
     $grade_grade->rawgrade = 88;
     $grade_grade->rawgrademax = 110;
     $grade_grade->rawgrademin = 18;
     $grade_grade->load_grade_item();
     $grade_grade->insert();
     $grade_grade->grade_item->update_final_grade($this->user[0]->id, 100, 'gradebook', '', FORMAT_MOODLE);
     $grade_grade->update();
     $task->execute();
     $grade_grade = grade_grade::fetch(array('userid' => $this->user[0]->id, 'itemid' => $this->grade_items[0]->id));
     $this->assertFalse($grade_grade->is_locked());
 }
コード例 #2
0
/**
 * Grading cron job. Performs background clean up on the gradebook
 */
function grade_cron()
{
    global $CFG, $DB;
    $now = time();
    $sql = "SELECT i.*\n              FROM {grade_items} i\n             WHERE i.locked = 0 AND i.locktime > 0 AND i.locktime < ? AND EXISTS (\n                SELECT 'x' FROM {grade_items} c WHERE c.itemtype='course' AND c.needsupdate=0 AND c.courseid=i.courseid)";
    // go through all courses that have proper final grades and lock them if needed
    $rs = $DB->get_recordset_sql($sql, array($now));
    foreach ($rs as $item) {
        $grade_item = new grade_item($item, false);
        $grade_item->locked = $now;
        $grade_item->update('locktime');
    }
    $rs->close();
    $grade_inst = new grade_grade();
    $fields = 'g.' . implode(',g.', $grade_inst->required_fields);
    $sql = "SELECT {$fields}\n              FROM {grade_grades} g, {grade_items} i\n             WHERE g.locked = 0 AND g.locktime > 0 AND g.locktime < ? AND g.itemid=i.id AND EXISTS (\n                SELECT 'x' FROM {grade_items} c WHERE c.itemtype='course' AND c.needsupdate=0 AND c.courseid=i.courseid)";
    // go through all courses that have proper final grades and lock them if needed
    $rs = $DB->get_recordset_sql($sql, array($now));
    foreach ($rs as $grade) {
        $grade_grade = new grade_grade($grade, false);
        $grade_grade->locked = $now;
        $grade_grade->update('locktime');
    }
    $rs->close();
    //TODO: do not run this cleanup every cron invocation
    // cleanup history tables
    if (!empty($CFG->gradehistorylifetime)) {
        // value in days
        $histlifetime = $now - $CFG->gradehistorylifetime * 3600 * 24;
        $tables = array('grade_outcomes_history', 'grade_categories_history', 'grade_items_history', 'grade_grades_history', 'scale_history');
        foreach ($tables as $table) {
            if ($DB->delete_records_select($table, "timemodified < ?", array($histlifetime))) {
                mtrace("    Deleted old grade history records from '{$table}'");
            }
        }
    }
}
コード例 #3
0
ファイル: restorelib.php プロジェクト: kai707/ITSA-backup
/**
 * This function migrades all the pre 1.9 gradebook data from xml
 */
function restore_migrate_old_gradebook($restore, $xml_file)
{
    global $CFG;
    $status = true;
    //Check it exists
    if (!file_exists($xml_file)) {
        return false;
    }
    // Get info from xml
    // info will contain the number of record to process
    $info = restore_read_xml_gradebook($restore, $xml_file);
    // If we have info, then process
    if (empty($info)) {
        return $status;
    }
    // make sure top course category exists
    $course_category = grade_category::fetch_course_category($restore->course_id);
    $course_category->load_grade_item();
    // we need to know if all grade items that were backed up are being restored
    // if that is not the case, we do not restore grade categories nor gradeitems of category type or course type
    // i.e. the aggregated grades of that category
    $restoreall = true;
    // set to false if any grade_item is not selected/restored
    $importing = !empty($SESSION->restore->importing);
    // there should not be a way to import old backups, but anyway ;-)
    if ($importing) {
        $restoreall = false;
    } else {
        $prev_grade_items = grade_item::fetch_all(array('courseid' => $restore->course_id));
        $prev_grade_cats = grade_category::fetch_all(array('courseid' => $restore->course_id));
        // if any categories already present, skip restore of categories from backup
        if (count($prev_grade_items) > 1 or count($prev_grade_cats) > 1) {
            $restoreall = false;
        }
        unset($prev_grade_items);
        unset($prev_grade_cats);
    }
    // force creation of all grade_items - the course_modules already exist
    grade_force_full_regrading($restore->course_id);
    grade_grab_course_grades($restore->course_id);
    // Start ul
    if (!defined('RESTORE_SILENTLY')) {
        echo '<ul>';
    }
    /// Process letters
    $context = get_context_instance(CONTEXT_COURSE, $restore->course_id);
    // respect current grade letters if defined
    if ($status and $restoreall and !record_exists('grade_letters', 'contextid', $context->id)) {
        if (!defined('RESTORE_SILENTLY')) {
            echo '<li>' . get_string('gradeletters', 'grades') . '</li>';
        }
        // Fetch recordset_size records in each iteration
        $recs = get_records_select("backup_ids", "table_name = 'grade_letter' AND backup_code = {$restore->backup_unique_code}", "", "old_id");
        if ($recs) {
            foreach ($recs as $rec) {
                // Get the full record from backup_ids
                $data = backup_getid($restore->backup_unique_code, 'grade_letter', $rec->old_id);
                if ($data) {
                    $info = $data->info;
                    $dbrec = new object();
                    $dbrec->contextid = $context->id;
                    $dbrec->lowerboundary = backup_todb($info['GRADE_LETTER']['#']['GRADE_LOW']['0']['#']);
                    $dbrec->letter = backup_todb($info['GRADE_LETTER']['#']['LETTER']['0']['#']);
                    insert_record('grade_letters', $dbrec);
                }
            }
        }
    }
    if (!defined('RESTORE_SILENTLY')) {
        echo '<li>' . get_string('categories', 'grades') . '</li>';
    }
    //Fetch recordset_size records in each iteration
    $recs = get_records_select("backup_ids", "table_name = 'grade_category' AND backup_code = {$restore->backup_unique_code}", "old_id", "old_id");
    $cat_count = count($recs);
    if ($recs) {
        foreach ($recs as $rec) {
            //Get the full record from backup_ids
            $data = backup_getid($restore->backup_unique_code, 'grade_category', $rec->old_id);
            if ($data) {
                //Now get completed xmlized object
                $info = $data->info;
                if ($restoreall) {
                    if ($cat_count == 1) {
                        $course_category->fullname = backup_todb($info['GRADE_CATEGORY']['#']['NAME']['0']['#'], false);
                        $course_category->droplow = backup_todb($info['GRADE_CATEGORY']['#']['DROP_X_LOWEST']['0']['#'], false);
                        $course_category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2;
                        $course_category->aggregateonlygraded = 0;
                        $course_category->update('restore');
                        $grade_category = $course_category;
                    } else {
                        $grade_category = new grade_category();
                        $grade_category->courseid = $restore->course_id;
                        $grade_category->fullname = backup_todb($info['GRADE_CATEGORY']['#']['NAME']['0']['#'], false);
                        $grade_category->droplow = backup_todb($info['GRADE_CATEGORY']['#']['DROP_X_LOWEST']['0']['#'], false);
                        $grade_category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2;
                        $grade_category->aggregateonlygraded = 0;
                        $grade_category->insert('restore');
                        $grade_category->load_grade_item();
                        // force cretion of grade_item
                    }
                } else {
                    $grade_category = null;
                }
                /// now, restore grade_items
                $items = array();
                if (!empty($info['GRADE_CATEGORY']['#']['GRADE_ITEMS']['0']['#']['GRADE_ITEM'])) {
                    //Iterate over items
                    foreach ($info['GRADE_CATEGORY']['#']['GRADE_ITEMS']['0']['#']['GRADE_ITEM'] as $ite_info) {
                        $modname = backup_todb($ite_info['#']['MODULE_NAME']['0']['#'], false);
                        $olditeminstance = backup_todb($ite_info['#']['CMINSTANCE']['0']['#'], false);
                        if (!($mod = backup_getid($restore->backup_unique_code, $modname, $olditeminstance))) {
                            continue;
                            // not restored
                        }
                        $iteminstance = $mod->new_id;
                        if (!($cm = get_coursemodule_from_instance($modname, $iteminstance, $restore->course_id))) {
                            continue;
                            // does not exist
                        }
                        if (!($grade_item = grade_item::fetch(array('itemtype' => 'mod', 'itemmodule' => $cm->modname, 'iteminstance' => $cm->instance, 'courseid' => $cm->course, 'itemnumber' => 0)))) {
                            continue;
                            // no item yet??
                        }
                        if ($grade_category) {
                            $grade_item->sortorder = backup_todb($ite_info['#']['SORT_ORDER']['0']['#'], false);
                            $grade_item->set_parent($grade_category->id);
                        }
                        if ($importing or $grade_item->itemtype == 'mod' and !restore_userdata_selected($restore, $grade_item->itemmodule, $olditeminstance)) {
                            // module instance not selected when restored using granular
                            // skip this item
                            continue;
                        }
                        //Now process grade excludes
                        if (empty($ite_info['#']['GRADE_EXCEPTIONS'])) {
                            continue;
                        }
                        foreach ($ite_info['#']['GRADE_EXCEPTIONS']['0']['#']['GRADE_EXCEPTION'] as $exc_info) {
                            if ($u = backup_getid($restore->backup_unique_code, "user", backup_todb($exc_info['#']['USERID']['0']['#']))) {
                                $userid = $u->new_id;
                                $grade_grade = new grade_grade(array('itemid' => $grade_item->id, 'userid' => $userid));
                                $grade_grade->excluded = 1;
                                if ($grade_grade->id) {
                                    $grade_grade->update('restore');
                                } else {
                                    $grade_grade->insert('restore');
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    if (!defined('RESTORE_SILENTLY')) {
        //End ul
        echo '</ul>';
    }
    return $status;
}
コード例 #4
0
ファイル: grade_item.php プロジェクト: r007/PMoodle
 /**
  * internal function - does the final grade calculation
  */
 function use_formula($userid, $params, $useditems, $oldgrade)
 {
     if (empty($userid)) {
         return true;
     }
     // add missing final grade values
     // not graded (null) is counted as 0 - the spreadsheet way
     foreach ($useditems as $gi) {
         if (!array_key_exists('gi' . $gi, $params)) {
             $params['gi' . $gi] = 0;
         } else {
             $params['gi' . $gi] = (double) $params['gi' . $gi];
         }
     }
     // can not use own final grade during calculation
     unset($params['gi' . $this->id]);
     // insert final grade - will be needed later anyway
     if ($oldgrade) {
         $oldfinalgrade = $oldgrade->finalgrade;
         $grade = new grade_grade($oldgrade, false);
         // fetching from db is not needed
         $grade->grade_item =& $this;
     } else {
         $grade = new grade_grade(array('itemid' => $this->id, 'userid' => $userid), false);
         $grade->grade_item =& $this;
         $grade->insert('system');
         $oldfinalgrade = null;
     }
     // no need to recalculate locked or overridden grades
     if ($grade->is_locked() or $grade->is_overridden()) {
         return true;
     }
     // do the calculation
     $this->formula->set_params($params);
     $result = $this->formula->evaluate();
     if ($result === false) {
         $grade->finalgrade = null;
     } else {
         // normalize
         $result = bounded_number($this->grademin, $result, $this->grademax);
         if ($this->gradetype == GRADE_TYPE_SCALE) {
             $result = round($result + 1.0E-5);
             // round scales upwards
         }
         $grade->finalgrade = $result;
     }
     // update in db if changed
     if (grade_floats_different($grade->finalgrade, $oldfinalgrade)) {
         $grade->update('compute');
     }
     if ($result !== false) {
         //lock grade if needed
     }
     if ($result === false) {
         return false;
     } else {
         return true;
     }
 }
コード例 #5
0
 /**
  * Lock the grade if needed. Make sure this is called only when final grades are valid
  *
  * @param array $items array of all grade item ids
  * @return void
  */
 public static function check_locktime_all($items)
 {
     global $CFG, $DB;
     $now = time();
     // no rounding needed, this is not supposed to be called every 10 seconds
     list($usql, $params) = $DB->get_in_or_equal($items);
     $params[] = $now;
     $rs = $DB->get_recordset_select('grade_grades', "itemid {$usql} AND locked = 0 AND locktime > 0 AND locktime < ?", $params);
     foreach ($rs as $grade) {
         $grade_grade = new grade_grade($grade, false);
         $grade_grade->locked = time();
         $grade_grade->update('locktime');
     }
     $rs->close();
 }
コード例 #6
0
ファイル: grade_item.php プロジェクト: mongo0se/moodle
 /**
  * Internal function that does the final grade calculation
  *
  * @param int $userid The user ID
  * @param array $params An array of grade items of the form {'gi'.$itemid]} => $finalgrade
  * @param array $useditems An array of grade item IDs that this grade item depends on plus its own ID
  * @param grade_grade $oldgrade A grade_grade instance containing the old values from the database
  * @return bool False if an error occurred
  */
 public function use_formula($userid, $params, $useditems, $oldgrade)
 {
     if (empty($userid)) {
         return true;
     }
     // add missing final grade values
     // not graded (null) is counted as 0 - the spreadsheet way
     $allinputsnull = true;
     foreach ($useditems as $gi) {
         if (!array_key_exists('gi' . $gi, $params) || is_null($params['gi' . $gi])) {
             $params['gi' . $gi] = 0;
         } else {
             $params['gi' . $gi] = (double) $params['gi' . $gi];
             if ($gi != $this->id) {
                 $allinputsnull = false;
             }
         }
     }
     // can not use own final grade during calculation
     unset($params['gi' . $this->id]);
     // 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);
     $rawminandmaxchanged = false;
     // insert final grade - will be needed later anyway
     if ($oldgrade) {
         // Only run through this code if the gradebook isn't frozen.
         if ($gradebookcalculationsfreeze && (int) $gradebookcalculationsfreeze <= 20150627) {
             // Do nothing.
         } else {
             // The grade_grade for a calculated item should have the raw grade maximum and minimum set to the
             // grade_item grade maximum and minimum respectively.
             if ($oldgrade->rawgrademax != $this->grademax || $oldgrade->rawgrademin != $this->grademin) {
                 $rawminandmaxchanged = true;
                 $oldgrade->rawgrademax = $this->grademax;
                 $oldgrade->rawgrademin = $this->grademin;
             }
         }
         $oldfinalgrade = $oldgrade->finalgrade;
         $grade = new grade_grade($oldgrade, false);
         // fetching from db is not needed
         $grade->grade_item =& $this;
     } else {
         $grade = new grade_grade(array('itemid' => $this->id, 'userid' => $userid), false);
         $grade->grade_item =& $this;
         $rawminandmaxchanged = false;
         if ($gradebookcalculationsfreeze && (int) $gradebookcalculationsfreeze <= 20150627) {
             // Do nothing.
         } else {
             // The grade_grade for a calculated item should have the raw grade maximum and minimum set to the
             // grade_item grade maximum and minimum respectively.
             $rawminandmaxchanged = true;
             $grade->rawgrademax = $this->grademax;
             $grade->rawgrademin = $this->grademin;
         }
         $grade->insert('system');
         $oldfinalgrade = null;
     }
     // no need to recalculate locked or overridden grades
     if ($grade->is_locked() or $grade->is_overridden()) {
         return true;
     }
     if ($allinputsnull) {
         $grade->finalgrade = null;
         $result = true;
     } else {
         // do the calculation
         $this->formula->set_params($params);
         $result = $this->formula->evaluate();
         if ($result === false) {
             $grade->finalgrade = null;
         } else {
             // normalize
             $grade->finalgrade = $this->bounded_grade($result);
         }
     }
     // Only run through this code if the gradebook isn't frozen.
     if ($gradebookcalculationsfreeze && (int) $gradebookcalculationsfreeze <= 20150627) {
         // Update in db if changed.
         if (grade_floats_different($grade->finalgrade, $oldfinalgrade)) {
             $grade->timemodified = time();
             $success = $grade->update('compute');
             // If successful trigger a user_graded event.
             if ($success) {
                 \core\event\user_graded::create_from_grade($grade)->trigger();
             }
         }
     } else {
         // Update in db if changed.
         if (grade_floats_different($grade->finalgrade, $oldfinalgrade) || $rawminandmaxchanged) {
             $grade->timemodified = time();
             $success = $grade->update('compute');
             // If successful trigger a user_graded event.
             if ($success) {
                 \core\event\user_graded::create_from_grade($grade)->trigger();
             }
         }
     }
     if ($result !== false) {
         //lock grade if needed
     }
     if ($result === false) {
         return false;
     } else {
         return true;
     }
 }
コード例 #7
0
 /**
  * Lock the grade if needed - make sure this is called only when final grades are valid
  * @param array $items array of all grade item ids
  * @return void
  */
 function check_locktime_all($items)
 {
     global $CFG;
     $items_sql = implode(',', $items);
     $now = time();
     // no rounding needed, this is not supposed to be called every 10 seconds
     if ($rs = get_recordset_select('grade_grades', "itemid IN ({$items_sql}) AND locked = 0 AND locktime > 0 AND locktime < {$now}")) {
         while ($grade = rs_fetch_next_record($rs)) {
             $grade_grade = new grade_grade($grade, false);
             $grade_grade->locked = time();
             $grade_grade->update('locktime');
         }
         rs_close($rs);
     }
 }
コード例 #8
0
             } else {
                 tlog('   ' . strtoupper($target) . ' (' . $score . ') inserted for user ' . $enrollee->userid . ' on course ' . $course->id . '.');
             }
         } else {
             // If the row already exists, UPDATE, but don't ever *update* the TAG.
             if ($target == 'mag' && !$score) {
                 // For MAGs, we don't want to update to a zero or null score as that may overwrite a manually-entered MAG.
                 tlog('   ' . strtoupper($target) . ' of 0 or null (' . $score . ') purposefully not updated for user ' . $enrollee->userid . ' on course ' . $course->id . '.');
                 $logging['not_updated'][] = $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->studentid . ') [' . $enrollee->userid . '] on course ' . $course->id . ': ' . strtoupper($target) . ' of \'' . $score . '\'.';
             } else {
                 if ($target != 'tag') {
                     $grade->id = $gradegrade->id;
                     // We don't want to set this again, but we do want the modified time set.
                     unset($grade->timecreated);
                     $grade->timemodified = time();
                     if (!($gl = $grade->update())) {
                         tlog('   ' . strtoupper($target) . ' update failed for user ' . $enrollee->userid . ' on course ' . $course->id . '.', 'EROR');
                     } else {
                         tlog('   ' . strtoupper($target) . ' (' . $score . ') update for user ' . $enrollee->userid . ' on course ' . $course->id . '.');
                     }
                 } else {
                     tlog('   ' . strtoupper($target) . ' purposefully not updated for user ' . $enrollee->userid . ' on course ' . $course->id . '.', 'skip');
                     $logging['not_updated'][] = $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->studentid . ') [' . $enrollee->userid . '] on course ' . $course->id . ': ' . strtoupper($target) . ' of \'' . $score . '\'.';
                 }
             }
             // END ignore updating the TAG.
         }
         // END insert or update check.
     }
     // END foreach loop.
 }
コード例 #9
0
 /**
  * Internal function that does the final grade calculation
  *
  * @param int $userid The user ID
  * @param array $params An array of grade items of the form {'gi'.$itemid]} => $finalgrade
  * @param array $useditems An array of grade item IDs that this grade item depends on plus its own ID
  * @param grade_grade $oldgrade A grade_grade instance containing the old values from the database
  * @return bool False if an error occurred
  */
 public function use_formula($userid, $params, $useditems, $oldgrade)
 {
     if (empty($userid)) {
         return true;
     }
     // add missing final grade values
     // not graded (null) is counted as 0 - the spreadsheet way
     $allinputsnull = true;
     foreach ($useditems as $gi) {
         if (!array_key_exists('gi' . $gi, $params) || is_null($params['gi' . $gi])) {
             $params['gi' . $gi] = 0;
         } else {
             $params['gi' . $gi] = (double) $params['gi' . $gi];
             if ($gi != $this->id) {
                 $allinputsnull = false;
             }
         }
     }
     // can not use own final grade during calculation
     unset($params['gi' . $this->id]);
     // insert final grade - will be needed later anyway
     if ($oldgrade) {
         $oldfinalgrade = $oldgrade->finalgrade;
         $grade = new grade_grade($oldgrade, false);
         // fetching from db is not needed
         $grade->grade_item =& $this;
     } else {
         $grade = new grade_grade(array('itemid' => $this->id, 'userid' => $userid), false);
         $grade->grade_item =& $this;
         $grade->insert('system');
         $oldfinalgrade = null;
     }
     // no need to recalculate locked or overridden grades
     if ($grade->is_locked() or $grade->is_overridden()) {
         return true;
     }
     if ($allinputsnull) {
         $grade->finalgrade = null;
         $result = true;
     } else {
         // do the calculation
         $this->formula->set_params($params);
         $result = $this->formula->evaluate();
         if ($result === false) {
             $grade->finalgrade = null;
         } else {
             // normalize
             $grade->finalgrade = $this->bounded_grade($result);
         }
     }
     // update in db if changed
     if (grade_floats_different($grade->finalgrade, $oldfinalgrade)) {
         $grade->timemodified = time();
         $grade->update('compute');
     }
     if ($result !== false) {
         //lock grade if needed
     }
     if ($result === false) {
         return false;
     } else {
         return true;
     }
 }
コード例 #10
0
                    } else {
                        $gradeval = $grEval->finalgrade;
                    }
                    break;
                }
                //foreach($gradeEvalcomix as $grEval)
            }
            //if($gradeEvalcomix)
        }
        //else
    }
    //else
}
//if($rst = get_record('grade_grades_history', 'userid', $userid, 'itemid', $grade->grade_item->id, 'source', 'evalcomixAdd'))
//Comprobamos que el item actual no es un "resultado"
if ($grade->grade_item->itemnumber == 0) {
    $modulo = $grade->grade_item->itemmodule;
    $instancia = $grade->grade_item->iteminstance;
    $maxgrade = $grade->grade_item->grademax;
    $student = $userid;
    $courseid = $courseid;
    if (!($grade_item = grade_item::fetch(array('iteminstance' => $instancia, 'itemmodule' => $modulo, 'courseid' => $courseid, 'itemnumber' => 0)))) {
        error('Can not find grade_item 2');
    }
    $grade_old = new grade_grade(array('itemid' => $grade_item->id, 'userid' => $student));
    $overridden = $grade_old->overridden;
    $grade_item->update_final_grade($student, $gradeval, 'evalcomixDelete');
    $grade1 = new grade_grade(array('itemid' => $grade_item->id, 'userid' => $student));
    $grade1->overridden = $overridden;
    $grade1->update();
}
コード例 #11
0
ファイル: gradeevx.php プロジェクト: srpaedagogus/aulavirtual
                        if ($category->aggregation >= 100) {
                            // grade >100% hack
                            $grademax = $grademax * $maxcoef;
                        }
                    }
                }
                if ($gradeval > $grademax) {
                    $gradeval = $grademax;
                }
                if ($gradeval < $grade_item->grademin) {
                    $gradeval = $grade_item->grademin;
                }
            } elseif ($gradeval == '-' || $gradeval == '') {
                $grade_old = new grade_grade(array('itemid' => $grade_item->id, 'userid' => $student));
                $gradeval = $finalgrade;
                $gradeval *= $multfactor;
                $gradeval += $plusfactor;
                if ($gradeval > 100) {
                    $gradeval = 100;
                }
            }
            if (!$grade_item->scaleid && $grade->grade_item->itemnumber == 0) {
                $overridden = $grade_old->overridden;
                $grade_item->update_final_grade($student, $gradeval, 'evalcomixAdd');
                $grade = new grade_grade(array('itemid' => $grade_item->id, 'userid' => $student));
                $grade->overridden = $overridden;
                $grade->update('EvalcomixUpdate');
            }
        }
    }
}
コード例 #12
0
 /**
  * This is the function which runs when cron calls.
  */
 public function execute()
 {
     global $CFG, $DB;
     echo "BEGIN >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n";
     /**
      * A script, to be run overnight, to pull L3VA scores from Leap and generate
      * the MAG, for each student on specifically-tagged courses, and add it into
      * our live Moodle.
      *
      * @copyright 2014-2015 Paul Vaughan
      * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      */
     // Script start time.
     $time_start = microtime(true);
     // Null or an int (course's id): run the script only for this course. For testing or one-offs.
     // TODO: Consider changing $thiscourse as an array, not an integer.
     $thiscourse = null;
     // null or e.g. 1234
     // TODO: can we use *all* the details in version.php? It would make a lot more sense.
     $version = '1.0.20';
     //$build      = '20150128';
     $build = get_config('block_leap', 'version');
     // Debugging.
     define('DEBUG', true);
     // Debugging.
     define('TRUNCATE_LOG', true);
     // Truncate the log table.
     if (TRUNCATE_LOG) {
         echo 'Truncating block_leap_log...';
         $DB->delete_records('block_leap_log', null);
         echo " done.\n";
     }
     overnight::tlog('GradeTracker script, v' . $version . ', ' . $build . '.', 'hiya');
     overnight::tlog('Started at ' . date('c', $time_start) . '.', ' go ');
     if ($thiscourse) {
         overnight::tlog('IMPORTANT! Processing only course \'' . $thiscourse . '\'.', 'warn');
     }
     overnight::tlog('', '----');
     // Before almost anything has the chance to fail, reset the fail delay setting back to 0.
     if (DEBUG) {
         if (!($reset = $DB->set_field('task_scheduled', 'faildelay', 0, array('component' => 'block_leap', 'classname' => '\\block_leap\\task\\overnight')))) {
             overnight::tlog('Scheduled task "fail delay" could not be reset.', 'warn');
         } else {
             overnight::tlog('Scheduled task "fail delay" reset to 0.', 'dbug');
         }
     }
     $leap_url = get_config('block_leap', 'leap_url');
     $auth_token = get_config('block_leap', 'auth_token');
     define('LEAP_API_URL', $leap_url . '/people/%s/views/courses.json?token=' . $auth_token);
     //overnight::tlog( 'Leap API URL: ' . LEAP_API_URL, 'dbug' );
     // Number of decimal places in the processed targets (and elsewhere).
     define('DECIMALS', 3);
     // Search term to use when searching for courses to process.
     define('IDNUMBERLIKE', 'leapcore_%');
     //define( 'IDNUMBERLIKE', 'leapcore_test' );
     // Category details for the above columns to go into.
     define('CATNAME', get_string('gradebook:category_title', 'block_leap'));
     // Include some details.
     require dirname(__FILE__) . '/../../details.php';
     // Logging array for the end-of-script summary.
     $logging = array('courses' => array(), 'students_processed' => array(), 'students_unique' => array(), 'no_l3va' => array(), 'not_updated' => array(), 'grade_types' => array('btec' => 0, 'a level' => 0, 'gcse' => 0, 'refer and pass' => 0, 'noscale' => 0, 'develop, pass' => 0), 'poor_grades' => array(), 'num' => array('courses' => 0, 'students_processed' => 0, 'students_unique' => 0, 'no_l3va' => 0, 'not_updated' => 0, 'grade_types' => 0, 'grade_types_in_use' => 0, 'poor_grades' => 0));
     // Small array to store the GCSE English and maths grades from the JSON.
     $gcse = array('english' => null, 'maths' => null);
     // Just for internal use, defines the grade type (int) and what it is (string).
     $gradetypes = array(0 => 'None', 1 => 'Value', 2 => 'Scale', 3 => 'Text');
     // Define the wanted column names (will appear in this order in the Gradebook, initially).
     // These column names are an integral part of this plugin and should not be changed.
     $column_names = array(get_string('gradebook:tag', 'block_leap') => get_string('gradebook:tag_desc', 'block_leap'));
     // Make an array keyed to the column names to store the grades in.
     $targets = array();
     foreach ($column_names as $name => $desc) {
         $targets[strtolower($name)] = '';
     }
     /**
      * The next section looks through all courses for those with a properly configured Leap block
      * and adds it (and the tracking configuration) to the $courses array.
      */
     overnight::tlog('', '----');
     $courses = $DB->get_records('course', null, null, 'id,shortname,fullname');
     $allcourses = array();
     foreach ($courses as $course) {
         if ($course->id != 1) {
             $coursecontext = \context_course::instance($course->id);
             if (!($blockrecord = $DB->get_record('block_instances', array('blockname' => 'leap', 'parentcontextid' => $coursecontext->id)))) {
                 if (DEBUG) {
                     overnight::tlog('No Leap block found for course "' . $course->id . '" (' . $course->shortname . ')', 'dbug');
                 }
                 continue;
             }
             if (!($blockinstance = block_instance('leap', $blockrecord))) {
                 if (DEBUG) {
                     overnight::tlog('No Leap block instance found for course "' . $course->id . '" (' . $course->shortname . ')', 'dbug');
                 }
                 continue;
             }
             if (isset($blockinstance->config->trackertype) && !empty($blockinstance->config->trackertype)) {
                 $course->trackertype = $blockinstance->config->trackertype;
                 $course->scalename = null;
                 $course->scaleid = null;
                 $course->gradeid = null;
                 $allcourses[] = $course;
                 if (DEBUG) {
                     overnight::tlog('Tracker "' . $blockinstance->config->trackertype . '" found in course ' . $course->id . ' (' . $course->shortname . ')', 'dbug');
                 }
             } else {
                 if (DEBUG) {
                     overnight::tlog('<Tracker not found in course ' . $course->id . ' (' . $course->shortname . ')', 'dbug');
                 }
             }
         }
         // END if $course != 1
     }
     // END foreach $courses as $course
     /*
             foreach ( $allcourses as $course ) {
                 // Ignore the course with id = 1, as it's the front page.
                 if ( $course->id == 1 ) {
                     continue;
                 } else {
                     // First get course context.
                     $coursecontext  = \context_course::instance( $course->id );
                     $blockrecord    = $DB->get_record( 'block_instances', array( 'blockname' => 'leap', 'parentcontextid' => $coursecontext->id ) );
                     $blockinstance  = block_instance( 'leap', $blockrecord );
                     // Check and add trackertype and coursetype to the $course object.
                     if (
                         isset( $blockinstance->config->trackertype ) &&
                         !empty( $blockinstance->config->trackertype ) )
                         //!empty( $blockinstance->config->trackertype ) &&
                         //isset( $blockinstance->config->coursetype ) &&
                         //!empty( $blockinstance->config->coursetype ) )
                     {
                         $course->trackertype    = $blockinstance->config->trackertype;
                         //$course->coursetype     = $blockinstance->config->coursetype;
                         // Setting some more variables we'll need in due course.
                         $course->scalename      = null;
                         $course->scaleid        = null;
                         $course->gradeid        = null;
                         // All good, so...
                         $courses[] = $course;
                         overnight::tlog( 'Course \'' . $course->fullname . '\' (' . $course->shortname . ') [' . $course->id . '] added to process list.', 'info');
                         if ( DEBUG ) {
                             overnight::tlog( json_encode( $course ), 'dbug');
                         }
                     }
                 }
             }
     */
     //var_dump($courses); exit(0);
     /* Example $courses array.
     array(2) {
       [0]=>
       object(stdClass)#91 (7) {
         ["id"]=>
         string(1) "2"
         ["shortname"]=>
         string(5) "TC101"
         ["fullname"]=>
         string(15) "Test Course 101"
         ["trackertype"]=>
         string(7) "english"
         ["coursetype"]=>
         string(14) "a2_englishlang"
         ["scalename"]=>
         string(0) ""
         ["scaleid"]=>
         NULL
       }
       [1]=>
       object(stdClass)#92 (7) {
         ["id"]=>
         string(1) "3"
         ["shortname"]=>
         string(5) "TC201"
         ["fullname"]=>
         string(15) "Test course 201"
         ["trackertype"]=>
         string(7) "english"
         ["coursetype"]=>
         string(6) "a2_law"
         ["scalename"]=>
         string(0) ""
         ["scaleid"]=>
         NULL
       }
     }
     
     */
     $num_courses = count($allcourses);
     $cur_courses = 0;
     if ($num_courses == 0) {
         overnight::tlog('No courses found to process, so halting.', 'EROR');
         // Returning false indicates failure. We didn't fail, just found no courses to process.
         return true;
     }
     overnight::tlog('', '----');
     /**
      * Sets up each configured course with a category and columns within it.
      */
     foreach ($allcourses as $course) {
         $cur_courses++;
         overnight::tlog('Processing course (' . $cur_courses . '/' . $num_courses . ') ' . $course->fullname . ' (' . $course->shortname . ') [' . $course->id . '] at ' . date('c', time()) . '.', 'info');
         $logging['courses'][] = $course->fullname . ' (' . $course->shortname . ') [' . $course->id . '].';
         //overnight::tlog( $course->coursetype, 'PVDB' );
         /*
         We need to give serious thought to NOT doing this, as it's basically impossible to set the correct scale at this point.
         Grades and that:
         
         Develop / Pass - would be used for all pass only type qualifications but develop will be used instead of refer, retake, fail etc.
         U, G, F, E, D, C, B, A, A*  - to be used for GCSE / A-level plus any others that have letter grades.
         Refer, Pass, Merit, Distinction.
         Refer, PP, PM, MM, MD, DD
         Refer, PPP, PPM, PMM, MMM, MMD, MDD, DDD
         Numbers from 0 - 100 - in traffic light systems the numbers will be compared and anything lower than the target will be red, higher than target will be green.
         */
         /*
                     // Work out the scale from the course type.
                     if ( stristr( $course->coursetype, 'as_' ) || stristr( $course->coursetype, 'a2_' ) ) {
                         $course->scalename  = 'A Level';
                     } else if ( stristr( $course->coursetype, 'gcse_' ) ) {
                         $course->scalename  = 'GCSE';
                     } else if ( stristr( $course->coursetype, 'btec_' ) ) {
                         $course->scalename  = 'BTEC';
                     }
                     overnight::tlog( 'Course ' . $course->id . ' appears to be a ' . $course->scalename . ' course.', 'info' );
         
                     // Get the scale ID.
                     if ( !$moodlescale = $DB->get_record( 'scale', array( 'name' => $course->scalename ), 'id' ) ) {
                         overnight::tlog( '- Could not find a scale called \'' . $course->scalename . '\' for course ' . $course->id . '.', 'warn' );
         
                     } else {
                         // Scale located.
                         $course->scaleid = $moodlescale->id;
                         overnight::tlog( '- Scale called \'' . $course->scalename . '\' found with ID ' . $moodlescale->id . '.', 'info' );
                     }
         
                     overnight::tlog( json_encode( $course ), '>dbg');
                     //var_dump($courses); exit(0);
         */
         overnight::tlog(json_encode($course), '>dbg');
         // Figure out the grade type and scale here, pulled directly from the course's gradebook's course itemtype.
         $coursegradescale = $DB->get_record('grade_items', array('courseid' => $course->id, 'itemtype' => 'course'), 'gradetype, scaleid');
         $course->gradeid = $coursegradescale->gradetype;
         $course->scaleid = $coursegradescale->scaleid;
         overnight::tlog(json_encode($course), 'PVDB');
         if ($course->gradeid == 2) {
             if ($coursescale = $DB->get_record('scale', array('id' => $course->scaleid))) {
                 $course->scalename = $coursescale->name;
                 $tolog = '- Scale \'' . $course->scaleid . '\' (' . $coursescale->name . ') found [' . $coursescale->scale . ']';
                 $tolog .= $coursescale->courseid ? ' (which is specific to course ' . $coursescale->courseid . ').' : ' (which is global).';
                 overnight::tlog($tolog, 'info');
             } else {
                 // If the scale doesn't exist that the course is using, this is a problem.
                 overnight::tlog('- Gradetype \'2\' set, but no matching scale found.', 'warn');
             }
         }
         overnight::tlog(json_encode($course), 'PVDB');
         /*
                     if ( $coursegradescale = $DB->get_record( 'grade_items', array( 'courseid' => $course->id, 'itemtype' => 'course' ), 'gradetype, scaleid' ) ) {
         
            $course->gradeid = $coursegradescale->gradetype;
            $course->scaleid = $coursegradescale->scaleid;
         
            overnight::tlog( 'Gradetype \'' . $course->gradeid . '\' (' . $gradetypes[$course->gradeid] . ') found.', 'info' );
         
            // If the grade type is 2 / scale.
            if ( $course->gradeid == 2 ) {
                if ( $coursescale = $DB->get_record( 'scale', array( 'id' => $course->scaleid ) ) ) {
         
                    $course->scalename  = $coursescale->name;
                    //$course->scaleid    = $scaleid;
         
                    //$course->coursetype = $coursescale->name;
         
                    $tolog = '- Scale \'' . $course->scaleid . '\' (' . $course->scalename . ') found';
                    $tolog .= ( $coursescale->courseid ) ? ' (which is specific to course ' . $coursescale->courseid . ').' : ' (which is global).';
                    overnight::tlog( $tolog, 'info' );
         
                } else {
         
                    // If the scale doesn't exist that the course is using, this is a problem.
                    overnight::tlog( '- Gradetype \'2\' set, but no matching scale found.', 'warn' );
         
                }
         
            } else if ( $course->gradeid == 1 ) {
                // If the grade type is 1 / value.
                $course->scalename  = 'noscale';
                $course->scaleid    = 1;
                // Already set, above.
                //$course->coursetype = 'Value';
         
                $tolog = ' Using \'' . $gradetypes[$gradeid] . '\' gradetype.';
         
            }
         
         
                     } else {
            // Set it to default if no good scale could be found/used.
            $gradeid = 0;
            $scaleid = 0;
            overnight::tlog('No \'gradetype\' found, so using defaults instead.', 'info');
                     }
         
         
                     // You may get errors here (unknown index IIRC) if no scalename is generated because a scale (or anything) hasn't been set
                     // for that course (e.g. 'cos it's a new course). Catch this earlier!
                     $logging['grade_types'][strtolower($course->scalename)]++;
         
                     /**
         * Category checking: create or skip.
         */
         if ($DB->get_record('grade_categories', array('courseid' => $course->id, 'fullname' => CATNAME))) {
             // Category exists, so skip creation.
             overnight::tlog('Category \'' . CATNAME . '\' already exists for course ' . $course->id . '.', 'skip');
         } else {
             $grade_category = new \grade_category();
             // Create a category for this course.
             $grade_category->courseid = $course->id;
             // Course id.
             $grade_category->fullname = CATNAME;
             // Set the category name (no description).
             $grade_category->sortorder = 1;
             // Need a better way of changing column order.
             $grade_category->hidden = 1;
             // Attempting to hide the totals.
             // Save all that...
             if (!($gc = $grade_category->insert())) {
                 overnight::tlog('Category \'' . CATNAME . '\' could not be inserted for course ' . $course->id . '.', 'EROR');
                 return false;
             } else {
                 overnight::tlog('Category \'' . CATNAME . '\' (' . $gc . ') created for course ' . $course->id . '.');
             }
         }
         // We've either checked a category exists or created one, so this *should* always work.
         $cat_id = $DB->get_record('grade_categories', array('courseid' => $course->id, 'fullname' => CATNAME));
         $cat_id = $cat_id->id;
         // One thing we need to do is set 'gradetype' to 0 on that newly created category, which prevents a category total showing
         // and the grades counting towards the total course grade.
         $DB->set_field_select('grade_items', 'gradetype', 0, "courseid = " . $course->id . " AND itemtype = 'category' AND iteminstance = " . $cat_id);
         /**
          * Column checking: create or update.
          */
         // Step through each column name.
         foreach ($column_names as $col_name => $col_desc) {
             // Need to check for previously-created columns and force an update if they already exist.
             //if ( $DB->get_record('grade_items', array( 'courseid' => $course->id, 'itemname' => $col_name, 'itemtype' => 'manual' ) ) ) {
             //    // Column exists, so update instead.
             //    overnight::tlog('- Column \'' . $col_name . '\' already exists for course ' . $course->id . '.', 'skip');
             //} else {
             $grade_item = new \grade_item();
             // Create a new item object.
             $grade_item->courseid = $course->id;
             // Course id.
             $grade_item->itemtype = 'manual';
             // Set the category name (no description).
             $grade_item->itemname = $col_name;
             // The item's name.
             $grade_item->iteminfo = $col_desc;
             // Description of the item.
             $grade_item->categoryid = $cat_id;
             // Set the immediate parent category.
             $grade_item->hidden = 0;
             // Don't want it hidden (by default).
             $grade_item->locked = 1;
             // Lock it (by default).
             // Per-column specifics.
             if ($col_name == 'TAG') {
                 $grade_item->sortorder = 1;
                 // In-category sort order.
                 //$grade_item->gradetype  = $course->gradeid;
                 $grade_item->gradetype = 3;
                 // Text field.
                 //$grade_item->scaleid    = $course->scaleid;
                 //$grade_item->display    = 1;        // 'Real'. MIGHT need to seperate out options for BTEC and A Level.
                 $grade_item->display = 0;
                 // No frills.
             }
             //if ( $col_name == 'L3VA' ) {
             //    // Lock the L3VA col as it's calculated elsewhere.
             //    $grade_item->sortorder  = 2;
             //    $grade_item->locked     = 1;
             //    $grade_item->decimals   = 0;
             //    $grade_item->display    = 1; // 'Real'.
             //}
             //if ( $col_name == 'MAG' ) {
             //    $grade_item->sortorder  = 3;
             //    //$grade_item->locked     = 1;
             //    $grade_item->gradetype  = $gradeid;
             //    $grade_item->scaleid    = $scaleid;
             //    $grade_item->display    = 1; // 'Real'.
             //}
             // Scale ID, generated earlier. An int, 0 or greater.
             // TODO: Check if we need this any more!!
             //$grade_item->scale = $course->scaleid;
             // Check to see if this record already exists, determining if we insert or update.
             if (!($grade_items_exists = $DB->get_record('grade_items', array('courseid' => $course->id, 'itemname' => $col_name, 'itemtype' => 'manual')))) {
                 // INSERT a new record.
                 if (!($gi = $grade_item->insert())) {
                     overnight::tlog('- Column \'' . $col_name . '\' could not be inserted for course ' . $course->id . '.', 'EROR');
                     return false;
                 } else {
                     overnight::tlog('- Column \'' . $col_name . '\' created for course ' . $course->id . '.');
                 }
             } else {
                 // UPDATE the existing record.
                 $grade_item->id = $grade_items_exists->id;
                 if (!($gi = $grade_item->update())) {
                     overnight::tlog('- Column \'' . $col_name . '\' could not be updated for course ' . $course->id . '.', 'EROR');
                     return false;
                 } else {
                     overnight::tlog('- Column \'' . $col_name . '\' updated for course ' . $course->id . '.');
                 }
             }
             //} // END skip processing if manual column(s) already found in course.
         }
         // END while working through each rquired column.
         // Good to here.
         /**
          * Move the category to the first location in the gradebook if it isn't already.
          */
         //$gtree = new grade_tree($course->id, false, false);
         //$temp = grade_edit_tree::move_elements(1, '')
         /**
          * Collect enrolments based on each of those courses
          */
         // EPIC 'get enrolled students' query from Stack Overflow:
         // http://stackoverflow.com/questions/22161606/sql-query-for-courses-enrolment-on-moodle
         // Only selects manually enrolled, not self-enrolled student roles (redacted!).
         $sql = "SELECT DISTINCT u.id AS userid, firstname, lastname, username\n                FROM mdl_user u\n                    JOIN mdl_user_enrolments ue ON ue.userid = u.id\n                    JOIN mdl_enrol e ON e.id = ue.enrolid\n                        -- AND e.enrol = 'leap'\n                    JOIN mdl_role_assignments ra ON ra.userid = u.id\n                    JOIN mdl_context ct ON ct.id = ra.contextid\n                        AND ct.contextlevel = 50\n                    JOIN mdl_course c ON c.id = ct.instanceid\n                        AND e.courseid = c.id\n                    JOIN mdl_role r ON r.id = ra.roleid\n                        AND r.shortname = 'student'\n                WHERE courseid = " . $course->id . "\n                    AND e.status = 0\n                    AND u.suspended = 0\n                    AND u.deleted = 0\n                    AND (\n                        ue.timeend = 0\n                        OR ue.timeend > NOW()\n                    )\n                    AND ue.status = 0\n                ORDER BY userid ASC;";
         if (!($enrollees = $DB->get_records_sql($sql))) {
             overnight::tlog('No enrolled students found for course ' . $course->id . '.', 'warn');
         } else {
             $num_enrollees = count($enrollees);
             overnight::tlog('Found ' . $num_enrollees . ' students enrolled onto course ' . $course->id . '.', 'info');
             // A variable to store which enrollee we're processing.
             $cur_enrollees = 0;
             foreach ($enrollees as $enrollee) {
                 $cur_enrollees++;
                 // Attempt to extract the student ID from the username.
                 $tmp = explode('@', $enrollee->username);
                 $enrollee->studentid = $tmp[0];
                 // A proper student, hopefully.
                 overnight::tlog('- Processing user (' . $cur_enrollees . '/' . $num_enrollees . ') ' . $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->userid . ') [' . $enrollee->studentid . '] on course ' . $course->id . '.', 'info');
                 $logging['students_processed'][] = $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->studentid . ') [' . $enrollee->userid . '] on course ' . $course->id . '.';
                 $logging['students_unique'][$enrollee->userid] = $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->studentid . ') [' . $enrollee->userid . '].';
                 // Assemble the URL with the correct data.
                 $leapdataurl = sprintf(LEAP_API_URL, $enrollee->studentid);
                 //if ( DEBUG ) {
                 //    overnight::tlog('-- Leap URL: ' . $leapdataurl, 'dbug');
                 //}
                 // Use fopen to read from the API.
                 if (!($handle = fopen($leapdataurl, 'r'))) {
                     // If the API can't be reached for some reason.
                     overnight::tlog('- Cannot open ' . $leapdataurl . '.', 'EROR');
                 } else {
                     // API reachable, get the data.
                     $leapdata = fgets($handle);
                     fclose($handle);
                     if (DEBUG) {
                         overnight::tlog('-- Returned JSON: ' . $leapdata, 'dbug');
                     }
                     // Handle an empty result from the API.
                     if (strlen($leapdata) == 0) {
                         overnight::tlog('-- API returned 0 bytes.', 'EROR');
                     } else {
                         // Decode the JSON into an object.
                         $leapdata = json_decode($leapdata);
                         // Checking for JSON decoding errors, seems only right.
                         if (json_last_error()) {
                             overnight::tlog('-- JSON decoding returned error code ' . json_last_error() . ' for user ' . $enrollee->studentid . '.', 'EROR');
                         } else {
                             // We have a L3VA score! And possibly GCSE English and maths grades too.
                             //$targets['l3va']    = number_format( $leapdata->person->l3va, DECIMALS );
                             //$gcse['english']    = $leapdata->person->gcse_english;
                             //$gcse['maths']      = $leapdata->person->gcse_maths;
                             //if ( $targets['l3va'] == '' || !is_numeric( $targets['l3va'] ) || $targets['l3va'] <= 0 ) {
                             //    // If the L3VA isn't good.
                             //    overnight::tlog('-- L3VA is not good: \'' . $targets['l3va'] . '\'.', 'warn');
                             //    $logging['no_l3va'][$enrollee->userid] = $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->studentid . ') [' . $enrollee->userid . '].';
                             //
                             //} else {
                             //overnight::tlog('-- ' . $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->userid . ') [' . $enrollee->studentid . '] L3VA score: ' . $targets['l3va'] . '.', 'info');
                             overnight::tlog('-- ' . $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->userid . ') [' . $enrollee->studentid . '].', 'info');
                             // If this course is tagged as a GCSE English or maths course, use the grades supplied in the JSON.
                             /*
                                                                 if ( $course->coursetype == 'leapcore_gcse_english' ) {
                                                                     $magtemp        = overnight::make_mag( $gcse['english'], $course->coursetype, $course->scalename );
                                                                     $tagtemp        = array( null, null );
                             
                                                                 } else if ( $course->coursetype == 'leapcore_gcse_maths' ) {
                                                                     $magtemp        = overnight::make_mag( $gcse['maths'], $course->coursetype, $course->scalename );
                                                                     $tagtemp        = array( null, null );
                             
                                                                 } else {
                                                                     // Make the MAG from the L3VA.
                                                                     $magtemp        = overnight::make_mag( $targets['l3va'], $course->coursetype, $course->scalename );
                                                                     // Make the TAG in the same way, setting 'true' at the end for the next grade up.
                                                                     $tagtemp        = overnight::make_mag( $targets['l3va'], $course->coursetype, $course->scalename, true );
                             
                                                                 }
                                                                 $targets['mag'] = $magtemp[0];
                                                                 $targets['tag'] = $tagtemp[0];
                             */
                             /*
                                                                 if ( $course->coursetype == 'leapcore_gcse_english' || $course->coursetype == 'leapcore_gcse_maths' ) {
                                                                     overnight::tlog('--- GCSEs passed through from Leap JSON: MAG: \'' . $targets['mag'] . '\' ['. $magtemp[1] .']. TAG: \'' . $targets['tag'] . '\' ['. $tagtemp[1] .'].', 'info');
                                                                 } else {
                                                                     overnight::tlog('--- Generated data: MAG: \'' . $targets['mag'] . '\' ['. $magtemp[1] .']. TAG: \'' . $targets['tag'] . '\' ['. $tagtemp[1] .'].', 'info');
                                                                 }
                             
                                                                 if ( $targets['mag'] == '0' || $targets['mag'] == '1' ) {
                                                                     $logging['poor_grades'][] = 'MAG ' . $targets['mag'] . ' assigned to ' . $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->studentid . ') [' . $enrollee->userid . '] on course ' . $course->id . '.';
                                                                 }
                                                                 if ( $targets['tag'] == '0' || $targets['tag'] == '1' ) {
                                                                     $logging['poor_grades'][] = 'TAG ' . $targets['tag'] . ' assigned to ' . $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->studentid . ') [' . $enrollee->userid . '] on course ' . $course->id . '.';
                                                                 }
                             */
                             // Loop through all settable, updateable grades.
                             foreach ($targets as $target => $score) {
                                 // Need the grade_items.id for grade_grades.itemid.
                                 $gradeitem = $DB->get_record('grade_items', array('courseid' => $course->id, 'itemname' => strtoupper($target)), 'id, categoryid');
                                 // Check to see if this data already exists in the database, so we can insert or update.
                                 $gradegrade = $DB->get_record('grade_grades', array('itemid' => $gradeitem->id, 'userid' => $enrollee->userid), 'id');
                                 // New grade_grade object.
                                 $grade = new \grade_grade();
                                 $grade->userid = $enrollee->userid;
                                 $grade->itemid = $gradeitem->id;
                                 $grade->categoryid = $gradeitem->categoryid;
                                 $grade->rawgrade = $score;
                                 // Will stay as set.
                                 $grade->finalgrade = $score;
                                 // Will change with the grade, e.g. 3.
                                 $grade->timecreated = time();
                                 $grade->timemodified = $grade->timecreated;
                                 // TODO: "excluded" is a thing and prevents a grade being aggregated.
                                 $grade->excluded = true;
                                 // If no id exists, INSERT.
                                 if (!$gradegrade) {
                                     if (!($gl = $grade->insert())) {
                                         overnight::tlog('--- ' . strtoupper($target) . ' insert failed for user ' . $enrollee->userid . ' on course ' . $course->id . '.', 'EROR');
                                     } else {
                                         overnight::tlog('--- ' . strtoupper($target) . ' (' . $score . ') inserted for user ' . $enrollee->userid . ' on course ' . $course->id . '.');
                                     }
                                 } else {
                                     // If the row already exists, UPDATE, but don't ever *update* the TAG.
                                     //if ( $target == 'mag' && !$score ) {
                                     //    // For MAGs, we don't want to update to a zero or null score as that may overwrite a manually-entered MAG.
                                     //    overnight::tlog('--- ' . strtoupper( $target ) . ' of 0 or null (' . $score . ') purposefully not updated for user ' . $enrollee->userid . ' on course ' . $course->id . '.' );
                                     //    $logging['not_updated'][] = $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->studentid . ') [' . $enrollee->userid . '] on course ' . $course->id . ': ' . strtoupper( $target ) . ' of \'' . $score . '\'.';
                                     //} else if ( $target != 'tag' ) {
                                     $grade->id = $gradegrade->id;
                                     // We don't want to set this again, but we do want the modified time set.
                                     unset($grade->timecreated);
                                     $grade->timemodified = time();
                                     if (!($gl = $grade->update())) {
                                         overnight::tlog('--- ' . strtoupper($target) . ' update failed for user ' . $enrollee->userid . ' on course ' . $course->id . '.', 'EROR');
                                     } else {
                                         overnight::tlog('--- ' . strtoupper($target) . ' (' . $score . ') update for user ' . $enrollee->userid . ' on course ' . $course->id . '.');
                                     }
                                     //} else {
                                     //    overnight::tlog('--- ' . strtoupper( $target ) . ' purposefully not updated for user ' . $enrollee->userid . ' on course ' . $course->id . '.', 'skip' );
                                     //    $logging['not_updated'][] = $enrollee->firstname . ' ' . $enrollee->lastname . ' (' . $enrollee->studentid . ') [' . $enrollee->userid . '] on course ' . $course->id . ': ' . strtoupper( $target ) . ' of \'' . $score . '\'.';
                                     //} // END ignore updating the TAG.
                                 }
                                 // END insert or update check.
                             }
                             // END foreach loop.
                             //} // END L3VA check.
                         }
                         // END any json_decode errors.
                     }
                     // END empty API result.
                 }
                 // END open leap API for reading.
             }
             // END cycle through each course enrollee.
         }
         // END enrollee query.
         // Final blank-ish log entry to separate out one course from another.
         overnight::tlog('', '----');
     }
     // END foreach course tagged 'leapcore_*'.
     // Sort and dump the summary log.
     overnight::tlog('Summary of all performed operations.', 'smry');
     asort($logging['courses']);
     asort($logging['students_processed']);
     asort($logging['students_unique']);
     asort($logging['no_l3va']);
     asort($logging['not_updated']);
     arsort($logging['grade_types']);
     asort($logging['poor_grades']);
     // Processing.
     $logging['num']['courses'] = count($logging['courses']);
     $logging['num']['students_processed'] = count($logging['students_processed']);
     $logging['num']['students_unique'] = count($logging['students_unique']);
     $logging['num']['no_l3va'] = count($logging['no_l3va']);
     $logging['num']['not_updated'] = count($logging['not_updated']);
     $logging['num']['grade_types'] = count($logging['grade_types']);
     foreach ($logging['grade_types'] as $value) {
         $logging['num']['grade_types_in_use'] += $value;
     }
     $logging['num']['poor_grades'] = count($logging['poor_grades']);
     if ($logging['num']['courses']) {
         overnight::tlog($logging['num']['courses'] . ' courses:', 'smry');
         $count = 0;
         foreach ($logging['courses'] as $course) {
             overnight::tlog('- ' . sprintf('%4s', ++$count) . ': ' . $course, 'smry');
         }
     } else {
         overnight::tlog('No courses processed.', 'warn');
     }
     if ($logging['num']['students_processed']) {
         overnight::tlog($logging['num']['students_processed'] . ' student-courses processed:', 'smry');
         $count = 0;
         foreach ($logging['students_processed'] as $student) {
             overnight::tlog('- ' . sprintf('%4s', ++$count) . ': ' . $student, 'smry');
         }
     } else {
         overnight::tlog('No student-courses processed.', 'warn');
     }
     if ($logging['num']['students_unique']) {
         overnight::tlog($logging['num']['students_unique'] . ' unique students:', 'smry');
         $count = 0;
         foreach ($logging['students_unique'] as $student) {
             echo sprintf('%4s', ++$count) . ': ' . $student . "\n";
             overnight::tlog('- ' . sprintf('%4s', $count) . ': ' . $student, 'smry');
         }
     } else {
         overnight::tlog('No unique students processed.', 'warn');
     }
     if ($logging['num']['no_l3va']) {
         overnight::tlog($logging['num']['no_l3va'] . ' students with no L3VA:', 'smry');
         $count = 0;
         foreach ($logging['no_l3va'] as $no_l3va) {
             echo sprintf('%4s', ++$count) . ': ' . $no_l3va . "\n";
             overnight::tlog('- ' . sprintf('%4s', $count) . ': ' . $no_l3va, 'smry');
         }
     } else {
         overnight::tlog('No missing L3VAs.', 'warn');
     }
     if ($logging['num']['not_updated']) {
         overnight::tlog($logging['num']['not_updated'] . ' students purposefully not updated (0 or null grade):', 'smry');
         $count = 0;
         foreach ($logging['not_updated'] as $not_updated) {
             overnight::tlog('- ' . sprintf('%4s', ++$count) . ': ' . $not_updated, 'smry');
         }
     } else {
         overnight::tlog('No students purposefully not updated.', 'warn');
     }
     if ($logging['num']['grade_types']) {
         overnight::tlog($logging['num']['grade_types'] . ' grade types with ' . $logging['num']['grade_types_in_use'] . ' grades set:', 'smry');
         $count = 0;
         foreach ($logging['grade_types'] as $grade_type => $num_grades) {
             overnight::tlog('- ' . sprintf('%4s', ++$count) . ': ' . $grade_type . ': ' . $num_grades, 'smry');
         }
     } else {
         overnight::tlog('No grade_types found.', 'warn');
     }
     if ($logging['num']['poor_grades']) {
         overnight::tlog($logging['num']['poor_grades'] . ' poor grades:', 'smry');
         $count = 0;
         foreach ($logging['poor_grades'] as $poorgrade) {
             overnight::tlog('- ' . sprintf('%4s', ++$count) . ': ' . $poorgrade, 'smry');
         }
     } else {
         overnight::tlog('No poor grades found. Good!', 'smry');
     }
     // Finish time.
     $time_end = microtime(true);
     $duration = $time_end - $time_start;
     $mins = floor($duration / 60) == 0 ? '' : floor($duration / 60) . ' minutes';
     $secs = $duration % 60 == 0 ? '' : $duration % 60 . ' seconds';
     $secs = $mins == '' ? $secs : ' ' . $secs;
     overnight::tlog('', '----');
     overnight::tlog('Finished at ' . date('c', $time_end) . ', took ' . $mins . $secs . ' (' . number_format($duration, DECIMALS) . ' seconds).', 'byby');
     //exit(0);
     return true;
     echo "\nEND   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n";
 }
コード例 #13
0
 /**
  * Updates final grade value for given user, this is a only way to update final
  * grades from gradebook and import because it logs the change in history table
  * and deals with overridden flag. This flag is set to prevent later overriding
  * from raw grades submitted from modules.
  *
  * @param int $userid the graded user
  * @param mixed $finalgrade float value of final grade - false means do not change
  * @param string $howmodified modification source
  * @param string $note optional note
  * @param mixed $feedback teachers feedback as string - false means do not change
  * @param int $feedbackformat
  * @return boolean success
  */
 function update_final_grade($userid, $finalgrade = false, $source = NULL, $feedback = false, $feedbackformat = FORMAT_MOODLE, $usermodified = null)
 {
     global $USER, $CFG;
     $result = true;
     // no grading used or locked
     if ($this->gradetype == GRADE_TYPE_NONE or $this->is_locked()) {
         return false;
     }
     $grade = new grade_grade(array('itemid' => $this->id, 'userid' => $userid));
     $grade->grade_item =& $this;
     // prevent db fetching of this grade_item
     if (empty($usermodified)) {
         $grade->usermodified = $USER->id;
     } else {
         $grade->usermodified = $usermodified;
     }
     if ($grade->is_locked()) {
         // do not update locked grades at all
         return false;
     }
     $locktime = $grade->get_locktime();
     if ($locktime and $locktime < time()) {
         // do not update grades that should be already locked, force regrade instead
         $this->force_regrading();
         return false;
     }
     $oldgrade = new object();
     $oldgrade->finalgrade = $grade->finalgrade;
     $oldgrade->overridden = $grade->overridden;
     $oldgrade->feedback = $grade->feedback;
     $oldgrade->feedbackformat = $grade->feedbackformat;
     // changed grade?
     if ($finalgrade !== false) {
         if ($this->is_overridable_item()) {
             $grade->overridden = time();
         } else {
             $grade->overridden = 0;
         }
         $grade->finalgrade = $this->bounded_grade($finalgrade);
     }
     // do we have comment from teacher?
     if ($feedback !== false) {
         if ($this->is_overridable_item_feedback()) {
             // external items (modules, plugins) may have own feedback
             $grade->overridden = time();
         }
         $grade->feedback = $feedback;
         $grade->feedbackformat = $feedbackformat;
     }
     // HACK: Bob Puffer to allow accurate max score to be inserted into the grade_item record
     $grade->rawgrademax = $this->grademax;
     // END OF HACK
     if (empty($grade->id)) {
         $grade->timecreated = null;
         // hack alert - date submitted - no submission yet
         $grade->timemodified = time();
         // hack alert - date graded
         $result = (bool) $grade->insert($source);
     } else {
         if (grade_floats_different($grade->finalgrade, $oldgrade->finalgrade) or $grade->feedback !== $oldgrade->feedback or $grade->feedbackformat != $oldgrade->feedbackformat or $grade->overridden != $oldgrade->overridden) {
             $grade->timemodified = time();
             // hack alert - date graded
             $result = $grade->update($source);
         } else {
             // no grade change
             return $result;
         }
     }
     if (!$result) {
         // something went wrong - better force final grade recalculation
         $this->force_regrading();
     } else {
         if ($this->is_course_item() and !$this->needsupdate) {
             if (grade_regrade_final_grades($this->courseid, $userid, $this) !== true) {
                 $this->force_regrading();
             }
         } else {
             if (!$this->needsupdate) {
                 $course_item = grade_item_local::fetch_course_item($this->courseid);
                 if (!$course_item->needsupdate) {
                     if (grade_regrade_final_grades_local($this->courseid, $userid, $this) !== true) {
                         $this->force_regrading();
                     }
                 } else {
                     $this->force_regrading();
                 }
             }
         }
     }
     return $result;
 }
コード例 #14
0
/**
 * Grading cron job
 */
function grade_cron()
{
    global $CFG;
    $now = time();
    $sql = "SELECT i.*\n              FROM {$CFG->prefix}grade_items i\n             WHERE i.locked = 0 AND i.locktime > 0 AND i.locktime < {$now} AND EXISTS (\n                SELECT 'x' FROM {$CFG->prefix}grade_items c WHERE c.itemtype='course' AND c.needsupdate=0 AND c.courseid=i.courseid)";
    // go through all courses that have proper final grades and lock them if needed
    if ($rs = get_recordset_sql($sql)) {
        if ($rs->RecordCount() > 0) {
            while ($item = rs_fetch_next_record($rs)) {
                $grade_item = new grade_item($item, false);
                $grade_item->locked = $now;
                $grade_item->update('locktime');
            }
        }
        rs_close($rs);
    }
    $grade_inst = new grade_grade();
    $fields = 'g.' . implode(',g.', $grade_inst->required_fields);
    $sql = "SELECT {$fields}\n              FROM {$CFG->prefix}grade_grades g, {$CFG->prefix}grade_items i\n             WHERE g.locked = 0 AND g.locktime > 0 AND g.locktime < {$now} AND g.itemid=i.id AND EXISTS (\n                SELECT 'x' FROM {$CFG->prefix}grade_items c WHERE c.itemtype='course' AND c.needsupdate=0 AND c.courseid=i.courseid)";
    // go through all courses that have proper final grades and lock them if needed
    if ($rs = get_recordset_sql($sql)) {
        if ($rs->RecordCount() > 0) {
            while ($grade = rs_fetch_next_record($rs)) {
                $grade_grade = new grade_grade($grade, false);
                $grade_grade->locked = $now;
                $grade_grade->update('locktime');
            }
        }
        rs_close($rs);
    }
}
コード例 #15
0
ファイル: grade_category.php プロジェクト: jtibbetts/moodle
 /**
  * Internal function for grade category grade aggregation
  *
  * @param int    $userid The User ID
  * @param array  $items Grade items
  * @param array  $grade_values Array of grade values
  * @param object $oldgrade Old grade
  * @param array  $excluded Excluded
  * @param array  $grademinoverrides User specific grademin values if different to the grade_item grademin (key is itemid)
  * @param array  $grademaxoverrides User specific grademax values if different to the grade_item grademax (key is itemid)
  */
 private function aggregate_grades($userid, $items, $grade_values, $oldgrade, $excluded, $grademinoverrides, $grademaxoverrides)
 {
     global $CFG, $DB;
     // Remember these so we can set flags on them to describe how they were used in the aggregation.
     $novalue = array();
     $dropped = array();
     $extracredit = array();
     $usedweights = array();
     if (empty($userid)) {
         //ignore first call
         return;
     }
     if ($oldgrade) {
         $oldfinalgrade = $oldgrade->finalgrade;
         $grade = new grade_grade($oldgrade, false);
         $grade->grade_item =& $this->grade_item;
     } else {
         // insert final grade - it will be needed later anyway
         $grade = new grade_grade(array('itemid' => $this->grade_item->id, 'userid' => $userid), false);
         $grade->grade_item =& $this->grade_item;
         $grade->insert('system');
         $oldfinalgrade = null;
     }
     // no need to recalculate locked or overridden grades
     if ($grade->is_locked() or $grade->is_overridden()) {
         return;
     }
     // can not use own final category grade in calculation
     unset($grade_values[$this->grade_item->id]);
     // Make sure a grade_grade exists for every grade_item.
     // We need to do this so we can set the aggregationstatus
     // with a set_field call instead of checking if each one exists and creating/updating.
     if (!empty($items)) {
         list($ggsql, $params) = $DB->get_in_or_equal(array_keys($items), SQL_PARAMS_NAMED, 'g');
         $params['userid'] = $userid;
         $sql = "SELECT itemid\n                      FROM {grade_grades}\n                     WHERE itemid {$ggsql} AND userid = :userid";
         $existingitems = $DB->get_records_sql($sql, $params);
         $notexisting = array_diff(array_keys($items), array_keys($existingitems));
         foreach ($notexisting as $itemid) {
             $gradeitem = $items[$itemid];
             $gradegrade = new grade_grade(array('itemid' => $itemid, 'userid' => $userid, 'rawgrademin' => $gradeitem->grademin, 'rawgrademax' => $gradeitem->grademax), false);
             $gradegrade->grade_item = $gradeitem;
             $gradegrade->insert('system');
         }
     }
     // if no grades calculation possible or grading not allowed clear final grade
     if (empty($grade_values) or empty($items) or $this->grade_item->gradetype != GRADE_TYPE_VALUE and $this->grade_item->gradetype != GRADE_TYPE_SCALE) {
         $grade->finalgrade = null;
         if (!is_null($oldfinalgrade)) {
             $success = $grade->update('aggregation');
             // If successful trigger a user_graded event.
             if ($success) {
                 \core\event\user_graded::create_from_grade($grade)->trigger();
             }
         }
         $dropped = $grade_values;
         $this->set_usedinaggregation($userid, $usedweights, $novalue, $dropped, $extracredit);
         return;
     }
     // Normalize the grades first - all will have value 0...1
     // ungraded items are not used in aggregation.
     foreach ($grade_values as $itemid => $v) {
         if (is_null($v)) {
             // If null, it means no grade.
             if ($this->aggregateonlygraded) {
                 unset($grade_values[$itemid]);
                 // Mark this item as "excluded empty" because it has no grade.
                 $novalue[$itemid] = 0;
                 continue;
             }
         }
         if (in_array($itemid, $excluded)) {
             unset($grade_values[$itemid]);
             $dropped[$itemid] = 0;
             continue;
         }
         // Check for user specific grade min/max overrides.
         $usergrademin = $items[$itemid]->grademin;
         $usergrademax = $items[$itemid]->grademax;
         if (isset($grademinoverrides[$itemid])) {
             $usergrademin = $grademinoverrides[$itemid];
         }
         if (isset($grademaxoverrides[$itemid])) {
             $usergrademax = $grademaxoverrides[$itemid];
         }
         if ($this->aggregation == GRADE_AGGREGATE_SUM) {
             // Assume that the grademin is 0 when standardising the score, to preserve negative grades.
             $grade_values[$itemid] = grade_grade::standardise_score($v, 0, $usergrademax, 0, 1);
         } else {
             $grade_values[$itemid] = grade_grade::standardise_score($v, $usergrademin, $usergrademax, 0, 1);
         }
     }
     // For items with no value, and not excluded - either set their grade to 0 or exclude them.
     foreach ($items as $itemid => $value) {
         if (!isset($grade_values[$itemid]) and !in_array($itemid, $excluded)) {
             if (!$this->aggregateonlygraded) {
                 $grade_values[$itemid] = 0;
             } else {
                 // We are specifically marking these items as "excluded empty".
                 $novalue[$itemid] = 0;
             }
         }
     }
     // limit and sort
     $allvalues = $grade_values;
     if ($this->can_apply_limit_rules()) {
         $this->apply_limit_rules($grade_values, $items);
     }
     $moredropped = array_diff($allvalues, $grade_values);
     foreach ($moredropped as $drop => $unused) {
         $dropped[$drop] = 0;
     }
     foreach ($grade_values as $itemid => $val) {
         if (self::is_extracredit_used() && $items[$itemid]->aggregationcoef > 0) {
             $extracredit[$itemid] = 0;
         }
     }
     asort($grade_values, SORT_NUMERIC);
     // let's see we have still enough grades to do any statistics
     if (count($grade_values) == 0) {
         // not enough attempts yet
         $grade->finalgrade = null;
         if (!is_null($oldfinalgrade)) {
             $success = $grade->update('aggregation');
             // If successful trigger a user_graded event.
             if ($success) {
                 \core\event\user_graded::create_from_grade($grade)->trigger();
             }
         }
         $this->set_usedinaggregation($userid, $usedweights, $novalue, $dropped, $extracredit);
         return;
     }
     // do the maths
     $result = $this->aggregate_values_and_adjust_bounds($grade_values, $items, $usedweights, $grademinoverrides, $grademaxoverrides);
     $agg_grade = $result['grade'];
     // Set the actual grademin and max to bind the grade properly.
     $this->grade_item->grademin = $result['grademin'];
     $this->grade_item->grademax = $result['grademax'];
     if ($this->aggregation == GRADE_AGGREGATE_SUM) {
         // The natural aggregation always displays the range as coming from 0 for categories.
         // However, when we bind the grade we allow for negative values.
         $result['grademin'] = 0;
     }
     // Recalculate the grade back to requested range.
     $finalgrade = grade_grade::standardise_score($agg_grade, 0, 1, $result['grademin'], $result['grademax']);
     $grade->finalgrade = $this->grade_item->bounded_grade($finalgrade);
     $oldrawgrademin = $grade->rawgrademin;
     $oldrawgrademax = $grade->rawgrademax;
     $grade->rawgrademin = $result['grademin'];
     $grade->rawgrademax = $result['grademax'];
     // Update in db if changed.
     if (grade_floats_different($grade->finalgrade, $oldfinalgrade) || grade_floats_different($grade->rawgrademax, $oldrawgrademax) || grade_floats_different($grade->rawgrademin, $oldrawgrademin)) {
         $success = $grade->update('aggregation');
         // If successful trigger a user_graded event.
         if ($success) {
             \core\event\user_graded::create_from_grade($grade)->trigger();
         }
     }
     $this->set_usedinaggregation($userid, $usedweights, $novalue, $dropped, $extracredit);
     return;
 }
コード例 #16
0
ファイル: grade_category.php プロジェクト: covex-nn/moodle
 /**
  * Internal function for category grades summing
  *
  * @param grade_grade $grade The grade item
  * @param float $oldfinalgrade Old Final grade
  * @param array $items Grade items
  * @param array $grade_values Grade values
  * @param array $excluded Excluded
  */
 private function sum_grades(&$grade, $oldfinalgrade, $items, $grade_values, $excluded)
 {
     if (empty($items)) {
         return null;
     }
     // ungraded and excluded items are not used in aggregation
     foreach ($grade_values as $itemid => $v) {
         if (is_null($v)) {
             unset($grade_values[$itemid]);
         } else {
             if (in_array($itemid, $excluded)) {
                 unset($grade_values[$itemid]);
             }
         }
     }
     // use 0 if grade missing, droplow used and aggregating all items
     if (!$this->aggregateonlygraded and !empty($this->droplow)) {
         foreach ($items as $itemid => $value) {
             if (!isset($grade_values[$itemid]) and !in_array($itemid, $excluded)) {
                 $grade_values[$itemid] = 0;
             }
         }
     }
     $this->apply_limit_rules($grade_values, $items);
     $sum = array_sum($grade_values);
     $grade->finalgrade = $this->grade_item->bounded_grade($sum);
     // update in db if changed
     if (grade_floats_different($grade->finalgrade, $oldfinalgrade)) {
         $grade->update('aggregation');
     }
     return;
 }
コード例 #17
0
 /**
  * internal function for category grades aggregation
  */
 function aggregate_grades($userid, $items, $grade_values, $oldgrade, $excluded)
 {
     global $CFG;
     if (empty($userid)) {
         //ignore first call
         return;
     }
     if ($oldgrade) {
         $grade = new grade_grade($oldgrade, false);
         $grade->grade_item =& $this->grade_item;
     } else {
         // insert final grade - it will be needed later anyway
         $grade = new grade_grade(array('itemid' => $this->grade_item->id, 'userid' => $userid), false);
         $grade->insert('system');
         $grade->grade_item =& $this->grade_item;
         $oldgrade = new object();
         $oldgrade->finalgrade = $grade->finalgrade;
         $oldgrade->rawgrade = $grade->rawgrade;
         $oldgrade->rawgrademin = $grade->rawgrademin;
         $oldgrade->rawgrademax = $grade->rawgrademax;
         $oldgrade->rawscaleid = $grade->rawscaleid;
     }
     // no need to recalculate locked or overridden grades
     if ($grade->is_locked() or $grade->is_overridden()) {
         return;
     }
     // can not use own final category grade in calculation
     unset($grade_values[$this->grade_item->id]);
     // if no grades calculation possible or grading not allowed clear both final and raw
     if (empty($grade_values) or empty($items) or $this->grade_item->gradetype != GRADE_TYPE_VALUE and $this->grade_item->gradetype != GRADE_TYPE_SCALE) {
         $grade->finalgrade = null;
         $grade->rawgrade = null;
         if ($grade->finalgrade !== $oldgrade->finalgrade or $grade->rawgrade !== $oldgrade->rawgrade) {
             $grade->update('system');
         }
         return;
     }
     /// normalize the grades first - all will have value 0...1
     // ungraded items are not used in aggregation
     foreach ($grade_values as $itemid => $v) {
         if (is_null($v)) {
             // null means no grade
             unset($grade_values[$itemid]);
             continue;
         } else {
             if (in_array($itemid, $excluded)) {
                 unset($grade_values[$itemid]);
                 continue;
             }
         }
         $grade_values[$itemid] = grade_grade::standardise_score($v, $items[$itemid]->grademin, $items[$itemid]->grademax, 0, 1);
     }
     // If global aggregateonlygraded is set, override category value
     if ($CFG->grade_aggregateonlygraded != -1) {
         $this->aggregateonlygraded = $CFG->grade_aggregateonlygraded;
     }
     // use min grade if grade missing for these types
     if (!$this->aggregateonlygraded) {
         foreach ($items as $itemid => $value) {
             if (!isset($grade_values[$itemid]) and !in_array($itemid, $excluded)) {
                 $grade_values[$itemid] = 0;
             }
         }
     }
     // limit and sort
     $this->apply_limit_rules($grade_values);
     asort($grade_values, SORT_NUMERIC);
     // let's see we have still enough grades to do any statistics
     if (count($grade_values) == 0) {
         // not enough attempts yet
         $grade->finalgrade = null;
         $grade->rawgrade = null;
         if ($grade->finalgrade !== $oldgrade->finalgrade or $grade->rawgrade !== $oldgrade->rawgrade) {
             $grade->update('system');
         }
         return;
     }
     /// start the aggregation
     switch ($this->aggregation) {
         case GRADE_AGGREGATE_MEDIAN:
             // Middle point value in the set: ignores frequencies
             $num = count($grade_values);
             $grades = array_values($grade_values);
             if ($num % 2 == 0) {
                 $agg_grade = ($grades[intval($num / 2) - 1] + $grades[intval($num / 2)]) / 2;
             } else {
                 $agg_grade = $grades[intval($num / 2 - 0.5)];
             }
             break;
         case GRADE_AGGREGATE_MIN:
             $agg_grade = reset($grade_values);
             break;
         case GRADE_AGGREGATE_MAX:
             $agg_grade = array_pop($grade_values);
             break;
         case GRADE_AGGREGATE_MODE:
             // the most common value, average used if multimode
             $freq = array_count_values($grade_values);
             arsort($freq);
             // sort by frequency keeping keys
             $top = reset($freq);
             // highest frequency count
             $modes = array_keys($freq, $top);
             // search for all modes (have the same highest count)
             rsort($modes, SORT_NUMERIC);
             // get highes mode
             $agg_grade = reset($modes);
             break;
         case GRADE_AGGREGATE_WEIGHTED_MEAN:
             // Weighted average of all existing final grades
             $weightsum = 0;
             $sum = 0;
             foreach ($grade_values as $itemid => $grade_value) {
                 if ($items[$itemid]->aggregationcoef <= 0) {
                     continue;
                 }
                 $weightsum += $items[$itemid]->aggregationcoef;
                 $sum += $items[$itemid]->aggregationcoef * $grade_value;
             }
             if ($weightsum == 0) {
                 $agg_grade = null;
             } else {
                 $agg_grade = $sum / $weightsum;
             }
             break;
         case GRADE_AGGREGATE_EXTRACREDIT_MEAN:
             // special average
             $num = 0;
             $sum = 0;
             foreach ($grade_values as $itemid => $grade_value) {
                 if ($items[$itemid]->aggregationcoef == 0) {
                     $num += 1;
                     $sum += $grade_value;
                 } else {
                     if ($items[$itemid]->aggregationcoef > 0) {
                         $sum += $items[$itemid]->aggregationcoef * $grade_value;
                     }
                 }
             }
             if ($num == 0) {
                 $agg_grade = $sum;
                 // only extra credits or wrong coefs
             } else {
                 $agg_grade = $sum / $num;
             }
             break;
         case GRADE_AGGREGATE_MEAN:
             // Arithmetic average of all grade items (if ungraded aggregated, NULL counted as minimum)
         // Arithmetic average of all grade items (if ungraded aggregated, NULL counted as minimum)
         default:
             $num = count($grade_values);
             $sum = array_sum($grade_values);
             $agg_grade = $sum / $num;
             break;
     }
     /// prepare update of new raw grade
     $grade->rawgrademin = $this->grade_item->grademin;
     $grade->rawgrademax = $this->grade_item->grademax;
     $grade->rawscaleid = $this->grade_item->scaleid;
     $grade->rawgrade = null;
     // categories do not use raw grades
     // recalculate the rawgrade back to requested range
     $finalgrade = grade_grade::standardise_score($agg_grade, 0, 1, $this->grade_item->grademin, $this->grade_item->grademax);
     if (!is_null($finalgrade)) {
         $grade->finalgrade = bounded_number($this->grade_item->grademin, $finalgrade, $this->grade_item->grademax);
     } else {
         $grade->finalgrade = $finalgrade;
     }
     // update in db if changed
     if ($grade->finalgrade !== $oldgrade->finalgrade or $grade->rawgrade !== $oldgrade->rawgrade or $grade->rawgrademin !== $oldgrade->rawgrademin or $grade->rawgrademax !== $oldgrade->rawgrademax or $grade->rawscaleid !== $oldgrade->rawscaleid) {
         $grade->update('system');
     }
     return;
 }