function regrade_question_in_attempt in Quiz 6.6
Same name and namespace in other branches
- 6.5 includes/moodle/lib/questionlib.php \regrade_question_in_attempt()
For a given question in an attempt we walk the complete history of states and recalculate the grades as we go along.
This is used when a question is changed and old student responses need to be marked with the new version of a question.
TODO: Make sure this is not quiz-specific
Parameters
object $question A question object:
object $attempt The attempt, in which the question needs to be regraded.:
object $cmoptions:
boolean $verbose Optional. Whether to print progress information or not.:
Return value
boolean Indicates whether the grade has changed
File
- includes/
moodle/ lib/ questionlib.php, line 1187
Code
function regrade_question_in_attempt($question, $attempt, $cmoptions, $verbose = false) {
// load all states for this question in this attempt, ordered in sequence
if ($states = get_records_select('question_states', "attempt = '{$attempt->uniqueid}' AND question = '{$question->id}'", 'seq_number ASC')) {
$states = array_values($states);
// Subtract the grade for the latest state from $attempt->sumgrades to get the
// sumgrades for the attempt without this question.
$attempt->sumgrades -= $states[count($states) - 1]->grade;
// Initialise the replaystate
$state = clone $states[0];
$state->manualcomment = get_field('question_sessions', 'manualcomment', 'attemptid', $attempt->uniqueid, 'questionid', $question->id);
restore_question_state($question, $state);
$state->sumpenalty = 0.0;
$replaystate = clone $state;
$replaystate->last_graded = $state;
$changed = false;
for ($j = 1; $j < count($states); $j++) {
restore_question_state($question, $states[$j]);
$action = new stdClass();
$action->responses = $states[$j]->responses;
$action->timestamp = $states[$j]->timestamp;
// Change event to submit so that it will be reprocessed
if (QUESTION_EVENTCLOSE == $states[$j]->event or QUESTION_EVENTGRADE == $states[$j]->event or QUESTION_EVENTCLOSEANDGRADE == $states[$j]->event) {
$action->event = QUESTION_EVENTSUBMIT;
// By default take the event that was saved in the database
}
else {
$action->event = $states[$j]->event;
}
if ($action->event == QUESTION_EVENTMANUALGRADE) {
// Ensure that the grade is in range - in the past this was not checked,
// but now it is (MDL-14835) - so we need to ensure the data is valid before
// proceeding.
if ($states[$j]->grade < 0) {
$states[$j]->grade = 0;
}
else {
if ($states[$j]->grade > $question->maxgrade) {
$states[$j]->grade = $question->maxgrade;
}
}
$error = question_process_comment($question, $replaystate, $attempt, $replaystate->manualcomment, $states[$j]->grade);
if (is_string($error)) {
notify($error);
}
}
else {
// Reprocess (regrade) responses
if (!question_process_responses($question, $replaystate, $action, $cmoptions, $attempt)) {
$verbose && notify("Couldn't regrade state #{$state->id}!");
}
}
// We need rounding here because grades in the DB get truncated
// e.g. 0.33333 != 0.3333333, but we want them to be equal here
if (round((double) $replaystate->raw_grade, 5) != round((double) $states[$j]->raw_grade, 5) or round((double) $replaystate->penalty, 5) != round((double) $states[$j]->penalty, 5) or round((double) $replaystate->grade, 5) != round((double) $states[$j]->grade, 5)) {
$changed = true;
}
$replaystate->id = $states[$j]->id;
$replaystate->changed = true;
$replaystate->update = true;
// This will ensure that the existing database entry is updated rather than a new one created
save_question_session($question, $replaystate);
}
if ($changed) {
// TODO, call a method in quiz to do this, where 'quiz' comes from
// the question_attempts table.
update_record('quiz_attempts', $attempt);
}
return $changed;
}
return false;
}