From: gustav_delius Date: Sun, 19 Mar 2006 18:28:29 +0000 (+0000) Subject: Improved handling of event types so that now graded states can always be distinguishe... X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=f30bbcaf5bfbb60c2766d9e5b718fde71ab98f5c;p=moodle.git Improved handling of event types so that now graded states can always be distinguished from ungraded ones. --- diff --git a/lang/en_utf8/quiz.php b/lang/en_utf8/quiz.php index af6e8254aa..e62c3cc2e6 100644 --- a/lang/en_utf8/quiz.php +++ b/lang/en_utf8/quiz.php @@ -159,6 +159,8 @@ $string['event2'] = 'Save'; $string['event3'] = 'Grade'; $string['event5'] = 'Validate'; $string['event6'] = 'Close'; +$string['event7'] = 'Submit'; +$string['event8'] = 'Close'; $string['examview'] = 'Examview'; $string['existingcategory1'] = 'a literal from an already existing set of literals that are also used by other questions in this category'; $string['existingcategory2'] = 'a file from an already existing set of files that are also used by other questions in this category'; diff --git a/lib/questionlib.php b/lib/questionlib.php index 7532245cf7..3e22162d7e 100644 --- a/lib/questionlib.php +++ b/lib/questionlib.php @@ -22,14 +22,15 @@ /**#@+ * The different types of events that can create question states */ -define('QUESTION_EVENTOPEN', '0'); -define('QUESTION_EVENTNAVIGATE', '1'); -define('QUESTION_EVENTSAVE', '2'); -define('QUESTION_EVENTGRADE', '3'); -define('QUESTION_EVENTDUPLICATEGRADE', '4'); -define('QUESTION_EVENTVALIDATE', '5'); -define('QUESTION_EVENTCLOSE', '6'); -define('QUESTION_EVENTSUBMIT', '7'); +define('QUESTION_EVENTOPEN', '0'); // The state was created by Moodle +define('QUESTION_EVENTNAVIGATE', '1'); // The responses were saved because the student navigated to another page (this is not currently used) +define('QUESTION_EVENTSAVE', '2'); // The student has requested that the responses should be saved but not submitted or validated +define('QUESTION_EVENTGRADE', '3'); // Moodle has graded the responses. A SUBMIT event can be changed to a GRADE event by Moodle. +define('QUESTION_EVENTDUPLICATE', '4'); // The responses submitted were the same as previously +define('QUESTION_EVENTVALIDATE', '5'); // The student has requested a validation. This causes the responses to be saved as well, but not graded. +define('QUESTION_EVENTCLOSEANDGRADE', '6'); // Moodle has graded the responses. A CLOSE event can be changed to a CLOSEANDGRADE event by Moodle. +define('QUESTION_EVENTSUBMIT', '7'); // The student response has been submitted but it has not yet been marked +define('QUESTION_EVENTCLOSE', '8'); // The response has been submitted and the session has been closed, either because the student requested it or because Moodle did it (e.g. because of a timelimit). The responses have not been graded. /**#@-*/ /**#@+ @@ -275,7 +276,6 @@ function get_question_states(&$questions, $cmoptions, $attempt) { $states[$i]->last_graded = $gradedstates[$i]; } else { $states[$i]->last_graded = clone($states[$i]); - $states[$i]->last_graded->responses = array('' => ''); } } else { // Create a new state object @@ -445,7 +445,17 @@ function save_question_session(&$question, &$state) { * @param object $state */ function question_state_is_graded($state) { - return ($state->event == QUESTION_EVENTGRADE or $state->event == QUESTION_EVENTCLOSE); + return ($state->event == QUESTION_EVENTGRADE or $state->event == QUESTION_EVENTCLOSEANDGRADE); +} + +/** +* Determines whether a state has been closed by looking at the event field +* +* @return boolean true if the state has been closed +* @param object $state +*/ +function question_state_is_closed($state) { + return ($state->event == QUESTION_EVENTCLOSE or $state->event == QUESTION_EVENTCLOSEANDGRADE); } @@ -479,7 +489,7 @@ function question_extract_responses($questions, $responses, $defaultevent) { if ($key === 'validate') { $actions[$quid]->event = QUESTION_EVENTVALIDATE; } else if ($key === 'mark') { - $actions[$quid]->event = QUESTION_EVENTGRADE; + $actions[$quid]->event = QUESTION_EVENTSUBMIT; } else { $actions[$quid]->event = $defaultevent; } @@ -537,10 +547,11 @@ function regrade_question_in_attempt($question, $attempt, $cmoptions, $verbose=f if (((count($states) - 1) === $j) && ($attempt->timefinish > 0)) { $action->event = QUESTION_EVENTCLOSE; - // Grade instead of closing, question_process_responses will then - // work out whether to close it - } else if (QUESTION_EVENTCLOSE == $states[$j]->event) { - $action->event = QUESTION_EVENTGRADE; + // Change event to submit so that it will be reprocessed + } else 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 { @@ -605,14 +616,15 @@ function question_process_responses(&$question, &$state, $action, $cmoptions, &$ unset($action->responses['mark'], $action->responses['validate']); // Check the question session is still open - if (QUESTION_EVENTCLOSE == $state->event) { + if (question_state_is_closed($state)) { return true; } + // If $action->event is not set that implies saving if (! isset($action->event)) { $action->event = QUESTION_EVENTSAVE; } - // Check if we are grading the question; compare against last graded + // 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; @@ -622,6 +634,8 @@ function question_process_responses(&$question, &$state, $action, $cmoptions, &$ $sameresponses = (($state->responses == $action->responses) or ($state->responses == array(''=>'') && array_keys(array_count_values($action->responses))===array(''))); + // 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; @@ -646,11 +660,10 @@ function question_process_responses(&$question, &$state, $action, $cmoptions, &$ // Grade the response but don't update the overall grade $QTYPES[$question->qtype]->grade_responses( $question, $state, $cmoptions); - // Force the event to save or validate (even if the grading caused the - // state to close) + // Don't allow the processing to change the event type $state->event = $action->event; - } else if (QUESTION_EVENTGRADE == $action->event) { + } else if (QUESTION_EVENTSUBMIT == $action->event) { // Work out if the current responses (or equivalent responses) were // already given in @@ -658,33 +671,35 @@ function question_process_responses(&$question, &$state, $action, $cmoptions, &$ // b. any other graded attempt if($QTYPES[$question->qtype]->compare_responses( $question, $state, $state->last_graded)) { - $state->event = QUESTION_EVENTDUPLICATEGRADE; + $state->event = QUESTION_EVENTDUPLICATE; } else { if ($cmoptions->optionflags & QUIZ_IGNORE_DUPRESP) { /* Walk back through the previous graded states looking for one where the responses are equivalent to the current responses. If such a state is found, set the current grading details to those of that state and set the event to - QUESTION_EVENTDUPLICATEGRADE */ + QUESTION_EVENTDUPLICATE */ question_search_for_duplicate_responses($question, $state); } - // If we did not find a duplicate, perform grading - if (QUESTION_EVENTDUPLICATEGRADE != $state->event) { - // Decrease sumgrades by previous grade and then later add new grade - $attempt->sumgrades -= (float)$state->last_graded->grade; - - $QTYPES[$question->qtype]->grade_responses( - $question, $state, $cmoptions); - // Calculate overall grade using correct penalty method - question_apply_penalty_and_timelimit($question, $state, $attempt, $cmoptions); - // Update the last graded state (don't simplify!) - unset($state->last_graded); - $state->last_graded = clone($state); - unset($state->last_graded->changed); - - $attempt->sumgrades += (float)$state->last_graded->grade; - } } + + // If we did not find a duplicate, perform grading + if (QUESTION_EVENTDUPLICATE != $state->event) { + // Decrease sumgrades by previous grade and then later add new grade + $attempt->sumgrades -= (float)$state->last_graded->grade; + + $QTYPES[$question->qtype]->grade_responses( + $question, $state, $cmoptions); + // Calculate overall grade using correct penalty method + question_apply_penalty_and_timelimit($question, $state, $attempt, $cmoptions); + // Update the last graded state (don't simplify!) + unset($state->last_graded); + $state->last_graded = clone($state); + unset($state->last_graded->changed); + + $attempt->sumgrades += (float)$state->last_graded->grade; + } + } else if (QUESTION_EVENTCLOSE == $action->event) { // decrease sumgrades by previous grade and then later add new grade $attempt->sumgrades -= (float)$state->last_graded->grade; @@ -696,8 +711,6 @@ function question_process_responses(&$question, &$state, $action, $cmoptions, &$ // Calculate overall grade using correct penalty method question_apply_penalty_and_timelimit($question, $state, $attempt, $cmoptions); } - // Force the state to close (as the attempt is closing) - $state->event = QUESTION_EVENTCLOSE; // Update the last graded state (don't simplify!) unset($state->last_graded); @@ -715,7 +728,7 @@ function question_process_responses(&$question, &$state, $action, $cmoptions, &$ * Determine if event requires grading */ function question_isgradingevent($event) { - return (QUESTION_EVENTGRADE == $event || QUESTION_EVENTCLOSE == $event); + return (QUESTION_EVENTSUBMIT == $event || QUESTION_EVENTCLOSE == $event); } /** @@ -725,6 +738,8 @@ function question_isgradingevent($event) { * to ignore the marking request for the current response. However this * check against all previous graded responses is only performed if * the QUIZ_IGNORE_DUPRESP bit in $cmoptions->optionflags is set +* If the current response is a duplicate of a previously graded response then +* $STATE->event is set to QUESTION_EVENTDUPLICATE. * @return boolean Indicates if a state with duplicate responses was * found. * @param object $question @@ -743,12 +758,12 @@ function question_search_for_duplicate_responses(&$question, &$state) { $question, $oldstate)) { if(!$QTYPES[$question->qtype]->compare_responses( $question, $state, $oldstate)) { - $state->event = QUESTION_EVENTDUPLICATEGRADE; + $state->event = QUESTION_EVENTDUPLICATE; break; } } } - return (QUESTION_EVENTDUPLICATEGRADE == $state->event); + return (QUESTION_EVENTDUPLICATE == $state->event); } /** @@ -771,7 +786,7 @@ function question_search_for_duplicate_responses(&$question, &$state) { * for incorrect earlier responses are subtracted. */ function question_apply_penalty_and_timelimit(&$question, &$state, $attempt, $cmoptions) { - // deal with penaly + // deal with penalty if ($cmoptions->penaltyscheme) { $state->grade = $state->raw_grade - $state->sumpenalty; $state->sumpenalty += (float) $state->penalty; diff --git a/question/questiontypes/essay/questiontype.php b/question/questiontypes/essay/questiontype.php index c0777688d7..a571073e5b 100644 --- a/question/questiontypes/essay/questiontype.php +++ b/question/questiontypes/essay/questiontype.php @@ -173,12 +173,13 @@ class question_essay_qtype extends quiz_default_questiontype { function grade_responses(&$question, &$state, $cmoptions) { $state->raw_grade = 0; // if a fraction is submitted, then we use it, otherwise the raw grade is 0 - if (isset($state->responses['fraction'])) { + if ($state->responses['fraction']) { $state->raw_grade = $state->responses['fraction']; $state->options->graded = 1; + // mark the state as graded + $state->event = ($state->event == QUESTION_EVENTCLOSE) ? QUESTION_EVENTCLOSEANDGRADE : QUESTION_EVENTGRADE; } else { $state->raw_grade = 0; - $state->event = QUESTION_EVENTSUBMIT; } // Make sure we don't assign negative or too high marks diff --git a/question/questiontypes/match/questiontype.php b/question/questiontypes/match/questiontype.php index baca527267..623c120683 100644 --- a/question/questiontypes/match/questiontype.php +++ b/question/questiontypes/match/questiontype.php @@ -294,6 +294,9 @@ class question_match_qtype extends quiz_default_questiontype { 0.0), 1.0) * $question->maxgrade; $state->penalty = $question->penalty * $question->maxgrade; + // mark the state as graded + $state->event = ($state->event == QUESTION_EVENTCLOSE) ? QUESTION_EVENTCLOSEANDGRADE : QUESTION_EVENTGRADE; + return true; } diff --git a/question/questiontypes/multianswer/questiontype.php b/question/questiontypes/multianswer/questiontype.php index e8c8460323..2f9d572d78 100644 --- a/question/questiontypes/multianswer/questiontype.php +++ b/question/questiontypes/multianswer/questiontype.php @@ -356,6 +356,9 @@ class quiz_embedded_cloze_qtype extends quiz_default_questiontype { } $state->penalty = $question->penalty * $question->maxgrade; + // mark the state as graded + $state->event = ($state->event == QUESTION_EVENTCLOSE) ? QUESTION_EVENTCLOSEANDGRADE : QUESTION_EVENTGRADE; + return true; } diff --git a/question/questiontypes/multichoice/questiontype.php b/question/questiontypes/multichoice/questiontype.php index 7f3ca8708b..43a964171e 100644 --- a/question/questiontypes/multichoice/questiontype.php +++ b/question/questiontypes/multichoice/questiontype.php @@ -350,6 +350,9 @@ class question_multichoice_qtype extends quiz_default_questiontype { // Apply the penalty for this attempt $state->penalty = $question->penalty * $question->maxgrade; + // mark the state as graded + $state->event = ($state->event == QUESTION_EVENTCLOSE) ? QUESTION_EVENTCLOSEANDGRADE : QUESTION_EVENTGRADE; + return true; } diff --git a/question/questiontypes/numerical/questiontype.php b/question/questiontypes/numerical/questiontype.php index a3919f7739..1675fac934 100644 --- a/question/questiontypes/numerical/questiontype.php +++ b/question/questiontypes/numerical/questiontype.php @@ -292,6 +292,9 @@ class question_numerical_qtype extends question_shortanswer_qtype { 0.0), 1.0) * $question->maxgrade; $state->penalty = $question->penalty * $question->maxgrade; + // mark the state as graded + $state->event = ($state->event == QUESTION_EVENTCLOSE) ? QUESTION_EVENTCLOSEANDGRADE : QUESTION_EVENTGRADE; + return true; } diff --git a/question/questiontypes/questiontype.php b/question/questiontypes/questiontype.php index 7702da4e23..1aa9e58319 100644 --- a/question/questiontypes/questiontype.php +++ b/question/questiontypes/questiontype.php @@ -458,7 +458,7 @@ class quiz_default_questiontype { $grade = ''; if ($question->maxgrade and $options->scores) { if ($cmoptions->optionflags & QUIZ_ADAPTIVE) { - $grade = (!($state->last_graded->event == QUESTION_EVENTGRADE)) ? '--/' : round($state->last_graded->grade, $cmoptions->decimalpoints).'/'; + $grade = (!question_state_is_graded($state->last_graded)) ? '--/' : round($state->last_graded->grade, $cmoptions->decimalpoints).'/'; } $grade .= $question->maxgrade; } @@ -470,7 +470,7 @@ class quiz_default_questiontype { $states = get_records_select('question_states', "attempt = '$state->attempt' AND question = '$question->id' AND event > '0'", 'seq_number DESC'); } else { // show only graded states - $states = get_records_select('question_states', "attempt = '$state->attempt' AND question = '$question->id' AND event = '".QUESTION_EVENTGRADE."'", 'seq_number DESC'); + $states = get_records_select('question_states', "attempt = '$state->attempt' AND question = '$question->id' AND event IN (".QUESTION_EVENTGRADE.','.QUESTION_EVENTCLOSEANDGRADE.")", 'seq_number DESC'); } if (count($states) > 1) { $strreviewquestion = get_string('reviewresponse', 'quiz'); @@ -531,12 +531,12 @@ class quiz_default_questiontype { attempt and displays the overall grade obtained counting all previous responses (and penalties) */ - if (QUESTION_EVENTDUPLICATEGRADE == $state->event) { + if (QUESTION_EVENTDUPLICATE == $state->event) { echo ' '; print_string('duplicateresponse', 'quiz'); } if (!empty($question->maxgrade) && $options->scores) { - if ($state->last_graded->event == QUESTION_EVENTGRADE) { + if (question_state_is_graded($state->last_graded)) { // Display the grading details from the last graded state $grade->cur = round($state->last_graded->grade, $cmoptions->decimalpoints); $grade->max = $question->maxgrade; @@ -566,7 +566,7 @@ class quiz_default_questiontype { } // print info about new penalty // penalty is relevant only if the answer is not correct and further attempts are possible - if (($state->last_graded->raw_grade < $question->maxgrade) and (QUESTION_EVENTCLOSE !== $state->event)) { + if (($state->last_graded->raw_grade < $question->maxgrade) and (QUESTION_EVENTCLOSEANDGRADE !== $state->event)) { if ('' !== $state->last_graded->penalty && ((float)$state->last_graded->penalty) > 0.0) { // A penalty was applied so display it print_string('gradingdetailspenalty', 'quiz', $state->last_graded->penalty); @@ -718,6 +718,7 @@ class quiz_default_questiontype { // arrays. The ordering of the arrays does not matter. // Question types may wish to override this (eg. to ignore trailing // white space or to make "7.0" and "7" compare equal). + if ($question->qtype = MATCH) {var_dump($state->responses);echo '
Teststate:';var_dump($teststate->responses);} return $state->responses == $teststate->responses; } @@ -750,7 +751,7 @@ class quiz_default_questiontype { * must be updated. The method is able to * close the question session (preventing any further * attempts at this question) by setting - * $state->event to QUESTION_EVENTCLOSE. + * $state->event to QUESTION_EVENTCLOSEANDGRADE * @param object $cmoptions */ function grade_responses(&$question, &$state, $cmoptions) { @@ -776,6 +777,9 @@ class quiz_default_questiontype { // Only allow one attempt at the question $state->penalty = 1; + // mark the state as graded + $state->event = ($state->event == QUESTION_EVENTCLOSE) ? QUESTION_EVENTCLOSEANDGRADE : QUESTION_EVENTGRADE; + return true; } diff --git a/question/questiontypes/rqp/questiontype.php b/question/questiontypes/rqp/questiontype.php index 119e58c552..0d46a0099a 100644 --- a/question/questiontypes/rqp/questiontype.php +++ b/question/questiontypes/rqp/questiontype.php @@ -360,6 +360,7 @@ class question_rqp_qtype extends quiz_default_questiontype { * This function calls RQP_Render to perform response processing and grading * and updates the state accordingly. It also caches the rendering output in case * it is needed later. + * TODO: set $state->event appropriately * @return boolean Indicates success or failure. * @param object $question The question to be graded. * @param object $state The state of the question to grade. The current diff --git a/question/questiontypes/shortanswer/questiontype.php b/question/questiontypes/shortanswer/questiontype.php index f96300559e..d8d9f83dc0 100644 --- a/question/questiontypes/shortanswer/questiontype.php +++ b/question/questiontypes/shortanswer/questiontype.php @@ -198,6 +198,9 @@ class question_shortanswer_qtype extends quiz_default_questiontype { 0.0), 1.0) * $question->maxgrade; $state->penalty = $question->penalty * $question->maxgrade; + // mark the state as graded + $state->event = ($state->event == QUESTION_EVENTCLOSE) ? QUESTION_EVENTCLOSEANDGRADE : QUESTION_EVENTGRADE; + return true; } diff --git a/question/questiontypes/truefalse/questiontype.php b/question/questiontypes/truefalse/questiontype.php index 5b9076cb9d..e7cc30671f 100644 --- a/question/questiontypes/truefalse/questiontype.php +++ b/question/questiontypes/truefalse/questiontype.php @@ -194,6 +194,9 @@ class question_truefalse_qtype extends quiz_default_questiontype { // Only allow one attempt at the question $state->penalty = 1; + // mark the state as graded + $state->event = ($state->event == QUESTION_EVENTCLOSE) ? QUESTION_EVENTCLOSEANDGRADE : QUESTION_EVENTGRADE; + return true; }