function test_question_state_is_closed() { $state = new stdClass(); $state->event = QUESTION_EVENTOPEN; $this->assertFalse(question_state_is_closed($state)); $state->event = QUESTION_EVENTNAVIGATE; $this->assertFalse(question_state_is_closed($state)); $state->event = QUESTION_EVENTSAVE; $this->assertFalse(question_state_is_closed($state)); $state->event = QUESTION_EVENTGRADE; $this->assertFalse(question_state_is_closed($state)); $state->event = QUESTION_EVENTDUPLICATE; $this->assertFalse(question_state_is_closed($state)); $state->event = QUESTION_EVENTVALIDATE; $this->assertFalse(question_state_is_closed($state)); $state->event = QUESTION_EVENTSUBMIT; $this->assertFalse(question_state_is_closed($state)); $state->event = QUESTION_EVENTCLOSEANDGRADE; $this->assertTrue(question_state_is_closed($state)); $state->event = QUESTION_EVENTCLOSE; $this->assertTrue(question_state_is_closed($state)); $state->event = QUESTION_EVENTMANUALGRADE; $this->assertTrue(question_state_is_closed($state)); }
/** * Determine render options * * @param int $reviewoptions * @param object $state */ function quiz_get_renderoptions($reviewoptions, $state) { $options = new stdClass(); // Show the question in readonly (review) mode if the question is in // the closed state $options->readonly = question_state_is_closed($state); // Show feedback once the question has been graded (if allowed by the quiz) $options->feedback = question_state_is_graded($state) && $reviewoptions & QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_IMMEDIATELY; // Show validation only after a validation event $options->validation = QUESTION_EVENTVALIDATE === $state->event; // Show correct responses in readonly mode if the quiz allows it $options->correct_responses = $options->readonly && $reviewoptions & QUIZ_REVIEW_ANSWERS & QUIZ_REVIEW_IMMEDIATELY; // Show general feedback if the question has been graded and the quiz allows it. $options->generalfeedback = question_state_is_graded($state) && $reviewoptions & QUIZ_REVIEW_GENERALFEEDBACK & QUIZ_REVIEW_IMMEDIATELY; // Show overallfeedback once the attempt is over. $options->overallfeedback = false; // Always show responses and scores $options->responses = true; $options->scores = true; $options->quizstate = QUIZ_STATE_DURING; return $options; }
/** * Processes an array of student responses, grading and saving them as appropriate * * @param object $question Full question object, passed by reference * @param object $state Full state object, passed by reference * @param object $action object with the fields ->responses which * is an array holding the student responses, * ->action which specifies the action, e.g., QUESTION_EVENTGRADE, * and ->timestamp which is a timestamp from when the responses * were submitted by the student. * @param object $cmoptions * @param object $attempt The attempt is passed by reference so that * during grading its ->sumgrades field can be updated * @return boolean Indicates success/failure */ function question_process_responses(&$question, &$state, $action, $cmoptions, &$attempt) { global $QTYPES; // if no responses are set initialise to empty response if (!isset($action->responses)) { $action->responses = array('' => ''); } // make sure these are gone! unset($action->responses['submit'], $action->responses['validate']); // Check the question session is still open if (question_state_is_closed($state)) { return true; } // If $action->event is not set that implies saving if (!isset($action->event)) { debugging('Ambiguous action in question_process_responses.', DEBUG_DEVELOPER); $action->event = QUESTION_EVENTSAVE; } // If submitted then compare against last graded // responses, not last given responses in this case if (question_isgradingevent($action->event)) { $state->responses = $state->last_graded->responses; } // Check for unchanged responses (exactly unchanged, not equivalent). // We also have to catch questions that the student has not yet attempted $sameresponses = $QTYPES[$question->qtype]->compare_responses($question, $action, $state); if (!empty($state->last_graded) && $state->last_graded->event == QUESTION_EVENTOPEN && question_isgradingevent($action->event)) { $sameresponses = false; } // If the response has not been changed then we do not have to process it again // unless the attempt is closing or validation is requested if ($sameresponses and QUESTION_EVENTCLOSE != $action->event and QUESTION_EVENTVALIDATE != $action->event) { return true; } // Roll back grading information to last graded state and set the new // responses $newstate = clone $state->last_graded; $newstate->responses = $action->responses; $newstate->seq_number = $state->seq_number + 1; $newstate->changed = true; // will assure that it gets saved to the database $newstate->last_graded = clone $state->last_graded; $newstate->timestamp = $action->timestamp; $state = $newstate; // Set the event to the action we will perform. The question type specific // grading code may override this by setting it to QUESTION_EVENTCLOSE if the // attempt at the question causes the session to close $state->event = $action->event; if (!question_isgradingevent($action->event)) { // Grade the response but don't update the overall grade if (!$QTYPES[$question->qtype]->grade_responses($question, $state, $cmoptions)) { return false; } // Temporary hack because question types are not given enough control over what is going // on. Used by Opaque questions. // TODO fix this code properly. if (!empty($state->believeevent)) { // If the state was graded we need to ... if (question_state_is_graded($state)) { question_apply_penalty_and_timelimit($question, $state, $attempt, $cmoptions); // update the attempt grade $attempt->sumgrades -= (double) $state->last_graded->grade; $attempt->sumgrades += (double) $state->grade; // and update the last_graded field. unset($state->last_graded); $state->last_graded = clone $state; unset($state->last_graded->changed); } } else { // Don't allow the processing to change the event type $state->event = $action->event; } } else { // grading event // Unless the attempt is closing, we want to work out if the current responses // (or equivalent responses) were already given in the last graded attempt. if (QUESTION_EVENTCLOSE != $action->event && QUESTION_EVENTOPEN != $state->last_graded->event && $QTYPES[$question->qtype]->compare_responses($question, $state, $state->last_graded)) { $state->event = QUESTION_EVENTDUPLICATE; } // If we did not find a duplicate or if the attempt is closing, perform grading if (!$sameresponses and QUESTION_EVENTDUPLICATE != $state->event or QUESTION_EVENTCLOSE == $action->event) { if (!$QTYPES[$question->qtype]->grade_responses($question, $state, $cmoptions)) { return false; } // Calculate overall grade using correct penalty method question_apply_penalty_and_timelimit($question, $state, $attempt, $cmoptions); } // If the state was graded we need to ... if (question_state_is_graded($state)) { // update the attempt grade $attempt->sumgrades -= (double) $state->last_graded->grade; $attempt->sumgrades += (double) $state->grade; // and update the last_graded field. unset($state->last_graded); $state->last_graded = clone $state; unset($state->last_graded->changed); } } $attempt->timemodified = $action->timestamp; return true; }
/** * Determine render options * * @param int $reviewoptions * @param object $state */ function quiz_get_renderoptions($quiz, $attempt, $context, $state) { $reviewoptions = $quiz->review; $options = new stdClass(); $options->flags = quiz_get_flag_option($attempt, $context); // Show the question in readonly (review) mode if the question is in // the closed state $options->readonly = question_state_is_closed($state); // Show feedback once the question has been graded (if allowed by the quiz) $options->feedback = question_state_is_graded($state) && $reviewoptions & QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_IMMEDIATELY; // Show correct responses in readonly mode if the quiz allows it $options->correct_responses = $options->readonly && $reviewoptions & QUIZ_REVIEW_ANSWERS & QUIZ_REVIEW_IMMEDIATELY; // Show general feedback if the question has been graded and the quiz allows it. $options->generalfeedback = question_state_is_graded($state) && $reviewoptions & QUIZ_REVIEW_GENERALFEEDBACK & QUIZ_REVIEW_IMMEDIATELY; // Show overallfeedback once the attempt is over. $options->overallfeedback = false; // Always show responses and scores $options->responses = true; $options->scores = true; $options->quizstate = QUIZ_STATE_DURING; $options->history = false; return $options; }
/** * @param string $colname the name of the column. * @param object $attempt the row of data - see the SQL in display() in * mod/quiz/report/overview/report.php to see what fields are present, * and what they are called. * @return string the contents of the cell. */ function other_cols($colname, $attempt) { global $OUTPUT; if (preg_match('/^qsgrade([0-9]+)$/', $colname, $matches)) { $questionid = $matches[1]; $question = $this->questions[$questionid]; if (isset($this->gradedstatesbyattempt[$attempt->attemptuniqueid][$questionid])) { $stateforqinattempt = $this->gradedstatesbyattempt[$attempt->attemptuniqueid][$questionid]; } else { $stateforqinattempt = false; } if ($stateforqinattempt && question_state_is_graded($stateforqinattempt)) { $grade = quiz_rescale_grade($stateforqinattempt->grade, $this->quiz, 'question'); if (!$this->is_downloading()) { if (isset($this->regradedqs[$attempt->attemptuniqueid][$questionid])) { $gradefromdb = $grade; $newgrade = quiz_rescale_grade($this->regradedqs[$attempt->attemptuniqueid][$questionid]->newgrade, $this->quiz, 'question'); $oldgrade = quiz_rescale_grade($this->regradedqs[$attempt->attemptuniqueid][$questionid]->oldgrade, $this->quiz, 'question'); $grade = '<del>' . $oldgrade . '</del><br />' . $newgrade; } $link = new moodle_url("/mod/quiz/reviewquestion.php?attempt={$attempt->attempt}&question={$question->id}"); $action = new popup_action('click', $link, 'reviewquestion', array('height' => 450, 'width' => 650)); $linktopopup = $OUTPUT->action_link($link, $grade, $action, array('title' => get_string('reviewresponsetoq', 'quiz', $question->formattedname))); if ($this->questions[$questionid]->maxgrade != 0) { $fractionofgrade = $stateforqinattempt->grade / $this->questions[$questionid]->maxgrade; $qclass = question_get_feedback_class($fractionofgrade); $feedbackimg = question_get_feedback_image($fractionofgrade); $questionclass = "que"; return "<span class=\"{$questionclass}\"><span class=\"{$qclass}\">" . $linktopopup . "</span></span>{$feedbackimg}"; } else { return $linktopopup; } } else { return $grade; } } else { if ($stateforqinattempt && question_state_is_closed($stateforqinattempt)) { $text = get_string('requiresgrading', 'quiz_overview'); if (!$this->is_downloading()) { $link = new moodle_url("/mod/quiz/reviewquestion.php?attempt={$attempt->attempt}&question={$question->id}"); $action = new popup_action('click', $link, 'reviewquestion', array('height' => 450, 'width' => 650)); return $OUTPUT->action_link($link, $text, $action, array('title' => get_string('reviewresponsetoq', 'quiz', $question->formattedname))); } else { return $text; } } else { return '--'; } } } else { return NULL; } }
function reader_get_renderoptions($reviewoptions, $state) { $options = new stdClass(); $options->readonly = question_state_is_closed($state); $options->feedback = question_state_is_graded($state) && $reviewoptions & READER_REVIEW_FEEDBACK & READER_REVIEW_IMMEDIATELY; $options->validation = QUESTION_EVENTVALIDATE === $state->event; $options->correct_responses = $options->readonly && $reviewoptions & READER_REVIEW_ANSWERS & READER_REVIEW_IMMEDIATELY; $options->generalfeedback = question_state_is_graded($state) && $reviewoptions & READER_REVIEW_GENERALFEEDBACK & READER_REVIEW_IMMEDIATELY; $options->overallfeedback = false; $options->responses = true; $options->scores = true; $options->readerstate = READER_STATE_DURING; return $options; }