Example #1
0
/**
 * 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();
 }
Example #3
0
 /**
  * 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;
 }
Example #4
0
/**
 * 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;
    }
}