Improved handling of event types so that now graded states can always be distinguished from ungraded ones.
This commit is contained in:
@@ -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';
|
||||
|
||||
+59
-44
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 '<br>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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user