Esempio n. 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;
    }
}
Esempio n. 2
0
/**
 * Updates all final grades in course.
 *
 * @param int $courseid
 * @param int $userid if specified, try to do a quick regrading of grades of this user only
 * @param object $updated_item the item in which
 * @return boolean true if ok, array of errors if problems found (item id is used as key)
 */
function grade_regrade_final_grades($courseid, $userid = null, $updated_item = null)
{
    $course_item = grade_item::fetch_course_item($courseid);
    if ($userid) {
        // one raw grade updated for one user
        if (empty($updated_item)) {
            error("updated_item_id can not be null!");
        }
        if ($course_item->needsupdate) {
            $updated_item->force_regrading();
            return 'Can not do fast regrading after updating of raw grades';
        }
    } else {
        if (!$course_item->needsupdate) {
            // nothing to do :-)
            return true;
        }
    }
    $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();
    }
    $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;
            }
            $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] = 'Probably circular reference or broken calculation formula';
                // TODO: localize
            }
            break;
            // oki, found error
        }
    }
    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;
    }
}
Esempio n. 3
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
 * @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)
{
    // This may take a very long time.
    \core_php_time_limit::raise();
    $course_item = grade_item::fetch_course_item($courseid);
    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();
    }
    $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;
            }
            $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.
        }
    }
    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;
    }
}