MDL-40541 need to be able to select random q
authorJamie Pratt <me@jamiep.org>
Sun, 14 Jul 2013 12:08:53 +0000 (19:08 +0700)
committerDan Poltawski <dan@moodle.com>
Tue, 23 Jul 2013 07:08:32 +0000 (15:08 +0800)
in unit tests

mod/quiz/locallib.php
mod/quiz/tests/attempt_walkthrough_test.php
question/engine/questionusage.php
question/type/random/questiontype.php

index 1d5d881..4a4d796 100644 (file)
@@ -145,10 +145,12 @@ function quiz_create_attempt(quiz $quizobj, $attemptnumber, $lastattempt, $timen
  * @param object                        $attempt
  * @param integer                       $attemptnumber      starting from 1
  * @param integer                       $timenow            the attempt start time
+ * @param array   $questionids slot number => question id. Used for random questions, to force the choice of a particular actual
+ *                              question. Intended for testing purposes only.
+ * @throws moodle_exception
  * @return object                       modified attempt object
- * @throws moodle_exception             if a random question exhausts the available questions
  */
-function quiz_start_new_attempt($quizobj, $quba, $attempt, $attemptnumber, $timenow) {
+function quiz_start_new_attempt($quizobj, $quba, $attempt, $attemptnumber, $timenow, $questionids = array()) {
     // Fully load all the questions in this quiz.
     $quizobj->preload_questions();
     $quizobj->load_questions();
@@ -164,8 +166,14 @@ function quiz_start_new_attempt($quizobj, $quba, $attempt, $attemptnumber, $time
             $question = question_bank::make_question($questiondata);
 
         } else {
+            if (!isset($questionids[$quba->next_slot_number()])) {
+                $forcequestionid = null;
+            } else {
+                $forcequestionid = $questionids[$quba->next_slot_number()];
+            }
+
             $question = question_bank::get_qtype('random')->choose_other_question(
-                $questiondata, $questionsinuse, $quizobj->get_quiz()->shuffleanswers);
+                $questiondata, $questionsinuse, $quizobj->get_quiz()->shuffleanswers, $forcequestionid);
             if (is_null($question)) {
                 throw new moodle_exception('notenoughrandomquestions', 'quiz',
                                            $quizobj->view_url(), $questiondata);
index 6e3d817..1d42151 100644 (file)
@@ -88,7 +88,6 @@ class mod_quiz_attempt_walkthrough_testcase extends advanced_testcase {
         $prefix1 = $quba->get_field_prefix(1);
         $prefix2 = $quba->get_field_prefix(2);
 
-
         $tosubmit = array(1 => array('answer' => 'frog'),
                           2 => array('answer' => '3.14'));
 
@@ -121,7 +120,7 @@ class mod_quiz_attempt_walkthrough_testcase extends advanced_testcase {
     }
 
     /**
-     * Create a second quiz with questions and walk through a quiz attempt this time with a random question.
+     * Create a quiz with a random as well as other questions and walk through quiz attempts.
      */
     public function test_quiz_with_random_question_attempt_walkthrough() {
         global $SITE;
@@ -161,65 +160,63 @@ class mod_quiz_attempt_walkthrough_testcase extends advanced_testcase {
 
         quiz_add_quiz_question($multichoicesingle->id, $quiz, 0);
 
-        // Make a user to do the quiz.
-        $user1 = $this->getDataGenerator()->create_user();
-        $this->setUser($user1);
+        foreach (array($saq->id => 'frog', $numq->id => '3.14') as $randomqidtoselect => $randqanswer) {
+            // Make a new user to do the quiz each loop.
+            $user1 = $this->getDataGenerator()->create_user();
+            $this->setUser($user1);
 
-        $quizobj = quiz::create($quiz->id, $user1->id);
+            $quizobj = quiz::create($quiz->id, $user1->id);
 
-        // Start the attempt.
-        $quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
-        $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
+            // Start the attempt.
+            $quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
+            $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
 
-        $timenow = time();
-        $attempt = quiz_create_attempt($quizobj, 1, false, $timenow);
+            $timenow = time();
+            $attempt = quiz_create_attempt($quizobj, 1, false, $timenow);
 
-        quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
+            quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow, array(1 => $randomqidtoselect));
 
-        quiz_attempt_save_started($quizobj, $quba, $attempt);
+            quiz_attempt_save_started($quizobj, $quba, $attempt);
 
-        // Process some responses from the student.
-        $attemptobj = quiz_attempt::create($attempt->id);
+            // Process some responses from the student.
+            $attemptobj = quiz_attempt::create($attempt->id);
 
-        $tosubmit = array();
-        $selectedquestionid = $quba->get_question_attempt(1)->get_question()->id;
-        if ($selectedquestionid == $numq->id) {
-            $tosubmit[1] = array('answer' => '3.14');
-        } else {
-            $tosubmit[1] = array('answer' => 'frog');
-        }
-        $tosubmit[2] = array(
-            0 => 'amphibian',
-            1 => 'mammal',
-            2 => 'amphibian');
-        $tosubmit[3] = array('1', '0', '1', '0'); // First and third choice.
-        $tosubmit[4] = array('answer' => 0); // The first choice.
+            $tosubmit = array();
+            $selectedquestionid = $quba->get_question_attempt(1)->get_question()->id;
+            $tosubmit[1] = array('answer' => $randqanswer);
+            $tosubmit[2] = array(
+                0 => 'amphibian',
+                1 => 'mammal',
+                2 => 'amphibian');
+            $tosubmit[3] = array('1', '0', '1', '0'); // First and third choice.
+            $tosubmit[4] = array('answer' => 0); // The first choice.
 
-        $attemptobj->process_submitted_actions($timenow, false, $tosubmit);
+            $attemptobj->process_submitted_actions($timenow, false, $tosubmit);
 
-        // Finish the attempt.
-        $attemptobj = quiz_attempt::create($attempt->id);
-        $attemptobj->process_finish($timenow, false);
+            // Finish the attempt.
+            $attemptobj = quiz_attempt::create($attempt->id);
+            $attemptobj->process_finish($timenow, false);
 
-        // Re-load quiz attempt data.
-        $attemptobj = quiz_attempt::create($attempt->id);
+            // Re-load quiz attempt data.
+            $attemptobj = quiz_attempt::create($attempt->id);
 
-        // Check that results are stored as expected.
-        $this->assertEquals(1, $attemptobj->get_attempt_number());
-        $this->assertEquals(4, $attemptobj->get_sum_marks());
-        $this->assertEquals(true, $attemptobj->is_finished());
-        $this->assertEquals($timenow, $attemptobj->get_submitted_date());
-        $this->assertEquals($user1->id, $attemptobj->get_userid());
+            // Check that results are stored as expected.
+            $this->assertEquals(1, $attemptobj->get_attempt_number());
+            $this->assertEquals(4, $attemptobj->get_sum_marks());
+            $this->assertEquals(true, $attemptobj->is_finished());
+            $this->assertEquals($timenow, $attemptobj->get_submitted_date());
+            $this->assertEquals($user1->id, $attemptobj->get_userid());
 
-        // Check quiz grades.
-        $grades = quiz_get_user_grades($quiz, $user1->id);
-        $grade = array_shift($grades);
-        $this->assertEquals(100.0, $grade->rawgrade);
+            // Check quiz grades.
+            $grades = quiz_get_user_grades($quiz, $user1->id);
+            $grade = array_shift($grades);
+            $this->assertEquals(100.0, $grade->rawgrade);
 
-        // Check grade book.
-        $gradebookgrades = grade_get_grades($SITE->id, 'mod', 'quiz', $quiz->id, $user1->id);
-        $gradebookitem = array_shift($gradebookgrades->items);
-        $gradebookgrade = array_shift($gradebookitem->grades);
-        $this->assertEquals(100, $gradebookgrade->grade);
+            // Check grade book.
+            $gradebookgrades = grade_get_grades($SITE->id, 'mod', 'quiz', $quiz->id, $user1->id);
+            $gradebookitem = array_shift($gradebookgrades->items);
+            $gradebookgrade = array_shift($gradebookitem->grades);
+            $this->assertEquals(100, $gradebookgrade->grade);
+        }
     }
 }
index 05fafde..96b5355 100644 (file)
@@ -166,17 +166,19 @@ class question_usage_by_activity {
      */
     public function add_question(question_definition $question, $maxmark = null) {
         $qa = new question_attempt($question, $this->get_id(), $this->observer, $maxmark);
-        if (count($this->questionattempts) == 0) {
-            $this->questionattempts[1] = $qa;
-        } else {
-            $this->questionattempts[] = $qa;
-        }
-        end($this->questionattempts); // Ready to get the last key on the next line.
-        $qa->set_slot(key($this->questionattempts));
+        $qa->set_slot($this->next_slot_number());
+        $this->questionattempts[$this->next_slot_number()] = $qa;
         $this->observer->notify_attempt_added($qa);
         return $qa->get_slot();
     }
 
+    /**
+     * The slot number that will be allotted to the next question added.
+     */
+    public function next_slot_number() {
+        return count($this->questionattempts) + 1;
+    }
+
     /**
      * Get the question_definition for a question in this attempt.
      * @param int $slot the number used to identify this question within this usage.
index 7834cbc..c57e360 100644 (file)
@@ -218,19 +218,29 @@ class qtype_random extends question_type {
 
     /**
      * Load the definition of another question picked randomly by this question.
-     * @param object $questiondata the data defining a random question.
-     * @param array $excludedquestions of question ids. We will no pick any
-     *      question whose id is in this list.
-     * @param bool $allowshuffle if false, then any shuffle option on the
-     *      selected quetsion is disabled.
+     * @param object       $questiondata the data defining a random question.
+     * @param array        $excludedquestions of question ids. We will no pick any question whose id is in this list.
+     * @param bool         $allowshuffle      if false, then any shuffle option on the selected quetsion is disabled.
+     * @param null|integer $forcequestionid   if not null then force the picking of question with id $forcequestionid.
+     * @throws coding_exception
      * @return question_definition|null the definition of the question that was
      *      selected, or null if no suitable question could be found.
      */
-    public function choose_other_question($questiondata, $excludedquestions, $allowshuffle = true) {
+    public function choose_other_question($questiondata, $excludedquestions, $allowshuffle = true, $forcequestionid = null) {
         $available = $this->get_available_questions_from_category($questiondata->category,
                 !empty($questiondata->questiontext));
         shuffle($available);
 
+        if ($forcequestionid !== null) {
+            $forcedquestionkey = array_search($forcequestionid, $available);
+            if ($forcedquestionkey !== false) {
+                unset($available[$forcedquestionkey]);
+                array_unshift($available, $forcequestionid);
+            } else {
+                throw new coding_exception('thisquestionidisnotavailable', $forcequestionid);
+            }
+        }
+
         foreach ($available as $questionid) {
             if (in_array($questionid, $excludedquestions)) {
                 continue;