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