516cf3eb |
1 | <?php // $Id$ |
2 | |
3 | ////////////// |
4 | /// RANDOM /// |
5 | ////////////// |
6 | |
7 | /// QUESTION TYPE CLASS ////////////////// |
8 | class quiz_random_qtype extends quiz_default_questiontype { |
9 | |
10 | var $excludedtypes = array(RANDOM, RANDOMSAMATCH, ESSAY, DESCRIPTION); |
11 | |
12 | // Carries questions available as randoms sorted by category |
13 | // This array is used when needed only |
14 | var $catrandoms = array(); |
15 | |
16 | function name() { |
17 | return 'random'; |
18 | } |
19 | |
20 | function get_question_options(&$question) { |
21 | // Don't do anything here, because the random question has no options. |
22 | // Everything is handled by the create- or restore_session_and_responses |
23 | // functions. |
24 | return true; |
25 | } |
26 | |
27 | function save_question_options($question) { |
28 | // No options, but we use the parent field to hide random questions. |
29 | // To avoid problems we set the parent field to the question id. |
30 | return (set_field('quiz_questions', 'parent', $question->id, 'id', |
31 | $question->id) ? true : false); |
32 | } |
33 | |
34 | function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) { |
35 | // Choose a random question from the category: |
36 | // We need to make sure that no question is used more than once in the |
37 | // quiz. Therfore the following need to be excluded: |
38 | // 1. All questions that are explicitly assigned to the quiz |
39 | // 2. All random questions |
40 | // 3. All questions that are already chosen by an other random question |
41 | if (!isset($cmoptions->questionsinuse)) { |
42 | $cmoptions->questionsinuse = $attempt->layout; |
43 | } |
44 | |
45 | if (!isset($this->catrandoms[$question->category][$question->questiontext])) { |
46 | // Need to fetch random questions from category $question->category" |
47 | // (Note: $this refers to the questiontype, not the question.) |
48 | global $CFG; |
49 | $excludedtypes = implode(',', $this->excludedtypes); |
50 | if ($question->questiontext == "1") { |
51 | // recurse into subcategories |
52 | $categorylist = quiz_categorylist($question->category); |
53 | } else { |
54 | $categorylist = $question->category; |
55 | } |
56 | if ($catrandoms = get_records_sql |
57 | ("SELECT id,id FROM {$CFG->prefix}quiz_questions |
58 | WHERE category IN ($categorylist) |
59 | AND parent = '0' |
60 | AND id NOT IN ($cmoptions->questionsinuse) |
61 | AND qtype NOT IN ($excludedtypes)")) { |
62 | $this->catrandoms[$question->category][$question->questiontext] = |
63 | draw_rand_array($catrandoms, count($catrandoms)); // from bug 1889 |
64 | } else { |
65 | $this->catrandoms[$question->category][$question->questiontext] = array(); |
66 | } |
67 | } |
68 | |
69 | while ($wrappedquestion = |
70 | array_pop($this->catrandoms[$question->category][$question->questiontext])) { |
71 | if (!ereg("(^|,)$wrappedquestion->id(,|$)", $cmoptions->questionsinuse)) { |
72 | /// $randomquestion is not in use and will therefore be used |
73 | /// as the randomquestion here... |
74 | $wrappedquestion = get_record('quiz_questions', 'id', $wrappedquestion->id); |
75 | global $QUIZ_QTYPES; |
76 | $QUIZ_QTYPES[$wrappedquestion->qtype] |
77 | ->get_question_options($wrappedquestion); |
78 | $QUIZ_QTYPES[$wrappedquestion->qtype] |
79 | ->create_session_and_responses($wrappedquestion, |
80 | $state, $cmoptions, $attempt); |
81 | $wrappedquestion->name_prefix = $question->name_prefix; |
82 | $wrappedquestion->maxgrade = $question->maxgrade; |
83 | $cmoptions->questionsinuse .= ",$wrappedquestion->id"; |
84 | $state->options->question = &$wrappedquestion; |
85 | return true; |
86 | } |
87 | } |
88 | $question->questiontext = '<span class="notifyproblem">'. |
89 | get_string('toomanyrandom', 'quiz'). '</span>'; |
90 | $question->qtype = DESCRIPTION; |
91 | $state->responses = array('' => ''); |
92 | return true; |
93 | } |
94 | |
95 | function restore_session_and_responses(&$question, &$state) { |
96 | /// The raw response records for random questions come in two flavours: |
97 | /// ---- 1 ---- |
98 | /// For responses stored by Moodle version 1.5 and later the answer |
99 | /// field has the pattern random#-* where the # part is the numeric |
100 | /// question id of the actual question shown in the quiz attempt |
101 | /// and * represents the student response to that actual question. |
102 | /// ---- 2 ---- |
103 | /// For responses stored by older Moodle versions - the answer field is |
104 | /// simply the question id of the actual question. The student response |
105 | /// to the actual question is stored in a separate response record. |
106 | /// ----------------------- |
107 | /// This means that prior to Moodle version 1.5, random questions needed |
108 | /// two response records for storing the response to a single question. |
109 | /// From version 1.5 and later the question type random works like all |
110 | /// the other question types in that it now only needs one response |
111 | /// record per question. |
112 | global $QUIZ_QTYPES; |
113 | if (!ereg('^random([0-9]+)-(.*)$', $state->responses[''], $answerregs)) { |
114 | if (empty($state->responses[''])) { |
115 | // This is the case if there weren't enough questions available in the category. |
116 | $question->questiontext = '<span class="notifyproblem">'. |
117 | get_string('toomanyrandom', 'quiz'). '</span>'; |
118 | $question->qtype = DESCRIPTION; |
119 | return true; |
120 | } |
121 | // this must be an old-style state which stores only the id for the wrapped question |
122 | if (!$wrappedquestion = get_record('quiz_questions', 'id', $state->responses[''])) { |
123 | notify("Can not find wrapped question {$state->responses['']}"); |
124 | } |
125 | // In the old model the actual response was stored in a separate entry in |
126 | // the state table and fortunately there was only a single state per question |
127 | if (!$state->responses[''] = get_field('quiz_states', 'answer', 'attempt', $state->attempt, 'question', $wrappedquestion->id)) { |
128 | notify("Wrapped state missing"); |
129 | } |
130 | } else { |
131 | if (!$wrappedquestion = get_record('quiz_questions', 'id', $answerregs[1])) { |
132 | return false; |
133 | } |
134 | $state->responses[''] = (false === $answerregs[2]) ? '' : $answerregs[2]; |
135 | } |
136 | |
137 | if (!$QUIZ_QTYPES[$wrappedquestion->qtype] |
138 | ->get_question_options($wrappedquestion)) { |
139 | return false; |
140 | } |
141 | |
142 | if (!$QUIZ_QTYPES[$wrappedquestion->qtype] |
143 | ->restore_session_and_responses($wrappedquestion, $state)) { |
144 | return false; |
145 | } |
146 | $wrappedquestion->name_prefix = $question->name_prefix; |
147 | $wrappedquestion->maxgrade = $question->maxgrade; |
148 | $state->options->question = &$wrappedquestion; |
149 | return true; |
150 | } |
151 | |
152 | function save_session_and_responses(&$question, &$state) { |
153 | global $QUIZ_QTYPES; |
154 | $wrappedquestion = &$state->options->question; |
155 | |
156 | // Trick the wrapped question into pretending to be the random one. |
157 | $realqid = $wrappedquestion->id; |
158 | $wrappedquestion->id = $question->id; |
159 | $QUIZ_QTYPES[$wrappedquestion->qtype] |
160 | ->save_session_and_responses($wrappedquestion, $state); |
161 | |
162 | // Read what the wrapped question has just set the answer field to |
163 | // (if anything) |
164 | $response = get_field('quiz_states', 'answer', 'id', $state->id); |
165 | if(false === $response) { |
166 | return false; |
167 | } |
168 | |
169 | // Prefix the answer field... |
170 | $response = "random$realqid-$response"; |
171 | |
172 | // ... and save it again. |
173 | if (!set_field('quiz_states', 'answer', addslashes($response), 'id', $state->id)) { |
174 | return false; |
175 | } |
176 | |
177 | // Restore the real id |
178 | $wrappedquestion->id = $realqid; |
179 | return true; |
180 | } |
181 | |
182 | function get_correct_responses(&$question, &$state) { |
183 | global $QUIZ_QTYPES; |
184 | $wrappedquestion = &$state->options->question; |
185 | return $QUIZ_QTYPES[$wrappedquestion->qtype] |
186 | ->get_correct_responses($wrappedquestion, $state); |
187 | } |
188 | |
189 | // ULPGC ecastro |
190 | function get_all_responses(&$question, &$state){ |
191 | global $QUIZ_QTYPES; |
192 | $wrappedquestion = &$state->options->question; |
193 | return $QUIZ_QTYPES[$wrappedquestion->qtype] |
194 | ->get_all_responses($wrappedquestion, $state); |
195 | } |
196 | |
197 | // ULPGC ecastro |
198 | function get_actual_response(&$question, &$state){ |
199 | global $QUIZ_QTYPES; |
200 | $wrappedquestion = &$state->options->question; |
201 | return $QUIZ_QTYPES[$wrappedquestion->qtype] |
202 | ->get_actual_response($wrappedquestion, $state); |
203 | } |
204 | |
205 | |
206 | function print_question(&$question, &$state, &$number, $cmoptions, $options) { |
207 | global $QUIZ_QTYPES; |
208 | $wrappedquestion = &$state->options->question; |
209 | $QUIZ_QTYPES[$wrappedquestion->qtype] |
210 | ->print_question($wrappedquestion, $state, $number, $cmoptions, $options); |
211 | } |
212 | |
213 | function grade_responses(&$question, &$state, $cmoptions) { |
214 | global $QUIZ_QTYPES; |
215 | $wrappedquestion = &$state->options->question; |
216 | return $QUIZ_QTYPES[$wrappedquestion->qtype] |
217 | ->grade_responses($wrappedquestion, $state, $cmoptions); |
218 | } |
219 | |
220 | function get_texsource(&$question, &$state, $cmoptions, $type) { |
221 | global $QUIZ_QTYPES; |
222 | $wrappedquestion = &$state->options->question; |
223 | return $QUIZ_QTYPES[$wrappedquestion->qtype] |
224 | ->get_texsource($wrappedquestion, $state, $cmoptions, $type); |
225 | } |
226 | |
227 | function compare_responses(&$question, $state, $teststate) { |
228 | global $QUIZ_QTYPES; |
229 | $wrappedquestion = &$state->options->question; |
230 | return $QUIZ_QTYPES[$wrappedquestion->qtype] |
231 | ->compare_responses($wrappedquestion, $state, $teststate); |
232 | } |
233 | |
234 | } |
235 | //// END OF CLASS //// |
236 | |
237 | ////////////////////////////////////////////////////////////////////////// |
238 | //// INITIATION - Without this line the question type is not in use... /// |
239 | ////////////////////////////////////////////////////////////////////////// |
240 | $QUIZ_QTYPES[RANDOM]= new quiz_random_qtype(); |
241 | |
242 | ?> |