Random questions now store both the wrapped question id and the response in the same state record, essentially going back to Julian's model.
This commit is contained in:
@@ -7,13 +7,7 @@
|
||||
/// QUESTION TYPE CLASS //////////////////
|
||||
class quiz_random_qtype extends quiz_default_questiontype {
|
||||
|
||||
var $possiblerandomqtypes = array(SHORTANSWER,
|
||||
NUMERICAL,
|
||||
MULTICHOICE,
|
||||
MATCH,
|
||||
// RANDOMSAMATCH,// Can cause unexpected outcomes
|
||||
TRUEFALSE,
|
||||
MULTIANSWER);
|
||||
var $excludedtypes = array(RANDOM, RANDOMSAMATCH);
|
||||
|
||||
// Carries questions available as randoms sorted by category
|
||||
// This array is used when needed only
|
||||
@@ -52,8 +46,7 @@ class quiz_random_qtype extends quiz_default_questiontype {
|
||||
// Need to fetch random questions from category $question->category"
|
||||
// (Note: $this refers to the questiontype, not the question.)
|
||||
global $CFG;
|
||||
$possiblerandomqtypes = "'"
|
||||
. implode("','", $this->possiblerandomqtypes) . "'";
|
||||
$excludedtypes = implode(',', $this->excludedtypes);
|
||||
if ($question->questiontext == "1") {
|
||||
// recurse into subcategories
|
||||
$categorylist = quiz_categorylist($question->category);
|
||||
@@ -65,7 +58,7 @@ class quiz_random_qtype extends quiz_default_questiontype {
|
||||
WHERE category IN ($categorylist)
|
||||
AND parent = '0'
|
||||
AND id NOT IN ($quiz->questionsinuse)
|
||||
AND qtype IN ($possiblerandomqtypes)");
|
||||
AND qtype NOT IN ($excludedtypes)");
|
||||
$this->catrandoms[$question->category] =
|
||||
draw_rand_array($this->catrandoms[$question->category],
|
||||
count($this->catrandoms[$question->category])); // from bug 1889
|
||||
@@ -80,13 +73,6 @@ class quiz_random_qtype extends quiz_default_questiontype {
|
||||
global $QUIZ_QTYPES;
|
||||
$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->get_question_options($wrappedquestion);
|
||||
|
||||
// Backup the original state of the random question
|
||||
// And change the $state to match the wrapped question. This
|
||||
// is sensible, because so the wrapped question's state gets
|
||||
// put through all the generic processing.
|
||||
$state->options->state = clone($state);
|
||||
$state->question = $wrappedquestion->id;
|
||||
$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->create_session_and_responses($wrappedquestion,
|
||||
$state, $quiz, $attempt);
|
||||
@@ -105,33 +91,45 @@ class quiz_random_qtype extends quiz_default_questiontype {
|
||||
}
|
||||
|
||||
function restore_session_and_responses(&$question, &$state) {
|
||||
/// The raw response records for random questions come in two flavours:
|
||||
/// ---- 1 ----
|
||||
/// For responses stored by Moodle version 1.5 and later the answer
|
||||
/// field has the pattern random#-* where the # part is the numeric
|
||||
/// question id of the actual question shown in the quiz attempt
|
||||
/// and * represents the student response to that actual question.
|
||||
/// ---- 2 ----
|
||||
/// For responses stored by older Moodle versions - the answer field is
|
||||
/// simply the question id of the actual question. The student response
|
||||
/// to the actual question is stored in a separate response record.
|
||||
/// -----------------------
|
||||
/// This means that prior to Moodle version 1.5, random questions needed
|
||||
/// two response records for storing the response to a single question.
|
||||
/// From version 1.5 and later the question type random works like all
|
||||
/// the other question types in that it now only needs one response
|
||||
/// record per question.
|
||||
global $QUIZ_QTYPES;
|
||||
if(!$randomstate = get_record('quiz_states', 'question',
|
||||
$question->id, 'attempt', $state->attempt)) {
|
||||
return false;
|
||||
if (!ereg('^random([0-9]+)-(.*)$', $state->responses[''], $answerregs)) {
|
||||
// this must be an old-style state which stores only the id for the wrapped question
|
||||
if (!$wrappedquestion = get_record('quiz_questions', 'id', $state->responses[''])) {
|
||||
error("Can not find wrapped question $state->responses['']");
|
||||
}
|
||||
// In the old model the actual response was stored in a separate entry in
|
||||
// the state table
|
||||
if (!$state->responses[''] = get_field('quiz_states', 'answer', 'attempt', $state->attempt, 'question', $wrappedquestion->id)) {
|
||||
error("Wrapped state missing");
|
||||
}
|
||||
} else {
|
||||
if (!$wrappedquestion = get_record('quiz_questions', 'id', $answerregs[1])) {
|
||||
return false;
|
||||
}
|
||||
$state->responses[''] = $answerregs[2];
|
||||
}
|
||||
|
||||
if (!$wrappedquestion = get_record('quiz_questions', 'id',
|
||||
$randomstate->answer)) {
|
||||
return false;
|
||||
}
|
||||
$state->question = $wrappedquestion->id;
|
||||
|
||||
if (!$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->get_question_options($wrappedquestion)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We need to set responses[''] to whatever was saved in the most recent
|
||||
// state of the wrapped question.
|
||||
if(!$wrappedstates = get_records_select('quiz_states',
|
||||
"question = $wrappedquestion->id AND attempt = $state->attempt",
|
||||
'seq_number DESC')) {
|
||||
return false;
|
||||
}
|
||||
$wrappedstates = array_values($wrappedstates);
|
||||
$state->responses = array('' => $wrappedstates[0]->answer);
|
||||
|
||||
if (!$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->restore_session_and_responses($wrappedquestion, $state)) {
|
||||
return false;
|
||||
@@ -139,21 +137,12 @@ class quiz_random_qtype extends quiz_default_questiontype {
|
||||
$wrappedquestion->name_prefix = $question->name_prefix;
|
||||
$wrappedquestion->maxgrade = $question->maxgrade;
|
||||
$state->options->question = &$wrappedquestion;
|
||||
$state->options->state = &$randomstate;
|
||||
return true;
|
||||
}
|
||||
|
||||
function save_session_and_responses(&$question, &$state) {
|
||||
global $QUIZ_QTYPES;
|
||||
$wrappedquestion = &$state->options->question;
|
||||
$randomstate = &$state->options->state;
|
||||
|
||||
// We need to save the randomstate manually, because we can only process
|
||||
// one response record automatically
|
||||
if (empty($randomstate->id)) {
|
||||
$randomstate->answer = $wrappedquestion->id;
|
||||
$randomstate->id = insert_record('quiz_states', $randomstate);
|
||||
}
|
||||
|
||||
// Trick the wrapped question into pretending to be the random one.
|
||||
$realqid = $wrappedquestion->id;
|
||||
@@ -161,6 +150,21 @@ class quiz_random_qtype extends quiz_default_questiontype {
|
||||
$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->save_session_and_responses($wrappedquestion, $state);
|
||||
|
||||
// Read what the wrapped question has just set the answer field to
|
||||
// (if anything)
|
||||
$response = get_field('quiz_states', 'answer', 'id', $state->id);
|
||||
if(false === $response) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prefix the answer field...
|
||||
$response = "random$realqid-$response";
|
||||
|
||||
// ... and save it again.
|
||||
if (!set_field('quiz_states', 'answer', $response, 'id', $state->id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Restore the real id
|
||||
$wrappedquestion->id = $realqid;
|
||||
return true;
|
||||
@@ -196,34 +200,7 @@ class quiz_random_qtype extends quiz_default_questiontype {
|
||||
$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->print_question($wrappedquestion, $state, $number, $quiz, $options);
|
||||
}
|
||||
/*
|
||||
function print_question_grading_details(&$question, &$state, $quiz,
|
||||
$options) {
|
||||
global $QUIZ_QTYPES;
|
||||
$wrappedquestion = &$state->options->question;
|
||||
$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->print_question_grading_details($wrappedquestion, $state, $quiz,
|
||||
$options);
|
||||
}
|
||||
|
||||
function print_question_formulation_and_controls(&$question, &$state, $quiz,
|
||||
$options) {
|
||||
global $QUIZ_QTYPES;
|
||||
$wrappedquestion = &$state->options->question;
|
||||
$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->print_question_formulation_and_controls($wrappedquestion, $state,
|
||||
$quiz, $options);
|
||||
}
|
||||
|
||||
function print_question_submit_buttons(&$question, &$state, $quiz,
|
||||
$options) {
|
||||
global $QUIZ_QTYPES;
|
||||
$wrappedquestion = &$state->options->question;
|
||||
$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->print_question_submit_buttons($wrappedquestion, $state, $quiz,
|
||||
$options);
|
||||
}
|
||||
*/
|
||||
function grade_responses(&$question, &$state, $quiz) {
|
||||
global $QUIZ_QTYPES;
|
||||
$wrappedquestion = &$state->options->question;
|
||||
@@ -258,173 +235,7 @@ class quiz_random_qtype extends quiz_default_questiontype {
|
||||
return $QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->print_question_form_end($wrappedquestion, $state, $quizid);
|
||||
}
|
||||
/*
|
||||
function convert_to_response_answer_field($questionresponse) {
|
||||
global $QUIZ_QTYPES;
|
||||
|
||||
foreach ($questionresponse as $key => $response) {
|
||||
if (ereg('[^0-9][0-9]+random$', $key)) {
|
||||
unset($questionresponse[$key]);
|
||||
$randomquestion = get_record('quiz_questions',
|
||||
'id', $response);
|
||||
return "random$response-"
|
||||
.$QUIZ_QTYPES[$randomquestion->qtype]
|
||||
->convert_to_response_answer_field($questionresponse);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
|
||||
function create_response($question, $nameprefix, $questionsinuse) {
|
||||
// It's for question types like RANDOMSAMATCH and RANDOM that
|
||||
// the true power of the pattern with this function comes to the surface.
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
/*
|
||||
function print_question_formulation_and_controls($question,
|
||||
$quiz, $readonly, $answers, $correctanswers, $nameprefix) {
|
||||
global $QUIZ_QTYPES;
|
||||
|
||||
// Get the wrapped question...
|
||||
if ($actualquestion = $this->get_wrapped_question($question,
|
||||
$nameprefix)) {
|
||||
echo '<input type="hidden" name="' . $nameprefix
|
||||
. '" value="' . $actualquestion->id . '" />';
|
||||
return $QUIZ_QTYPES[$actualquestion->qtype]
|
||||
->print_question_formulation_and_controls($actualquestion,
|
||||
$quiz, $readonly, $answers, $correctanswers,
|
||||
quiz_qtype_nameprefix($actualquestion, $nameprefix));
|
||||
} else {
|
||||
echo '<p>' . get_string('random', 'quiz') . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function get_wrapped_question($question, $nameprefix) {
|
||||
if (!empty($question->response[$nameprefix])
|
||||
and $actualquestion = get_record('quiz_questions',
|
||||
'id', $question->response[$nameprefix])) {
|
||||
$actualquestion->response = $question->response;
|
||||
unset($actualquestion->response[$nameprefix]);
|
||||
$actualquestion->maxgrade = $question->maxgrade;
|
||||
return $actualquestion;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function grade_response($question, $nameprefix) {
|
||||
global $QUIZ_QTYPES;
|
||||
|
||||
// Get the wrapped question...
|
||||
if ($actualquestion = $this->get_wrapped_question($question,
|
||||
$nameprefix)) {
|
||||
return $QUIZ_QTYPES[$actualquestion->qtype]->grade_response(
|
||||
$actualquestion,
|
||||
quiz_qtype_nameprefix($actualquestion, $nameprefix));
|
||||
} else {
|
||||
$result->grade = 0.0;
|
||||
$result->answers = array();
|
||||
$result->correctanswers = array();
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
function extract_response($rawresponse, $nameprefix) {
|
||||
global $QUIZ_QTYPES;
|
||||
|
||||
/// The raw response records for random questions come in two flavours:
|
||||
/// ---- 1 ----
|
||||
/// For responses stored by Moodle version 1.5 and later the answer
|
||||
/// field has the pattern random#-* where the # part is the numeric
|
||||
/// question id of the actual question shown in the quiz attempt
|
||||
/// and * represents the student response to that actual question.
|
||||
/// ---- 2 ----
|
||||
/// For responses stored by older Moodle versions - the answer field is
|
||||
/// simply the question id of the actual question. The student response
|
||||
/// to the actual question is stored in a separate response record.
|
||||
/// -----------------------
|
||||
/// This means that prior to Moodle version 1.5, random questions needed
|
||||
/// two response records for storing the response to a single question.
|
||||
/// From version 1.5 and later the question type random works like all
|
||||
/// the other question types in that it now only needs one response
|
||||
/// record per question.
|
||||
/// Because updating the old response records to fit the new response
|
||||
/// record format could need hours of CPU time and the equivalent
|
||||
/// amount of down time for the Moodle site and because a response
|
||||
/// storage with two response formats for random question only effect
|
||||
/// this function, where the response record is translated, this
|
||||
/// function is now able to handle both types of response record.
|
||||
|
||||
|
||||
// Pick random question id from the answer field in a way that
|
||||
/// works for both formats:
|
||||
if (!ereg('^(random)?([0-9]+)(-(.*))?$', $rawresponse->answer, $answerregs)) {
|
||||
error("The answer value '$rawresponse->answer' for the response with "
|
||||
."id=$rawresponse->id to the random question "
|
||||
."$rawresponse->question is malformated."
|
||||
." - No response can be extracted!");
|
||||
}
|
||||
$randomquestionid = $answerregs[2];
|
||||
|
||||
if ($randomquestion = get_record('quiz_questions',
|
||||
'id', $randomquestionid)) {
|
||||
|
||||
if ($answerregs[1] && $answerregs[3]) {
|
||||
// The raw response is formatted according to
|
||||
// Moodle version 1.5 or later
|
||||
$randomresponse = $rawresponse;
|
||||
$randomresponse->question = $randomquestionid;
|
||||
$randomresponse->answer = $answerregs[4];
|
||||
|
||||
} else if ($randomresponse = get_record
|
||||
('quiz_responses', 'question', $rawresponse->answer,
|
||||
'attempt', $rawresponse->attempt)) {
|
||||
// The response was stored by an older version of Moodle
|
||||
// :-)
|
||||
|
||||
} else {
|
||||
notify("Error: Cannot find response to random question $randomquestionid");
|
||||
unset($randomresponse);
|
||||
}
|
||||
|
||||
if (isset($randomresponse)) {
|
||||
/// The prefered case:
|
||||
/// There is a random question and a response field, from
|
||||
/// which the response array can be extracted:
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
/// Instead: workaround by creating a new response:
|
||||
$response = $QUIZ_QTYPES[$randomquestion->qtype]
|
||||
->create_response($randomquestion,
|
||||
quiz_qtype_nameprefix($randomquestion, $nameprefix),
|
||||
"$rawresponse->question,$randomquestionid");
|
||||
// (That last argument is instead of $questionsinuse.
|
||||
// It is not correct but it would be very messy to
|
||||
// determine the correct value, while very few
|
||||
// question types actually use it and they who do have
|
||||
// good chances to execute properly anyway.)
|
||||
}
|
||||
$response[$nameprefix] = $randomquestionid;
|
||||
//return $response;
|
||||
return '';
|
||||
} else {
|
||||
notify("Error: Unable to find random question $rawresponse->question");
|
||||
/// No new random question is picked as this is probably
|
||||
/// not what the moodle user has in mind anyway
|
||||
return array();
|
||||
}
|
||||
}
|
||||
}
|
||||
//// END OF CLASS ////
|
||||
|
||||
|
||||
Reference in New Issue
Block a user