You are here

function regrade_question_in_attempt in Quiz 6.5

Same name and namespace in other branches
  1. 6.6 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;
}