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