Index: mod/quiz/attempt.php =================================================================== RCS file: /cvsroot/moodle/moodle/mod/quiz/attempt.php,v retrieving revision 1.131.2.17 diff -u -r1.131.2.17 attempt.php --- mod/quiz/attempt.php 30 Sep 2009 10:58:03 -0000 1.131.2.17 +++ mod/quiz/attempt.php 5 Nov 2009 18:36:45 -0000 @@ -264,6 +264,11 @@ } } + // MDL-6340: random question should know old attempts of this user to avoid using one question twice + $oldattempts = array_keys(get_records_select('quiz_attempts', "userid=$USER->id AND attempt<>$attempt->attempt", '','uniqueid')); + $oldattemptslist = implode(',', $oldattempts); + $quiz->oldattempts = $oldattemptslist; + // Restore the question sessions to their most recent states // creating new sessions where required if (!$states = get_question_states($questions, $quiz, $attempt, $lastattemptid)) { Index: question/type/random/questiontype.php =================================================================== RCS file: /cvsroot/moodle/moodle/question/type/random/questiontype.php,v retrieving revision 1.12.2.9 diff -u -r1.12.2.9 questiontype.php --- question/type/random/questiontype.php 17 Feb 2009 06:14:48 -0000 1.12.2.9 +++ question/type/random/questiontype.php 5 Nov 2009 19:37:39 -0000 @@ -136,22 +136,41 @@ * @param string $questionsinuse comma-separated list of question ids to exclude from consideration. * @return array of question records. */ - function get_usable_questions_from_category($categoryid, $subcategories, $questionsinuse) { + function get_usable_questions_from_category($categoryid, $subcategories, $cmoptions) { $this->init_qtype_lists(); if ($subcategories) { $categorylist = question_categorylist($categoryid); } else { $categorylist = $categoryid; } + + // Get id`s of questions that was used in previous attempts + $usedanswers = get_records_select('question_states', "attempt IN ($cmoptions->oldattempts)", '', 'answer'); + foreach ($usedanswers as $answer) { + ereg('^random([0-9]+)-', $answer->answer, $cmpresult); + $usedids[]=$cmpresult[1]; + } + $usedlist = implode(',', $usedids); + if (!$catrandoms = get_records_select('question', "category IN ($categorylist) AND parent = 0 AND hidden = 0 - AND id NOT IN ($questionsinuse) + AND id NOT IN ($cmoptions->questionsinuse) + AND id NOT IN ($usedlist) AND qtype NOT IN ($this->excludedqtypes)", '', 'id')) { - $catrandoms = array(); + // If all questions was used in previous attempts fallback to all questions + if (!$catrandoms = get_records_select('question', + "category IN ($categorylist) + AND parent = 0 + AND hidden = 0 + AND id NOT IN ($cmoptions->questionsinuse) + AND qtype NOT IN ($this->excludedqtypes)", '', 'id')) { + $catrandoms = array(); + } } - return $catrandoms; + + return array_keys($catrandoms); } function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) { @@ -167,18 +186,25 @@ $cmoptions->questionsinuse = $attempt->layout; } - if (!isset($this->catrandoms[$question->category][$question->questiontext])) { + // We should get list of usable questions in 2 cases: + // 1. it isn`t already cached in $this->catrandoms; + // 2. all questions in cached version are already used. + // If we run out of available questions in the middle of attempt generation we should call get_usable_questions_from_category + // once more to fallback on using questions from previous attempts + if (!isset($this->catrandoms[$question->category][$question->questiontext]) || + (count(array_diff($this->catrandoms[$question->category][$question->questiontext], + explode(',',$cmoptions->questionsinuse))) == 0)) { $catrandoms = $this->get_usable_questions_from_category($question->category, - $question->questiontext == "1", $cmoptions->questionsinuse); + $question->questiontext == '1', $cmoptions); $this->catrandoms[$question->category][$question->questiontext] = swapshuffle_assoc($catrandoms); } while ($wrappedquestion = array_pop($this->catrandoms[$question->category][$question->questiontext])) { - if (!ereg("(^|,)$wrappedquestion->id(,|$)", $cmoptions->questionsinuse)) { + if (!ereg("(^|,)$wrappedquestion(,|$)", $cmoptions->questionsinuse)) { /// $randomquestion is not in use and will therefore be used /// as the randomquestion here... - $wrappedquestion = get_record('question', 'id', $wrappedquestion->id); + $wrappedquestion = get_record('question', 'id', $wrappedquestion); $QTYPES[$wrappedquestion->qtype] ->get_question_options($wrappedquestion); $QTYPES[$wrappedquestion->qtype]