/** * Updates all final grades in course. * * @param int $courseid The course ID * @param int $userid If specified try to do a quick regrading of the grades of this user only * @param object $updated_item Optional grade item to be marked for regrading * @param \core\progress\base $progress If provided, will be used to update progress on this long operation. * @return bool true if ok, array of errors if problems found. Grade item id => error message */ function grade_regrade_final_grades($courseid, $userid = null, $updated_item = null, $progress = null) { // This may take a very long time. \core_php_time_limit::raise(); $course_item = grade_item::fetch_course_item($courseid); if ($progress == null) { $progress = new \core\progress\none(); } if ($userid) { // one raw grade updated for one user if (empty($updated_item)) { print_error("cannotbenull", 'debug', '', "updated_item"); } if ($course_item->needsupdate) { $updated_item->force_regrading(); return array($course_item->id => 'Can not do fast regrading after updating of raw grades'); } } else { if (!$course_item->needsupdate) { // nothing to do :-) return true; } } // Categories might have to run some processing before we fetch the grade items. // This gives them a final opportunity to update and mark their children to be updated. // We need to work on the children categories up to the parent ones, so that, for instance, // if a category total is updated it will be reflected in the parent category. $cats = grade_category::fetch_all(array('courseid' => $courseid)); $flatcattree = array(); foreach ($cats as $cat) { if (!isset($flatcattree[$cat->depth])) { $flatcattree[$cat->depth] = array(); } $flatcattree[$cat->depth][] = $cat; } krsort($flatcattree); foreach ($flatcattree as $depth => $cats) { foreach ($cats as $cat) { $cat->pre_regrade_final_grades(); } } $progresstotal = 0; $progresscurrent = 0; $grade_items = grade_item::fetch_all(array('courseid' => $courseid)); $depends_on = array(); foreach ($grade_items as $gid => $gitem) { if ((!empty($updated_item) and $updated_item->id == $gid) || $gitem->is_course_item() || $gitem->is_category_item() || $gitem->is_calculated()) { $grade_items[$gid]->needsupdate = 1; } // We load all dependencies of these items later we can discard some grade_items based on this. if ($grade_items[$gid]->needsupdate) { $depends_on[$gid] = $grade_items[$gid]->depends_on(); $progresstotal++; } } $progress->start_progress('regrade_course', $progresstotal); $errors = array(); $finalids = array(); $updatedids = array(); $gids = array_keys($grade_items); $failed = 0; while (count($finalids) < count($gids)) { // work until all grades are final or error found $count = 0; foreach ($gids as $gid) { if (in_array($gid, $finalids)) { continue; // already final } if (!$grade_items[$gid]->needsupdate) { $finalids[] = $gid; // we can make it final - does not need update continue; } $thisprogress = $progresstotal; foreach ($grade_items as $item) { if ($item->needsupdate) { $thisprogress--; } } // Clip between $progresscurrent and $progresstotal. $thisprogress = max(min($thisprogress, $progresstotal), $progresscurrent); $progress->progress($thisprogress); $progresscurrent = $thisprogress; foreach ($depends_on[$gid] as $did) { if (!in_array($did, $finalids)) { // This item depends on something that is not yet in finals array. continue 2; } } // If this grade item has no dependancy with any updated item at all, then remove it from being recalculated. // When we get here, all of this grade item's decendents are marked as final so they would be marked as updated too // if they would have been regraded. We don't need to regrade items which dependants (not only the direct ones // but any dependant in the cascade) have not been updated. // If $updated_item was specified we discard the grade items that do not depend on it or on any grade item that // depend on $updated_item. // Here we check to see if the direct decendants are marked as updated. if (!empty($updated_item) && $gid != $updated_item->id && !in_array($updated_item->id, $depends_on[$gid])) { // We need to ensure that none of this item's dependencies have been updated. // If we find that one of the direct decendants of this grade item is marked as updated then this // grade item needs to be recalculated and marked as updated. // Being marked as updated is done further down in the code. $updateddependencies = false; foreach ($depends_on[$gid] as $dependency) { if (in_array($dependency, $updatedids)) { $updateddependencies = true; break; } } if ($updateddependencies === false) { // If no direct descendants are marked as updated, then we don't need to update this grade item. We then mark it // as final. $finalids[] = $gid; continue; } } // Let's update, calculate or aggregate. $result = $grade_items[$gid]->regrade_final_grades($userid); if ($result === true) { // We should only update the database if we regraded all users. if (empty($userid)) { $grade_items[$gid]->regrading_finished(); // Do the locktime item locking. $grade_items[$gid]->check_locktime(); } else { $grade_items[$gid]->needsupdate = 0; } $count++; $finalids[] = $gid; $updatedids[] = $gid; } else { $grade_items[$gid]->force_regrading(); $errors[$gid] = $result; } } if ($count == 0) { $failed++; } else { $failed = 0; } if ($failed > 1) { foreach ($gids as $gid) { if (in_array($gid, $finalids)) { continue; // this one is ok } $grade_items[$gid]->force_regrading(); $errors[$grade_items[$gid]->id] = get_string('errorcalculationbroken', 'grades'); } break; // Found error. } } $progress->end_progress(); if (count($errors) == 0) { if (empty($userid)) { // do the locktime locking of grades, but only when doing full regrading grade_grade::check_locktime_all($gids); } return true; } else { return $errors; } }
/** * Load the needed users.xml file to backup_ids table for future reference * * @param string $restoreid Restore id * @param string $usersfile File path * @param \core\progress\base $progress Progress tracker */ public static function load_users_to_tempids($restoreid, $usersfile, \core\progress\base $progress = null) { if (!file_exists($usersfile)) { // Shouldn't happen ever, but... throw new backup_helper_exception('missing_users_xml_file', $usersfile); } // Set up progress tracking (indeterminate). if (!$progress) { $progress = new \core\progress\none(); } $progress->start_progress('Loading users into temporary table'); // Let's parse, custom processor will do its work, sending info to DB $xmlparser = new progressive_parser(); $xmlparser->set_file($usersfile); $xmlprocessor = new restore_users_parser_processor($restoreid); $xmlparser->set_processor($xmlprocessor); $xmlparser->set_progress($progress); $xmlparser->process(); // Finish progress. $progress->end_progress(); }
/** * Analyse responses for an array of questions or sub questions. * * @param object[] $questions as returned by self::load_and_initialise_questions_for_calculations. * @param qubaid_condition $qubaids the question usages whose responses to analyse. * @param string $whichtries which tries to analyse \question_attempt::FIRST_TRY, LAST_TRY or ALL_TRIES. * @param null|\core\progress\base $progress Used to indicate progress of task. * @param int[] $done array keys are ids of questions that have been analysed before calling method. * @return array array keys are ids of questions that were analysed after this method call. */ protected function analyse_responses_for_questions($questions, $qubaids, $whichtries, $progress = null, $done = array()) { $countquestions = count($questions); if (!$countquestions) { return array(); } if ($progress === null) { $progress = new \core\progress\none(); } $progress->start_progress('', $countquestions, $countquestions); foreach ($questions as $question) { $progress->increment_progress(); if (question_bank::get_qtype($question->qtype, false)->can_analyse_responses() && !isset($done[$question->id])) { $responesstats = new \core_question\statistics\responses\analyser($question, $whichtries); if ($responesstats->get_last_analysed_time($qubaids, $whichtries) === false) { $responesstats->calculate($qubaids, $whichtries); } } $done[$question->id] = 1; } $progress->end_progress(); return $done; }
/** * Updates all final grades in course. * * @param int $courseid The course ID * @param int $userid If specified try to do a quick regrading of the grades of this user only * @param object $updated_item Optional grade item to be marked for regrading * @param \core\progress\base $progress If provided, will be used to update progress on this long operation. * @return bool true if ok, array of errors if problems found. Grade item id => error message */ function grade_regrade_final_grades($courseid, $userid = null, $updated_item = null, $progress = null) { // This may take a very long time. \core_php_time_limit::raise(); $course_item = grade_item::fetch_course_item($courseid); if ($progress == null) { $progress = new \core\progress\none(); } if ($userid) { // one raw grade updated for one user if (empty($updated_item)) { print_error("cannotbenull", 'debug', '', "updated_item"); } if ($course_item->needsupdate) { $updated_item->force_regrading(); return array($course_item->id => 'Can not do fast regrading after updating of raw grades'); } } else { if (!$course_item->needsupdate) { // nothing to do :-) return true; } } // Categories might have to run some processing before we fetch the grade items. // This gives them a final opportunity to update and mark their children to be updated. // We need to work on the children categories up to the parent ones, so that, for instance, // if a category total is updated it will be reflected in the parent category. $cats = grade_category::fetch_all(array('courseid' => $courseid)); $flatcattree = array(); foreach ($cats as $cat) { if (!isset($flatcattree[$cat->depth])) { $flatcattree[$cat->depth] = array(); } $flatcattree[$cat->depth][] = $cat; } krsort($flatcattree); foreach ($flatcattree as $depth => $cats) { foreach ($cats as $cat) { $cat->pre_regrade_final_grades(); } } $grade_items = grade_item::fetch_all(array('courseid' => $courseid)); $depends_on = array(); // first mark all category and calculated items as needing regrading // this is slower, but 100% accurate foreach ($grade_items as $gid => $gitem) { if (!empty($updated_item) and $updated_item->id == $gid) { $grade_items[$gid]->needsupdate = 1; } else { if ($gitem->is_course_item() or $gitem->is_category_item() or $gitem->is_calculated()) { $grade_items[$gid]->needsupdate = 1; } } // construct depends_on lookup array $depends_on[$gid] = $grade_items[$gid]->depends_on(); } $progresstotal = 0; $progresscurrent = 0; // This progress total might not be 100% accurate, because more things might get marked as needsupdate // during the process. foreach ($grade_items as $item) { if ($item->needsupdate) { $progresstotal++; } } $progress->start_progress('regrade_course', $progresstotal); $errors = array(); $finalids = array(); $gids = array_keys($grade_items); $failed = 0; while (count($finalids) < count($gids)) { // work until all grades are final or error found $count = 0; foreach ($gids as $gid) { if (in_array($gid, $finalids)) { continue; // already final } if (!$grade_items[$gid]->needsupdate) { $finalids[] = $gid; // we can make it final - does not need update continue; } $thisprogress = $progresstotal; foreach ($grade_items as $item) { if ($item->needsupdate) { $thisprogress--; } } // Clip between $progresscurrent and $progresstotal. $thisprogress = max(min($thisprogress, $progresstotal), $progresscurrent); $progress->progress($thisprogress); $progresscurrent = $thisprogress; $doupdate = true; foreach ($depends_on[$gid] as $did) { if (!in_array($did, $finalids)) { $doupdate = false; continue; // this item depends on something that is not yet in finals array } } //oki - let's update, calculate or aggregate :-) if ($doupdate) { $result = $grade_items[$gid]->regrade_final_grades($userid); if ($result === true) { $grade_items[$gid]->regrading_finished(); $grade_items[$gid]->check_locktime(); // do the locktime item locking $count++; $finalids[] = $gid; } else { $grade_items[$gid]->force_regrading(); $errors[$gid] = $result; } } } if ($count == 0) { $failed++; } else { $failed = 0; } if ($failed > 1) { foreach ($gids as $gid) { if (in_array($gid, $finalids)) { continue; // this one is ok } $grade_items[$gid]->force_regrading(); $errors[$grade_items[$gid]->id] = get_string('errorcalculationbroken', 'grades'); } break; // Found error. } } $progress->end_progress(); if (count($errors) == 0) { if (empty($userid)) { // do the locktime locking of grades, but only when doing full regrading grade_grade::check_locktime_all($gids); } return true; } else { return $errors; } }