Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

MDL-6269, MDL-8958, MDL-8990 - Major fix to do with each attempt buil…

…ds on last:

It used not to work with random questions, and there were problems with the computation of the grade, and regrading. Fix thanks to Paulo Matos, who not only fixed the bug and tested it, but was also incredibly patient waiting for me to have time to commit the changes to CVS.

This commit also includes a backport of a small state logic changes to the 1.6 stable branch.

Backported from MOODLE_17_STABLE.
  • Loading branch information...
commit e7a84ec034fa2db4c424c6b0f48a0d9cca62f521 1 parent 00e53e7
tjhunt authored
Showing with 88 additions and 71 deletions.
  1. +75 −24 lib/questionlib.php
  2. +13 −47 mod/quiz/attempt.php
View
99 lib/questionlib.php
@@ -319,20 +319,20 @@ function delete_attempt($attemptid) {
global $QTYPES;
$states = get_records('question_states', 'attempt', $attemptid);
- $stateslist = implode(',', array_keys($states));
-
- // delete questiontype-specific data
- foreach ($QTYPES as $qtype) {
- $qtype->delete_states($stateslist);
+ if ($states) {
+ $stateslist = implode(',', array_keys($states));
+
+ // delete question-type specific data
+ foreach ($QTYPES as $qtype) {
+ $qtype->delete_states($stateslist);
+ }
}
-
+
// delete entries from all other question tables
// It is important that this is done only after calling the questiontype functions
delete_records("question_states", "attempt", $attemptid);
delete_records("question_sessions", "attemptid", $attemptid);
delete_records("question_attempts", "id", $attemptid);
-
- return;
}
/**
@@ -561,8 +561,10 @@ function get_question_options(&$questions) {
* @param object $cmoptions
* @param object $attempt The attempt for which the question sessions are
* to be restored or created.
+* @param mixed either the id of a previous attempt, if this attmpt is
+* building on a previous one, or false for a clean attempt.
*/
-function get_question_states(&$questions, $cmoptions, $attempt) {
+function get_question_states(&$questions, $cmoptions, $attempt, $lastattemptid = false) {
global $CFG, $QTYPES;
// get the question ids
@@ -601,8 +603,33 @@ function get_question_states(&$questions, $cmoptions, $attempt) {
$states[$i]->last_graded = clone($states[$i]);
}
} else {
- // create a new empty state
- $states[$i] = new object;
+ // If the new attempt is to be based on a previous attempt get it and clean things
+ // Having lastattemptid filled implies that (should we double check?):
+ // $attempt->attempt > 1 and $cmoptions->attemptonlast and !$attempt->preview
+ if ($lastattemptid) {
+ // find the responses from the previous attempt and save them to the new session
+
+ // Load the last graded state for the question
+ $statefields = 'n.questionid as question, s.*, n.sumpenalty';
+ $sql = "SELECT $statefields".
+ " FROM {$CFG->prefix}question_states s,".
+ " {$CFG->prefix}question_sessions n".
+ " WHERE s.id = n.newgraded".
+ " AND n.attemptid = '$lastattemptid'".
+ " AND n.questionid = '$i'";
+ if (!$laststate = get_record_sql($sql)) {
+ // Only restore previous responses that have been graded
+ continue;
+ }
+ // Restore the state so that the responses will be restored
+ restore_question_state($questions[$i], $laststate);
+ $states[$i] = clone ($laststate);
+ } else {
+ // create a new empty state
+ $states[$i] = new object;
+ }
+
+ // now fill/overide initial values
$states[$i]->attempt = $attempt->uniqueid;
$states[$i]->question = (int) $i;
$states[$i]->seq_number = 0;
@@ -613,15 +640,36 @@ function get_question_states(&$questions, $cmoptions, $attempt) {
$states[$i]->penalty = 0;
$states[$i]->sumpenalty = 0;
$states[$i]->comment = '';
- $states[$i]->responses = array('' => '');
+
+ // if building on last attempt we want to preserve responses
+ if (!$lastattemptid) {
+ $states[$i]->responses = array('' => '');
+ }
// Prevent further changes to the session from incrementing the
// sequence number
$states[$i]->changed = true;
- // Create the empty question type specific information
- if (!$QTYPES[$questions[$i]->qtype]
- ->create_session_and_responses($questions[$i], $states[$i], $cmoptions, $attempt)) {
- return false;
+ if ($lastattemptid) {
+ // prepare the previous responses for new processing
+ $action = new stdClass;
+ $action->responses = $laststate->responses;
+ $action->timestamp = $laststate->timestamp;
+ $action->event = QUESTION_EVENTSAVE; //emulate save of questions from all pages MDL-7631
+
+ // Process these responses ...
+ question_process_responses($questions[$i], $states[$i], $action, $cmoptions, $attempt);
+
+ // Fix for Bug #5506: When each attempt is built on the last one,
+ // preserve the options from any previous attempt.
+ if ( isset($laststate->options) ) {
+ $states[$i]->options = $laststate->options;
+ }
+ } else {
+ // Create the empty question type specific information
+ if (!$QTYPES[$questions[$i]->qtype]
+ ->create_session_and_responses($questions[$i], $states[$i], $cmoptions, $attempt)) {
+ return false;
+ }
}
$states[$i]->last_graded = clone($states[$i]);
}
@@ -943,8 +991,10 @@ function question_process_responses(&$question, &$state, $action, $cmoptions, &$
}
// Check for unchanged responses (exactly unchanged, not equivalent).
// We also have to catch questions that the student has not yet attempted
- $sameresponses = (($state->responses == $action->responses) or
- ($state->responses == array(''=>'') && array_keys(array_count_values($action->responses))===array('')));
+ $sameresponses = $QTYPES[$question->qtype]->compare_responses($question, $action, $state);
+ if ($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
@@ -977,15 +1027,16 @@ function question_process_responses(&$question, &$state, $action, $cmoptions, &$
} 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) and $QTYPES[$question->qtype]->compare_responses(
- $question, $state, $state->last_graded)) {
+ // 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 ((!$sameresponses and QUESTION_EVENTDUPLICATE != $state->event) or
+ QUESTION_EVENTCLOSE == $action->event) {
// Decrease sumgrades by previous grade and then later add new grade
$attempt->sumgrades -= (float)$state->last_graded->grade;
@@ -1580,4 +1631,4 @@ function default_export_filename($course,$category) {
return $export_name;
}
-?>
+?>
View
60 mod/quiz/attempt.php
@@ -280,9 +280,18 @@
error('Could not load question options');
}
+ // If the new attempt is to be based on a previous attempt find its id
+ $lastattemptid = false;
+ if ($newattempt and $attempt->attempt > 1 and $quiz->attemptonlast and !$attempt->preview) {
+ // Find the previous attempt
+ if (!$lastattemptid = get_field('quiz_attempts', 'uniqueid', 'quiz', $attempt->quiz, 'userid', $attempt->userid, 'attempt', $attempt->attempt-1)) {
+ error('Could not find previous attempt to build on');
+ }
+ }
+
// Restore the question sessions to their most recent states
// creating new sessions where required
- if (!$states = get_question_states($questions, $quiz, $attempt)) {
+ if (!$states = get_question_states($questions, $quiz, $attempt, $lastattemptid)) {
error('Could not restore question sessions');
}
@@ -293,49 +302,6 @@
}
}
- // If the new attempt is to be based on a previous attempt copy responses over
- if ($newattempt and $attempt->attempt > 1 and $quiz->attemptonlast and !$attempt->preview) {
- // Find the previous attempt
- if (!$lastattemptid = get_field('quiz_attempts', 'uniqueid', 'quiz', $attempt->quiz, 'userid', $attempt->userid, 'attempt', $attempt->attempt-1)) {
- error('Could not find previous attempt to build on');
- }
- // For each question find the responses from the previous attempt and save them to the new session
- foreach ($questions as $i => $question) {
- // Load the last graded state for the question
- $statefields = 'n.questionid as question, s.*, n.sumpenalty';
- $sql = "SELECT $statefields".
- " FROM {$CFG->prefix}question_states s,".
- " {$CFG->prefix}question_sessions n".
- " WHERE s.id = n.newgraded".
- " AND n.attemptid = '$lastattemptid'".
- " AND n.questionid = '$i'";
- if (!$laststate = get_record_sql($sql)) {
- // Only restore previous responses that have been graded
- continue;
- }
- // Restore the state so that the responses will be restored
- restore_question_state($questions[$i], $laststate);
- // prepare the previous responses for new processing
- $action = new stdClass;
- $action->responses = $laststate->responses;
- $action->timestamp = $laststate->timestamp;
- $action->event = QUESTION_EVENTSAVE; //emulate save of questions from all pages MDL-7631
-
- // Process these responses ...
- question_process_responses($questions[$i], $states[$i], $action, $quiz, $attempt);
-
- // Fix for Bug #5506: When each attempt is built on the last one,
- // preserve the options from any previous attempt.
- if ( isset($laststate->options) ) {
- $states[$i]->options = $laststate->options;
- }
-
- // ... and save the new states
- save_question_session($questions[$i], $states[$i]);
- }
- }
-
-
/// Process form data /////////////////////////////////////////////////
if ($responses = data_submitted() and empty($_POST['quizpassword'])) {
@@ -493,8 +459,8 @@ function navigate(page) {
var ourForm = document.forms['responseform'];
ourForm.page.value=page;
if (ourForm.onsubmit) {
- ourForm.onsubmit();
- }
+ ourForm.onsubmit();
+ }
ourForm.submit();
}
</script>
@@ -575,4 +541,4 @@ function navigate(page) {
print_footer($course);
}
-?>
+?>
Please sign in to comment.
Something went wrong with that request. Please try again.