730fd187 |
1 | <?PHP // $Id$ |
2 | |
a5e1f35c |
3 | /// Library of function for module quiz |
730fd187 |
4 | |
a5e1f35c |
5 | /// CONSTANTS /////////////////////////////////////////////////////////////////// |
730fd187 |
6 | |
a5e1f35c |
7 | define("GRADEHIGHEST", "1"); |
8 | define("GRADEAVERAGE", "2"); |
9 | define("ATTEMPTFIRST", "3"); |
10 | define("ATTEMPTLAST", "4"); |
11 | $QUIZ_GRADE_METHOD = array ( GRADEHIGHEST => get_string("gradehighest", "quiz"), |
12 | GRADEAVERAGE => get_string("gradeaverage", "quiz"), |
13 | ATTEMPTFIRST => get_string("attemptfirst", "quiz"), |
14 | ATTEMPTLAST => get_string("attemptlast", "quiz")); |
15 | |
54a67a59 |
16 | define("SHORTANSWER", "1"); |
17 | define("TRUEFALSE", "2"); |
18 | define("MULTICHOICE", "3"); |
19 | define("RANDOM", "4"); |
20 | define("MATCH", "5"); |
21 | define("RANDOMSAMATCH", "6"); |
401c8de6 |
22 | define("DESCRIPTION", "7"); |
361f649d |
23 | define("NUMERICAL", "8"); |
8b439f8c |
24 | define("MULTIANSWER", "9"); |
54a67a59 |
25 | |
26 | $QUIZ_QUESTION_TYPE = array ( MULTICHOICE => get_string("multichoice", "quiz"), |
27 | TRUEFALSE => get_string("truefalse", "quiz"), |
28 | SHORTANSWER => get_string("shortanswer", "quiz"), |
361f649d |
29 | NUMERICAL => get_string("numerical", "quiz"), |
54a67a59 |
30 | MATCH => get_string("match", "quiz"), |
361f649d |
31 | DESCRIPTION => get_string("description", "quiz"), |
34d52ad7 |
32 | RANDOM => get_string("random", "quiz"), |
8b439f8c |
33 | RANDOMSAMATCH => get_string("randomsamatch", "quiz"), |
34 | MULTIANSWER => get_string("multianswer", "quiz") |
361f649d |
35 | ); |
a5e1f35c |
36 | |
49220fa7 |
37 | |
5325f8b8 |
38 | define("QUIZ_PICTURE_MAX_HEIGHT", "600"); // Not currently implemented |
39 | define("QUIZ_PICTURE_MAX_WIDTH", "600"); // Not currently implemented |
a5e1f35c |
40 | |
29fb5974 |
41 | define("QUIZ_MAX_NUMBER_ANSWERS", "10"); |
54a67a59 |
42 | |
a5e1f35c |
43 | /// FUNCTIONS /////////////////////////////////////////////////////////////////// |
730fd187 |
44 | |
45 | function quiz_add_instance($quiz) { |
a5e1f35c |
46 | /// Given an object containing all the necessary data, |
47 | /// (defined by the form in mod.html) this function |
48 | /// will create a new instance and return the id number |
49 | /// of the new instance. |
730fd187 |
50 | |
49dcdd18 |
51 | $quiz->created = time(); |
730fd187 |
52 | $quiz->timemodified = time(); |
49dcdd18 |
53 | $quiz->timeopen = make_timestamp($quiz->openyear, $quiz->openmonth, $quiz->openday, |
c04c41c7 |
54 | $quiz->openhour, $quiz->openminute, 0); |
49dcdd18 |
55 | $quiz->timeclose = make_timestamp($quiz->closeyear, $quiz->closemonth, $quiz->closeday, |
c04c41c7 |
56 | $quiz->closehour, $quiz->closeminute, 0); |
730fd187 |
57 | |
7bd1aa1d |
58 | if (!$quiz->id = insert_record("quiz", $quiz)) { |
59 | return false; // some error occurred |
60 | } |
61 | |
10b9291c |
62 | // The grades for every question in this quiz are stored in an array |
7bd1aa1d |
63 | if ($quiz->grades) { |
64 | foreach ($quiz->grades as $question => $grade) { |
401c8de6 |
65 | if ($question) { |
8d94f5a0 |
66 | unset($questiongrade); |
67 | $questiongrade->quiz = $quiz->id; |
68 | $questiongrade->question = $question; |
69 | $questiongrade->grade = $grade; |
70 | if (!insert_record("quiz_question_grades", $questiongrade)) { |
71 | return false; |
72 | } |
7bd1aa1d |
73 | } |
74 | } |
75 | } |
730fd187 |
76 | |
7bd1aa1d |
77 | return $quiz->id; |
730fd187 |
78 | } |
79 | |
80 | |
81 | function quiz_update_instance($quiz) { |
a5e1f35c |
82 | /// Given an object containing all the necessary data, |
83 | /// (defined by the form in mod.html) this function |
84 | /// will update an existing instance with new data. |
730fd187 |
85 | |
86 | $quiz->timemodified = time(); |
49dcdd18 |
87 | $quiz->timeopen = make_timestamp($quiz->openyear, $quiz->openmonth, $quiz->openday, |
c04c41c7 |
88 | $quiz->openhour, $quiz->openminute, 0); |
49dcdd18 |
89 | $quiz->timeclose = make_timestamp($quiz->closeyear, $quiz->closemonth, $quiz->closeday, |
c04c41c7 |
90 | $quiz->closehour, $quiz->closeminute, 0); |
730fd187 |
91 | $quiz->id = $quiz->instance; |
92 | |
7bd1aa1d |
93 | if (!update_record("quiz", $quiz)) { |
94 | return false; // some error occurred |
95 | } |
730fd187 |
96 | |
7bd1aa1d |
97 | |
10b9291c |
98 | // The grades for every question in this quiz are stored in an array |
7bd1aa1d |
99 | // Insert or update records as appropriate |
100 | |
101 | $existing = get_records("quiz_question_grades", "quiz", $quiz->id, "", "question,grade,id"); |
102 | |
103 | if ($quiz->grades) { |
104 | foreach ($quiz->grades as $question => $grade) { |
401c8de6 |
105 | if ($question) { |
8d94f5a0 |
106 | unset($questiongrade); |
107 | $questiongrade->quiz = $quiz->id; |
108 | $questiongrade->question = $question; |
109 | $questiongrade->grade = $grade; |
110 | if (isset($existing[$question])) { |
111 | if ($existing[$question]->grade != $grade) { |
112 | $questiongrade->id = $existing[$question]->id; |
113 | if (!update_record("quiz_question_grades", $questiongrade)) { |
114 | return false; |
115 | } |
116 | } |
117 | } else { |
118 | if (!insert_record("quiz_question_grades", $questiongrade)) { |
7bd1aa1d |
119 | return false; |
120 | } |
121 | } |
7bd1aa1d |
122 | } |
123 | } |
124 | } |
125 | |
126 | return true; |
730fd187 |
127 | } |
128 | |
129 | |
130 | function quiz_delete_instance($id) { |
a5e1f35c |
131 | /// Given an ID of an instance of this module, |
132 | /// this function will permanently delete the instance |
133 | /// and any data that depends on it. |
730fd187 |
134 | |
135 | if (! $quiz = get_record("quiz", "id", "$id")) { |
136 | return false; |
137 | } |
138 | |
139 | $result = true; |
140 | |
464edd6d |
141 | if ($attempts = get_records("quiz_attempts", "quiz", "$quiz->id")) { |
10b9291c |
142 | foreach ($attempts as $attempt) { |
143 | if (! delete_records("quiz_responses", "attempt", "$attempt->id")) { |
144 | $result = false; |
145 | } |
146 | } |
147 | } |
148 | |
149 | if (! delete_records("quiz_attempts", "quiz", "$quiz->id")) { |
150 | $result = false; |
151 | } |
152 | |
153 | if (! delete_records("quiz_grades", "quiz", "$quiz->id")) { |
154 | $result = false; |
155 | } |
156 | |
157 | if (! delete_records("quiz_question_grades", "quiz", "$quiz->id")) { |
158 | $result = false; |
159 | } |
730fd187 |
160 | |
161 | if (! delete_records("quiz", "id", "$quiz->id")) { |
162 | $result = false; |
163 | } |
164 | |
165 | return $result; |
166 | } |
167 | |
b2d594c8 |
168 | function quiz_delete_course($course) { |
169 | /// Given a course object, this function will clean up anything that |
170 | /// would be leftover after all the instances were deleted |
464edd6d |
171 | /// In this case, all non-publish quiz categories and questions |
b2d594c8 |
172 | |
464edd6d |
173 | if ($categories = get_records_select("quiz_categories", "course = '$course->id' AND publish = '0'")) { |
b2d594c8 |
174 | foreach ($categories as $category) { |
175 | if ($questions = get_records("quiz_questions", "category", $category->id)) { |
176 | foreach ($questions as $question) { |
177 | delete_records("quiz_answers", "question", $question->id); |
178 | delete_records("quiz_match", "question", $question->id); |
179 | delete_records("quiz_match_sub", "question", $question->id); |
180 | delete_records("quiz_multianswers", "question", $question->id); |
181 | delete_records("quiz_multichoice", "question", $question->id); |
182 | delete_records("quiz_numerical", "question", $question->id); |
183 | delete_records("quiz_randommatch", "question", $question->id); |
184 | delete_records("quiz_responses", "question", $question->id); |
185 | delete_records("quiz_shortanswer", "question", $question->id); |
186 | delete_records("quiz_truefalse", "question", $question->id); |
187 | } |
188 | delete_records("quiz_questions", "category", $category->id); |
189 | } |
190 | } |
191 | return delete_records("quiz_categories", "course", $course->id); |
192 | } |
193 | return true; |
194 | } |
195 | |
196 | |
730fd187 |
197 | function quiz_user_outline($course, $user, $mod, $quiz) { |
a5e1f35c |
198 | /// Return a small object with summary information about what a |
199 | /// user has done with a given particular instance of this module |
200 | /// Used for user activity reports. |
201 | /// $return->time = the time they did it |
202 | /// $return->info = a short text description |
ebc3bd2b |
203 | if ($grade = get_record("quiz_grades", "userid", $user->id, "quiz", $quiz->id)) { |
98092498 |
204 | |
205 | if ($grade->grade) { |
206 | $result->info = get_string("grade").": $grade->grade"; |
207 | } |
208 | $result->time = $grade->timemodified; |
209 | return $result; |
210 | } |
211 | return NULL; |
730fd187 |
212 | |
213 | return $return; |
214 | } |
215 | |
216 | function quiz_user_complete($course, $user, $mod, $quiz) { |
a5e1f35c |
217 | /// Print a detailed representation of what a user has done with |
218 | /// a given particular instance of this module, for user activity reports. |
730fd187 |
219 | |
220 | return true; |
221 | } |
222 | |
730fd187 |
223 | function quiz_cron () { |
a5e1f35c |
224 | /// Function to be run periodically according to the moodle cron |
225 | /// This function searches for things that need to be done, such |
226 | /// as sending out mail, toggling flags etc ... |
730fd187 |
227 | |
228 | global $CFG; |
229 | |
230 | return true; |
231 | } |
232 | |
d0ac6bc2 |
233 | function quiz_grades($quizid) { |
858deff0 |
234 | /// Must return an array of grades, indexed by user, and a max grade. |
235 | |
ed1daaa9 |
236 | $quiz = get_record("quiz", "id", $quizid); |
237 | if (empty($quiz) or empty($quiz->grade)) { |
238 | return NULL; |
239 | } |
240 | |
ebc3bd2b |
241 | $return->grades = get_records_menu("quiz_grades", "quiz", $quizid, "", "userid,grade"); |
858deff0 |
242 | $return->maxgrade = get_field("quiz", "grade", "id", "$quizid"); |
243 | return $return; |
d0ac6bc2 |
244 | } |
245 | |
d061d883 |
246 | function quiz_get_participants($quizid) { |
247 | /// Returns an array of users who have data in a given quiz |
248 | /// (users with records in quiz_attempts, students) |
249 | |
250 | global $CFG; |
251 | |
252 | return get_records_sql("SELECT DISTINCT u.* |
253 | FROM {$CFG->prefix}user u, |
254 | {$CFG->prefix}quiz_attempts a |
255 | WHERE a.quiz = '$quizid' and |
256 | u.id = a.userid"); |
257 | } |
730fd187 |
258 | |
bdc23be0 |
259 | /// SQL FUNCTIONS //////////////////////////////////////////////////////////////////// |
260 | |
261 | function quiz_move_questions($category1, $category2) { |
262 | global $CFG; |
263 | return execute_sql("UPDATE {$CFG->prefix}quiz_questions |
264 | SET category = '$category2' |
265 | WHERE category = '$category1'", |
266 | false); |
267 | } |
268 | |
269 | function quiz_get_question_grades($quizid, $questionlist) { |
270 | global $CFG; |
271 | |
272 | return get_records_sql("SELECT question,grade |
273 | FROM {$CFG->prefix}quiz_question_grades |
274 | WHERE quiz = '$quizid' |
275 | AND question IN ($questionlist)"); |
276 | } |
277 | |
34d52ad7 |
278 | function quiz_get_random_categories($questionlist) { |
279 | /// Given an array of questions, this function looks for random |
280 | /// questions among them and returns a list of categories with |
281 | /// an associated count of random questions for each. |
282 | |
283 | global $CFG; |
284 | |
285 | return get_records_sql_menu("SELECT category,count(*) |
286 | FROM {$CFG->prefix}quiz_questions |
287 | WHERE id IN ($questionlist) |
288 | AND qtype = '".RANDOM."' |
289 | GROUP BY category "); |
290 | } |
291 | |
bdc23be0 |
292 | function quiz_get_grade_records($quiz) { |
293 | /// Gets all info required to display the table of quiz results |
294 | /// for report.php |
295 | global $CFG; |
296 | |
297 | return get_records_sql("SELECT qg.*, u.firstname, u.lastname, u.picture |
298 | FROM {$CFG->prefix}quiz_grades qg, |
299 | {$CFG->prefix}user u |
300 | WHERE qg.quiz = '$quiz->id' |
ebc3bd2b |
301 | AND qg.userid = u.id"); |
bdc23be0 |
302 | } |
303 | |
8b439f8c |
304 | function quiz_get_answers($question, $answerids=NULL) { |
34d52ad7 |
305 | // Given a question, returns the correct answers for a given question |
bdc23be0 |
306 | global $CFG; |
95dbc030 |
307 | |
8b439f8c |
308 | if (empty($answerids)) { |
309 | $answeridconstraint = ''; |
310 | } else { |
311 | $answeridconstraint = " AND a.id IN ($answerids) "; |
312 | } |
313 | |
a2fe7cc0 |
314 | switch ($question->qtype) { |
95dbc030 |
315 | case SHORTANSWER: // Could be multiple answers |
34d52ad7 |
316 | return get_records_sql("SELECT a.*, sa.usecase |
bdc23be0 |
317 | FROM {$CFG->prefix}quiz_shortanswer sa, |
34d52ad7 |
318 | {$CFG->prefix}quiz_answers a |
bdc23be0 |
319 | WHERE sa.question = '$question->id' |
8b439f8c |
320 | AND sa.question = a.question " |
321 | . $answeridconstraint); |
bdc23be0 |
322 | |
95dbc030 |
323 | case TRUEFALSE: // Should be always two answers |
34d52ad7 |
324 | return get_records("quiz_answers", "question", $question->id); |
bdc23be0 |
325 | |
95dbc030 |
326 | case MULTICHOICE: // Should be multiple answers |
34d52ad7 |
327 | return get_records_sql("SELECT a.*, mc.single |
bdc23be0 |
328 | FROM {$CFG->prefix}quiz_multichoice mc, |
34d52ad7 |
329 | {$CFG->prefix}quiz_answers a |
bdc23be0 |
330 | WHERE mc.question = '$question->id' |
8b439f8c |
331 | AND mc.question = a.question " |
332 | . $answeridconstraint); |
bdc23be0 |
333 | |
54a67a59 |
334 | case MATCH: |
34d52ad7 |
335 | return get_records("quiz_match_sub", "question", $question->id); |
54a67a59 |
336 | |
337 | case RANDOMSAMATCH: // Could be any of many answers, return them all |
34d52ad7 |
338 | return get_records_sql("SELECT a.* |
95dbc030 |
339 | FROM {$CFG->prefix}quiz_questions q, |
34d52ad7 |
340 | {$CFG->prefix}quiz_answers a |
95dbc030 |
341 | WHERE q.category = '$question->category' |
342 | AND q.qtype = ".SHORTANSWER." |
34d52ad7 |
343 | AND q.id = a.question "); |
95dbc030 |
344 | |
361f649d |
345 | case NUMERICAL: // Logical support for multiple answers |
346 | return get_records_sql("SELECT a.*, n.min, n.max |
347 | FROM {$CFG->prefix}quiz_numerical n, |
348 | {$CFG->prefix}quiz_answers a |
349 | WHERE a.question = '$question->id' |
8b439f8c |
350 | AND n.answer = a.id " |
351 | . $answeridconstraint); |
361f649d |
352 | |
44fc346f |
353 | case DESCRIPTION: |
354 | return true; // there are no answers for description |
355 | |
356 | case RANDOM: |
357 | return quiz_get_answers |
358 | (get_record('quiz_questions', 'id', $question->random)); |
361f649d |
359 | |
8b439f8c |
360 | case MULTIANSWER: // Includes subanswers |
52a88a87 |
361 | $answers = array(); |
362 | |
8b439f8c |
363 | $virtualquestion->id = $question->id; |
364 | |
52a88a87 |
365 | if ($multianswers = get_records('quiz_multianswers', 'question', $question->id)) { |
366 | foreach ($multianswers as $multianswer) { |
367 | $virtualquestion->qtype = $multianswer->answertype; |
368 | // Recursive call for subanswers |
369 | $multianswer->subanswers = quiz_get_answers($virtualquestion, $multianswer->answers); |
370 | $answers[] = $multianswer; |
371 | } |
8b439f8c |
372 | } |
373 | return $answers; |
374 | |
bdc23be0 |
375 | default: |
376 | return false; |
377 | } |
378 | } |
379 | |
380 | |
586b2c82 |
381 | function quiz_get_attempt_responses($attempt) { |
bdc23be0 |
382 | // Given an attempt object, this function gets all the |
383 | // stored responses and returns them in a format suitable |
384 | // for regrading using quiz_grade_attempt_results() |
385 | global $CFG; |
386 | |
5ca03851 |
387 | if (!$responses = get_records_sql("SELECT q.id, q.qtype, q.category, q.questiontext, |
388 | q.defaultgrade, q.image, r.answer |
bdc23be0 |
389 | FROM {$CFG->prefix}quiz_responses r, |
390 | {$CFG->prefix}quiz_questions q |
391 | WHERE r.attempt = '$attempt->id' |
34d52ad7 |
392 | AND q.id = r.question")) { |
bdc23be0 |
393 | notify("Could not find any responses for that attempt!"); |
394 | return false; |
395 | } |
396 | |
34d52ad7 |
397 | |
398 | foreach ($responses as $key => $response) { |
399 | if ($response->qtype == RANDOM) { |
400 | $responses[$key]->random = $response->answer; |
34d52ad7 |
401 | $responses[$response->answer]->delete = true; |
0a660b03 |
402 | |
403 | $realanswer = $responses[$response->answer]->answer; |
404 | |
405 | if (is_array($realanswer)) { |
406 | $responses[$key]->answer = $realanswer; |
407 | } else { |
408 | $responses[$key]->answer = explode(",", $realanswer); |
409 | } |
410 | |
3960a44b |
411 | } else if ($response->qtype == NUMERICAL or $response->qtype == SHORTANSWER) { |
412 | $responses[$key]->answer = array($response->answer); |
34d52ad7 |
413 | } else { |
414 | $responses[$key]->answer = explode(",",$response->answer); |
415 | } |
416 | } |
bdc23be0 |
417 | foreach ($responses as $key => $response) { |
34d52ad7 |
418 | if (!empty($response->delete)) { |
419 | unset($responses[$key]); |
420 | } |
bdc23be0 |
421 | } |
422 | |
423 | return $responses; |
424 | } |
425 | |
426 | |
a740ff66 |
427 | function get_list_of_questions($questionlist) { |
428 | /// Returns an ordered list of questions, including course for each |
429 | |
430 | global $CFG; |
431 | |
432 | return get_records_sql("SELECT q.*,c.course |
433 | FROM {$CFG->prefix}quiz_questions q, |
434 | {$CFG->prefix}quiz_categories c |
435 | WHERE q.id in ($questionlist) |
436 | AND q.category = c.id"); |
437 | } |
bdc23be0 |
438 | |
730fd187 |
439 | ////////////////////////////////////////////////////////////////////////////////////// |
a5e1f35c |
440 | /// Any other quiz functions go here. Each of them must have a name that |
441 | /// starts with quiz_ |
730fd187 |
442 | |
a8a372cc |
443 | function quiz_print_comment($text) { |
444 | global $THEME; |
445 | |
a0beda7a |
446 | echo "<span class=\"feedbacktext\">".format_text($text, true, false)."</span>"; |
a8a372cc |
447 | } |
448 | |
8db3eadd |
449 | function quiz_print_correctanswer($text) { |
450 | global $THEME; |
451 | |
d7512435 |
452 | echo "<p align=\"right\"><span class=\"highlight\">$text</span></p>"; |
8db3eadd |
453 | } |
454 | |
34d52ad7 |
455 | function quiz_print_question_icon($question, $editlink=true) { |
c74a0ca5 |
456 | // Prints a question icon |
cc3b8c75 |
457 | |
458 | global $QUIZ_QUESTION_TYPE; |
459 | |
34d52ad7 |
460 | if ($editlink) { |
361f649d |
461 | echo "<a href=\"question.php?id=$question->id\" title=\"".$QUIZ_QUESTION_TYPE[$question->qtype]."\">"; |
34d52ad7 |
462 | } |
a2fe7cc0 |
463 | switch ($question->qtype) { |
c74a0ca5 |
464 | case SHORTANSWER: |
89b8d337 |
465 | echo '<img border="0" height="16" width="16" src="pix/sa.gif">'; |
c74a0ca5 |
466 | break; |
467 | case TRUEFALSE: |
89b8d337 |
468 | echo '<img border="0" height="16" width="16" src="pix/tf.gif">'; |
c74a0ca5 |
469 | break; |
470 | case MULTICHOICE: |
89b8d337 |
471 | echo '<img border="0" height="16" width="16" src="pix/mc.gif">'; |
c74a0ca5 |
472 | break; |
473 | case RANDOM: |
89b8d337 |
474 | echo '<img border="0" height="16" width="16" src="pix/rs.gif">'; |
c74a0ca5 |
475 | break; |
54a67a59 |
476 | case MATCH: |
89b8d337 |
477 | echo '<img border="0" height="16" width="16" src="pix/ma.gif">'; |
54a67a59 |
478 | break; |
479 | case RANDOMSAMATCH: |
89b8d337 |
480 | echo '<img border="0" height="16" width="16" src="pix/rm.gif">'; |
95dbc030 |
481 | break; |
401c8de6 |
482 | case DESCRIPTION: |
89b8d337 |
483 | echo '<img border="0" height="16" width="16" src="pix/de.gif">'; |
361f649d |
484 | break; |
485 | case NUMERICAL: |
89b8d337 |
486 | echo '<img border="0" height="16" width="16" src="pix/nu.gif">'; |
401c8de6 |
487 | break; |
8b439f8c |
488 | case MULTIANSWER: |
89b8d337 |
489 | echo '<img border="0" height="16" width="16" src="pix/mu.gif">'; |
8b439f8c |
490 | break; |
c74a0ca5 |
491 | } |
34d52ad7 |
492 | if ($editlink) { |
361f649d |
493 | echo "</a>\n"; |
34d52ad7 |
494 | } |
c74a0ca5 |
495 | } |
496 | |
fe98ea90 |
497 | function quiz_print_possible_question_image($quizid, $question) { |
498 | // Includes the question image is there is one |
a8a372cc |
499 | |
fe98ea90 |
500 | global $CFG; |
501 | |
502 | if ($question->image) { |
89b8d337 |
503 | echo '<img border="0" src="'; |
fe98ea90 |
504 | |
89b8d337 |
505 | if (substr(strtolower($question->image), 0, 7) == 'http://') { |
fe98ea90 |
506 | echo $question->image; |
507 | |
508 | } else if ($CFG->slasharguments) { // Use this method if possible for better caching |
509 | echo "$CFG->wwwroot/mod/quiz/quizfile.php/$quizid/$question->id/$question->image"; |
510 | |
511 | } else { |
512 | echo "$CFG->wwwroot/mod/quiz/quizfile.php?file=/$quizid/$question->id/$question->image"; |
513 | } |
89b8d337 |
514 | echo '" />'; |
fe98ea90 |
515 | |
516 | } |
517 | } |
34d52ad7 |
518 | |
fe98ea90 |
519 | function quiz_print_question($number, $question, $grade, $quizid, |
34d52ad7 |
520 | $feedback=NULL, $response=NULL, $actualgrade=NULL, $correct=NULL, |
0c29498e |
521 | $realquestion=NULL, $shuffleanswers=false, $showgrades=true, $courseid=0) { |
34d52ad7 |
522 | |
523 | /// Prints a quiz question, any format |
524 | /// $question is provided as an object |
14d8c0b4 |
525 | |
c18490b8 |
526 | $question->questiontextformat = isset($question->questiontextformat) ? $question->questiontextformat : NULL; |
263cff8b |
527 | |
401c8de6 |
528 | if ($question->qtype == DESCRIPTION) { // Special case question - has no answers etc |
529 | echo '<p align="center">'; |
5cdb8a85 |
530 | echo format_text($question->questiontext, $question->questiontextformat, NULL, $courseid); |
fe98ea90 |
531 | quiz_print_possible_question_image($quizid, $question); |
401c8de6 |
532 | echo '</p>'; |
533 | return true; |
534 | } |
535 | |
a2fe7cc0 |
536 | if (empty($actualgrade)) { |
537 | $actualgrade = 0; |
538 | } |
539 | |
14d8c0b4 |
540 | $stranswer = get_string("answer", "quiz"); |
541 | $strmarks = get_string("marks", "quiz"); |
542 | |
89b8d337 |
543 | echo '<table width="100%" cellspacing="10">'; |
544 | echo '<tr><td nowrap="nowrap" width="100" valign="top">'; |
e0971655 |
545 | echo '<p align="center"><b>' . $number . '</b></p>'; |
081bf74f |
546 | if ($showgrades) { |
547 | if ($feedback or $response) { |
d7512435 |
548 | echo "<p align=\"center\"><font size=\"1\">$strmarks: $actualgrade/$grade</font></p>"; |
081bf74f |
549 | } else { |
d7512435 |
550 | echo "<p align=\"center\"><font size=\"1\">$grade $strmarks</font></p>"; |
081bf74f |
551 | } |
a8a372cc |
552 | } |
14d8c0b4 |
553 | print_spacer(1,100); |
5a24a018 |
554 | |
fe98ea90 |
555 | if (isset($question->recentlyadded) and $question->recentlyadded) { |
89b8d337 |
556 | echo '</td><td valign="top" align="right">'; |
5a24a018 |
557 | // Notify the user of this recently added question |
558 | echo '<font color="red">'; |
559 | echo get_string('recentlyaddedquestion', 'quiz'); |
560 | echo '</font>'; |
89b8d337 |
561 | echo '</td></tr><tr><td></td><td valign="top">'; |
5a24a018 |
562 | |
563 | } else { // The normal case |
89b8d337 |
564 | echo '</td><td valign="top">'; |
5a24a018 |
565 | } |
566 | |
34d52ad7 |
567 | |
568 | if (empty($realquestion)) { |
569 | $realquestion->id = $question->id; |
570 | } else { // Add a marker to connect this question to the actual random parent |
d7512435 |
571 | echo "<input type=\"hidden\" name=\"q{$realquestion->id}rq$question->id\" value=\"x\" />\n"; |
34d52ad7 |
572 | } |
14d8c0b4 |
573 | |
a2fe7cc0 |
574 | switch ($question->qtype) { |
34d52ad7 |
575 | |
a5e1f35c |
576 | case SHORTANSWER: |
361f649d |
577 | case NUMERICAL: |
5cdb8a85 |
578 | echo format_text($question->questiontext, $question->questiontextformat, NULL, $courseid); |
fe98ea90 |
579 | quiz_print_possible_question_image($quizid, $question); |
a8a372cc |
580 | if ($response) { |
a8d909c7 |
581 | $value = 'value="'.s($response[0]).'"'; |
a2fe7cc0 |
582 | } else { |
a8d909c7 |
583 | $value = ''; |
a8a372cc |
584 | } |
a9f80673 |
585 | echo "<p align=\"right\">$stranswer: <input type=\"text\" name=\"q$realquestion->id\" size=\"80\" $value /></p>"; |
a8a372cc |
586 | if ($feedback) { |
d7512435 |
587 | quiz_print_comment("<p align=\"right\">$feedback[0]</p>"); |
a8a372cc |
588 | } |
8db3eadd |
589 | if ($correct) { |
c74a0ca5 |
590 | $correctanswers = implode(", ", $correct); |
8db3eadd |
591 | quiz_print_correctanswer($correctanswers); |
592 | } |
14d8c0b4 |
593 | break; |
594 | |
a5e1f35c |
595 | case TRUEFALSE: |
14d8c0b4 |
596 | if (!$options = get_record("quiz_truefalse", "question", $question->id)) { |
597 | notify("Error: Missing question options!"); |
598 | } |
a2fe7cc0 |
599 | if (!$true = get_record("quiz_answers", "id", $options->trueanswer)) { |
14d8c0b4 |
600 | notify("Error: Missing question answers!"); |
601 | } |
a2fe7cc0 |
602 | if (!$false = get_record("quiz_answers", "id", $options->falseanswer)) { |
14d8c0b4 |
603 | notify("Error: Missing question answers!"); |
604 | } |
605 | if (!$true->answer) { |
606 | $true->answer = get_string("true", "quiz"); |
607 | } |
608 | if (!$false->answer) { |
609 | $false->answer = get_string("false", "quiz"); |
610 | } |
5cdb8a85 |
611 | echo format_text($question->questiontext, $question->questiontextformat, NULL, $courseid); |
fe98ea90 |
612 | quiz_print_possible_question_image($quizid, $question); |
a8a372cc |
613 | |
a2fe7cc0 |
614 | $truechecked = ""; |
615 | $falsechecked = ""; |
616 | |
617 | if (!empty($response[$true->id])) { |
89b8d337 |
618 | $truechecked = 'checked="checked"'; |
a8a372cc |
619 | $feedbackid = $true->id; |
a2fe7cc0 |
620 | } else if (!empty($response[$false->id])) { |
89b8d337 |
621 | $falsechecked = 'checked="checked"'; |
a8a372cc |
622 | $feedbackid = $false->id; |
623 | } |
a2fe7cc0 |
624 | |
625 | $truecorrect = ""; |
626 | $falsecorrect = ""; |
8db3eadd |
627 | if ($correct) { |
a2fe7cc0 |
628 | if (!empty($correct[$true->id])) { |
89b8d337 |
629 | $truecorrect = 'class="highlight"'; |
8db3eadd |
630 | } |
a2fe7cc0 |
631 | if (!empty($correct[$false->id])) { |
89b8d337 |
632 | $falsecorrect = 'class="highlight"'; |
8db3eadd |
633 | } |
634 | } |
d7512435 |
635 | echo "<table align=\"right\" cellpadding=\"5\"><tr><td align=\"right\">$stranswer: "; |
636 | echo "<td $truecorrect>"; |
637 | echo "<input $truechecked type=\"radio\" name=\"q$realquestion->id\" value=\"$true->id\" />$true->answer"; |
638 | echo "</td><td $falsecorrect>"; |
639 | echo "<input $falsechecked type=\"radio\" name=\"q$realquestion->id\" value=\"$false->id\" />$false->answer"; |
640 | echo "</td></tr></table><br clear=\"all\">";// changed from CLEAR=ALL jm |
a8a372cc |
641 | if ($feedback) { |
d7512435 |
642 | quiz_print_comment("<p align=\"right\">$feedback[$feedbackid]</p>"); |
a8a372cc |
643 | } |
644 | |
14d8c0b4 |
645 | break; |
646 | |
a5e1f35c |
647 | case MULTICHOICE: |
14d8c0b4 |
648 | if (!$options = get_record("quiz_multichoice", "question", $question->id)) { |
649 | notify("Error: Missing question options!"); |
650 | } |
a5e1f35c |
651 | if (!$answers = get_records_list("quiz_answers", "id", $options->answers)) { |
14d8c0b4 |
652 | notify("Error: Missing question answers!"); |
653 | } |
5cdb8a85 |
654 | echo format_text($question->questiontext, $question->questiontextformat, NULL, $courseid); |
fe98ea90 |
655 | quiz_print_possible_question_image($quizid, $question); |
d7512435 |
656 | echo "<table align=\"right\">"; |
657 | echo "<tr><td valign=\"top\">$stranswer: </td><td>"; |
658 | echo "<table>"; |
14d8c0b4 |
659 | $answerids = explode(",", $options->answers); |
a8a372cc |
660 | |
4b85b717 |
661 | if ($shuffleanswers) { |
662 | $answerids = swapshuffle($answerids); |
663 | } |
664 | |
14d8c0b4 |
665 | foreach ($answerids as $key => $answerid) { |
666 | $answer = $answers[$answerid]; |
68fefdbe |
667 | $qnumchar = chr(ord('a') + $key); |
a8a372cc |
668 | |
586b2c82 |
669 | if (empty($response[$answerid])) { |
a8a372cc |
670 | $checked = ""; |
8e6c87cc |
671 | } else { |
89b8d337 |
672 | $checked = 'checked="checked"'; |
a8a372cc |
673 | } |
89b8d337 |
674 | echo '<tr><td valign="top">'; |
a5e1f35c |
675 | if ($options->single) { |
d7512435 |
676 | echo "<input $checked type=\"radio\" name=\"q$realquestion->id\" value=\"$answer->id\" />"; |
14d8c0b4 |
677 | } else { |
d7512435 |
678 | echo "<input $checked type=\"checkbox\" name=\"q$realquestion->id"."a$answer->id\" value=\"$answer->id\" />"; |
14d8c0b4 |
679 | } |
d7512435 |
680 | echo "</td>"; |
8e6c87cc |
681 | if (empty($feedback) or empty($correct[$answer->id])) { |
e8da8d70 |
682 | echo '<td valign="top">'.format_text("$qnumchar. $answer->answer").'</td>'; |
8e6c87cc |
683 | } else { |
e8da8d70 |
684 | echo '<td valign="top" class="highlight">'.format_text("$qnumchar. $answer->answer").'</td>'; |
8db3eadd |
685 | } |
8e6c87cc |
686 | if (!empty($feedback)) { |
d7512435 |
687 | echo "<td valign=\"top\"> "; |
8e6c87cc |
688 | if (!empty($response[$answerid])) { |
a8a372cc |
689 | quiz_print_comment($feedback[$answerid]); |
690 | } |
d7512435 |
691 | echo "</td>"; |
a8a372cc |
692 | } |
d7512435 |
693 | echo "</tr>"; |
14d8c0b4 |
694 | } |
d7512435 |
695 | echo "</table>"; |
696 | echo "</td></tr></table>"; |
14d8c0b4 |
697 | break; |
698 | |
54a67a59 |
699 | case MATCH: |
700 | if (!$options = get_record("quiz_match", "question", $question->id)) { |
701 | notify("Error: Missing question options!"); |
702 | } |
703 | if (!$subquestions = get_records_list("quiz_match_sub", "id", $options->subquestions)) { |
704 | notify("Error: Missing subquestions for this question!"); |
705 | } |
96192c44 |
706 | if (!empty($question->questiontext)) { |
5cdb8a85 |
707 | echo format_text($question->questiontext, $question->questiontextformat, NULL, $courseid); |
4b85b717 |
708 | } |
fe98ea90 |
709 | quiz_print_possible_question_image($quizid, $question); |
54a67a59 |
710 | |
96192c44 |
711 | if ($shuffleanswers) { |
712 | $subquestions = draw_rand_array($subquestions, count($subquestions)); |
713 | } |
54a67a59 |
714 | foreach ($subquestions as $subquestion) { |
715 | $answers[$subquestion->id] = $subquestion->answertext; |
716 | } |
717 | |
718 | $answers = draw_rand_array($answers, count($answers)); |
719 | |
89b8d337 |
720 | echo '<table border="0" cellpadding="10" align="right">'; |
54a67a59 |
721 | foreach ($subquestions as $key => $subquestion) { |
89b8d337 |
722 | echo '<tr><td align="left" valign="top">'; |
54a67a59 |
723 | echo $subquestion->questiontext; |
89b8d337 |
724 | echo '</td>'; |
54a67a59 |
725 | if (empty($response)) { |
89b8d337 |
726 | echo '<td align="right" valign="top">'; |
34d52ad7 |
727 | choose_from_menu($answers, "q$realquestion->id"."r$subquestion->id"); |
54a67a59 |
728 | } else { |
729 | if (empty($response[$key])) { |
89b8d337 |
730 | echo '<td align="right" valign="top">'; |
34d52ad7 |
731 | choose_from_menu($answers, "q$realquestion->id"."r$subquestion->id"); |
54a67a59 |
732 | } else { |
733 | if ($response[$key] == $correct[$key]) { |
89b8d337 |
734 | echo '<td align="right" valign="top" class="highlight">'; |
34d52ad7 |
735 | choose_from_menu($answers, "q$realquestion->id"."r$subquestion->id", $response[$key]); |
54a67a59 |
736 | } else { |
89b8d337 |
737 | echo '<td align="right" valign="top">'; |
34d52ad7 |
738 | choose_from_menu($answers, "q$realquestion->id"."r$subquestion->id", $response[$key]); |
54a67a59 |
739 | } |
740 | } |
741 | |
742 | if (!empty($feedback[$key])) { |
743 | quiz_print_comment($feedback[$key]); |
744 | } |
745 | } |
89b8d337 |
746 | echo '</td></tr>'; |
54a67a59 |
747 | } |
89b8d337 |
748 | echo '</table>'; |
54a67a59 |
749 | |
750 | break; |
751 | |
752 | case RANDOMSAMATCH: |
753 | if (!$options = get_record("quiz_randomsamatch", "question", $question->id)) { |
95dbc030 |
754 | notify("Error: Missing question options!"); |
755 | } |
5cdb8a85 |
756 | echo format_text($question->questiontext, $question->questiontextformat, NULL, $courseid); |
fe98ea90 |
757 | quiz_print_possible_question_image($quizid, $question); |
95dbc030 |
758 | |
759 | /// First, get all the questions available |
760 | |
761 | $allquestions = get_records_select("quiz_questions", |
762 | "category = $question->category AND qtype = ".SHORTANSWER); |
763 | if (count($allquestions) < $options->choose) { |
764 | notify("Error: could not find enough Short Answer questions in the database!"); |
765 | notify("Found ".count($allquestions).", need $options->choose."); |
766 | break; |
767 | } |
768 | |
769 | if (empty($response)) { // Randomly pick the questions |
770 | if (!$randomquestions = draw_rand_array($allquestions, $options->choose)) { |
771 | notify("Error choosing $options->choose random questions"); |
772 | break; |
773 | } |
774 | } else { // Use existing questions |
775 | $randomquestions = array(); |
776 | foreach ($response as $key => $rrr) { |
777 | $rrr = explode("-", $rrr); |
778 | $randomquestions[$key] = $allquestions[$key]; |
779 | $responseanswer[$key] = $rrr[1]; |
780 | } |
781 | } |
782 | |
783 | /// For each selected, find the best matching answers |
784 | |
785 | foreach ($randomquestions as $randomquestion) { |
786 | $shortanswerquestion = get_record("quiz_shortanswer", "question", $randomquestion->id); |
787 | $questionanswers = get_records_list("quiz_answers", "id", $shortanswerquestion->answers); |
788 | $bestfraction = 0; |
789 | $bestanswer = NULL; |
790 | foreach ($questionanswers as $questionanswer) { |
791 | if ($questionanswer->fraction > $bestfraction) { |
792 | $bestanswer = $questionanswer; |
793 | } |
794 | } |
795 | if (empty($bestanswer)) { |
796 | notify("Error: Could not find the best answer for question: ".$randomquestions->name); |
797 | break; |
798 | } |
799 | $randomanswers[$bestanswer->id] = trim($bestanswer->answer); |
800 | } |
801 | |
802 | if (!$randomanswers = draw_rand_array($randomanswers, $options->choose)) { // Mix them up |
803 | notify("Error randomising answers!"); |
804 | break; |
805 | } |
806 | |
89b8d337 |
807 | echo '<table border="0" cellpadding="10">'; |
95dbc030 |
808 | foreach ($randomquestions as $key => $randomquestion) { |
89b8d337 |
809 | echo '<tr><td align="left" valign="top">'; |
95dbc030 |
810 | echo $randomquestion->questiontext; |
89b8d337 |
811 | echo '</td>'; |
812 | echo '<td align="right" valign="top">'; |
95dbc030 |
813 | if (empty($response)) { |
34d52ad7 |
814 | choose_from_menu($randomanswers, "q$realquestion->id"."r$randomquestion->id"); |
95dbc030 |
815 | } else { |
816 | if (!empty($correct[$key])) { |
817 | if ($randomanswers[$responseanswer[$key]] == $correct[$key]) { |
89b8d337 |
818 | echo '<span="highlight">'; |
34d52ad7 |
819 | choose_from_menu($randomanswers, "q$realquestion->id"."r$randomquestion->id", $responseanswer[$key]); |
d7143408 |
820 | echo '</span><br />'; |
95dbc030 |
821 | } else { |
34d52ad7 |
822 | choose_from_menu($randomanswers, "q$realquestion->id"."r$randomquestion->id", $responseanswer[$key]); |
95dbc030 |
823 | quiz_print_correctanswer($correct[$key]); |
824 | } |
825 | } else { |
34d52ad7 |
826 | choose_from_menu($randomanswers, "q$realquestion->id"."r$randomquestion->id", $responseanswer[$key]); |
95dbc030 |
827 | } |
828 | if (!empty($feedback[$key])) { |
829 | quiz_print_comment($feedback[$key]); |
830 | } |
831 | } |
89b8d337 |
832 | echo '</td></tr>'; |
95dbc030 |
833 | } |
89b8d337 |
834 | echo '</table>'; |
95dbc030 |
835 | break; |
836 | |
8b439f8c |
837 | case MULTIANSWER: |
838 | // For this question type, we better print the image on top: |
fe98ea90 |
839 | quiz_print_possible_question_image($quizid, $question); |
8b439f8c |
840 | |
d396ba79 |
841 | $qtextremaining = format_text($question->questiontext, $question->questiontextformat, NULL, $courseid); |
842 | |
843 | // The regex will recognize text snippets of type {#X} |
844 | // where the X can be any text not containg } or white-space characters. |
845 | |
846 | while (ereg('\{#([^[:space:]}]*)}', $qtextremaining, $regs)) { |
847 | $qtextsplits = explode($regs[0], $qtextremaining, 2); |
848 | echo $qtextsplits[0]; |
849 | $qtextremaining = $qtextsplits[1]; |
850 | |
851 | $multianswer = get_record('quiz_multianswers', 'question', $question->id, 'positionkey', $regs[1]); |
852 | |
853 | $inputname= " name=\"q{$realquestion->id}ma$multianswer->id\" "; |
854 | |
855 | if (!empty($response) && ereg('(.[^-]*)-(.+)', array_shift($response), $responseitems)) { |
856 | $responsefractiongrade = (float)$responseitems[1]; |
857 | $actualresponse = $responseitems[2]; |
858 | |
859 | if (1.0 == $responsefractiongrade) { |
860 | $style = 'style="background-color:lime"'; |
861 | } else if (0.0 < $responsefractiongrade) { |
862 | $style = 'style="background-color:yellow"'; |
863 | } else if ('' != $actualresponse) { |
864 | // The response must have been totally wrong: |
865 | $style = 'style="background-color:red"'; |
866 | } else { |
867 | // There was no response given |
868 | $style = ''; |
869 | } |
870 | } else { |
871 | $responsefractiongrade = 0.0; |
872 | $actualresponse = ''; |
873 | $style = ''; |
874 | } |
8b439f8c |
875 | |
d396ba79 |
876 | $feedbackitem = ''; |
877 | switch ($multianswer->answertype) { |
878 | case SHORTANSWER: |
879 | case NUMERICAL: |
880 | if (isset($feedback[$regs[1]-1])) { |
881 | $title = " title=\"".s($feedback[$regs[1]-1])."\" "; |
882 | } else { |
883 | $title = ""; |
884 | } |
885 | echo " <input $style $title $inputname value=\"$actualresponse\" type=\"text\" size=\"12\" /> "; |
886 | break; |
887 | case MULTICHOICE: |
888 | $outputoptions = ''; |
889 | $answers = get_records_list("quiz_answers", "id", $multianswer->answers); |
890 | $outputoptions .= '<option></option>'; // Default empty option |
891 | foreach ($answers as $answer) { |
892 | if ($answer->id == $actualresponse) { |
893 | $selected = 'selected'; |
894 | $feedbackitem = $answer->feedback; |
895 | } else { |
896 | $selected = ''; |
897 | } |
898 | $outputoptions .= "<option value=\"$answer->id\" $selected>$answer->answer</option>"; |
899 | } |
900 | if ($feedbackitem) { |
901 | echo "<select title=\"".s($feedbackitem)."\" $style $inputname>"; |
902 | } else { |
903 | echo "<select $style $inputname>"; |
904 | } |
905 | echo $outputoptions; |
906 | echo '</select>'; |
907 | break; |
908 | default: |
909 | error("Unable to recognized answertype $answer->answertype"); |
910 | break; |
911 | } |
912 | } |
8b439f8c |
913 | |
d396ba79 |
914 | // Print the final piece of question text: |
915 | echo $qtextremaining; |
916 | break; |
8b439f8c |
917 | |
34d52ad7 |
918 | case RANDOM: |
5a24a018 |
919 | // This can only happen if it is a recently added question |
920 | |
d7512435 |
921 | echo '<p>' . get_string('random', 'quiz') . '</p>'; |
34d52ad7 |
922 | break; |
8db3eadd |
923 | |
14d8c0b4 |
924 | default: |
925 | notify("Error: Unknown question type!"); |
926 | } |
927 | |
d7512435 |
928 | echo "</td></tr></table>"; |
3a506ca2 |
929 | } |
930 | |
34d52ad7 |
931 | |
932 | |
96192c44 |
933 | function quiz_print_quiz_questions($quiz, $results=NULL, $questions=NULL, $shuffleorder=NULL) { |
a5e1f35c |
934 | // Prints a whole quiz on one page. |
935 | |
34d52ad7 |
936 | /// Get the questions |
937 | |
34d52ad7 |
938 | if (!$questions) { |
434802d5 |
939 | if (empty($quiz->questions)) { |
940 | notify("No questions have been defined!"); |
941 | return false; |
942 | } |
943 | |
34d52ad7 |
944 | if (!$questions = get_records_list("quiz_questions", "id", $quiz->questions, "")) { |
434802d5 |
945 | notify("Error when reading questions from the database!"); |
34d52ad7 |
946 | return false; |
947 | } |
d288fa52 |
948 | } |
4b85b717 |
949 | |
d288fa52 |
950 | if (!$shuffleorder) { |
951 | if (!empty($quiz->shufflequestions)) { // Mix everything up |
4b85b717 |
952 | $questions = swapshuffle_assoc($questions); |
ada728e2 |
953 | } else { |
d288fa52 |
954 | $shuffleorder = explode(",", $quiz->questions); // Use originally defined order |
4b85b717 |
955 | } |
34d52ad7 |
956 | } |
957 | |
e1122620 |
958 | if ($shuffleorder) { // Order has been defined, so reorder questions |
959 | $oldquestions = $questions; |
960 | $questions = array(); |
5a24a018 |
961 | foreach ($shuffleorder as $key) { |
962 | if (empty($oldquestions[$key])) { // Check for recently added questions |
963 | if ($recentlyaddedquestion = |
964 | get_record("quiz_questions", "id", $key)) { |
965 | $recentlyaddedquestion->recentlyadded = true; |
966 | $questions[] = $recentlyaddedquestion; |
967 | } |
968 | } else { |
969 | $questions[] = $oldquestions[$key]; // This loses the index key, but doesn't matter |
970 | } |
e1122620 |
971 | } |
972 | } |
973 | |
434802d5 |
974 | if (!$grades = get_records_list("quiz_question_grades", "question", $quiz->questions, "", "question,grade")) { |
975 | notify("No grades were found for these questions!"); |
976 | return false; |
977 | } |
978 | |
34d52ad7 |
979 | |
980 | /// Examine the set of questions for random questions, and retrieve them |
981 | |
434802d5 |
982 | if (empty($results)) { // Choose some new random questions |
34d52ad7 |
983 | if ($randomcats = quiz_get_random_categories($quiz->questions)) { |
984 | foreach ($randomcats as $randomcat => $randomdraw) { |
985 | /// Get the appropriate amount of random questions from this category |
7bfa4fad |
986 | if (!$catquestions[$randomcat] = quiz_choose_random_questions($randomcat, $randomdraw, $quiz->questions)) { |
434802d5 |
987 | notify(get_string("toomanyrandom", "quiz", $randomcat)); |
34d52ad7 |
988 | return false; |
989 | } |
990 | } |
991 | } |
434802d5 |
992 | } else { // Get the previously chosen questions |
993 | $chosen = array(); |
994 | foreach ($questions as $question) { |
995 | if (isset($question->random)) { |
996 | $chosen[] = $question->random; |
997 | } |
998 | } |
999 | if ($chosen) { |
1000 | $chosenlist = implode(",", $chosen); |
1001 | if (!$chosen = get_records_list("quiz_questions", "id", $chosenlist, "")) { |
1002 | notify("Error when reading questions from the database!"); |
1003 | return false; |
1004 | } |
1005 | } |
a5e1f35c |
1006 | } |
1007 | |
9307692f |
1008 | $strconfirmattempt = addslashes(get_string("readytosend", "quiz")); |
1009 | |
45376a51 |
1010 | if (empty($quiz->grade)) { |
1011 | $onsubmit = ""; |
1012 | } else { |
1013 | $onsubmit = "onsubmit=\"return confirm('$strconfirmattempt');\""; |
1014 | } |
1015 | |
d7512435 |
1016 | echo "<form method=\"post\" action=\"attempt.php\" $onsubmit>\n"; |
1017 | echo "<input type=\"hidden\" name=\"q\" value=\"$quiz->id\" />\n"; |
a8a372cc |
1018 | |
34d52ad7 |
1019 | $count = 0; |
96192c44 |
1020 | $questionorder = array(); |
1021 | |
34d52ad7 |
1022 | foreach ($questions as $question) { |
401c8de6 |
1023 | |
1024 | if ($question->qtype != DESCRIPTION) { // Description questions are not counted |
1025 | $count++; |
1026 | } |
34d52ad7 |
1027 | |
e1122620 |
1028 | $questionorder[] = $question->id; |
1029 | |
8db3eadd |
1030 | $feedback = NULL; |
1031 | $response = NULL; |
1032 | $actualgrades = NULL; |
1033 | $correct = NULL; |
34d52ad7 |
1034 | $randomquestion = NULL; |
1035 | |
1036 | if (empty($results)) { |
1037 | if ($question->qtype == RANDOM ) { // Set up random questions |
1038 | $randomquestion = $question; |
1039 | $question = array_pop($catquestions[$randomquestion->category]); |
1040 | $grades[$question->id]->grade = $grades[$randomquestion->id]->grade; |
8e6c87cc |
1041 | } |
34d52ad7 |
1042 | } else { |
1043 | if (!empty($results->feedback[$question->id])) { |
1044 | $feedback = $results->feedback[$question->id]; |
8e6c87cc |
1045 | } |
34d52ad7 |
1046 | if (!empty($results->response[$question->id])) { |
1047 | $response = $results->response[$question->id]; |
1048 | } |
1049 | if (!empty($results->grades[$question->id])) { |
1050 | $actualgrades = $results->grades[$question->id]; |
8e6c87cc |
1051 | } |
8db3eadd |
1052 | if ($quiz->correctanswers) { |
34d52ad7 |
1053 | if (!empty($results->correct[$question->id])) { |
1054 | $correct = $results->correct[$question->id]; |
8e6c87cc |
1055 | } |
8db3eadd |
1056 | } |
34d52ad7 |
1057 | if (!empty($question->random)) { |
1058 | $randomquestion = $question; |
434802d5 |
1059 | $question = $chosen[$question->random]; |
34d52ad7 |
1060 | $grades[$question->id]->grade = $grades[$randomquestion->id]->grade; |
1061 | } |
a8a372cc |
1062 | } |
8db3eadd |
1063 | |
d7512435 |
1064 | print_simple_box_start("center", "90%"); |
fe98ea90 |
1065 | quiz_print_question($count, $question, $grades[$question->id]->grade, $quiz->id, |
4b85b717 |
1066 | $feedback, $response, $actualgrades, $correct, |
0c29498e |
1067 | $randomquestion, $quiz->shuffleanswers, $quiz->grade, $quiz->course); |
a5e1f35c |
1068 | print_simple_box_end(); |
d7512435 |
1069 | echo "<br />"; |
a5e1f35c |
1070 | } |
a8a372cc |
1071 | |
ff4c50a9 |
1072 | $attemptbuildsonthelast = isset($results->attemptbuildsonthelast) ? $results->attemptbuildsonthelast : NULL; |
c18490b8 |
1073 | |
ff4c50a9 |
1074 | if (empty($results) || $attemptbuildsonthelast) { |
96192c44 |
1075 | if (!empty($quiz->shufflequestions)) { // Things have been mixed up, so pass the question order |
1076 | $shuffleorder = implode(',', $questionorder); |
d7512435 |
1077 | echo "<input type=\"hidden\" name=\"shuffleorder\" value=\"$shuffleorder\" />\n"; |
96192c44 |
1078 | } |
d7512435 |
1079 | echo "<center>\n<input type=\"submit\" value=\"".get_string("savemyanswers", "quiz")."\" />\n</center>"; |
a8a372cc |
1080 | } |
96192c44 |
1081 | echo "</form>"; |
10b9291c |
1082 | |
1083 | return true; |
a5e1f35c |
1084 | } |
34d52ad7 |
1085 | |
1086 | |
6a952ce7 |
1087 | |
1088 | function quiz_get_default_category($courseid) { |
34d52ad7 |
1089 | /// Returns the current category |
1090 | |
6a952ce7 |
1091 | if ($categories = get_records("quiz_categories", "course", $courseid, "id")) { |
1092 | foreach ($categories as $category) { |
1093 | return $category; // Return the first one (lowest id) |
1094 | } |
1095 | } |
1096 | |
1097 | // Otherwise, we need to make one |
10b9291c |
1098 | $category->name = get_string("default", "quiz"); |
1099 | $category->info = get_string("defaultinfo", "quiz"); |
6a952ce7 |
1100 | $category->course = $courseid; |
1101 | $category->publish = 0; |
25b6ff9d |
1102 | $category->stamp = make_unique_id_code(); |
6a952ce7 |
1103 | |
1104 | if (!$category->id = insert_record("quiz_categories", $category)) { |
1105 | notify("Error creating a default category!"); |
1106 | return false; |
1107 | } |
1108 | return $category; |
1109 | } |
1110 | |
c74a0ca5 |
1111 | function quiz_get_category_menu($courseid, $published=false) { |
da5fb074 |
1112 | /// Returns the list of categories |
1113 | $publish = ""; |
c74a0ca5 |
1114 | if ($published) { |
1115 | $publish = "OR publish = '1'"; |
1116 | } |
bdc23be0 |
1117 | return get_records_select_menu("quiz_categories", "course='$courseid' $publish", "name ASC", "id,name"); |
c74a0ca5 |
1118 | } |
1119 | |
6a952ce7 |
1120 | function quiz_print_category_form($course, $current) { |
1121 | // Prints a form to choose categories |
1122 | |
facbf40f |
1123 | if (!$categories = get_records_select("quiz_categories", "course = '$course->id' OR publish = '1'", "name ASC")) { |
6a952ce7 |
1124 | if (!$category = quiz_get_default_category($course->id)) { |
1125 | notify("Error creating a default category!"); |
1126 | return false; |
1127 | } |
cb62c00a |
1128 | $categories[$category->id] = $category; |
6a952ce7 |
1129 | } |
8d94f5a0 |
1130 | foreach ($categories as $key => $category) { |
facbf40f |
1131 | if ($catcourse = get_record("course", "id", $category->course)) { |
1132 | if ($category->publish) { |
b55a466b |
1133 | $category->name .= " ($catcourse->shortname)"; |
8d94f5a0 |
1134 | } |
facbf40f |
1135 | $catmenu[$category->id] = $category->name; |
8d94f5a0 |
1136 | } |
1137 | } |
6a952ce7 |
1138 | $strcategory = get_string("category", "quiz"); |
1139 | $strshow = get_string("show", "quiz"); |
6b069ece |
1140 | $streditcats = get_string("editcategories", "quiz"); |
6a952ce7 |
1141 | |
2d1c22bb |
1142 | echo "<table width=\"100%\"><tr><td width=\"20\" nowrap=\"nowrap\">"; |
d7512435 |
1143 | echo "<b>$strcategory:</b> "; |
2d1c22bb |
1144 | echo "</td><td>"; |
22592150 |
1145 | popup_form ("edit.php?cat=", $catmenu, "catmenu", $current, "choose", "", "", false, "self"); |
d7512435 |
1146 | echo "</td><td align=\"right\">"; |
1147 | echo "<form method=\"get\" action=\"category.php\">"; |
1148 | echo "<input type=\"hidden\" name=\"id\" value=\"$course->id\" />"; |
1149 | echo "<input type=\"submit\" value=\"$streditcats\" />"; |
1150 | echo "</form>"; |
1151 | echo "</td></tr></table>"; |
6a952ce7 |
1152 | } |
1153 | |
1154 | |
34d52ad7 |
1155 | |
7bfa4fad |
1156 | function quiz_choose_random_questions($category, $draws, $excluded=0) { |
34d52ad7 |
1157 | /// Given a question category and a number of draws, this function |
1158 | /// creates a random subset of that size - returned as an array of questions |
1159 | |
1160 | if (!$pool = get_records_select_menu("quiz_questions", |
575de48f |
1161 | "category = '$category' AND id NOT IN ($excluded) |
1162 | AND qtype <> ".RANDOM." |
1163 | AND qtype <> ".DESCRIPTION, |
1164 | "", "id,qtype")) { |
34d52ad7 |
1165 | return false; |
1166 | } |
1167 | |
1168 | $countpool = count($pool); |
1169 | |
1170 | if ($countpool == $draws) { |
1171 | $chosen = $pool; |
1172 | } else if ($countpool < $draws) { |
1173 | return false; |
1174 | } else { |
1175 | $chosen = draw_rand_array($pool, $draws); |
1176 | } |
1177 | |
1178 | $chosenlist = implode(",", array_keys($chosen)); |
1179 | return get_records_list("quiz_questions", "id", $chosenlist); |
1180 | } |
1181 | |
1182 | |
7bd1aa1d |
1183 | function quiz_get_all_question_grades($questionlist, $quizid) { |
1184 | // Given a list of question IDs, finds grades or invents them to |
1185 | // create an array of matching grades |
1186 | |
5a25f84d |
1187 | if (empty($questionlist)) { |
1188 | return array(); |
1189 | } |
1190 | |
bdc23be0 |
1191 | $questions = quiz_get_question_grades($quizid, $questionlist); |
7bd1aa1d |
1192 | |
1193 | $list = explode(",", $questionlist); |
1194 | $grades = array(); |
1195 | |
1196 | foreach ($list as $qid) { |
1197 | if (isset($questions[$qid])) { |
1198 | $grades[$qid] = $questions[$qid]->grade; |
1199 | } else { |
1200 | $grades[$qid] = 1; |
1201 | } |
1202 | } |
1203 | return $grades; |
1204 | } |
1205 | |
004c02e0 |
1206 | function quiz_gradesmenu_options($defaultgrade) { |
1207 | // Especially for multianswer questions it is often |
1208 | // desirable to have the grade of the question in a quiz |
1209 | // larger than the earlier maximum of 10 points. |
1210 | // This function makes quiz question list grade selector drop-down |
1211 | // have the maximum grade option set to the highest value between 10 |
1212 | // and the defaultgrade of the question. |
1213 | |
1214 | if ($defaultgrade && $defaultgrade>10) { |
1215 | $maxgrade = $defaultgrade; |
1216 | } else { |
1217 | $maxgrade = 10; |
1218 | } |
1219 | |
1220 | unset($gradesmenu); |
1221 | for ($i=$maxgrade ; $i>=0 ; --$i) { |
1222 | $gradesmenu[$i] = $i; |
1223 | } |
1224 | return $gradesmenu; |
1225 | } |
7bd1aa1d |
1226 | |
1227 | function quiz_print_question_list($questionlist, $grades) { |
6a952ce7 |
1228 | // Prints a list of quiz questions in a small layout form with knobs |
7bd1aa1d |
1229 | // $questionlist is comma-separated list |
1230 | // $grades is an array of corresponding grades |
6a952ce7 |
1231 | |
1232 | global $THEME; |
1233 | |
1234 | if (!$questionlist) { |
d7512435 |
1235 | echo "<p align=\"center\">"; |
6a952ce7 |
1236 | print_string("noquestions", "quiz"); |
d7512435 |
1237 | echo "</p>"; |
6a952ce7 |
1238 | return; |
1239 | } |
1240 | |
1241 | $order = explode(",", $questionlist); |
1242 | |
a740ff66 |
1243 | if (!$questions = get_list_of_questions($questionlist)) { |
d7512435 |
1244 | echo "<p align=\"center\">"; |
9a652ddb |
1245 | print_string("noquestions", "quiz"); |
d7512435 |
1246 | echo "</p>"; |
9a652ddb |
1247 | return; |
1248 | |
6a952ce7 |
1249 | } |
1250 | |
1251 | $strorder = get_string("order"); |
1252 | $strquestionname = get_string("questionname", "quiz"); |
1253 | $strgrade = get_string("grade"); |
1254 | $strdelete = get_string("delete"); |
1255 | $stredit = get_string("edit"); |
1256 | $strmoveup = get_string("moveup"); |
1257 | $strmovedown = get_string("movedown"); |
1258 | $strsavegrades = get_string("savegrades", "quiz"); |
c74a0ca5 |
1259 | $strtype = get_string("type", "quiz"); |
6a952ce7 |
1260 | |
6a952ce7 |
1261 | $count = 0; |
1262 | $sumgrade = 0; |
1263 | $total = count($order); |
d7512435 |
1264 | echo "<form method=\"post\" action=\"edit.php\">"; |
1265 | echo "<table border=\"0\" cellpadding=\"5\" cellspacing=\"2\" width=\"100%\">\n"; |
1266 | echo "<tr><th width=\"*\" colspan=\"3\" nowrap=\"nowrap\">$strorder</th><th align=\"left\" width=\"100%\" nowrap=\"nowrap\">$strquestionname</th><th width=\"*\" nowrap=\"nowrap\">$strtype</th><th width=\"*\" nowrap=\"nowrap\">$strgrade</th><th width=\"*\" nowrap=\"nowrap\">$stredit</th></tr>\n"; |
6a952ce7 |
1267 | foreach ($order as $qnum) { |
95dbc030 |
1268 | if (empty($questions[$qnum])) { |
1269 | continue; |
1270 | } |
bca64a12 |
1271 | $question = $questions[$qnum]; |
a740ff66 |
1272 | $canedit = isteacheredit($question->course); |
6a952ce7 |
1273 | $count++; |
d7512435 |
1274 | echo "<tr bgcolor=\"$THEME->cellcontent\">"; |
1275 | echo "<td>$count</td>"; |
1276 | echo "<td>"; |
6a952ce7 |
1277 | if ($count != 1) { |
d7512435 |
1278 | echo "<a title=\"$strmoveup\" href=\"edit.php?up=$qnum\"><img |
1279 | src=\"../../pix/t/up.gif\" border=\"0\"></a>"; |
6a952ce7 |
1280 | } |
d7512435 |
1281 | echo "</td>"; |
1282 | echo "<td>"; |
6a952ce7 |
1283 | if ($count != $total) { |
d7512435 |
1284 | echo "<a title=\"$strmovedown\" href=\"edit.php?down=$qnum\"><img |
1285 | src=\"../../pix/t/down.gif\" border=\"0\"></a>"; |
6a952ce7 |
1286 | } |
d7512435 |
1287 | echo "</td>"; |
1288 | echo "<td>$question->name</td>"; |
1289 | echo "<td align=\"center\">"; |
a740ff66 |
1290 | quiz_print_question_icon($question, $canedit); |
d7512435 |
1291 | echo "</td>"; |
1292 | echo "<td>"; |
bca64a12 |
1293 | if ($question->qtype == DESCRIPTION) { |
d7512435 |
1294 | echo "<input type=\"hidden\" name=\"q$qnum\" value=\"0\" /> \n"; |
bca64a12 |
1295 | } else { |
004c02e0 |
1296 | choose_from_menu(quiz_gradesmenu_options($question->defaultgrade), |
1297 | "q$qnum", (string)$grades[$qnum], ""); |
bca64a12 |
1298 | } |
d7512435 |
1299 | echo "<td>"; |
1300 | echo "<a title=\"$strdelete\" href=\"edit.php?delete=$qnum\"><img |
1301 | src=\"../../pix/t/delete.gif\" border=\"0\"></a> "; |
a740ff66 |
1302 | if ($canedit) { |
1303 | echo "<a title=\"$stredit\" href=\"question.php?id=$qnum\"><img |
1304 | src=\"../../pix/t/edit.gif\" border=\"0\"></a>\n"; |
1305 | } |
d7512435 |
1306 | echo "</td>"; |
6a952ce7 |
1307 | |
7bd1aa1d |
1308 | $sumgrade += $grades[$qnum]; |
6a952ce7 |
1309 | } |
d7512435 |
1310 | echo "<tr><td colspan=5 align=\"right\">\n"; |
1311 | echo "<input type=\"submit\" value=\"$strsavegrades:\" />\n"; |
1312 | echo "<input type=\"hidden\" name=\"setgrades\" value=\"save\" />\n"; |
1313 | echo "<td align=\"left\" bgcolor=\"$THEME->cellcontent\">\n"; |
1314 | echo "<b>$sumgrade</b>"; |
1315 | echo "</td><td>\n</td></tr>\n"; |
1316 | echo "</table>\n"; |
1317 | echo "</form>\n"; |
10b9291c |
1318 | |
1319 | return $sumgrade; |
6a952ce7 |
1320 | } |
1321 | |
1322 | |
1e085edc |
1323 | function quiz_print_cat_question_list($categoryid, $quizselected=true) { |
6a952ce7 |
1324 | // Prints a form to choose categories |
1325 | |
1326 | global $THEME, $QUIZ_QUESTION_TYPE; |
1327 | |
10b9291c |
1328 | $strcategory = get_string("category", "quiz"); |
6a952ce7 |
1329 | $strquestion = get_string("question", "quiz"); |
49220fa7 |
1330 | $straddquestions = get_string("addquestions", "quiz"); |
1331 | $strimportquestions = get_string("importquestions", "quiz"); |
6a952ce7 |
1332 | $strnoquestions = get_string("noquestions", "quiz"); |
1333 | $strselect = get_string("select", "quiz"); |
a01b2571 |
1334 | $strselectall = get_string("selectall", "quiz"); |
6a952ce7 |
1335 | $strcreatenewquestion = get_string("createnewquestion", "quiz"); |
1336 | $strquestionname = get_string("questionname", "quiz"); |
1337 | $strdelete = get_string("delete"); |
1338 | $stredit = get_string("edit"); |
1339 | $straddselectedtoquiz = get_string("addselectedtoquiz", "quiz"); |
c74a0ca5 |
1340 | $strtype = get_string("type", "quiz"); |
c6eed097 |
1341 | $strcreatemultiple = get_string("createmultiple", "quiz"); |
6a952ce7 |
1342 | |
1343 | if (!$categoryid) { |
d7512435 |
1344 | echo "<p align=\"center\"><b>"; |
6a952ce7 |
1345 | print_string("selectcategoryabove", "quiz"); |
5325f8b8 |
1346 | echo "</b></p>"; |
1e085edc |
1347 | if ($quizselected) { |
1348 | echo "<p>"; |
1349 | print_string("addingquestions", "quiz"); |
1350 | echo "</p>"; |
1351 | } |
6a952ce7 |
1352 | return; |
1353 | } |
a5e1f35c |
1354 | |
6a952ce7 |
1355 | if (!$category = get_record("quiz_categories", "id", "$categoryid")) { |
1356 | notify("Category not found!"); |
1357 | return; |
1358 | } |
d7512435 |
1359 | echo "<center>"; |
2d1c22bb |
1360 | echo format_text($category->info, FORMAT_MOODLE); |
1361 | |
1362 | echo '<table><tr>'; |
1363 | echo "<td valign=\"top\"><b>$strcreatenewquestion:</b></td>"; |
1364 | echo '<td valign="top" align="right">'; |
1365 | popup_form ("question.php?category=$category->id&qtype=", $QUIZ_QUESTION_TYPE, "addquestion", |
22592150 |
1366 | "", "choose", "", "", false, "self"); |
2d1c22bb |
1367 | echo '<td width="10" valign="top" align="right">'; |
cd63d77e |
1368 | helpbutton("questiontypes", $strcreatenewquestion, "quiz"); |
2d1c22bb |
1369 | echo '</td></tr>'; |
49220fa7 |
1370 | |
2d1c22bb |
1371 | echo '<tr><td colspan="3" align="right">'; |
1372 | echo '<form method="get" action="import.php">'; |
d7512435 |
1373 | echo "<input type=\"hidden\" name=\"category\" value=\"$category->id\" />"; |
1374 | echo "<input type=\"submit\" value=\"$strimportquestions\" />"; |
49220fa7 |
1375 | helpbutton("import", $strimportquestions, "quiz"); |
2d1c22bb |
1376 | echo '</form>'; |
1377 | echo '</td></tr>'; |
49220fa7 |
1378 | |
2d1c22bb |
1379 | echo '<tr><td colspan="3" align="right">'; |
1380 | echo '<form method="get" action="multiple.php">'; |
d7512435 |
1381 | echo "<input type=\"hidden\" name=\"category\" value=\"$category->id\" />"; |
1382 | echo "<input type=\"submit\" value=\"$strcreatemultiple\" />"; |
c6eed097 |
1383 | helpbutton("createmultiple", $strcreatemultiple, "quiz"); |
2d1c22bb |
1384 | echo '</form>'; |
1385 | echo '</td></tr>'; |
c6eed097 |
1386 | |
2d1c22bb |
1387 | echo '</table>'; |
49220fa7 |
1388 | |
2d1c22bb |
1389 | echo '</center>'; |
6a952ce7 |
1390 | |
14bdb238 |
1391 | if (!$questions = get_records("quiz_questions", "category", $category->id, "qtype ASC")) { |
d7512435 |
1392 | echo "<p align=\"center\">"; |
6a952ce7 |
1393 | print_string("noquestions", "quiz"); |
d7512435 |
1394 | echo "</p>"; |
6a952ce7 |
1395 | return; |
1396 | } |
1397 | |
1e085edc |
1398 | $canedit = isteacheredit($category->course); |
10b9291c |
1399 | |
d7512435 |
1400 | echo "<form method=\"post\" action=\"edit.php\">"; |
1401 | echo "<table border=\"0\" cellpadding=\"5\" cellspacing=\"2\" width=\"100%\">"; |
1402 | echo "<tr>"; |
1e085edc |
1403 | if ($quizselected) { |
d7512435 |
1404 | echo "<th width=\"*\" nowrap=\"nowrap\">$strselect</th>"; |
1e085edc |
1405 | } |
d7512435 |
1406 | echo "<th width=\"100%\" align=\"left\" nowrap=\"nowrap\">$strquestionname</th><th width=\"*\" nowrap=\"nowrap\">$strtype</th>"; |
10b9291c |
1407 | if ($canedit) { |
d7512435 |
1408 | echo "<th width=\"*\" nowrap=\"nowrap\">$stredit</th>"; |
10b9291c |
1409 | } |
d7512435 |
1410 | echo "</tr>\n"; |
6a952ce7 |
1411 | foreach ($questions as $question) { |
d7512435 |
1412 | echo "<tr bgcolor=\"$THEME->cellcontent\">\n"; |
1e085edc |
1413 | if ($quizselected) { |
d7512435 |
1414 | echo "<td align=\"center\">"; |
1415 | echo "<input type=\"checkbox\" name=\"q$question->id\" value=\"1\" />\n"; |
1416 | echo "</td>"; |
1e085edc |
1417 | } |
d7512435 |
1418 | echo "<td>".$question->name."</td>\n"; |
1419 | echo "<td align=\"center\">\n"; |
a740ff66 |
1420 | quiz_print_question_icon($question, $canedit); |
d7512435 |
1421 | echo "</td>\n"; |
10b9291c |
1422 | if ($canedit) { |
d7512435 |
1423 | echo "<td>\n"; |
1424 | echo "<a title=\"$strdelete\" href=\"question.php?id=$question->id&delete=$question->id\">\n<img |
1425 | src=\"../../pix/t/delete.gif\" border=0></a> "; |
1426 | echo "<a title=\"$stredit\" href=\"question.php?id=$question->id\"><img |
1427 | src=\"../../pix/t/edit.gif\" border=0></a>"; |
1428 | echo "</td>\n";// deleted </tr> jm |
10b9291c |
1429 | } |
d7512435 |
1430 | echo "</tr>\n"; |
6a952ce7 |
1431 | } |
1e085edc |
1432 | if ($quizselected) { |
d7512435 |
1433 | echo "<tr>\n<td colspan=\"3\">"; |
1434 | echo "<input type=\"submit\" name=\"add\" value=\"<< $straddselectedtoquiz\" />\n"; |
1435 | //echo "<input type=submit name=\"delete\" value=\"XX Delete selected\">"; |
89b8d337 |
1436 | echo "<input type=\"button\" onclick=\"checkall()\" value=\"$strselectall\" />\n"; |
d7512435 |
1437 | echo "</td></tr>"; |
1438 | } |
1439 | echo "</table>\n"; |
1440 | echo "</form>\n"; |
6a952ce7 |
1441 | } |
a5e1f35c |
1442 | |
3a506ca2 |
1443 | |
958aafe2 |
1444 | function quiz_start_attempt($quizid, $userid, $numattempt) { |
1445 | $attempt->quiz = $quizid; |
ebc3bd2b |
1446 | $attempt->userid = $userid; |
958aafe2 |
1447 | $attempt->attempt = $numattempt; |
1448 | $attempt->timestart = time(); |
1449 | $attempt->timefinish = 0; |
1450 | $attempt->timemodified = time(); |
1451 | |
1452 | return insert_record("quiz_attempts", $attempt); |
1453 | } |
1454 | |
1455 | function quiz_get_user_attempt_unfinished($quizid, $userid) { |
1456 | // Returns an object containing an unfinished attempt (if there is one) |
ebc3bd2b |
1457 | return get_record("quiz_attempts", "quiz", $quizid, "userid", $userid, "timefinish", 0); |
958aafe2 |
1458 | } |
1459 | |
3a506ca2 |
1460 | function quiz_get_user_attempts($quizid, $userid) { |
a5e1f35c |
1461 | // Returns a list of all attempts by a user |
ebc3bd2b |
1462 | return get_records_select("quiz_attempts", "quiz = '$quizid' AND userid = '$userid' AND timefinish > 0", |
bdc23be0 |
1463 | "attempt ASC"); |
3a506ca2 |
1464 | } |
1465 | |
8d94f5a0 |
1466 | |
1467 | function quiz_get_user_attempts_string($quiz, $attempts, $bestgrade) { |
1468 | /// Returns a simple little comma-separated list of all attempts, |
6d86b5dc |
1469 | /// with each grade linked to the feedback report and with the best grade highlighted |
8d94f5a0 |
1470 | |
1471 | $bestgrade = format_float($bestgrade); |
1472 | foreach ($attempts as $attempt) { |
1473 | $attemptgrade = format_float(($attempt->sumgrades / $quiz->sumgrades) * $quiz->grade); |
1474 | if ($attemptgrade == $bestgrade) { |
d7512435 |
1475 | $userattempts[] = "<span class=\"highlight\"><a href=\"review.php?q=$quiz->id&attempt=$attempt->id\">$attemptgrade</a></span>"; |
8d94f5a0 |
1476 | } else { |
29d5d0b4 |
1477 | $userattempts[] = "<a href=\"review.php?q=$quiz->id&attempt=$attempt->id\">$attemptgrade</a>"; |
8d94f5a0 |
1478 | } |
1479 | } |
1480 | return implode(",", $userattempts); |
1481 | } |
1482 | |
a5e1f35c |
1483 | function quiz_get_best_grade($quizid, $userid) { |
1484 | /// Get the best current grade for a particular user in a quiz |
ebc3bd2b |
1485 | if (!$grade = get_record("quiz_grades", "quiz", $quizid, "userid", $userid)) { |
b0be6c79 |
1486 | return ""; |
3a506ca2 |
1487 | } |
1488 | |
2383cadb |
1489 | return (round($grade->grade,0)); |
3a506ca2 |
1490 | } |
1491 | |
e331eb06 |
1492 | function quiz_save_best_grade($quiz, $userid) { |
a5e1f35c |
1493 | /// Calculates the best grade out of all attempts at a quiz for a user, |
1494 | /// and then saves that grade in the quiz_grades table. |
1495 | |
e331eb06 |
1496 | if (!$attempts = quiz_get_user_attempts($quiz->id, $userid)) { |
e909c8d0 |
1497 | notify("Could not find any user attempts"); |
a5e1f35c |
1498 | return false; |
1499 | } |
1500 | |
1501 | $bestgrade = quiz_calculate_best_grade($quiz, $attempts); |
1502 | $bestgrade = (($bestgrade / $quiz->sumgrades) * $quiz->grade); |
1503 | |
ebc3bd2b |
1504 | if ($grade = get_record("quiz_grades", "quiz", $quiz->id, "userid", $userid)) { |
1d2603b1 |
1505 | $grade->grade = round($bestgrade, 2); |
a5e1f35c |
1506 | $grade->timemodified = time(); |
1507 | if (!update_record("quiz_grades", $grade)) { |
e909c8d0 |
1508 | notify("Could not update best grade"); |
a5e1f35c |
1509 | return false; |
1510 | } |
1511 | } else { |
1512 | $grade->quiz = $quiz->id; |
ebc3bd2b |
1513 | $grade->userid = $userid; |
38f03e5a |
1514 | $grade->grade = round($bestgrade, 2); |
a5e1f35c |
1515 | $grade->timemodified = time(); |
1516 | if (!insert_record("quiz_grades", $grade)) { |
e909c8d0 |
1517 | notify("Could not insert new best grade"); |
a5e1f35c |
1518 | return false; |
1519 | } |
1520 | } |
1521 | return true; |
1522 | } |
1523 | |
1524 | |
3a506ca2 |
1525 | function quiz_calculate_best_grade($quiz, $attempts) { |
a5e1f35c |
1526 | /// Calculate the best grade for a quiz given a number of attempts by a particular user. |
3a506ca2 |
1527 | |
1528 | switch ($quiz->grademethod) { |
a5e1f35c |
1529 | |
1530 | case ATTEMPTFIRST: |
3a506ca2 |
1531 | foreach ($attempts as $attempt) { |
a5e1f35c |
1532 | return $attempt->sumgrades; |
3a506ca2 |
1533 | } |
a5e1f35c |
1534 | break; |
1535 | |
1536 | case ATTEMPTLAST: |
1537 | foreach ($attempts as $attempt) { |
1538 | $final = $attempt->sumgrades; |
1539 | } |
1540 | return $final; |
3a506ca2 |
1541 | |
a5e1f35c |
1542 | case GRADEAVERAGE: |
3a506ca2 |
1543 | $sum = 0; |
1544 | $count = 0; |
1545 | foreach ($attempts as $attempt) { |
a5e1f35c |
1546 | $sum += $attempt->sumgrades; |
3a506ca2 |
1547 | $count++; |
1548 | } |
1549 | return (float)$sum/$count; |
1550 | |
3a506ca2 |
1551 | default: |
a5e1f35c |
1552 | case GRADEHIGHEST: |
1553 | $max = 0; |
3a506ca2 |
1554 | foreach ($attempts as $attempt) { |
a5e1f35c |
1555 | if ($attempt->sumgrades > $max) { |
1556 | $max = $attempt->sumgrades; |
1557 | } |
3a506ca2 |
1558 | } |
a5e1f35c |
1559 | return $max; |
1560 | } |
1561 | } |
1562 | |
34d52ad7 |
1563 | |
40b1a221 |
1564 | function quiz_calculate_best_attempt($quiz, $attempts) { |
1565 | /// Return the attempt with the best grade for a quiz |
1566 | |
1567 | switch ($quiz->grademethod) { |
1568 | |
1569 | case ATTEMPTFIRST: |
1570 | foreach ($attempts as $attempt) { |
1571 | return $attempt; |
1572 | } |
1573 | break; |
1574 | |
1575 | case GRADEAVERAGE: // need to do something with it :-) |
1576 | case ATTEMPTLAST: |
1577 | foreach ($attempts as $attempt) { |
1578 | $final = $attempt; |
1579 | } |
1580 | return $final; |
1581 | |
1582 | default: |
1583 | case GRADEHIGHEST: |
1584 | $max = -1; |
1585 | foreach ($attempts as $attempt) { |
1586 | if ($attempt->sumgrades > $max) { |
1587 | $max = $attempt->sumgrades; |
1588 | $maxattempt = $attempt; |
1589 | } |
1590 | } |
1591 | return $maxattempt; |
1592 | } |
1593 | } |
1594 | |
1595 | |
a5e1f35c |
1596 | function quiz_save_attempt($quiz, $questions, $result, $attemptnum) { |
1597 | /// Given a quiz, a list of attempted questions and a total grade |
1598 | /// this function saves EVERYTHING so it can be reconstructed later |
1599 | /// if necessary. |
1600 | |
1601 | global $USER; |
1602 | |
958aafe2 |
1603 | // First find the attempt in the database (start of attempt) |
1604 | |
1605 | if (!$attempt = quiz_get_user_attempt_unfinished($quiz->id, $USER->id)) { |
1606 | notify("Trying to save an attempt that was not started!"); |
1607 | return false; |
1608 | } |
1609 | |
1610 | if ($attempt->attempt != $attemptnum) { // Double check. |
1611 | notify("Number of this attempt is different to the unfinished one!"); |
1612 | return false; |
1613 | } |
1614 | |
1615 | // Now let's complete this record and save it |
a5e1f35c |
1616 | |
a5e1f35c |
1617 | $attempt->sumgrades = $result->sumgrades; |
958aafe2 |
1618 | $attempt->timefinish = time(); |
a5e1f35c |
1619 | $attempt->timemodified = time(); |
1620 | |
958aafe2 |
1621 | if (! update_record("quiz_attempts", $attempt)) { |
7520988b |
1622 | notify("Error while saving attempt"); |
a5e1f35c |
1623 | return false; |
1624 | } |
1625 | |
1626 | // Now let's save all the questions for this attempt |
1627 | |
1628 | foreach ($questions as $question) { |
1629 | $response->attempt = $attempt->id; |
1630 | $response->question = $question->id; |
1631 | $response->grade = $result->grades[$question->id]; |
34d52ad7 |
1632 | |
77cff589 |
1633 | if (!empty($question->random)) { |
34d52ad7 |
1634 | // First save the response of the random question |
1635 | // the answer is the id of the REAL response |
1636 | $response->answer = $question->random; |
1637 | if (!insert_record("quiz_responses", $response)) { |
1638 | notify("Error while saving response"); |
1639 | return false; |
1640 | } |
1641 | $response->question = $question->random; |
1642 | } |
1643 | |
54d0590b |
1644 | if (!empty($question->answer)) { |
a5e1f35c |
1645 | $response->answer = implode(",",$question->answer); |
1646 | } else { |
1647 | $response->answer = ""; |
1648 | } |
1649 | if (!insert_record("quiz_responses", $response)) { |
7520988b |
1650 | notify("Error while saving response"); |
a5e1f35c |
1651 | return false; |
1652 | } |
3a506ca2 |
1653 | } |
fc4a5a2f |
1654 | return $attempt; |
3a506ca2 |
1655 | } |
730fd187 |
1656 | |
7c9c2a8d |
1657 | function quiz_grade_attempt_question_result($question, |
1658 | $answers, |
1659 | $gradecanbenegative= false) |
1660 | { |
44fc346f |
1661 | $grade = 0; // default |
1662 | $correct = array(); |
1663 | $feedback = array(); |
1664 | $response = array(); |
1665 | |
1666 | switch ($question->qtype) { |
1667 | case SHORTANSWER: |
1668 | if ($question->answer) { |
1669 | $question->answer = trim(stripslashes($question->answer[0])); |
1670 | } else { |
1671 | $question->answer = ""; |
1672 | } |
1673 | $response[0] = $question->answer; |
5276dc48 |
1674 | $feedback[0] = ''; // Default |
44fc346f |
1675 | foreach ($answers as $answer) { // There might be multiple right answers |
d7512435 |
1676 | |
1677 | $answer->answer = trim($answer->answer); // Just in case |
1678 | |
5276dc48 |
1679 | if ($answer->fraction >= 1.0) { |
1680 | $correct[] = $answer->answer; |
44fc346f |
1681 | } |
1682 | if (!$answer->usecase) { // Don't compare case |
1683 | $answer->answer = strtolower($answer->answer); |
1684 | $question->answer = strtolower($question->answer); |
1685 | } |
0ce4aa1a |
1686 | |
5276dc48 |
1687 | $potentialgrade = (float)$answer->fraction * $question->grade; |
1688 | |
8c0f4966 |
1689 | if ($potentialgrade >= $grade and (strpos(' '.$answer->answer, '*'))) { |
1690 | $answer->answer = str_replace('\*','@@@@@@',$answer->answer); |
1691 | $answer->answer = str_replace('*','.*',$answer->answer); |
1692 | $answer->answer = str_replace('@@@@@@', '\*',$answer->answer); |
a3848092 |
1693 | $answer->answer = str_replace('+', '\+',$answer->answer); |
0ce4aa1a |
1694 | if (eregi('^'.$answer->answer.'$', $question->answer)) { |
1695 | $feedback[0] = $answer->feedback; |
5276dc48 |
1696 | $grade = $potentialgrade; |
0ce4aa1a |
1697 | } |
5276dc48 |
1698 | |
1699 | } else if ($answer->answer == $question->answer) { |
1700 | $feedback[0] = $answer->feedback; |
1701 | $grade = $potentialgrade; |
44fc346f |
1702 | } |
1703 | } |
d396ba79 |
1704 | |
44fc346f |
1705 | break; |
1706 | |
1707 | case NUMERICAL: |
1708 | if ($question->answer) { |
1709 | $question->answer = trim(stripslashes($question->answer[0])); |
1710 | } else { |
1711 | $question->answer = ""; |
1712 | } |
1713 | $response[0] = $question->answer; |
1714 | $bestshortanswer = 0; |
1715 | foreach ($answers as $answer) { // There might be multiple right answers |
1716 | if ($answer->fraction > $bestshortanswer) { |
1717 | $correct[$answer->id] = $answer->answer; |
1718 | $bestshortanswer = $answer->fraction; |
16a19172 |
1719 | $feedback[0] = $answer->feedback; // Show feedback for best answer |
44fc346f |
1720 | } |
bdb63d64 |
1721 | if ('' != $question->answer // Must not be mixed up with zero! |
1722 | && (float)$answer->fraction > (float)$grade // Do we need to bother? |
1723 | and // and has lower procedence than && and ||. |
1724 | strtolower($question->answer) == strtolower($answer->answer) |
1725 | || '' != trim($answer->min) |
1726 | && ((float)$question->answer >= (float)$answer->min) |
1727 | && ((float)$question->answer <= (float)$answer->max)) |
1728 | { |
16a19172 |
1729 | //$feedback[0] = $answer->feedback; No feedback was shown for wrong answers |
bdb63d64 |
1730 | $grade = (float)$answer->fraction; |
44fc346f |
1731 | } |
1732 | } |
bdb63d64 |
1733 | $grade *= $question->grade; // Normalize to correct weight |
44fc346f |
1734 | break; |
1735 | |
1736 | case TRUEFALSE: |
1737 | if ($question->answer) { |
1738 | $question->answer = $question->answer[0]; |
1739 | } else { |
1740 | $question->answer = NULL; |
1741 | } |
1742 | foreach($answers as $answer) { // There should be two answers (true and false) |
1743 | $feedback[$answer->id] = $answer->feedback; |
1744 | if ($answer->fraction > 0) { |
1745 | $correct[$answer->id] = true; |
1746 | } |
1747 | if ($question->answer == $answer->id) { |
1748 | $grade = (float)$answer->fraction * $question->grade; |
1749 | $response[$answer->id] = true; |
1750 | } |
1751 | } |
1752 | break; |
1753 | |
1754 | |
1755 | case MULTICHOICE: |
1756 | foreach($answers as $answer) { // There will be multiple answers, perhaps more than one is right |
1757 | $feedback[$answer->id] = $answer->feedback; |
1758 | if ($answer->fraction > 0) { |
1759 | $correct[$answer->id] = true; |
1760 | } |
1761 | if (!empty($question->answer)) { |
1762 | foreach ($question->answer as $questionanswer) { |
1763 | if ($questionanswer == $answer->id) { |
1764 | $response[$answer->id] = true; |
1765 | if ($answer->single) { |
1766 | $grade = (float)$answer->fraction * $question->grade; |
1767 | continue; |
1768 | } else { |
1769 | $grade += (float)$answer->fraction * $question->grade; |
1770 | } |
1771 | } |
1772 | } |
1773 | } |
1774 | } |
1775 | break; |
1776 | |
1777 | case MATCH: |
1778 | $matchcount = $totalcount = 0; |
1779 | |
1780 | foreach ($question->answer as $questionanswer) { // Each answer is "subquestionid-answerid" |
1781 | $totalcount++; |
1782 | $qarr = explode('-', $questionanswer); // Extract subquestion/answer. |
1783 | $subquestionid = $qarr[0]; |
1784 | $subanswerid = $qarr[1]; |
1785 | if ($subquestionid and $subanswerid and (($subquestionid == $subanswerid) or |
1786 | ($answers[$subquestionid]->answertext == $answers[$subanswerid]->answertext))) { |
1787 | // Either the ids match exactly, or the answertexts match exactly |
1788 | // (in case two subquestions had the same answer) |
1789 | $matchcount++; |
1790 | $correct[$subquestionid] = true; |
1791 | } else { |
1792 | $correct[$subquestionid] = false; |
1793 | } |
1794 | $response[$subquestionid] = $subanswerid; |
1795 | } |
1796 | |
1797 | $grade = $question->grade * $matchcount / $totalcount; |
1798 | |
1799 | break; |
1800 | |
1801 | case RANDOMSAMATCH: |
1802 | $bestanswer = array(); |
1803 | foreach ($answers as $answer) { // Loop through them all looking for correct answers |
1804 | if (empty($bestanswer[$answer->question])) { |
1805 | $bestanswer[$answer->question] = 0; |
1806 | $correct[$answer->question] = ""; |
1807 | } |
1808 | if ($answer->fraction > $bestanswer[$answer->question]) { |
1809 | $bestanswer[$answer->question] = $answer->fraction; |
1810 | $correct[$answer->question] = $answer->answer; |
1811 | } |
1812 | } |
1813 | $answerfraction = 1.0 / (float) count($question->answer); |
1814 | foreach ($question->answer as $questionanswer) { // For each random answered question |
1815 | $rqarr = explode('-', $questionanswer); // Extract question/answer. |
1816 | $rquestion = $rqarr[0]; |
1817 | $ranswer = $rqarr[1]; |
1818 | $response[$rquestion] = $questionanswer; |
1819 | if (isset($answers[$ranswer])) { // If the answer exists in the list |
1820 | $answer = $answers[$ranswer]; |
1821 | $feedback[$rquestion] = $answer->feedback; |
1822 | if ($answer->question == $rquestion) { // Check that this answer matches the question |
1823 | $grade += (float)$answer->fraction * $question->grade * $answerfraction; |
1824 | } |
1825 | } |
1826 | } |
1827 | break; |
1828 | |
8b439f8c |
1829 | case MULTIANSWER: |
1830 | // Default setting that avoids a possible divide by zero: |
1831 | $subquestion->grade = 1.0; |
1832 | |
1833 | foreach ($question->answer as $questionanswer) { |
1834 | |
1835 | // Resetting default values for subresult: |
1836 | $subresult->grade = 0.0; |
1837 | $subresult->correct = array(); |
1838 | $subresult->feedback = array(); |
1839 | |
1840 | // Resetting subquestion responses: |
1841 | $subquestion->answer = array(); |
1842 | |
1843 | $qarr = explode('-', $questionanswer, 2); |
1844 | $subquestion->answer[] = $qarr[1]; // Always single answer for subquestions |
1845 | foreach ($answers as $multianswer) { |
1846 | if ($multianswer->id == $qarr[0]) { |
1847 | $subquestion->qtype = $multianswer->answertype; |
1848 | $subquestion->grade = $multianswer->norm; |
d396ba79 |
1849 | $subresult = quiz_grade_attempt_question_result($subquestion, $multianswer->subanswers, true); |
8b439f8c |
1850 | break; |
1851 | } |
1852 | } |
1853 | |
d396ba79 |
1854 | |
8b439f8c |
1855 | // Summarize subquestion results: |
1856 | $grade += $subresult->grade; |
1857 | $feedback[] = $subresult->feedback[0]; |
1858 | $correct[] = $subresult->correct[0]; |
1859 | |
1860 | // Each response instance also contains the partial |
1861 | // fraction grade for the response: |
1862 | $response[] = $subresult->grade/$subquestion->grade |
1863 | . '-' . $subquestion->answer[0]; |
1864 | } |
1865 | // Normalize grade: |
1866 | $grade *= $question->grade/($question->defaultgrade); |
1867 | break; |
1868 | |
44fc346f |
1869 | case DESCRIPTION: // Descriptions are not graded. |
1870 | break; |
1871 | |
1872 | case RANDOM: // Returns a recursive call with the real question |
1873 | $realquestion = get_record |
1874 | ('quiz_questions', 'id', $question->random); |
1875 | $realquestion->answer = $question->answer; |
1876 | $realquestion->grade = $question->grade; |
1877 | return quiz_grade_attempt_question_result($realquestion, $answers); |
1878 | } |
1879 | |
7c9c2a8d |
1880 | $result->grade = |
1881 | $gradecanbenegative ? $grade // Grade can be negative |
1882 | : max(0.0, $grade); // Grade must not be negative |
44fc346f |
1883 | $result->correct = $correct; |
1884 | $result->feedback = $feedback; |
1885 | $result->response = $response; |
1886 | return $result; |
1887 | } |
a5e1f35c |
1888 | |
1889 | function quiz_grade_attempt_results($quiz, $questions) { |
1890 | /// Given a list of questions (including answers for each one) |
1891 | /// this function does all the hard work of calculating the |
1892 | /// grades for each question, as well as a total grade for |
1893 | /// for the whole quiz. It returns everything in a structure |
1894 | /// that looks like: |
1895 | /// $result->sumgrades (sum of all grades for all questions) |
1896 | /// $result->percentage (Percentage of grades that were correct) |
1897 | /// $result->grade (final grade result for the whole quiz) |
1898 | /// $result->grades[] (array of grades, indexed by question id) |
a8a372cc |
1899 | /// $result->response[] (array of response arrays, indexed by question id) |
a5e1f35c |
1900 | /// $result->feedback[] (array of feedback arrays, indexed by question id) |
8db3eadd |
1901 | /// $result->correct[] (array of feedback arrays, indexed by question id) |
a5e1f35c |
1902 | |
1903 | if (!$questions) { |
1904 | error("No questions!"); |
1905 | } |
34d52ad7 |
1906 | |
1907 | if (!$grades = get_records_menu("quiz_question_grades", "quiz", $quiz->id, "", "question,grade")) { |
1908 | error("No grades defined for these quiz questions!"); |
1909 | } |
1910 | |
a5e1f35c |
1911 | $result->sumgrades = 0; |
1912 | |
1913 | foreach ($questions as $question) { |
34d52ad7 |
1914 | |
44fc346f |
1915 | $question->grade = $grades[$question->id]; |
34d52ad7 |
1916 | |
44fc346f |
1917 | if (!$answers = quiz_get_answers($question)) { |
1918 | error("No answers defined for question id $question->id!"); |
a5e1f35c |
1919 | } |
34d52ad7 |
1920 | |
44fc346f |
1921 | $questionresult = quiz_grade_attempt_question_result($question, |
1922 | $answers); |
10b9291c |
1923 | |
44fc346f |
1924 | $result->grades[$question->id] = round($questionresult->grade, 2); |
1925 | $result->sumgrades += $questionresult->grade; |
1926 | $result->feedback[$question->id] = $questionresult->feedback; |
1927 | $result->response[$question->id] = $questionresult->response; |
1928 | $result->correct[$question->id] = $questionresult->correct; |
a5e1f35c |
1929 | } |
1930 | |
8d94f5a0 |
1931 | $fraction = (float)($result->sumgrades / $quiz->sumgrades); |
1932 | $result->percentage = format_float($fraction * 100.0); |
1933 | $result->grade = format_float($fraction * $quiz->grade); |
3a50203f |
1934 | $result->sumgrades = round($result->sumgrades, 2); |
a5e1f35c |
1935 | |
1936 | return $result; |
1937 | } |
6d86b5dc |
1938 | |
1939 | |
49220fa7 |
1940 | function quiz_save_question_options($question) { |
1941 | /// Given some question info and some data about the the answers |
1942 | /// this function parses, organises and saves the question |
1943 | /// It is used by question.php when saving new data from a |
1944 | /// form, and also by import.php when importing questions |
77cff589 |
1945 | /// |
1946 | /// If this is an update, and old answers already exist, then |
1947 | /// these are overwritten using an update(). To do this, it |
1948 | /// it is assumed that the IDs in quiz_answers are in the same |
1949 | /// sort order as the new answers being saved. This should always |
1950 | /// be true, but it's something to keep in mind if fiddling with |
1951 | /// question.php |
49220fa7 |
1952 | /// |
1953 | /// Returns $result->error or $result->notice |
a5e1f35c |
1954 | |
49220fa7 |
1955 | switch ($question->qtype) { |
1956 | case SHORTANSWER: |
77cff589 |
1957 | |
e1122620 |
1958 | if (!$oldanswers = get_records("quiz_answers", "question", $question->id, "id ASC")) { |
1959 | $oldanswers = array(); |
1960 | } |
49220fa7 |
1961 | |
1962 | $answers = array(); |
1963 | $maxfraction = -1; |
1964 | |
1965 | // Insert all the new answers |
1966 | foreach ($question->answer as $key => $dataanswer) { |
1967 | if ($dataanswer != "") { |
77cff589 |
1968 | if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it |
1969 | $answer = $oldanswer; |
d7512435 |
1970 | $answer->answer = trim($dataanswer); |
77cff589 |
1971 | $answer->fraction = $question->fraction[$key]; |
1972 | $answer->feedback = $question->feedback[$key]; |
1973 | if (!update_record("quiz_answers", $answer)) { |
1974 | $result->error = "Could not update quiz answer! (id=$answer->id)"; |
1975 | return $result; |
1976 | } |
1977 | } else { // This is a completely new answer |
1978 | unset($answer); |
d7512435 |
1979 | $answer->answer = trim($dataanswer); |
77cff589 |
1980 | $answer->question = $question->id; |
1981 | $answer->fraction = $question->fraction[$key]; |
1982 | $answer->feedback = $question->feedback[$key]; |
1983 | if (!$answer->id = insert_record("quiz_answers", $answer)) { |
1984 | $result->error = "Could not insert quiz answer!"; |
1985 | return $result; |
1986 | } |
49220fa7 |
1987 | } |
1988 | $answers[] = $answer->id; |
1989 | if ($question->fraction[$key] > $maxfraction) { |
1990 | $maxfraction = $question->fraction[$key]; |
1991 | } |
1992 | } |
1993 | } |
1994 | |
77cff589 |
1995 | if ($options = get_record("quiz_shortanswer", "question", $question->id)) { |
1996 | $options->answers = implode(",",$answers); |
1997 | $options->usecase = $question->usecase; |
1998 | if (!update_record("quiz_shortanswer", $options)) { |
1999 | $result->error = "Could not update quiz shortanswer options! (id=$options->id)"; |
2000 | return $result; |
2001 | } |
2002 | } else { |
2003 | unset($options); |
2004 | $options->question = $question->id; |
2005 | $options->answers = implode(",",$answers); |
2006 | $options->usecase = $question->usecase; |
2007 | if (!insert_record("quiz_shortanswer", $options)) { |
2008 | $result->error = "Could not insert quiz shortanswer options!"; |
2009 | return $result; |
2010 | } |
49220fa7 |
2011 | } |
2012 | |
2013 | /// Perform sanity checks on fractional grades |
2014 | if ($maxfraction != 1) { |
2015 | $maxfraction = $maxfraction * 100; |
2016 | $result->notice = get_string("fractionsnomax", "quiz", $maxfraction); |
2017 | return $result; |
2018 | } |
2019 | break; |
77cff589 |
2020 | |
361f649d |
2021 | case NUMERICAL: // Note similarities to SHORTANSWER |
2022 | |
2023 | if (!$oldanswers = get_records("quiz_answers", "question", $question->id, "id ASC")) { |
2024 | $oldanswers = array(); |
2025 | } |
2026 | |
2027 | $answers = array(); |
2028 | $maxfraction = -1; |
2029 | |
2030 | // Insert all the new answers |
2031 | foreach ($question->answer as $key => $dataanswer) { |
2032 | if ($dataanswer != "") { |
2033 | if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it |
2034 | $answer = $oldanswer; |
2035 | $answer->answer = $dataanswer; |
2036 | $answer->fraction = $question->fraction[$key]; |
2037 | $answer->feedback = $question->feedback[$key]; |
2038 | if (!update_record("quiz_answers", $answer)) { |
2039 | $result->error = "Could not update quiz answer! (id=$answer->id)"; |
2040 | return $result; |
2041 | } |
2042 | } else { // This is a completely new answer |
2043 | unset($answer); |
2044 | $answer->answer = $dataanswer; |
2045 | $answer->question = $question->id; |
2046 | $answer->fraction = $question->fraction[$key]; |
2047 | $answer->feedback = $question->feedback[$key]; |
2048 | if (!$answer->id = insert_record("quiz_answers", $answer)) { |
2049 | $result->error = "Could not insert quiz answer!"; |
2050 | return $result; |
2051 | } |
2052 | } |
2053 | $answers[] = $answer->id; |
2054 | if ($question->fraction[$key] > $maxfraction) { |
2055 | $maxfraction = $question->fraction[$key]; |
2056 | } |
2057 | |
2058 | if ($options = get_record("quiz_numerical", "answer", $answer->id)) { |
2059 | $options->min= $question->min[$key]; |
2060 | $options->max= $question->max[$key]; |
2061 | if (!update_record("quiz_numerical", $options)) { |
2062 | $result->error = "Could not update quiz numerical options! (id=$options->id)"; |
2063 | return $result; |
2064 | } |
2065 | } else { // completely new answer |
2066 | unset($options); |
9c026610 |
2067 | $options->question = $question->id; |
2068 | $options->answer = $answer->id; |
2069 | $options->min = $question->min[$key]; |
2070 | $options->max = $question->max[$key]; |
361f649d |
2071 | if (!insert_record("quiz_numerical", $options)) { |
2072 | $result->error = "Could not insert quiz numerical options!"; |
2073 | return $result; |
2074 | } |
2075 | } |
2076 | } |
2077 | } |
2078 | |
2079 | /// Perform sanity checks on fractional grades |
2080 | if ($maxfraction != 1) { |
2081 | $maxfraction = $maxfraction * 100; |
2082 | $result->notice = get_string("fractionsnomax", "quiz", $maxfraction); |
2083 | return $result; |
2084 | } |
2085 | break; |
77cff589 |
2086 | |
2087 | |
49220fa7 |
2088 | case TRUEFALSE: |
77cff589 |
2089 | |
e1122620 |
2090 | if (!$oldanswers = get_records("quiz_answers", "question", $question->id, "id ASC")) { |
2091 | $oldanswers = array(); |
2092 | } |
77cff589 |
2093 | |
2094 | if ($true = array_shift($oldanswers)) { // Existing answer, so reuse it |
4d01ada3 |
2095 | $true->answer = get_string("true", "quiz"); |
77cff589 |
2096 | $true->fraction = $question->answer; |
2097 | $true->feedback = $question->feedbacktrue; |
2098 | if (!update_record("quiz_answers", $true)) { |
2099 | $result->error = "Could not update quiz answer \"true\")!"; |
2100 | return $result; |
2101 | } |
2102 | } else { |
2103 | unset($true); |
4d01ada3 |
2104 | $true->answer = get_string("true", "quiz"); |
77cff589 |
2105 | $true->question = $question->id; |
2106 | $true->fraction = $question->answer; |
2107 | $true->feedback = $question->feedbacktrue; |
2108 | if (!$true->id = insert_record("quiz_answers", $true)) { |
2109 | $result->error = "Could not insert quiz answer \"true\")!"; |
2110 | return $result; |
2111 | } |
49220fa7 |
2112 | } |
2113 | |
77cff589 |
2114 | if ($false = array_shift($oldanswers)) { // Existing answer, so reuse it |
4d01ada3 |
2115 | $false->answer = get_string("false", "quiz"); |
77cff589 |
2116 | $false->fraction = 1 - (int)$question->answer; |
2117 | $false->feedback = $question->feedbackfalse; |
2118 | if (!update_record("quiz_answers", $false)) { |
2119 | $result->error = "Could not insert quiz answer \"false\")!"; |
2120 | return $result; |
2121 | } |
2122 | } else { |
2123 | unset($false); |
4d01ada3 |
2124 | $false->answer = get_string("false", "quiz"); |
77cff589 |
2125 | $false->question = $question->id; |
2126 | $false->fraction = 1 - (int)$question->answer; |
2127 | $false->feedback = $question->feedbackfalse; |
2128 | if (!$false->id = insert_record("quiz_answers", $false)) { |
2129 | $result->error = "Could not insert quiz answer \"false\")!"; |
2130 | return $result; |
2131 | } |
49220fa7 |
2132 | } |
2133 | |
77cff589 |
2134 | if ($options = get_record("quiz_truefalse", "question", $question->id)) { |
2135 | // No need to do anything, since the answer IDs won't have changed |
2136 | // But we'll do it anyway, just for robustness |
2137 | $options->trueanswer = $true->id; |
2138 | $options->falseanswer = $false->id; |
2139 | if (!update_record("quiz_truefalse", $options)) { |
2140 | $result->error = "Could not update quiz truefalse options! (id=$options->id)"; |
2141 | return $result; |
2142 | } |
2143 | } else { |
2144 | unset($options); |
2145 | $options->question = $question->id; |
2146 | $options->trueanswer = $true->id; |
2147 | $options->falseanswer = $false->id; |
2148 | if (!insert_record("quiz_truefalse", $options)) { |
2149 | $result->error = "Could not insert quiz truefalse options!"; |
2150 | return $result; |
2151 | } |
49220fa7 |
2152 | } |
2153 | break; |
77cff589 |
2154 | |
2155 | |
49220fa7 |
2156 | case MULTICHOICE: |
77cff589 |
2157 | |
e1122620 |
2158 | if (!$oldanswers = get_records("quiz_answers", "question", $question->id, "id ASC")) { |
2159 | $oldanswers = array(); |
2160 | } |
49220fa7 |
2161 | |
2162 | $totalfraction = 0; |
2163 | $maxfraction = -1; |
2164 | |
2165 | $answers = array(); |
2166 | |
2167 | // Insert all the new answers |
2168 | foreach ($question->answer as $key => $dataanswer) { |
2169 | if ($dataanswer != "") { |
77cff589 |
2170 | if ($answer = array_shift($oldanswers)) { // Existing answer, so reuse it |
2171 | $answer->answer = $dataanswer; |
2172 | $answer->fraction = $question->fraction[$key]; |
2173 | $answer->feedback = $question->feedback[$key]; |
2174 | if (!update_record("quiz_answers", $answer)) { |
2175 | $result->error = "Could not update quiz answer! (id=$answer->id)"; |
2176 | return $result; |
2177 | } |
2178 | } else { |
2179 | unset($answer); |
2180 | $answer->answer = $dataanswer; |
2181 | $answer->question = $question->id; |
2182 | $answer->fraction = $question->fraction[$key]; |
2183 | $answer->feedback = $question->feedback[$key]; |
2184 | if (!$answer->id = insert_record("quiz_answers", $answer)) { |
2185 | $result->error = "Could not insert quiz answer! "; |
2186 | return $result; |
2187 | } |
49220fa7 |
2188 | } |
2189 | $answers[] = $answer->id; |
2190 | |
2191 | if ($question->fraction[$key] > 0) { // Sanity checks |
2192 | $totalfraction += $question->fraction[$key]; |
2193 | } |
2194 | if ($question->fraction[$key] > $maxfraction) { |
2195 | $maxfraction = $question->fraction[$key]; |
2196 | } |
2197 | } |
2198 | } |
2199 | |
77cff589 |
2200 | if ($options = get_record("quiz_multichoice", "question", $question->id)) { |
2201 | $options->answers = implode(",",$answers); |
2202 | $options->single = $question->single; |
2203 | if (!update_record("quiz_multichoice", $options)) { |
2204 | $result->error = "Could not update quiz multichoice options! (id=$options->id)"; |
2205 | return $result; |
2206 | } |
2207 | } else { |
2208 | unset($options); |
2209 | $options->question = $question->id; |
2210 | $options->answers = implode(",",$answers); |
2211 | $options->single = $question->single; |
2212 | if (!insert_record("quiz_multichoice", $options)) { |
2213 | $result->error = "Could not insert quiz multichoice options!"; |
2214 | return $result; |
2215 | } |
49220fa7 |
2216 | } |
2217 | |
2218 | /// Perform sanity checks on fractional grades |
2219 | if ($options->single) { |
2220 | if ($maxfraction != 1) { |
2221 | $maxfraction = $maxfraction * 100; |
2222 | $result->notice = get_string("fractionsnomax", "quiz", $maxfraction); |
2223 | return $result; |
2224 | } |
2225 | } else { |
2226 | $totalfraction = round($totalfraction,2); |
2227 | if ($totalfraction != 1) { |
2228 | $totalfraction = $totalfraction * 100; |
2229 | $result->notice = get_string("fractionsaddwrong", "quiz", $totalfraction); |
2230 | return $result; |
2231 | } |
2232 | } |
2233 | break; |
95dbc030 |
2234 | |
54a67a59 |
2235 | case MATCH: |
77cff589 |
2236 | |
e1122620 |
2237 | if (!$oldsubquestions = get_records("quiz_match_sub", "question", $question->id, "id ASC")) { |
2238 | $oldsubquestions = array(); |
2239 | } |
54a67a59 |
2240 | |
2241 | $subquestions = array(); |
2242 | |
2243 | // Insert all the new question+answer pairs |
2244 | foreach ($question->subquestions as $key => $questiontext) { |
2245 | $answertext = $question->subanswers[$key]; |
2246 | if (!empty($questiontext) and !empty($answertext)) { |
77cff589 |
2247 | if ($subquestion = array_shift($oldsubquestions)) { // Existing answer, so reuse it |
2248 | $subquestion->questiontext = $questiontext; |
2249 | $subquestion->answertext = $answertext; |
2250 | if (!update_record("quiz_match_sub", $subquestion)) { |
2251 | $result->error = "Could not insert quiz match subquestion! (id=$subquestion->id)"; |
2252 | return $result; |
2253 | } |
2254 | } else { |
2255 | unset($subquestion); |
2256 | $subquestion->question = $question->id; |
2257 | $subquestion->questiontext = $questiontext; |
2258 | $subquestion->answertext = $answertext; |
2259 | if (!$subquestion->id = insert_record("quiz_match_sub", $subquestion)) { |
2260 | $result->error = "Could not insert quiz match subquestion!"; |
2261 | return $result; |
2262 | } |
54a67a59 |
2263 | } |
2264 | $subquestions[] = $subquestion->id; |
2265 | } |
2266 | } |
2267 | |
2268 | if (count($subquestions) < 3) { |
2269 | $result->notice = get_string("notenoughsubquestions", "quiz"); |
2270 | return $result; |
2271 | } |
2272 | |
77cff589 |
2273 | if ($options = get_record("quiz_match", "question", $question->id)) { |
2274 | $options->subquestions = implode(",",$subquestions); |
2275 | if (!update_record("quiz_match", $options)) { |
2276 | $result->error = "Could not update quiz match options! (id=$options->id)"; |
2277 | return $result; |
2278 | } |
2279 | } else { |
2280 | unset($options); |
2281 | $options->question = $question->id; |
2282 | $options->subquestions = implode(",",$subquestions); |
2283 | if (!insert_record("quiz_match", $options)) { |
2284 | $result->error = "Could not insert quiz match options!"; |
2285 | return $result; |
2286 | } |
54a67a59 |
2287 | } |
2288 | |
2289 | break; |
2290 | |
77cff589 |
2291 | |
54a67a59 |
2292 | case RANDOMSAMATCH: |
95dbc030 |
2293 | $options->question = $question->id; |
2294 | $options->choose = $question->choose; |
54a67a59 |
2295 | if ($existing = get_record("quiz_randomsamatch", "question", $options->question)) { |
95dbc030 |
2296 | $options->id = $existing->id; |
54a67a59 |
2297 | if (!update_record("quiz_randomsamatch", $options)) { |
2298 | $result->error = "Could not update quiz randomsamatch options!"; |
95dbc030 |
2299 | return $result; |
2300 | } |
2301 | } else { |
54a67a59 |
2302 | if (!insert_record("quiz_randomsamatch", $options)) { |
2303 | $result->error = "Could not insert quiz randomsamatch options!"; |
95dbc030 |
2304 | return $result; |
2305 | } |
2306 | } |
2307 | break; |
2308 | |
8b439f8c |
2309 | case MULTIANSWER: |
2310 | if (!$oldmultianswers = get_records("quiz_multianswers", "question", $question->id, "id ASC")) { |
2311 | $oldmultianswers = array(); |
2312 | } |
2313 | |
2314 | // Insert all the new multi answers |
2315 | foreach ($question->answers as $dataanswer) { |
2316 | if ($oldmultianswer = array_shift($oldmultianswers)) { // Existing answer, so reuse it |
2317 | $multianswer = $oldmultianswer; |
2318 | $multianswer->positionkey = $dataanswer->positionkey; |
2319 | $multianswer->norm = $dataanswer->norm; |
2320 | $multianswer->answertype = $dataanswer->answertype; |
2321 | |
2322 | if (! $multianswer->answers = quiz_save_multianswer_alternatives |
2323 | ($question->id, $dataanswer->answertype, |
2324 | $dataanswer->alternatives, $oldmultianswer->answers)) |
2325 | { |
2326 | $result->error = "Could not update multianswer alternatives! (id=$multianswer->id)"; |
2327 | return $result; |
2328 | } |
2329 | if (!update_record("quiz_multianswers", $multianswer)) { |
2330 | $result->error = "Could not update quiz multianswer! (id=$multianswer->id)"; |
2331 | return $result; |
2332 | } |
2333 | } else { // This is a completely new answer |
2334 | unset($multianswer); |
2335 | $multianswer->question = $question->id; |
2336 | $multianswer->positionkey = $dataanswer->positionkey; |
2337 | $multianswer->norm = $dataanswer->norm; |
2338 | $multianswer->answertype = $dataanswer->answertype; |
2339 | |
2340 | if (! $multianswer->answers = quiz_save_multianswer_alternatives |
2341 | ($question->id, $dataanswer->answertype, |
2342 | $dataanswer->alternatives)) |
2343 | { |
2344 | $result->error = "Could not insert multianswer alternatives! (questionid=$question->id)"; |
2345 | return $result; |
2346 | } |
2347 | if (!insert_record("quiz_multianswers", $multianswer)) { |
2348 | $result->error = "Could not insert quiz multianswer!"; |
2349 | return $result; |
2350 | } |
2351 | } |
2352 | } |
2353 | break; |
2354 | |
34d52ad7 |
2355 | case RANDOM: |
2356 | break; |
2357 | |
401c8de6 |
2358 | case DESCRIPTION: |
2359 | break; |
2360 | |
49220fa7 |
2361 | default: |
2362 | $result->error = "Unsupported question type ($question->qtype)!"; |
2363 | return $result; |
2364 | break; |
2365 | } |
2366 | return true; |
2367 | } |
2368 | |
2369 | |
29d5d0b4 |
2370 | function quiz_remove_unwanted_questions(&$questions, $quiz) { |
40b1a221 |
2371 | /// Given an array of questions, and a list of question IDs, |
2372 | /// this function removes unwanted questions from the array |
586b2c82 |
2373 | /// Used by review.php and attempt.php to counter changing quizzes |
29d5d0b4 |
2374 | |
2375 | $quizquestions = array(); |
2376 | $quizids = explode(",", $quiz->questions); |
2377 | foreach ($quizids as $quizid) { |
2378 | $quizquestions[$quizid] = true; |
2379 | } |
2380 | foreach ($questions as $key => $question) { |
2381 | if (!isset($quizquestions[$question->id])) { |
2382 | unset($questions[$key]); |
2383 | } |
2384 | } |
2385 | } |
2386 | |
8b439f8c |
2387 | function quiz_save_multianswer_alternatives |
2388 | ($questionid, $answertype, $alternatives, $oldalternativeids= NULL) |
2389 | { |
2390 | // Returns false if something goes wrong, |
2391 | // otherwise the ids of the answers. |
95dbc030 |
2392 | |
8b439f8c |
2393 | if (empty($oldalternativeids) |
17546249 |
2394 | or !($oldalternatives = |
2395 | get_records_list('quiz_answers', 'id', $oldalternativeids))) |
8b439f8c |
2396 | { |
2397 | $oldalternatives = array(); |
2398 | } |
2399 | |
2400 | $alternativeids = array(); |
2401 | |
2402 | foreach ($alternatives as $altdata) { |
2403 | |
2404 | if ($altold = array_shift($oldalternatives)) { // Use existing one... |
2405 | $alt = $altold; |
2406 | $alt->answer = $altdata->answer; |
2407 | $alt->fraction = $altdata->fraction; |
2408 | $alt->feedback = $altdata->feedback; |
2409 | if (!update_record("quiz_answers", $alt)) { |
2410 | return false; |
2411 | } |
2412 | |
2413 | } else { // Completely new one |
2414 | unset($alt); |
2415 | $alt->question= $questionid; |
2416 | $alt->answer = $altdata->answer; |
2417 | $alt->fraction = $altdata->fraction; |
2418 | $alt->feedback = $altdata->feedback; |
17546249 |
2419 | if (!($alt->id = insert_record("quiz_answers", $alt))) { |
8b439f8c |
2420 | return false; |
2421 | } |
2422 | } |
2423 | |
2424 | // For the answer type numerical, each alternative has individual options: |
2425 | if ($answertype == NUMERICAL) { |
2426 | if ($numericaloptions = |
2427 | get_record('quiz_numerical', 'answer', $alt->id)) |
2428 | { |
2429 | // Reuse existing numerical options |
2430 | $numericaloptions->min = $altdata->min; |
2431 | $numericaloptions->max = $altdata->max; |
2432 | if (!update_record('quiz_numerical', $numericaloptions)) { |
2433 | return false; |
2434 | } |
2435 | } else { |
2436 | // New numerical options |
2437 | $numericaloptions->answer = $alt->id; |
2438 | $numericaloptions->question = $questionid; |
2439 | $numericaloptions->min = $altdata->min; |
2440 | $numericaloptions->max = $altdata->max; |
2441 | if (!insert_record("quiz_numerical", $numericaloptions)) { |
2442 | return false; |
2443 | } |
2444 | } |
2445 | } else { // Delete obsolete numerical options |
2446 | delete_records('quiz_numerical', 'answer', $alt->id); |
2447 | } // end if NUMERICAL |
2448 | |
2449 | $alternativeids[] = $alt->id; |
2450 | } // end foreach $alternatives |
2451 | $answers = implode(',', $alternativeids); |
2452 | |
2453 | // Removal of obsolete alternatives from answers and quiz_numerical: |
2454 | while ($altobsolete = array_shift($oldalternatives)) { |
2455 | delete_records("quiz_answers", "id", $altobsolete->id); |
2456 | |
2457 | // Possibly obsolute numerical options are also to be deleted: |
17546249 |
2458 | delete_records("quiz_numerical", 'answer', $altobsolete->id); |
8b439f8c |
2459 | } |
2460 | |
2461 | // Common alternative options and removal of obsolete options |
2462 | switch ($answertype) { |
2463 | case NUMERICAL: |
2464 | if (!empty($oldalternativeids)) { |
2465 | delete_records('quiz_shortanswer', 'answers', |
2466 | $oldalternativeids); |
2467 | delete_records('quiz_multichoice', 'answers', |
2468 | $oldalternativeids); |
2469 | } |
2470 | break; |
2471 | case SHORTANSWER: |
2472 | if (!empty($oldalternativeids)) { |
2473 | delete_records('quiz_multichoice', 'answers', |
2474 | $oldalternativeids); |
2475 | $options = get_record('quiz_shortanswer', |
2476 | 'answers', $oldalternativeids); |
2477 | } else { |
2478 | unset($options); |
2479 | } |
2480 | if (empty($options)) { |
2481 | // Create new shortanswer options |
2482 | $options->question = $questionid; |
2483 | $options->usecase = 0; |
2484 | $options->answers = $answers; |
2485 | if (!insert_record('quiz_shortanswer', $options)) { |
2486 | return false; |
2487 | } |
2488 | } else if ($answers != $oldalternativeids) { |
2489 | // Shortanswer options needs update: |
2490 | $options->answers = $answers; |
2491 | if (!update_record('quiz_shortanswer', $options)) { |
2492 | return false; |
2493 | } |
2494 | } |
2495 | break; |
2496 | case MULTICHOICE: |
2497 | if (!empty($oldalternativeids)) { |
2498 | delete_records('quiz_shortanswer', 'answers', |
2499 | $oldalternativeids); |
2500 | $options = get_record('quiz_multichoice', |
2501 | 'answers', $oldalternativeids); |
2502 | } else { |
2503 | unset($options); |
2504 | } |
2505 | if (empty($options)) { |
2506 | // Create new multichoice options |
2507 | $options->question = $questionid; |
2508 | $options->layout = 0; |
2509 | $options->single = 1; |
2510 | $options->answers = $answers; |
2511 | if (!insert_record('quiz_multichoice', $options)) { |
2512 | return false; |
2513 | } |
2514 | } else if ($answers != $oldalternativeids) { |
2515 | // Multichoice options needs update: |
2516 | $options->answers = $answers; |
2517 | if (!update_record('quiz_multichoice', $options)) { |
2518 | return false; |
2519 | } |
2520 | } |
2521 | break; |
2522 | default: |
2523 | return false; |
2524 | } |
2525 | return $answers; |
2526 | } |
95dbc030 |
2527 | |
740f94d8 |
2528 | function quiz_get_recent_mod_activity(&$activities, &$index, $sincetime, $courseid, $quiz="0", $user="", $groupid="") { |
fdba9bb4 |
2529 | // Returns all quizzes since a given time. If quiz is specified then |
2530 | // this restricts the results |
2531 | |
2532 | global $CFG; |
2533 | |
2534 | if ($quiz) { |
740f94d8 |
2535 | $quizselect = " AND cm.id = '$quiz'"; |
fdba9bb4 |
2536 | } else { |
2537 | $quizselect = ""; |
2538 | } |
0eb7764d |
2539 | if ($user) { |
2540 | $userselect = " AND u.id = '$user'"; |
2541 | } else { |
2542 | $userselect = ""; |
2543 | } |
fdba9bb4 |
2544 | |
740f94d8 |
2545 | $quizzes = get_records_sql("SELECT qa.*, q.name, u.firstname, u.lastname, u.picture, |
2546 | q.course, q.sumgrades as maxgrade, cm.instance, cm.section |
2547 | FROM {$CFG->prefix}quiz_attempts qa, |
2548 | {$CFG->prefix}quiz q, |
2549 | {$CFG->prefix}user u, |
2550 | {$CFG->prefix}course_modules cm |
2551 | WHERE qa.timefinish > '$sincetime' |
2552 | AND qa.userid = u.id $userselect |
2553 | AND qa.quiz = q.id $quizselect |
2554 | AND cm.instance = q.id |
2555 | AND cm.course = '$courseid' |
2556 | AND q.course = cm.course |
2557 | ORDER BY qa.timefinish ASC"); |
fdba9bb4 |
2558 | |
6a0c9cf8 |
2559 | if (empty($quizzes)) |
2560 | return; |
fdba9bb4 |
2561 | |
6a0c9cf8 |
2562 | foreach ($quizzes as $quiz) { |
2563 | if (empty($groupid) || ismember($groupid, $quiz->userid)) { |
fdba9bb4 |
2564 | |
6a0c9cf8 |
2565 | $tmpactivity->type = "quiz"; |
2566 | $tmpactivity->defaultindex = $index; |
2567 | $tmpactivity->instance = $quiz->quiz; |
fdba9bb4 |
2568 | |
6a0c9cf8 |
2569 | $tmpactivity->name = $quiz->name; |
2570 | $tmpactivity->section = $quiz->section; |
fdba9bb4 |
2571 | |
6a0c9cf8 |
2572 | $tmpactivity->content->attemptid = $quiz->id; |
2573 | $tmpactivity->content->sumgrades = $quiz->sumgrades; |
2574 | $tmpactivity->content->maxgrade = $quiz->maxgrade; |
2575 | $tmpactivity->content->attempt = $quiz->attempt; |
fdba9bb4 |
2576 | |
6a0c9cf8 |
2577 | $tmpactivity->user->userid = $quiz->userid; |
2578 | $tmpactivity->user->fullname = fullname($quiz); |
2579 | $tmpactivity->user->picture = $quiz->picture; |
fdba9bb4 |
2580 | |
6a0c9cf8 |
2581 | $tmpactivity->timestamp = $quiz->timefinish; |
fdba9bb4 |
2582 | |
6a0c9cf8 |
2583 | $activities[] = $tmpactivity; |
2584 | |
2585 | $index++; |
2586 | } |
2587 | } |
fdba9bb4 |
2588 | |
740f94d8 |
2589 | return; |
2590 | } |
fdba9bb4 |
2591 | |
6a0c9cf8 |
2592 | |
740f94d8 |
2593 | function quiz_print_recent_mod_activity($activity, $course, $detail=false) { |
2594 | global $CFG, $THEME; |
fdba9bb4 |
2595 | |
740f94d8 |
2596 | echo '<table border="0" cellpadding="3" cellspacing="0">'; |
fdba9bb4 |
2597 | |
740f94d8 |
2598 | echo "<tr><td bgcolor=\"$THEME->cellcontent2\" class=\"forumpostpicture\" width=\"35\" valign=\"top\">"; |
2599 | print_user_picture($activity->user->userid, $course, $activity->user->picture); |
2600 | echo "</td><td width=\"100%\"><font size=2>"; |
2601 | |
2602 | if ($detail) { |
2603 | echo "<img src=\"$CFG->modpixpath/$activity->type/icon.gif\" ". |
2604 | "height=16 width=16 alt=\"$activity->type\"> "; |
2605 | echo "<a href=\"$CFG->wwwroot/mod/quiz/view.php?id=" . $activity->instance . "\">" |
2606 | . $activity->name . "</a> - "; |
2607 | |
2608 | } |
2609 | |
2610 | if (isteacher($USER)) { |
2611 | $grades = "(" . $activity->content->sumgrades . " / " . $activity->content->maxgrade . ") "; |
2612 | echo "<a href=\"$CFG->wwwroot/mod/quiz/review.php?q=" |
2613 | . $activity->instance . "&attempt=" |
2614 | . $activity->content->attemptid . "\">" . $grades . "</a> "; |
2615 | |
2616 | echo get_string("attempt", "quiz") . " - " . $activity->content->attempt . "<br>"; |
fdba9bb4 |
2617 | } |
740f94d8 |
2618 | echo "<a href=\"$CFG->wwwroot/user/view.php?id=" |
2619 | . $activity->user->userid . "&course=$course\">" |
2620 | . $activity->user->fullname . "</a> "; |
2621 | |
2622 | echo " - " . userdate($activity->timestamp); |
2623 | |
2624 | echo "</font></td></tr>"; |
2625 | echo "</table>"; |
2626 | |
2627 | return; |
fdba9bb4 |
2628 | } |
eb452548 |
2629 | |
730fd187 |
2630 | ?> |