Whoops forgot to check these babies in
[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");
54a67a59 23
24$QUIZ_QUESTION_TYPE = array ( MULTICHOICE => get_string("multichoice", "quiz"),
25 TRUEFALSE => get_string("truefalse", "quiz"),
26 SHORTANSWER => get_string("shortanswer", "quiz"),
27 MATCH => get_string("match", "quiz"),
34d52ad7 28 RANDOM => get_string("random", "quiz"),
401c8de6 29 RANDOMSAMATCH => get_string("randomsamatch", "quiz"),
30 DESCRIPTION => get_string("description", "quiz") );
a5e1f35c 31
49220fa7 32$QUIZ_FILE_FORMAT = array ( "custom" => get_string("custom", "quiz"),
c6bcd06a 33 "missingword" => get_string("missingword", "quiz"),
34 "blackboard" => get_string("blackboard", "quiz"),
35 "aon" => "AON"
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
ebc3bd2b 207 $return->grades = get_records_menu("quiz_grades", "quiz", $quizid, "", "userid,grade");
858deff0 208 $return->maxgrade = get_field("quiz", "grade", "id", "$quizid");
209 return $return;
d0ac6bc2 210}
211
730fd187 212
bdc23be0 213/// SQL FUNCTIONS ////////////////////////////////////////////////////////////////////
214
215function quiz_move_questions($category1, $category2) {
216 global $CFG;
217 return execute_sql("UPDATE {$CFG->prefix}quiz_questions
218 SET category = '$category2'
219 WHERE category = '$category1'",
220 false);
221}
222
223function quiz_get_question_grades($quizid, $questionlist) {
224 global $CFG;
225
226 return get_records_sql("SELECT question,grade
227 FROM {$CFG->prefix}quiz_question_grades
228 WHERE quiz = '$quizid'
229 AND question IN ($questionlist)");
230}
231
34d52ad7 232function quiz_get_random_categories($questionlist) {
233/// Given an array of questions, this function looks for random
234/// questions among them and returns a list of categories with
235/// an associated count of random questions for each.
236
237 global $CFG;
238
239 return get_records_sql_menu("SELECT category,count(*)
240 FROM {$CFG->prefix}quiz_questions
241 WHERE id IN ($questionlist)
242 AND qtype = '".RANDOM."'
243 GROUP BY category ");
244}
245
bdc23be0 246function quiz_get_grade_records($quiz) {
247/// Gets all info required to display the table of quiz results
248/// for report.php
249 global $CFG;
250
251 return get_records_sql("SELECT qg.*, u.firstname, u.lastname, u.picture
252 FROM {$CFG->prefix}quiz_grades qg,
253 {$CFG->prefix}user u
254 WHERE qg.quiz = '$quiz->id'
ebc3bd2b 255 AND qg.userid = u.id");
bdc23be0 256}
257
258function quiz_get_answers($question) {
34d52ad7 259// Given a question, returns the correct answers for a given question
bdc23be0 260 global $CFG;
95dbc030 261
a2fe7cc0 262 switch ($question->qtype) {
95dbc030 263 case SHORTANSWER: // Could be multiple answers
34d52ad7 264 return get_records_sql("SELECT a.*, sa.usecase
bdc23be0 265 FROM {$CFG->prefix}quiz_shortanswer sa,
34d52ad7 266 {$CFG->prefix}quiz_answers a
bdc23be0 267 WHERE sa.question = '$question->id'
34d52ad7 268 AND sa.question = a.question ");
bdc23be0 269 break;
270
95dbc030 271 case TRUEFALSE: // Should be always two answers
34d52ad7 272 return get_records("quiz_answers", "question", $question->id);
bdc23be0 273 break;
274
95dbc030 275 case MULTICHOICE: // Should be multiple answers
34d52ad7 276 return get_records_sql("SELECT a.*, mc.single
bdc23be0 277 FROM {$CFG->prefix}quiz_multichoice mc,
34d52ad7 278 {$CFG->prefix}quiz_answers a
bdc23be0 279 WHERE mc.question = '$question->id'
34d52ad7 280 AND mc.question = a.question ");
bdc23be0 281 break;
282
54a67a59 283 case MATCH:
34d52ad7 284 return get_records("quiz_match_sub", "question", $question->id);
54a67a59 285 break;
286
287 case RANDOMSAMATCH: // Could be any of many answers, return them all
34d52ad7 288 return get_records_sql("SELECT a.*
95dbc030 289 FROM {$CFG->prefix}quiz_questions q,
34d52ad7 290 {$CFG->prefix}quiz_answers a
95dbc030 291 WHERE q.category = '$question->category'
292 AND q.qtype = ".SHORTANSWER."
34d52ad7 293 AND q.id = a.question ");
95dbc030 294 break;
295
bdc23be0 296 default:
297 return false;
298 }
299}
300
301
ef4145f6 302function quiz_get_attempt_responses($attempt, $quiz) {
bdc23be0 303// Given an attempt object, this function gets all the
304// stored responses and returns them in a format suitable
305// for regrading using quiz_grade_attempt_results()
306 global $CFG;
307
33de9f7b 308 if (!$responses = get_records_sql("SELECT q.id, q.qtype, q.category, q.questiontext, r.answer
bdc23be0 309 FROM {$CFG->prefix}quiz_responses r,
310 {$CFG->prefix}quiz_questions q
311 WHERE r.attempt = '$attempt->id'
34d52ad7 312 AND q.id = r.question")) {
bdc23be0 313 notify("Could not find any responses for that attempt!");
314 return false;
315 }
316
34d52ad7 317
318 foreach ($responses as $key => $response) {
319 if ($response->qtype == RANDOM) {
320 $responses[$key]->random = $response->answer;
321 $responses[$key]->answer = explode(",",$responses[$response->answer]->answer);
322 $responses[$response->answer]->delete = true;
323 } else {
324 $responses[$key]->answer = explode(",",$response->answer);
325 }
326 }
bdc23be0 327 foreach ($responses as $key => $response) {
34d52ad7 328 if (!empty($response->delete)) {
329 unset($responses[$key]);
330 }
bdc23be0 331 }
332
333 return $responses;
334}
335
336
337
730fd187 338//////////////////////////////////////////////////////////////////////////////////////
a5e1f35c 339/// Any other quiz functions go here. Each of them must have a name that
340/// starts with quiz_
730fd187 341
a8a372cc 342function quiz_print_comment($text) {
343 global $THEME;
344
c897eac5 345 echo "<SPAN CLASS=feedbacktext>".text_to_html($text, true, false)."</SPAN>";
a8a372cc 346}
347
8db3eadd 348function quiz_print_correctanswer($text) {
349 global $THEME;
350
351 echo "<P ALIGN=RIGHT><SPAN CLASS=highlight>$text</SPAN></P>";
352}
353
34d52ad7 354function quiz_print_question_icon($question, $editlink=true) {
c74a0ca5 355// Prints a question icon
cc3b8c75 356
357 global $QUIZ_QUESTION_TYPE;
358
34d52ad7 359 if ($editlink) {
360 echo "<A HREF=\"question.php?id=$question->id\" TITLE=\"".$QUIZ_QUESTION_TYPE[$question->qtype]."\">";
361 }
a2fe7cc0 362 switch ($question->qtype) {
c74a0ca5 363 case SHORTANSWER:
cc3b8c75 364 echo "<IMG BORDER=0 HEIGHT=16 WIDTH=16 SRC=\"pix/sa.gif\">";
c74a0ca5 365 break;
366 case TRUEFALSE:
cc3b8c75 367 echo "<IMG BORDER=0 HEIGHT=16 WIDTH=16 SRC=\"pix/tf.gif\">";
c74a0ca5 368 break;
369 case MULTICHOICE:
cc3b8c75 370 echo "<IMG BORDER=0 HEIGHT=16 WIDTH=16 SRC=\"pix/mc.gif\">";
c74a0ca5 371 break;
372 case RANDOM:
cc3b8c75 373 echo "<IMG BORDER=0 HEIGHT=16 WIDTH=16 SRC=\"pix/rs.gif\">";
c74a0ca5 374 break;
54a67a59 375 case MATCH:
376 echo "<IMG BORDER=0 HEIGHT=16 WIDTH=16 SRC=\"pix/ma.gif\">";
377 break;
378 case RANDOMSAMATCH:
95dbc030 379 echo "<IMG BORDER=0 HEIGHT=16 WIDTH=16 SRC=\"pix/rm.gif\">";
380 break;
401c8de6 381 case DESCRIPTION:
382 echo "<IMG BORDER=0 HEIGHT=16 WIDTH=16 SRC=\"pix/de.gif\">";
383 break;
c74a0ca5 384 }
34d52ad7 385 if ($editlink) {
386 echo "</A>\n";
387 }
c74a0ca5 388}
389
a8a372cc 390
34d52ad7 391
392function quiz_print_question($number, $question, $grade, $courseid,
393 $feedback=NULL, $response=NULL, $actualgrade=NULL, $correct=NULL,
4b85b717 394 $realquestion=NULL, $shuffleanswers=false) {
34d52ad7 395
396/// Prints a quiz question, any format
397/// $question is provided as an object
14d8c0b4 398
401c8de6 399 if ($question->qtype == DESCRIPTION) { // Special case question - has no answers etc
400 echo '<p align="center">';
401 echo text_to_html($question->questiontext);
402 if ($question->image) {
403 print_file_picture($question->image, $courseid);
404 }
405 echo '</p>';
406 return true;
407 }
408
a2fe7cc0 409 if (empty($actualgrade)) {
410 $actualgrade = 0;
411 }
412
14d8c0b4 413 $stranswer = get_string("answer", "quiz");
414 $strmarks = get_string("marks", "quiz");
415
416 echo "<TABLE WIDTH=100% CELLSPACING=10><TR><TD NOWRAP WIDTH=100 VALIGN=top>";
a8a372cc 417 echo "<P ALIGN=CENTER><B>$number</B></P>";
19c4f55c 418 if ($feedback or $response) {
a8a372cc 419 echo "<P ALIGN=CENTER><FONT SIZE=1>$strmarks: $actualgrade/$grade</FONT></P>";
420 } else {
421 echo "<P ALIGN=CENTER><FONT SIZE=1>$grade $strmarks</FONT></P>";
422 }
14d8c0b4 423 print_spacer(1,100);
4b85b717 424 echo "</TD><TD VALIGN=TOP>";
34d52ad7 425
426 if (empty($realquestion)) {
427 $realquestion->id = $question->id;
428 } else { // Add a marker to connect this question to the actual random parent
429 echo "<input type=\"hidden\" name=\"q{$realquestion->id}rq$question->id\" value=\"x\">\n";
430 }
14d8c0b4 431
a2fe7cc0 432 switch ($question->qtype) {
34d52ad7 433
a5e1f35c 434 case SHORTANSWER:
14d8c0b4 435 if (!$options = get_record("quiz_shortanswer", "question", $question->id)) {
436 notify("Error: Missing question options!");
437 }
2a2c9725 438 echo text_to_html($question->questiontext);
14d8c0b4 439 if ($question->image) {
5325f8b8 440 print_file_picture($question->image, $courseid);
14d8c0b4 441 }
a8a372cc 442 if ($response) {
443 $value = "VALUE=\"$response[0]\"";
a2fe7cc0 444 } else {
445 $value = "";
a8a372cc 446 }
34d52ad7 447 echo "<P ALIGN=RIGHT>$stranswer: <INPUT TYPE=TEXT NAME=q$realquestion->id SIZE=20 $value></P>";
a8a372cc 448 if ($feedback) {
449 quiz_print_comment("<P ALIGN=right>$feedback[0]</P>");
450 }
8db3eadd 451 if ($correct) {
c74a0ca5 452 $correctanswers = implode(", ", $correct);
8db3eadd 453 quiz_print_correctanswer($correctanswers);
454 }
14d8c0b4 455 break;
456
a5e1f35c 457 case TRUEFALSE:
14d8c0b4 458 if (!$options = get_record("quiz_truefalse", "question", $question->id)) {
459 notify("Error: Missing question options!");
460 }
a2fe7cc0 461 if (!$true = get_record("quiz_answers", "id", $options->trueanswer)) {
14d8c0b4 462 notify("Error: Missing question answers!");
463 }
a2fe7cc0 464 if (!$false = get_record("quiz_answers", "id", $options->falseanswer)) {
14d8c0b4 465 notify("Error: Missing question answers!");
466 }
467 if (!$true->answer) {
468 $true->answer = get_string("true", "quiz");
469 }
470 if (!$false->answer) {
471 $false->answer = get_string("false", "quiz");
472 }
2a2c9725 473 echo text_to_html($question->questiontext);
14d8c0b4 474 if ($question->image) {
5325f8b8 475 print_file_picture($question->image, $courseid);
14d8c0b4 476 }
a8a372cc 477
a2fe7cc0 478 $truechecked = "";
479 $falsechecked = "";
480
481 if (!empty($response[$true->id])) {
a8a372cc 482 $truechecked = "CHECKED";
483 $feedbackid = $true->id;
a2fe7cc0 484 } else if (!empty($response[$false->id])) {
a8a372cc 485 $falsechecked = "CHECKED";
486 $feedbackid = $false->id;
487 }
a2fe7cc0 488
489 $truecorrect = "";
490 $falsecorrect = "";
8db3eadd 491 if ($correct) {
a2fe7cc0 492 if (!empty($correct[$true->id])) {
8db3eadd 493 $truecorrect = "CLASS=highlight";
494 }
a2fe7cc0 495 if (!empty($correct[$false->id])) {
8db3eadd 496 $falsecorrect = "CLASS=highlight";
497 }
498 }
499 echo "<TABLE ALIGN=right cellpadding=5><TR><TD align=right>$stranswer:&nbsp;&nbsp;";
500 echo "<TD $truecorrect>";
34d52ad7 501 echo "<INPUT $truechecked TYPE=RADIO NAME=\"q$realquestion->id\" VALUE=\"$true->id\">$true->answer";
8db3eadd 502 echo "</TD><TD $falsecorrect>";
0bf2925d 503 echo "<INPUT $falsechecked TYPE=RADIO NAME=\"q$realquestion->id\" VALUE=\"$false->id\">$false->answer";
8db3eadd 504 echo "</TD></TR></TABLE><BR CLEAR=ALL>";
a8a372cc 505 if ($feedback) {
506 quiz_print_comment("<P ALIGN=right>$feedback[$feedbackid]</P>");
507 }
508
14d8c0b4 509 break;
510
a5e1f35c 511 case MULTICHOICE:
14d8c0b4 512 if (!$options = get_record("quiz_multichoice", "question", $question->id)) {
513 notify("Error: Missing question options!");
514 }
a5e1f35c 515 if (!$answers = get_records_list("quiz_answers", "id", $options->answers)) {
14d8c0b4 516 notify("Error: Missing question answers!");
517 }
2a2c9725 518 echo text_to_html($question->questiontext);
14d8c0b4 519 if ($question->image) {
5325f8b8 520 print_file_picture($question->image, $courseid);
14d8c0b4 521 }
522 echo "<TABLE ALIGN=right>";
523 echo "<TR><TD valign=top>$stranswer:&nbsp;&nbsp;</TD><TD>";
81b635c3 524 echo "<TABLE>";
14d8c0b4 525 $answerids = explode(",", $options->answers);
a8a372cc 526
4b85b717 527 if ($shuffleanswers) {
528 $answerids = swapshuffle($answerids);
529 }
530
14d8c0b4 531 foreach ($answerids as $key => $answerid) {
532 $answer = $answers[$answerid];
68fefdbe 533 $qnumchar = chr(ord('a') + $key);
a8a372cc 534
8e6c87cc 535 if (empty($feedback) or empty($response[$answerid])) {
a8a372cc 536 $checked = "";
8e6c87cc 537 } else {
538 $checked = "CHECKED";
a8a372cc 539 }
14d8c0b4 540 echo "<TR><TD valign=top>";
a5e1f35c 541 if ($options->single) {
34d52ad7 542 echo "<INPUT $checked TYPE=RADIO NAME=q$realquestion->id VALUE=\"$answer->id\">";
14d8c0b4 543 } else {
34d52ad7 544 echo "<INPUT $checked TYPE=CHECKBOX NAME=q$realquestion->id"."a$answer->id VALUE=\"$answer->id\">";
14d8c0b4 545 }
546 echo "</TD>";
8e6c87cc 547 if (empty($feedback) or empty($correct[$answer->id])) {
68fefdbe 548 echo "<TD valign=top>$qnumchar. $answer->answer</TD>";
8e6c87cc 549 } else {
68fefdbe 550 echo "<TD valign=top CLASS=highlight>$qnumchar. $answer->answer</TD>";
8db3eadd 551 }
8e6c87cc 552 if (!empty($feedback)) {
a8a372cc 553 echo "<TD valign=top>&nbsp;";
8e6c87cc 554 if (!empty($response[$answerid])) {
a8a372cc 555 quiz_print_comment($feedback[$answerid]);
556 }
557 echo "</TD>";
558 }
14d8c0b4 559 echo "</TR>";
560 }
561 echo "</TABLE>";
562 echo "</TABLE>";
563 break;
564
54a67a59 565 case MATCH:
566 if (!$options = get_record("quiz_match", "question", $question->id)) {
567 notify("Error: Missing question options!");
568 }
569 if (!$subquestions = get_records_list("quiz_match_sub", "id", $options->subquestions)) {
570 notify("Error: Missing subquestions for this question!");
571 }
96192c44 572 if (!empty($question->questiontext)) {
573 echo text_to_html($question->questiontext);
4b85b717 574 }
96192c44 575 if (!empty($question->image)) {
5325f8b8 576 print_file_picture($question->image, $courseid);
54a67a59 577 }
578
96192c44 579 if ($shuffleanswers) {
580 $subquestions = draw_rand_array($subquestions, count($subquestions));
581 }
54a67a59 582 foreach ($subquestions as $subquestion) {
583 $answers[$subquestion->id] = $subquestion->answertext;
584 }
585
586 $answers = draw_rand_array($answers, count($answers));
587
588 echo "<table border=0 cellpadding=10 align=right>";
589 foreach ($subquestions as $key => $subquestion) {
590 echo "<tr><td align=left valign=top>";
591 echo $subquestion->questiontext;
592 echo "</td>";
593 if (empty($response)) {
594 echo "<td align=right valign=top>";
34d52ad7 595 choose_from_menu($answers, "q$realquestion->id"."r$subquestion->id");
54a67a59 596 } else {
597 if (empty($response[$key])) {
598 echo "<td align=right valign=top>";
34d52ad7 599 choose_from_menu($answers, "q$realquestion->id"."r$subquestion->id");
54a67a59 600 } else {
601 if ($response[$key] == $correct[$key]) {
602 echo "<td align=right valign=top class=highlight>";
34d52ad7 603 choose_from_menu($answers, "q$realquestion->id"."r$subquestion->id", $response[$key]);
54a67a59 604 } else {
605 echo "<td align=right valign=top>";
34d52ad7 606 choose_from_menu($answers, "q$realquestion->id"."r$subquestion->id", $response[$key]);
54a67a59 607 }
608 }
609
610 if (!empty($feedback[$key])) {
611 quiz_print_comment($feedback[$key]);
612 }
613 }
614 echo "</td></tr>";
615 }
616 echo "</table>";
617
618 break;
619
620 case RANDOMSAMATCH:
621 if (!$options = get_record("quiz_randomsamatch", "question", $question->id)) {
95dbc030 622 notify("Error: Missing question options!");
623 }
624 echo text_to_html($question->questiontext);
625 if ($question->image) {
5325f8b8 626 print_file_picture($question->image, $courseid);
95dbc030 627 }
628
629 /// First, get all the questions available
630
631 $allquestions = get_records_select("quiz_questions",
632 "category = $question->category AND qtype = ".SHORTANSWER);
633 if (count($allquestions) < $options->choose) {
634 notify("Error: could not find enough Short Answer questions in the database!");
635 notify("Found ".count($allquestions).", need $options->choose.");
636 break;
637 }
638
639 if (empty($response)) { // Randomly pick the questions
640 if (!$randomquestions = draw_rand_array($allquestions, $options->choose)) {
641 notify("Error choosing $options->choose random questions");
642 break;
643 }
644 } else { // Use existing questions
645 $randomquestions = array();
646 foreach ($response as $key => $rrr) {
647 $rrr = explode("-", $rrr);
648 $randomquestions[$key] = $allquestions[$key];
649 $responseanswer[$key] = $rrr[1];
650 }
651 }
652
653 /// For each selected, find the best matching answers
654
655 foreach ($randomquestions as $randomquestion) {
656 $shortanswerquestion = get_record("quiz_shortanswer", "question", $randomquestion->id);
657 $questionanswers = get_records_list("quiz_answers", "id", $shortanswerquestion->answers);
658 $bestfraction = 0;
659 $bestanswer = NULL;
660 foreach ($questionanswers as $questionanswer) {
661 if ($questionanswer->fraction > $bestfraction) {
662 $bestanswer = $questionanswer;
663 }
664 }
665 if (empty($bestanswer)) {
666 notify("Error: Could not find the best answer for question: ".$randomquestions->name);
667 break;
668 }
669 $randomanswers[$bestanswer->id] = trim($bestanswer->answer);
670 }
671
672 if (!$randomanswers = draw_rand_array($randomanswers, $options->choose)) { // Mix them up
673 notify("Error randomising answers!");
674 break;
675 }
676
677 echo "<table border=0 cellpadding=10>";
678 foreach ($randomquestions as $key => $randomquestion) {
679 echo "<tr><td align=left valign=top>";
680 echo $randomquestion->questiontext;
681 echo "</td>";
682 echo "<td align=right valign=top>";
683 if (empty($response)) {
34d52ad7 684 choose_from_menu($randomanswers, "q$realquestion->id"."r$randomquestion->id");
95dbc030 685 } else {
686 if (!empty($correct[$key])) {
687 if ($randomanswers[$responseanswer[$key]] == $correct[$key]) {
688 echo "<span=highlight>";
34d52ad7 689 choose_from_menu($randomanswers, "q$realquestion->id"."r$randomquestion->id", $responseanswer[$key]);
95dbc030 690 echo "</span><br \>";
691 } else {
34d52ad7 692 choose_from_menu($randomanswers, "q$realquestion->id"."r$randomquestion->id", $responseanswer[$key]);
95dbc030 693 quiz_print_correctanswer($correct[$key]);
694 }
695 } else {
34d52ad7 696 choose_from_menu($randomanswers, "q$realquestion->id"."r$randomquestion->id", $responseanswer[$key]);
95dbc030 697 }
698 if (!empty($feedback[$key])) {
699 quiz_print_comment($feedback[$key]);
700 }
701 }
702 echo "</td></tr>";
703 }
704 echo "</table>";
705 break;
706
34d52ad7 707 case RANDOM:
708 echo "<P>Random questions should not be printed this way!</P>";
709 break;
8db3eadd 710
14d8c0b4 711 default:
712 notify("Error: Unknown question type!");
713 }
714
715 echo "</TD></TR></TABLE>";
3a506ca2 716}
717
34d52ad7 718
719
96192c44 720function quiz_print_quiz_questions($quiz, $results=NULL, $questions=NULL, $shuffleorder=NULL) {
a5e1f35c 721// Prints a whole quiz on one page.
722
34d52ad7 723 /// Get the questions
724
34d52ad7 725 if (!$questions) {
434802d5 726 if (empty($quiz->questions)) {
727 notify("No questions have been defined!");
728 return false;
729 }
730
34d52ad7 731 if (!$questions = get_records_list("quiz_questions", "id", $quiz->questions, "")) {
434802d5 732 notify("Error when reading questions from the database!");
34d52ad7 733 return false;
734 }
d288fa52 735 }
4b85b717 736
d288fa52 737 if (!$shuffleorder) {
738 if (!empty($quiz->shufflequestions)) { // Mix everything up
4b85b717 739 $questions = swapshuffle_assoc($questions);
ada728e2 740 } else {
d288fa52 741 $shuffleorder = explode(",", $quiz->questions); // Use originally defined order
4b85b717 742 }
34d52ad7 743 }
744
e1122620 745 if ($shuffleorder) { // Order has been defined, so reorder questions
746 $oldquestions = $questions;
747 $questions = array();
748 foreach ($shuffleorder as $key) {
749 $questions[] = $oldquestions[$key]; // This loses the index key, but doesn't matter
750 }
751 }
752
434802d5 753 if (!$grades = get_records_list("quiz_question_grades", "question", $quiz->questions, "", "question,grade")) {
754 notify("No grades were found for these questions!");
755 return false;
756 }
757
34d52ad7 758
759 /// Examine the set of questions for random questions, and retrieve them
760
434802d5 761 if (empty($results)) { // Choose some new random questions
34d52ad7 762 if ($randomcats = quiz_get_random_categories($quiz->questions)) {
763 foreach ($randomcats as $randomcat => $randomdraw) {
764 /// Get the appropriate amount of random questions from this category
7bfa4fad 765 if (!$catquestions[$randomcat] = quiz_choose_random_questions($randomcat, $randomdraw, $quiz->questions)) {
434802d5 766 notify(get_string("toomanyrandom", "quiz", $randomcat));
34d52ad7 767 return false;
768 }
769 }
770 }
434802d5 771 } else { // Get the previously chosen questions
772 $chosen = array();
773 foreach ($questions as $question) {
774 if (isset($question->random)) {
775 $chosen[] = $question->random;
776 }
777 }
778 if ($chosen) {
779 $chosenlist = implode(",", $chosen);
780 if (!$chosen = get_records_list("quiz_questions", "id", $chosenlist, "")) {
781 notify("Error when reading questions from the database!");
782 return false;
783 }
784 }
a5e1f35c 785 }
786
9307692f 787 $strconfirmattempt = addslashes(get_string("readytosend", "quiz"));
788
789 echo "<FORM METHOD=POST ACTION=attempt.php onsubmit=\"return confirm('$strconfirmattempt');\">";
a5e1f35c 790 echo "<INPUT TYPE=hidden NAME=q VALUE=\"$quiz->id\">";
a8a372cc 791
34d52ad7 792 $count = 0;
96192c44 793 $questionorder = array();
794
34d52ad7 795 foreach ($questions as $question) {
401c8de6 796
797 if ($question->qtype != DESCRIPTION) { // Description questions are not counted
798 $count++;
799 }
34d52ad7 800
e1122620 801 $questionorder[] = $question->id;
802
8db3eadd 803 $feedback = NULL;
804 $response = NULL;
805 $actualgrades = NULL;
806 $correct = NULL;
34d52ad7 807 $randomquestion = NULL;
808
809 if (empty($results)) {
810 if ($question->qtype == RANDOM ) { // Set up random questions
811 $randomquestion = $question;
812 $question = array_pop($catquestions[$randomquestion->category]);
813 $grades[$question->id]->grade = $grades[$randomquestion->id]->grade;
8e6c87cc 814 }
34d52ad7 815 } else {
816 if (!empty($results->feedback[$question->id])) {
817 $feedback = $results->feedback[$question->id];
8e6c87cc 818 }
34d52ad7 819 if (!empty($results->response[$question->id])) {
820 $response = $results->response[$question->id];
821 }
822 if (!empty($results->grades[$question->id])) {
823 $actualgrades = $results->grades[$question->id];
8e6c87cc 824 }
8db3eadd 825 if ($quiz->correctanswers) {
34d52ad7 826 if (!empty($results->correct[$question->id])) {
827 $correct = $results->correct[$question->id];
8e6c87cc 828 }
8db3eadd 829 }
34d52ad7 830 if (!empty($question->random)) {
831 $randomquestion = $question;
434802d5 832 $question = $chosen[$question->random];
34d52ad7 833 $grades[$question->id]->grade = $grades[$randomquestion->id]->grade;
834 }
a8a372cc 835 }
8db3eadd 836
2408867e 837
a5e1f35c 838 print_simple_box_start("CENTER", "90%");
34d52ad7 839 quiz_print_question($count, $question, $grades[$question->id]->grade, $quiz->course,
4b85b717 840 $feedback, $response, $actualgrades, $correct,
841 $randomquestion, $quiz->shuffleanswers);
a5e1f35c 842 print_simple_box_end();
96192c44 843 echo "<br \>";
a5e1f35c 844 }
a8a372cc 845
8e6c87cc 846 if (empty($results)) {
96192c44 847 if (!empty($quiz->shufflequestions)) { // Things have been mixed up, so pass the question order
848 $shuffleorder = implode(',', $questionorder);
849 echo "<input type=hidden name=shuffleorder value=\"$shuffleorder\">\n";
850 }
851 echo "<center><input type=submit value=\"".get_string("savemyanswers", "quiz")."\"></center>";
a8a372cc 852 }
96192c44 853 echo "</form>";
10b9291c 854
855 return true;
a5e1f35c 856}
34d52ad7 857
858
6a952ce7 859
860function quiz_get_default_category($courseid) {
34d52ad7 861/// Returns the current category
862
6a952ce7 863 if ($categories = get_records("quiz_categories", "course", $courseid, "id")) {
864 foreach ($categories as $category) {
865 return $category; // Return the first one (lowest id)
866 }
867 }
868
869 // Otherwise, we need to make one
10b9291c 870 $category->name = get_string("default", "quiz");
871 $category->info = get_string("defaultinfo", "quiz");
6a952ce7 872 $category->course = $courseid;
873 $category->publish = 0;
874
875 if (!$category->id = insert_record("quiz_categories", $category)) {
876 notify("Error creating a default category!");
877 return false;
878 }
879 return $category;
880}
881
c74a0ca5 882function quiz_get_category_menu($courseid, $published=false) {
da5fb074 883/// Returns the list of categories
884 $publish = "";
c74a0ca5 885 if ($published) {
886 $publish = "OR publish = '1'";
887 }
bdc23be0 888 return get_records_select_menu("quiz_categories", "course='$courseid' $publish", "name ASC", "id,name");
c74a0ca5 889}
890
6a952ce7 891function quiz_print_category_form($course, $current) {
892// Prints a form to choose categories
893
bdc23be0 894 if (!$categories = get_records_select("quiz_categories", "course='$course->id' OR publish = '1'", "name ASC")) {
6a952ce7 895 if (!$category = quiz_get_default_category($course->id)) {
896 notify("Error creating a default category!");
897 return false;
898 }
cb62c00a 899 $categories[$category->id] = $category;
6a952ce7 900 }
8d94f5a0 901 foreach ($categories as $key => $category) {
902 if ($category->publish) {
b55a466b 903 if ($catcourse = get_record("course", "id", $category->course)) {
904 $category->name .= " ($catcourse->shortname)";
8d94f5a0 905 }
906 }
b55a466b 907 $catmenu[$category->id] = $category->name;
8d94f5a0 908 }
6a952ce7 909 $strcategory = get_string("category", "quiz");
910 $strshow = get_string("show", "quiz");
6b069ece 911 $streditcats = get_string("editcategories", "quiz");
6a952ce7 912
467aaec6 913 echo "<TABLE width=\"100%\"><TR><TD NOWRAP>";
6a952ce7 914 echo "<FORM METHOD=POST ACTION=edit.php>";
915 echo "<B>$strcategory:</B>&nbsp;";
b55a466b 916 choose_from_menu($catmenu, "cat", "$current");
92a3c884 917 echo "<INPUT TYPE=submit VALUE=\"$strshow\">";
6a952ce7 918 echo "</FORM>";
6b069ece 919 echo "</TD><TD align=right>";
920 echo "<FORM METHOD=GET ACTION=category.php>";
921 echo "<INPUT TYPE=hidden NAME=id VALUE=\"$course->id\">";
922 echo "<INPUT TYPE=submit VALUE=\"$streditcats\">";
923 echo "</FORM>";
924 echo "</TD></TR></TABLE>";
6a952ce7 925}
926
927
34d52ad7 928
7bfa4fad 929function quiz_choose_random_questions($category, $draws, $excluded=0) {
34d52ad7 930/// Given a question category and a number of draws, this function
931/// creates a random subset of that size - returned as an array of questions
932
933 if (!$pool = get_records_select_menu("quiz_questions",
575de48f 934 "category = '$category' AND id NOT IN ($excluded)
935 AND qtype <> ".RANDOM."
936 AND qtype <> ".DESCRIPTION,
937 "", "id,qtype")) {
34d52ad7 938 return false;
939 }
940
941 $countpool = count($pool);
942
943 if ($countpool == $draws) {
944 $chosen = $pool;
945 } else if ($countpool < $draws) {
946 return false;
947 } else {
948 $chosen = draw_rand_array($pool, $draws);
949 }
950
951 $chosenlist = implode(",", array_keys($chosen));
952 return get_records_list("quiz_questions", "id", $chosenlist);
953}
954
955
7bd1aa1d 956function quiz_get_all_question_grades($questionlist, $quizid) {
957// Given a list of question IDs, finds grades or invents them to
958// create an array of matching grades
959
5a25f84d 960 if (empty($questionlist)) {
961 return array();
962 }
963
bdc23be0 964 $questions = quiz_get_question_grades($quizid, $questionlist);
7bd1aa1d 965
966 $list = explode(",", $questionlist);
967 $grades = array();
968
969 foreach ($list as $qid) {
970 if (isset($questions[$qid])) {
971 $grades[$qid] = $questions[$qid]->grade;
972 } else {
973 $grades[$qid] = 1;
974 }
975 }
976 return $grades;
977}
978
979
980function quiz_print_question_list($questionlist, $grades) {
6a952ce7 981// Prints a list of quiz questions in a small layout form with knobs
7bd1aa1d 982// $questionlist is comma-separated list
983// $grades is an array of corresponding grades
6a952ce7 984
985 global $THEME;
986
987 if (!$questionlist) {
988 echo "<P align=center>";
989 print_string("noquestions", "quiz");
990 echo "</P>";
991 return;
992 }
993
994 $order = explode(",", $questionlist);
995
7bd1aa1d 996 if (!$questions = get_records_list("quiz_questions", "id", $questionlist)) {
9a652ddb 997 echo "<P align=center>";
998 print_string("noquestions", "quiz");
999 echo "</P>";
1000 return;
1001
6a952ce7 1002 }
1003
1004 $strorder = get_string("order");
1005 $strquestionname = get_string("questionname", "quiz");
1006 $strgrade = get_string("grade");
1007 $strdelete = get_string("delete");
1008 $stredit = get_string("edit");
1009 $strmoveup = get_string("moveup");
1010 $strmovedown = get_string("movedown");
1011 $strsavegrades = get_string("savegrades", "quiz");
c74a0ca5 1012 $strtype = get_string("type", "quiz");
6a952ce7 1013
10b9291c 1014 for ($i=10; $i>=0; $i--) {
7bd1aa1d 1015 $gradesmenu[$i] = $i;
6a952ce7 1016 }
1017 $count = 0;
1018 $sumgrade = 0;
1019 $total = count($order);
1020 echo "<FORM METHOD=post ACTION=edit.php>";
1021 echo "<TABLE BORDER=0 CELLPADDING=5 CELLSPACING=2 WIDTH=\"100%\">";
467aaec6 1022 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 1023 foreach ($order as $qnum) {
95dbc030 1024 if (empty($questions[$qnum])) {
1025 continue;
1026 }
bca64a12 1027 $question = $questions[$qnum];
6a952ce7 1028 $count++;
1029 echo "<TR BGCOLOR=\"$THEME->cellcontent\">";
1030 echo "<TD>$count</TD>";
1031 echo "<TD>";
1032 if ($count != 1) {
1033 echo "<A TITLE=\"$strmoveup\" HREF=\"edit.php?up=$qnum\"><IMG
1034 SRC=\"../../pix/t/up.gif\" BORDER=0></A>";
1035 }
1036 echo "</TD>";
1037 echo "<TD>";
1038 if ($count != $total) {
1039 echo "<A TITLE=\"$strmovedown\" HREF=\"edit.php?down=$qnum\"><IMG
1040 SRC=\"../../pix/t/down.gif\" BORDER=0></A>";
1041 }
1042 echo "</TD>";
bca64a12 1043 echo "<TD>$question->name</TD>";
467aaec6 1044 echo "<TD ALIGN=CENTER>";
bca64a12 1045 quiz_print_question_icon($question);
c74a0ca5 1046 echo "</TD>";
6a952ce7 1047 echo "<TD>";
bca64a12 1048 if ($question->qtype == DESCRIPTION) {
1049 echo "<INPUT TYPE=hidden NAME=q$qnum VALUE=\"0\"> ";
1050 } else {
1051 choose_from_menu($gradesmenu, "q$qnum", (string)$grades[$qnum], "");
1052 }
6a952ce7 1053 echo "<TD>";
1054 echo "<A TITLE=\"$strdelete\" HREF=\"edit.php?delete=$qnum\"><IMG
1055 SRC=\"../../pix/t/delete.gif\" BORDER=0></A>&nbsp;";
1056 echo "<A TITLE=\"$stredit\" HREF=\"question.php?id=$qnum\"><IMG
1057 SRC=\"../../pix/t/edit.gif\" BORDER=0></A>";
1058 echo "</TD>";
1059
7bd1aa1d 1060 $sumgrade += $grades[$qnum];
6a952ce7 1061 }
c74a0ca5 1062 echo "<TR><TD COLSPAN=5 ALIGN=right>";
6a952ce7 1063 echo "<INPUT TYPE=submit VALUE=\"$strsavegrades:\">";
8d94f5a0 1064 echo "<INPUT TYPE=hidden NAME=setgrades VALUE=\"save\">";
6a952ce7 1065 echo "<TD ALIGN=LEFT BGCOLOR=\"$THEME->cellcontent\">";
1066 echo "<B>$sumgrade</B>";
1067 echo "</TD><TD></TD></TR>";
1068 echo "</TABLE>";
1069 echo "</FORM>";
10b9291c 1070
1071 return $sumgrade;
6a952ce7 1072}
1073
1074
1075function quiz_print_cat_question_list($categoryid) {
1076// Prints a form to choose categories
1077
1078 global $THEME, $QUIZ_QUESTION_TYPE;
1079
10b9291c 1080 $strcategory = get_string("category", "quiz");
6a952ce7 1081 $strquestion = get_string("question", "quiz");
49220fa7 1082 $straddquestions = get_string("addquestions", "quiz");
1083 $strimportquestions = get_string("importquestions", "quiz");
6a952ce7 1084 $strnoquestions = get_string("noquestions", "quiz");
1085 $strselect = get_string("select", "quiz");
a01b2571 1086 $strselectall = get_string("selectall", "quiz");
6a952ce7 1087 $strcreatenewquestion = get_string("createnewquestion", "quiz");
1088 $strquestionname = get_string("questionname", "quiz");
1089 $strdelete = get_string("delete");
1090 $stredit = get_string("edit");
1091 $straddselectedtoquiz = get_string("addselectedtoquiz", "quiz");
c74a0ca5 1092 $strtype = get_string("type", "quiz");
c6eed097 1093 $strcreatemultiple = get_string("createmultiple", "quiz");
6a952ce7 1094
1095 if (!$categoryid) {
5325f8b8 1096 echo "<p align=center><b>";
6a952ce7 1097 print_string("selectcategoryabove", "quiz");
5325f8b8 1098 echo "</b></p>";
1099 echo "<p>";
1100 print_string("addingquestions", "quiz");
1101 echo "</p>";
6a952ce7 1102 return;
1103 }
a5e1f35c 1104
6a952ce7 1105 if (!$category = get_record("quiz_categories", "id", "$categoryid")) {
1106 notify("Category not found!");
1107 return;
1108 }
8d94f5a0 1109 echo "<CENTER>";
10b9291c 1110 echo text_to_html($category->info);
6a952ce7 1111
49220fa7 1112 echo "<TABLE><TR>";
1113 echo "<TD valign=top><B>$straddquestions:</B></TD>";
1114 echo "<TD valign=top align=right>";
10b9291c 1115 echo "<FORM METHOD=GET ACTION=question.php>";
a2fe7cc0 1116 choose_from_menu($QUIZ_QUESTION_TYPE, "qtype", "", "");
6a952ce7 1117 echo "<INPUT TYPE=hidden NAME=category VALUE=\"$category->id\">";
7d2e5b65 1118 echo "<INPUT TYPE=submit VALUE=\"$strcreatenewquestion\">";
cd63d77e 1119 helpbutton("questiontypes", $strcreatenewquestion, "quiz");
6a952ce7 1120 echo "</FORM>";
49220fa7 1121
1122 echo "<FORM METHOD=GET ACTION=import.php>";
1123 echo "<INPUT TYPE=hidden NAME=category VALUE=\"$category->id\">";
1124 echo "<INPUT TYPE=submit VALUE=\"$strimportquestions\">";
1125 helpbutton("import", $strimportquestions, "quiz");
1126 echo "</FORM>";
1127
c6eed097 1128 echo "<FORM METHOD=GET ACTION=multiple.php>";
1129 echo "<INPUT TYPE=hidden NAME=category VALUE=\"$category->id\">";
1130 echo "<INPUT TYPE=submit VALUE=\"$strcreatemultiple\">";
1131 helpbutton("createmultiple", $strcreatemultiple, "quiz");
1132 echo "</FORM>";
1133
49220fa7 1134 echo "</TR></TABLE>";
1135
8d94f5a0 1136 echo "</CENTER>";
6a952ce7 1137
14bdb238 1138 if (!$questions = get_records("quiz_questions", "category", $category->id, "qtype ASC")) {
6a952ce7 1139 echo "<P align=center>";
1140 print_string("noquestions", "quiz");
1141 echo "</P>";
1142 return;
1143 }
1144
10b9291c 1145 $canedit = isteacher($category->course);
1146
6a952ce7 1147 echo "<FORM METHOD=post ACTION=edit.php>";
1148 echo "<TABLE BORDER=0 CELLPADDING=5 CELLSPACING=2 WIDTH=\"100%\">";
467aaec6 1149 echo "<TR><TH width=\"*\" NOWRAP>$strselect</TH><TH width=\"100%\" align=left NOWRAP>$strquestionname</TH><TH WIDTH=\"*\" NOWRAP>$strtype</TH>";
10b9291c 1150 if ($canedit) {
467aaec6 1151 echo "<TH width=\"*\" NOWRAP>$stredit</TH>";
10b9291c 1152 }
1153 echo "</TR>";
6a952ce7 1154 foreach ($questions as $question) {
1155 echo "<TR BGCOLOR=\"$THEME->cellcontent\">";
1156 echo "<TD ALIGN=CENTER>";
1157 echo "<INPUT TYPE=CHECKBOX NAME=q$question->id VALUE=\"1\">";
1158 echo "</TD>";
1159 echo "<TD>".$question->name."</TD>";
467aaec6 1160 echo "<TD ALIGN=CENTER>";
c74a0ca5 1161 quiz_print_question_icon($question);
1162 echo "</TD>";
10b9291c 1163 if ($canedit) {
1164 echo "<TD>";
e1c91df0 1165 echo "<A TITLE=\"$strdelete\" HREF=\"question.php?id=$question->id&delete=$question->id\"><IMG
10b9291c 1166 SRC=\"../../pix/t/delete.gif\" BORDER=0></A>&nbsp;";
1167 echo "<A TITLE=\"$stredit\" HREF=\"question.php?id=$question->id\"><IMG
1168 SRC=\"../../pix/t/edit.gif\" BORDER=0></A>";
1169 echo "</TD></TR>";
1170 }
1171 echo "</TR>";
6a952ce7 1172 }
1173 echo "<TR><TD COLSPAN=3>";
a01b2571 1174 echo "<INPUT TYPE=submit NAME=add VALUE=\"<< $straddselectedtoquiz\">";
1175 //echo "<INPUT TYPE=submit NAME=delete VALUE=\"XX Delete selected\">";
1176 echo "<INPUT type=button onclick=\"checkall()\" value=\"$strselectall\">";
6a952ce7 1177 echo "</TD></TR>";
1178 echo "</TABLE>";
1179 echo "</FORM>";
1180}
a5e1f35c 1181
3a506ca2 1182
958aafe2 1183function quiz_start_attempt($quizid, $userid, $numattempt) {
1184 $attempt->quiz = $quizid;
ebc3bd2b 1185 $attempt->userid = $userid;
958aafe2 1186 $attempt->attempt = $numattempt;
1187 $attempt->timestart = time();
1188 $attempt->timefinish = 0;
1189 $attempt->timemodified = time();
1190
1191 return insert_record("quiz_attempts", $attempt);
1192}
1193
1194function quiz_get_user_attempt_unfinished($quizid, $userid) {
1195// Returns an object containing an unfinished attempt (if there is one)
ebc3bd2b 1196 return get_record("quiz_attempts", "quiz", $quizid, "userid", $userid, "timefinish", 0);
958aafe2 1197}
1198
3a506ca2 1199function quiz_get_user_attempts($quizid, $userid) {
a5e1f35c 1200// Returns a list of all attempts by a user
ebc3bd2b 1201 return get_records_select("quiz_attempts", "quiz = '$quizid' AND userid = '$userid' AND timefinish > 0",
bdc23be0 1202 "attempt ASC");
3a506ca2 1203}
1204
8d94f5a0 1205
1206function quiz_get_user_attempts_string($quiz, $attempts, $bestgrade) {
1207/// Returns a simple little comma-separated list of all attempts,
6d86b5dc 1208/// with each grade linked to the feedback report and with the best grade highlighted
8d94f5a0 1209
1210 $bestgrade = format_float($bestgrade);
1211 foreach ($attempts as $attempt) {
1212 $attemptgrade = format_float(($attempt->sumgrades / $quiz->sumgrades) * $quiz->grade);
1213 if ($attemptgrade == $bestgrade) {
6d86b5dc 1214 $userattempts[] = "<SPAN CLASS=highlight><A HREF=\"report.php?q=$quiz->id&attempt=$attempt->id\">$attemptgrade</A></SPAN>";
8d94f5a0 1215 } else {
6d86b5dc 1216 $userattempts[] = "<A HREF=\"report.php?q=$quiz->id&attempt=$attempt->id\">$attemptgrade</A>";
8d94f5a0 1217 }
1218 }
1219 return implode(",", $userattempts);
1220}
1221
a5e1f35c 1222function quiz_get_best_grade($quizid, $userid) {
1223/// Get the best current grade for a particular user in a quiz
ebc3bd2b 1224 if (!$grade = get_record("quiz_grades", "quiz", $quizid, "userid", $userid)) {
3a506ca2 1225 return 0;
1226 }
1227
2383cadb 1228 return (round($grade->grade,0));
3a506ca2 1229}
1230
e331eb06 1231function quiz_save_best_grade($quiz, $userid) {
a5e1f35c 1232/// Calculates the best grade out of all attempts at a quiz for a user,
1233/// and then saves that grade in the quiz_grades table.
1234
e331eb06 1235 if (!$attempts = quiz_get_user_attempts($quiz->id, $userid)) {
e909c8d0 1236 notify("Could not find any user attempts");
a5e1f35c 1237 return false;
1238 }
1239
1240 $bestgrade = quiz_calculate_best_grade($quiz, $attempts);
1241 $bestgrade = (($bestgrade / $quiz->sumgrades) * $quiz->grade);
1242
ebc3bd2b 1243 if ($grade = get_record("quiz_grades", "quiz", $quiz->id, "userid", $userid)) {
1d2603b1 1244 $grade->grade = round($bestgrade, 2);
a5e1f35c 1245 $grade->timemodified = time();
1246 if (!update_record("quiz_grades", $grade)) {
e909c8d0 1247 notify("Could not update best grade");
a5e1f35c 1248 return false;
1249 }
1250 } else {
1251 $grade->quiz = $quiz->id;
ebc3bd2b 1252 $grade->userid = $userid;
38f03e5a 1253 $grade->grade = round($bestgrade, 2);
a5e1f35c 1254 $grade->timemodified = time();
1255 if (!insert_record("quiz_grades", $grade)) {
e909c8d0 1256 notify("Could not insert new best grade");
a5e1f35c 1257 return false;
1258 }
1259 }
1260 return true;
1261}
1262
1263
3a506ca2 1264function quiz_calculate_best_grade($quiz, $attempts) {
a5e1f35c 1265/// Calculate the best grade for a quiz given a number of attempts by a particular user.
3a506ca2 1266
1267 switch ($quiz->grademethod) {
a5e1f35c 1268
1269 case ATTEMPTFIRST:
3a506ca2 1270 foreach ($attempts as $attempt) {
a5e1f35c 1271 return $attempt->sumgrades;
3a506ca2 1272 }
a5e1f35c 1273 break;
1274
1275 case ATTEMPTLAST:
1276 foreach ($attempts as $attempt) {
1277 $final = $attempt->sumgrades;
1278 }
1279 return $final;
3a506ca2 1280
a5e1f35c 1281 case GRADEAVERAGE:
3a506ca2 1282 $sum = 0;
1283 $count = 0;
1284 foreach ($attempts as $attempt) {
a5e1f35c 1285 $sum += $attempt->sumgrades;
3a506ca2 1286 $count++;
1287 }
1288 return (float)$sum/$count;
1289
3a506ca2 1290 default:
a5e1f35c 1291 case GRADEHIGHEST:
1292 $max = 0;
3a506ca2 1293 foreach ($attempts as $attempt) {
a5e1f35c 1294 if ($attempt->sumgrades > $max) {
1295 $max = $attempt->sumgrades;
1296 }
3a506ca2 1297 }
a5e1f35c 1298 return $max;
1299 }
1300}
1301
34d52ad7 1302
a5e1f35c 1303function quiz_save_attempt($quiz, $questions, $result, $attemptnum) {
1304/// Given a quiz, a list of attempted questions and a total grade
1305/// this function saves EVERYTHING so it can be reconstructed later
1306/// if necessary.
1307
1308 global $USER;
1309
958aafe2 1310 // First find the attempt in the database (start of attempt)
1311
1312 if (!$attempt = quiz_get_user_attempt_unfinished($quiz->id, $USER->id)) {
1313 notify("Trying to save an attempt that was not started!");
1314 return false;
1315 }
1316
1317 if ($attempt->attempt != $attemptnum) { // Double check.
1318 notify("Number of this attempt is different to the unfinished one!");
1319 return false;
1320 }
1321
1322 // Now let's complete this record and save it
a5e1f35c 1323
a5e1f35c 1324 $attempt->sumgrades = $result->sumgrades;
958aafe2 1325 $attempt->timefinish = time();
a5e1f35c 1326 $attempt->timemodified = time();
1327
958aafe2 1328 if (! update_record("quiz_attempts", $attempt)) {
7520988b 1329 notify("Error while saving attempt");
a5e1f35c 1330 return false;
1331 }
1332
1333 // Now let's save all the questions for this attempt
1334
1335 foreach ($questions as $question) {
1336 $response->attempt = $attempt->id;
1337 $response->question = $question->id;
1338 $response->grade = $result->grades[$question->id];
34d52ad7 1339
77cff589 1340 if (!empty($question->random)) {
34d52ad7 1341 // First save the response of the random question
1342 // the answer is the id of the REAL response
1343 $response->answer = $question->random;
1344 if (!insert_record("quiz_responses", $response)) {
1345 notify("Error while saving response");
1346 return false;
1347 }
1348 $response->question = $question->random;
1349 }
1350
54d0590b 1351 if (!empty($question->answer)) {
a5e1f35c 1352 $response->answer = implode(",",$question->answer);
1353 } else {
1354 $response->answer = "";
1355 }
1356 if (!insert_record("quiz_responses", $response)) {
7520988b 1357 notify("Error while saving response");
a5e1f35c 1358 return false;
1359 }
3a506ca2 1360 }
a5e1f35c 1361 return true;
3a506ca2 1362}
730fd187 1363
a5e1f35c 1364
1365function quiz_grade_attempt_results($quiz, $questions) {
1366/// Given a list of questions (including answers for each one)
1367/// this function does all the hard work of calculating the
1368/// grades for each question, as well as a total grade for
1369/// for the whole quiz. It returns everything in a structure
1370/// that looks like:
1371/// $result->sumgrades (sum of all grades for all questions)
1372/// $result->percentage (Percentage of grades that were correct)
1373/// $result->grade (final grade result for the whole quiz)
1374/// $result->grades[] (array of grades, indexed by question id)
a8a372cc 1375/// $result->response[] (array of response arrays, indexed by question id)
a5e1f35c 1376/// $result->feedback[] (array of feedback arrays, indexed by question id)
8db3eadd 1377/// $result->correct[] (array of feedback arrays, indexed by question id)
a5e1f35c 1378
1379 if (!$questions) {
1380 error("No questions!");
1381 }
34d52ad7 1382
1383 if (!$grades = get_records_menu("quiz_question_grades", "quiz", $quiz->id, "", "question,grade")) {
1384 error("No grades defined for these quiz questions!");
1385 }
1386
a5e1f35c 1387 $result->sumgrades = 0;
1388
1389 foreach ($questions as $question) {
34d52ad7 1390
1391 if (!empty($question->random)) { // This question has been randomly chosen
1392 $randomquestion = $question; // Save it for later
1393 if (!$question = get_record("quiz_questions", "id", $question->random)) {
1394 error("Could not find the real question behind this random question!");
1395 }
61367e64 1396 if (isset($randomquestion->answer)) {
1397 $question->answer = $randomquestion->answer;
1398 } else {
1399 $question->answer = "";
1400 }
34d52ad7 1401 $question->grade = $grades[$randomquestion->id];
1402 } else {
1403 $question->grade = $grades[$question->id];
1404 }
1405
401c8de6 1406 if ($question->qtype != DESCRIPTION) { // All real questions need answers defined
1407 if (!$answers = quiz_get_answers($question)) {
1408 error("No answers defined for question id $question->id!");
1409 }
a5e1f35c 1410 }
1411
1412 $grade = 0; // default
34d52ad7 1413 $correct = array();
19c4f55c 1414 $feedback = array();
1415 $response = array();
a5e1f35c 1416
a2fe7cc0 1417 switch ($question->qtype) {
a5e1f35c 1418 case SHORTANSWER:
1419 if ($question->answer) {
41b95af2 1420 $question->answer = trim(stripslashes($question->answer[0]));
a5e1f35c 1421 } else {
19c4f55c 1422 $question->answer = "";
a5e1f35c 1423 }
a8a372cc 1424 $response[0] = $question->answer;
a2fe7cc0 1425 $bestshortanswer = 0;
a5e1f35c 1426 foreach($answers as $answer) { // There might be multiple right answers
8db3eadd 1427 if ($answer->fraction > $bestshortanswer) {
1428 $correct[$answer->id] = $answer->answer;
a2fe7cc0 1429 $bestshortanswer = $answer->fraction;
8db3eadd 1430 }
2a2c9725 1431 if (!$answer->usecase) { // Don't compare case
a5e1f35c 1432 $answer->answer = strtolower($answer->answer);
1433 $question->answer = strtolower($question->answer);
1434 }
1435 if ($question->answer == $answer->answer) {
a8a372cc 1436 $feedback[0] = $answer->feedback;
34d52ad7 1437 $grade = (float)$answer->fraction * $question->grade;
a5e1f35c 1438 }
1439 }
1440 break;
1441
1442
1443 case TRUEFALSE:
1444 if ($question->answer) {
1445 $question->answer = $question->answer[0];
1446 } else {
1447 $question->answer = NULL;
1448 }
1449 foreach($answers as $answer) { // There should be two answers (true and false)
1450 $feedback[$answer->id] = $answer->feedback;
8db3eadd 1451 if ($answer->fraction > 0) {
1452 $correct[$answer->id] = true;
1453 }
a5e1f35c 1454 if ($question->answer == $answer->id) {
34d52ad7 1455 $grade = (float)$answer->fraction * $question->grade;
a8a372cc 1456 $response[$answer->id] = true;
a5e1f35c 1457 }
1458 }
1459 break;
1460
1461
1462 case MULTICHOICE:
1463 foreach($answers as $answer) { // There will be multiple answers, perhaps more than one is right
1464 $feedback[$answer->id] = $answer->feedback;
8db3eadd 1465 if ($answer->fraction > 0) {
1466 $correct[$answer->id] = true;
1467 }
54d0590b 1468 if (!empty($question->answer)) {
a5e1f35c 1469 foreach ($question->answer as $questionanswer) {
1470 if ($questionanswer == $answer->id) {
95dbc030 1471 $response[$answer->id] = true;
a5e1f35c 1472 if ($answer->single) {
34d52ad7 1473 $grade = (float)$answer->fraction * $question->grade;
a5e1f35c 1474 continue;
1475 } else {
34d52ad7 1476 $grade += (float)$answer->fraction * $question->grade;
a5e1f35c 1477 }
1478 }
1479 }
1480 }
1481 }
1482 break;
95dbc030 1483
54a67a59 1484 case MATCH:
1485 $matchcount = $totalcount = 0;
1486
34d52ad7 1487 foreach ($question->answer as $questionanswer) { // Each answer is "subquestionid-answerid"
54a67a59 1488 $totalcount++;
34d52ad7 1489 $qarr = explode('-', $questionanswer); // Extract subquestion/answer.
1490 $subquestionid = $qarr[0];
1491 $subanswerid = $qarr[1];
e1122620 1492 if ($subquestionid and $subanswerid and (($subquestionid == $subanswerid) or
1493 ($answers[$subquestionid]->answertext == $answers[$subanswerid]->answertext))) {
34d52ad7 1494 // Either the ids match exactly, or the answertexts match exactly
1495 // (in case two subquestions had the same answer)
54a67a59 1496 $matchcount++;
34d52ad7 1497 $correct[$subquestionid] = true;
54a67a59 1498 } else {
34d52ad7 1499 $correct[$subquestionid] = false;
54a67a59 1500 }
34d52ad7 1501 $response[$subquestionid] = $subanswerid;
54a67a59 1502 }
1503
34d52ad7 1504 $grade = $question->grade * $matchcount / $totalcount;
54a67a59 1505
1506 break;
1507
1508 case RANDOMSAMATCH:
95dbc030 1509 $bestanswer = array();
1510 foreach ($answers as $answer) { // Loop through them all looking for correct answers
1511 if (empty($bestanswer[$answer->question])) {
1512 $bestanswer[$answer->question] = 0;
1513 $correct[$answer->question] = "";
1514 }
1515 if ($answer->fraction > $bestanswer[$answer->question]) {
1516 $bestanswer[$answer->question] = $answer->fraction;
1517 $correct[$answer->question] = $answer->answer;
1518 }
1519 }
1520 $answerfraction = 1.0 / (float) count($question->answer);
1521 foreach ($question->answer as $questionanswer) { // For each random answered question
1522 $rqarr = explode('-', $questionanswer); // Extract question/answer.
1523 $rquestion = $rqarr[0];
1524 $ranswer = $rqarr[1];
1525 $response[$rquestion] = $questionanswer;
1526 if (isset($answers[$ranswer])) { // If the answer exists in the list
1527 $answer = $answers[$ranswer];
1528 $feedback[$rquestion] = $answer->feedback;
1529 if ($answer->question == $rquestion) { // Check that this answer matches the question
34d52ad7 1530 $grade += (float)$answer->fraction * $question->grade * $answerfraction;
95dbc030 1531 }
1532 }
1533 }
8db3eadd 1534 break;
a5e1f35c 1535
a5e1f35c 1536 }
34d52ad7 1537
1538 if (!empty($randomquestion)) { // This question has been randomly chosen
1539 $question = $randomquestion; // Restore the question->id
1540 unset($randomquestion);
1541 }
1542
a5e1f35c 1543 if ($grade < 0.0) { // No negative grades
1544 $grade = 0.0;
1545 }
10b9291c 1546
3a50203f 1547 $result->grades[$question->id] = round($grade, 2);
a5e1f35c 1548 $result->sumgrades += $grade;
1549 $result->feedback[$question->id] = $feedback;
a8a372cc 1550 $result->response[$question->id] = $response;
8db3eadd 1551 $result->correct[$question->id] = $correct;
a5e1f35c 1552 }
1553
8d94f5a0 1554 $fraction = (float)($result->sumgrades / $quiz->sumgrades);
1555 $result->percentage = format_float($fraction * 100.0);
1556 $result->grade = format_float($fraction * $quiz->grade);
3a50203f 1557 $result->sumgrades = round($result->sumgrades, 2);
a5e1f35c 1558
1559 return $result;
1560}
6d86b5dc 1561
1562
49220fa7 1563function quiz_save_question_options($question) {
1564/// Given some question info and some data about the the answers
1565/// this function parses, organises and saves the question
1566/// It is used by question.php when saving new data from a
1567/// form, and also by import.php when importing questions
77cff589 1568///
1569/// If this is an update, and old answers already exist, then
1570/// these are overwritten using an update(). To do this, it
1571/// it is assumed that the IDs in quiz_answers are in the same
1572/// sort order as the new answers being saved. This should always
1573/// be true, but it's something to keep in mind if fiddling with
1574/// question.php
49220fa7 1575///
1576/// Returns $result->error or $result->notice
a5e1f35c 1577
49220fa7 1578 switch ($question->qtype) {
1579 case SHORTANSWER:
77cff589 1580
e1122620 1581 if (!$oldanswers = get_records("quiz_answers", "question", $question->id, "id ASC")) {
1582 $oldanswers = array();
1583 }
49220fa7 1584
1585 $answers = array();
1586 $maxfraction = -1;
1587
1588 // Insert all the new answers
1589 foreach ($question->answer as $key => $dataanswer) {
1590 if ($dataanswer != "") {
77cff589 1591 if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it
1592 $answer = $oldanswer;
1593 $answer->answer = $dataanswer;
1594 $answer->fraction = $question->fraction[$key];
1595 $answer->feedback = $question->feedback[$key];
1596 if (!update_record("quiz_answers", $answer)) {
1597 $result->error = "Could not update quiz answer! (id=$answer->id)";
1598 return $result;
1599 }
1600 } else { // This is a completely new answer
1601 unset($answer);
1602 $answer->answer = $dataanswer;
1603 $answer->question = $question->id;
1604 $answer->fraction = $question->fraction[$key];
1605 $answer->feedback = $question->feedback[$key];
1606 if (!$answer->id = insert_record("quiz_answers", $answer)) {
1607 $result->error = "Could not insert quiz answer!";
1608 return $result;
1609 }
49220fa7 1610 }
1611 $answers[] = $answer->id;
1612 if ($question->fraction[$key] > $maxfraction) {
1613 $maxfraction = $question->fraction[$key];
1614 }
1615 }
1616 }
1617
77cff589 1618 if ($options = get_record("quiz_shortanswer", "question", $question->id)) {
1619 $options->answers = implode(",",$answers);
1620 $options->usecase = $question->usecase;
1621 if (!update_record("quiz_shortanswer", $options)) {
1622 $result->error = "Could not update quiz shortanswer options! (id=$options->id)";
1623 return $result;
1624 }
1625 } else {
1626 unset($options);
1627 $options->question = $question->id;
1628 $options->answers = implode(",",$answers);
1629 $options->usecase = $question->usecase;
1630 if (!insert_record("quiz_shortanswer", $options)) {
1631 $result->error = "Could not insert quiz shortanswer options!";
1632 return $result;
1633 }
49220fa7 1634 }
1635
1636 /// Perform sanity checks on fractional grades
1637 if ($maxfraction != 1) {
1638 $maxfraction = $maxfraction * 100;
1639 $result->notice = get_string("fractionsnomax", "quiz", $maxfraction);
1640 return $result;
1641 }
1642 break;
77cff589 1643
1644
1645
49220fa7 1646 case TRUEFALSE:
77cff589 1647
e1122620 1648 if (!$oldanswers = get_records("quiz_answers", "question", $question->id, "id ASC")) {
1649 $oldanswers = array();
1650 }
77cff589 1651
1652 if ($true = array_shift($oldanswers)) { // Existing answer, so reuse it
4d01ada3 1653 $true->answer = get_string("true", "quiz");
77cff589 1654 $true->fraction = $question->answer;
1655 $true->feedback = $question->feedbacktrue;
1656 if (!update_record("quiz_answers", $true)) {
1657 $result->error = "Could not update quiz answer \"true\")!";
1658 return $result;
1659 }
1660 } else {
1661 unset($true);
4d01ada3 1662 $true->answer = get_string("true", "quiz");
77cff589 1663 $true->question = $question->id;
1664 $true->fraction = $question->answer;
1665 $true->feedback = $question->feedbacktrue;
1666 if (!$true->id = insert_record("quiz_answers", $true)) {
1667 $result->error = "Could not insert quiz answer \"true\")!";
1668 return $result;
1669 }
49220fa7 1670 }
1671
77cff589 1672 if ($false = array_shift($oldanswers)) { // Existing answer, so reuse it
4d01ada3 1673 $false->answer = get_string("false", "quiz");
77cff589 1674 $false->fraction = 1 - (int)$question->answer;
1675 $false->feedback = $question->feedbackfalse;
1676 if (!update_record("quiz_answers", $false)) {
1677 $result->error = "Could not insert quiz answer \"false\")!";
1678 return $result;
1679 }
1680 } else {
1681 unset($false);
4d01ada3 1682 $false->answer = get_string("false", "quiz");
77cff589 1683 $false->question = $question->id;
1684 $false->fraction = 1 - (int)$question->answer;
1685 $false->feedback = $question->feedbackfalse;
1686 if (!$false->id = insert_record("quiz_answers", $false)) {
1687 $result->error = "Could not insert quiz answer \"false\")!";
1688 return $result;
1689 }
49220fa7 1690 }
1691
77cff589 1692 if ($options = get_record("quiz_truefalse", "question", $question->id)) {
1693 // No need to do anything, since the answer IDs won't have changed
1694 // But we'll do it anyway, just for robustness
1695 $options->trueanswer = $true->id;
1696 $options->falseanswer = $false->id;
1697 if (!update_record("quiz_truefalse", $options)) {
1698 $result->error = "Could not update quiz truefalse options! (id=$options->id)";
1699 return $result;
1700 }
1701 } else {
1702 unset($options);
1703 $options->question = $question->id;
1704 $options->trueanswer = $true->id;
1705 $options->falseanswer = $false->id;
1706 if (!insert_record("quiz_truefalse", $options)) {
1707 $result->error = "Could not insert quiz truefalse options!";
1708 return $result;
1709 }
49220fa7 1710 }
1711 break;
77cff589 1712
1713
49220fa7 1714 case MULTICHOICE:
77cff589 1715
e1122620 1716 if (!$oldanswers = get_records("quiz_answers", "question", $question->id, "id ASC")) {
1717 $oldanswers = array();
1718 }
49220fa7 1719
1720 $totalfraction = 0;
1721 $maxfraction = -1;
1722
1723 $answers = array();
1724
1725 // Insert all the new answers
1726 foreach ($question->answer as $key => $dataanswer) {
1727 if ($dataanswer != "") {
77cff589 1728 if ($answer = array_shift($oldanswers)) { // Existing answer, so reuse it
1729 $answer->answer = $dataanswer;
1730 $answer->fraction = $question->fraction[$key];
1731 $answer->feedback = $question->feedback[$key];
1732 if (!update_record("quiz_answers", $answer)) {
1733 $result->error = "Could not update quiz answer! (id=$answer->id)";
1734 return $result;
1735 }
1736 } else {
1737 unset($answer);
1738 $answer->answer = $dataanswer;
1739 $answer->question = $question->id;
1740 $answer->fraction = $question->fraction[$key];
1741 $answer->feedback = $question->feedback[$key];
1742 if (!$answer->id = insert_record("quiz_answers", $answer)) {
1743 $result->error = "Could not insert quiz answer! ";
1744 return $result;
1745 }
49220fa7 1746 }
1747 $answers[] = $answer->id;
1748
1749 if ($question->fraction[$key] > 0) { // Sanity checks
1750 $totalfraction += $question->fraction[$key];
1751 }
1752 if ($question->fraction[$key] > $maxfraction) {
1753 $maxfraction = $question->fraction[$key];
1754 }
1755 }
1756 }
1757
77cff589 1758 if ($options = get_record("quiz_multichoice", "question", $question->id)) {
1759 $options->answers = implode(",",$answers);
1760 $options->single = $question->single;
1761 if (!update_record("quiz_multichoice", $options)) {
1762 $result->error = "Could not update quiz multichoice options! (id=$options->id)";
1763 return $result;
1764 }
1765 } else {
1766 unset($options);
1767 $options->question = $question->id;
1768 $options->answers = implode(",",$answers);
1769 $options->single = $question->single;
1770 if (!insert_record("quiz_multichoice", $options)) {
1771 $result->error = "Could not insert quiz multichoice options!";
1772 return $result;
1773 }
49220fa7 1774 }
1775
1776 /// Perform sanity checks on fractional grades
1777 if ($options->single) {
1778 if ($maxfraction != 1) {
1779 $maxfraction = $maxfraction * 100;
1780 $result->notice = get_string("fractionsnomax", "quiz", $maxfraction);
1781 return $result;
1782 }
1783 } else {
1784 $totalfraction = round($totalfraction,2);
1785 if ($totalfraction != 1) {
1786 $totalfraction = $totalfraction * 100;
1787 $result->notice = get_string("fractionsaddwrong", "quiz", $totalfraction);
1788 return $result;
1789 }
1790 }
1791 break;
95dbc030 1792
54a67a59 1793 case MATCH:
77cff589 1794
e1122620 1795 if (!$oldsubquestions = get_records("quiz_match_sub", "question", $question->id, "id ASC")) {
1796 $oldsubquestions = array();
1797 }
54a67a59 1798
1799 $subquestions = array();
1800
1801 // Insert all the new question+answer pairs
1802 foreach ($question->subquestions as $key => $questiontext) {
1803 $answertext = $question->subanswers[$key];
1804 if (!empty($questiontext) and !empty($answertext)) {
77cff589 1805 if ($subquestion = array_shift($oldsubquestions)) { // Existing answer, so reuse it
1806 $subquestion->questiontext = $questiontext;
1807 $subquestion->answertext = $answertext;
1808 if (!update_record("quiz_match_sub", $subquestion)) {
1809 $result->error = "Could not insert quiz match subquestion! (id=$subquestion->id)";
1810 return $result;
1811 }
1812 } else {
1813 unset($subquestion);
1814 $subquestion->question = $question->id;
1815 $subquestion->questiontext = $questiontext;
1816 $subquestion->answertext = $answertext;
1817 if (!$subquestion->id = insert_record("quiz_match_sub", $subquestion)) {
1818 $result->error = "Could not insert quiz match subquestion!";
1819 return $result;
1820 }
54a67a59 1821 }
1822 $subquestions[] = $subquestion->id;
1823 }
1824 }
1825
1826 if (count($subquestions) < 3) {
1827 $result->notice = get_string("notenoughsubquestions", "quiz");
1828 return $result;
1829 }
1830
77cff589 1831 if ($options = get_record("quiz_match", "question", $question->id)) {
1832 $options->subquestions = implode(",",$subquestions);
1833 if (!update_record("quiz_match", $options)) {
1834 $result->error = "Could not update quiz match options! (id=$options->id)";
1835 return $result;
1836 }
1837 } else {
1838 unset($options);
1839 $options->question = $question->id;
1840 $options->subquestions = implode(",",$subquestions);
1841 if (!insert_record("quiz_match", $options)) {
1842 $result->error = "Could not insert quiz match options!";
1843 return $result;
1844 }
54a67a59 1845 }
1846
1847 break;
1848
77cff589 1849
54a67a59 1850 case RANDOMSAMATCH:
95dbc030 1851 $options->question = $question->id;
1852 $options->choose = $question->choose;
54a67a59 1853 if ($existing = get_record("quiz_randomsamatch", "question", $options->question)) {
95dbc030 1854 $options->id = $existing->id;
54a67a59 1855 if (!update_record("quiz_randomsamatch", $options)) {
1856 $result->error = "Could not update quiz randomsamatch options!";
95dbc030 1857 return $result;
1858 }
1859 } else {
54a67a59 1860 if (!insert_record("quiz_randomsamatch", $options)) {
1861 $result->error = "Could not insert quiz randomsamatch options!";
95dbc030 1862 return $result;
1863 }
1864 }
1865 break;
1866
34d52ad7 1867 case RANDOM:
1868 break;
1869
401c8de6 1870 case DESCRIPTION:
1871 break;
1872
49220fa7 1873 default:
1874 $result->error = "Unsupported question type ($question->qtype)!";
1875 return $result;
1876 break;
1877 }
1878 return true;
1879}
1880
1881
95dbc030 1882
1883
730fd187 1884?>