Quiz generation is working now. Just need question and category editors.
[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
16define("SHORTANSWER", "1");
17define("TRUEFALSE", "2");
18define("MULTICHOICE", "3");
19$QUIZ_QUESTION_TYPE = array ( SHORTANSWER => get_string("shortanswer", "quiz"),
20 TRUEFALSE => get_string("truefalse", "quiz"),
21 MULTICHOICE => get_string("multichoice", "quiz"));
22
23
24
25/// FUNCTIONS ///////////////////////////////////////////////////////////////////
730fd187 26
27function quiz_add_instance($quiz) {
a5e1f35c 28/// Given an object containing all the necessary data,
29/// (defined by the form in mod.html) this function
30/// will create a new instance and return the id number
31/// of the new instance.
730fd187 32
33 $quiz->timemodified = time();
34
7bd1aa1d 35 if (!$quiz->id = insert_record("quiz", $quiz)) {
36 return false; // some error occurred
37 }
38
39 // The grades for each question in this quiz are stored in an array
40 if ($quiz->grades) {
41 foreach ($quiz->grades as $question => $grade) {
42 $questiongrade->quiz = $quiz->id;
43 $questiongrade->question = $question;
44 $questiongrade->grade = $grade;
45 if (!insert_record("quiz_question_grades", $questiongrade)) {
46 return false;
47 }
48 }
49 }
730fd187 50
7bd1aa1d 51 return $quiz->id;
730fd187 52}
53
54
55function quiz_update_instance($quiz) {
a5e1f35c 56/// Given an object containing all the necessary data,
57/// (defined by the form in mod.html) this function
58/// will update an existing instance with new data.
730fd187 59
60 $quiz->timemodified = time();
61 $quiz->id = $quiz->instance;
62
7bd1aa1d 63 if (!update_record("quiz", $quiz)) {
64 return false; // some error occurred
65 }
730fd187 66
7bd1aa1d 67
68 // The grades for each question in this quiz are stored in an array
69 // Insert or update records as appropriate
70
71 $existing = get_records("quiz_question_grades", "quiz", $quiz->id, "", "question,grade,id");
72
73 if ($quiz->grades) {
74 foreach ($quiz->grades as $question => $grade) {
75 $questiongrade->quiz = $quiz->id;
76 $questiongrade->question = $question;
77 $questiongrade->grade = $grade;
78 if (isset($existing[$question])) {
79 if ($existing[$question]->grade != $grade) {
80 $questiongrade->id = $existing[$question]->id;
81 if (!update_record("quiz_question_grades", $questiongrade)) {
82 return false;
83 }
84 }
85 } else {
86 if (!insert_record("quiz_question_grades", $questiongrade)) {
87 return false;
88 }
89 }
90 }
91 }
92
93 return true;
730fd187 94}
95
96
97function quiz_delete_instance($id) {
a5e1f35c 98/// Given an ID of an instance of this module,
99/// this function will permanently delete the instance
100/// and any data that depends on it.
730fd187 101
102 if (! $quiz = get_record("quiz", "id", "$id")) {
103 return false;
104 }
105
106 $result = true;
107
108 # Delete any dependent records here #
109
110 if (! delete_records("quiz", "id", "$quiz->id")) {
111 $result = false;
112 }
113
114 return $result;
115}
116
117function quiz_user_outline($course, $user, $mod, $quiz) {
a5e1f35c 118/// Return a small object with summary information about what a
119/// user has done with a given particular instance of this module
120/// Used for user activity reports.
121/// $return->time = the time they did it
122/// $return->info = a short text description
730fd187 123
124 return $return;
125}
126
127function quiz_user_complete($course, $user, $mod, $quiz) {
a5e1f35c 128/// Print a detailed representation of what a user has done with
129/// a given particular instance of this module, for user activity reports.
730fd187 130
131 return true;
132}
133
134function quiz_print_recent_activity(&$logs, $isteacher=false) {
a5e1f35c 135/// Given a list of logs, assumed to be those since the last login
136/// this function prints a short list of changes related to this module
137/// If isteacher is true then perhaps additional information is printed.
138/// This function is called from course/lib.php: print_recent_activity()
730fd187 139
140 global $CFG, $COURSE_TEACHER_COLOR;
141
142 return $content; // True if anything was printed, otherwise false
143}
144
145function quiz_cron () {
a5e1f35c 146/// Function to be run periodically according to the moodle cron
147/// This function searches for things that need to be done, such
148/// as sending out mail, toggling flags etc ...
730fd187 149
150 global $CFG;
151
152 return true;
153}
154
155
156//////////////////////////////////////////////////////////////////////////////////////
a5e1f35c 157/// Any other quiz functions go here. Each of them must have a name that
158/// starts with quiz_
730fd187 159
14d8c0b4 160function quiz_print_question($number, $questionid, $grade, $courseid) {
a5e1f35c 161/// Prints a quiz question, any format
14d8c0b4 162
163 if (!$question = get_record("quiz_questions", "id", $questionid)) {
164 notify("Error: Question not found!");
165 }
166
167 $stranswer = get_string("answer", "quiz");
168 $strmarks = get_string("marks", "quiz");
169
170 echo "<TABLE WIDTH=100% CELLSPACING=10><TR><TD NOWRAP WIDTH=100 VALIGN=top>";
171 echo "<P ALIGN=CENTER><B>$number</B><BR><FONT SIZE=1>$grade $strmarks</FONT></P>";
172 print_spacer(1,100);
173 echo "</TD><TD VALIGN=TOP>";
174
175 switch ($question->type) {
a5e1f35c 176 case SHORTANSWER:
14d8c0b4 177 if (!$options = get_record("quiz_shortanswer", "question", $question->id)) {
178 notify("Error: Missing question options!");
179 }
14d8c0b4 180 echo "<P>$question->question</P>";
181 if ($question->image) {
182 print_file_picture($question->image, $courseid, 200);
183 }
184 echo "<P ALIGN=RIGHT>$stranswer: <INPUT TYPE=TEXT NAME=q$question->id SIZE=20></P>";
185 break;
186
a5e1f35c 187 case TRUEFALSE:
14d8c0b4 188 if (!$options = get_record("quiz_truefalse", "question", $question->id)) {
189 notify("Error: Missing question options!");
190 }
191 if (!$true = get_record("quiz_answers", "id", $options->true)) {
192 notify("Error: Missing question answers!");
193 }
194 if (!$false = get_record("quiz_answers", "id", $options->false)) {
195 notify("Error: Missing question answers!");
196 }
197 if (!$true->answer) {
198 $true->answer = get_string("true", "quiz");
199 }
200 if (!$false->answer) {
201 $false->answer = get_string("false", "quiz");
202 }
203 echo "<P>$question->question</P>";
204 if ($question->image) {
205 print_file_picture($question->image, $courseid, 200);
206 }
207 echo "<P ALIGN=RIGHT>$stranswer:&nbsp;&nbsp;";
208 echo "<INPUT TYPE=RADIO NAME=\"q$question->id\" VALUE=\"$true->id\">$true->answer";
209 echo "&nbsp;&nbsp;&nbsp;";
210 echo "<INPUT TYPE=RADIO NAME=\"q$question->id\" VALUE=\"$false->id\">$false->answer</P>";
211 break;
212
a5e1f35c 213 case MULTICHOICE:
14d8c0b4 214 if (!$options = get_record("quiz_multichoice", "question", $question->id)) {
215 notify("Error: Missing question options!");
216 }
a5e1f35c 217 if (!$answers = get_records_list("quiz_answers", "id", $options->answers)) {
14d8c0b4 218 notify("Error: Missing question answers!");
219 }
220 echo "<P>$question->question</P>";
221 if ($question->image) {
222 print_file_picture($question->image, $courseid, 200);
223 }
224 echo "<TABLE ALIGN=right>";
225 echo "<TR><TD valign=top>$stranswer:&nbsp;&nbsp;</TD><TD>";
226 echo "<TABLE ALIGN=right>";
227 $answerids = explode(",", $options->answers);
228 foreach ($answerids as $key => $answerid) {
229 $answer = $answers[$answerid];
230 $qnum = $key + 1;
231 echo "<TR><TD valign=top>";
a5e1f35c 232 if ($options->single) {
14d8c0b4 233 echo "<INPUT TYPE=RADIO NAME=q$question->id VALUE=\"$answer->id\">";
234 } else {
a5e1f35c 235 echo "<INPUT TYPE=CHECKBOX NAME=q$question->id"."a$answer->id VALUE=\"$answer->id\">";
14d8c0b4 236 }
237 echo "</TD>";
238 echo "<TD valign=top>$qnum. $answer->answer</TD>";
239 echo "</TR>";
240 }
241 echo "</TABLE>";
242 echo "</TABLE>";
243 break;
244
245 default:
246 notify("Error: Unknown question type!");
247 }
248
249 echo "</TD></TR></TABLE>";
3a506ca2 250}
251
a5e1f35c 252function quiz_print_quiz_questions($quiz, $results=NULL) {
253// Prints a whole quiz on one page.
254
255 if (!$quiz->questions) {
256 error("No questions have been defined!", "view.php?id=$cm->id");
257 }
258
259 $questions = explode(",", $quiz->questions);
260
261 if (!$grades = get_records_list("quiz_question_grades", "question", $quiz->questions, "", "question,grade")) {
262 error("No grades were found for these questions!");
263 }
264
265 echo "<FORM METHOD=POST ACTION=attempt.php>";
266 echo "<INPUT TYPE=hidden NAME=q VALUE=\"$quiz->id\">";
267 foreach ($questions as $key => $questionid) {
268 print_simple_box_start("CENTER", "90%");
269 quiz_print_question($key+1, $questionid, $grades[$questionid]->grade, $course->id);
270 print_simple_box_end();
271 echo "<BR>";
272 }
273 echo "<CENTER><INPUT TYPE=submit VALUE=\"".get_string("savemyanswers", "quiz")."\"></CENTER>";
274 echo "</FORM>";
275}
6a952ce7 276
277function quiz_get_default_category($courseid) {
278 if ($categories = get_records("quiz_categories", "course", $courseid, "id")) {
279 foreach ($categories as $category) {
280 return $category; // Return the first one (lowest id)
281 }
282 }
283
284 // Otherwise, we need to make one
285 $category->name = get_string("miscellaneous", "quiz");
286 $category->info = get_string("miscellaneous", "quiz");
287 $category->course = $courseid;
288 $category->publish = 0;
289
290 if (!$category->id = insert_record("quiz_categories", $category)) {
291 notify("Error creating a default category!");
292 return false;
293 }
294 return $category;
295}
296
297function quiz_print_category_form($course, $current) {
298// Prints a form to choose categories
299
300 if (!$categories = get_records_sql_menu("SELECT id,name FROM quiz_categories
301 WHERE course='$course->id' OR publish = '1'
302 ORDER by name ASC")) {
303 if (!$category = quiz_get_default_category($course->id)) {
304 notify("Error creating a default category!");
305 return false;
306 }
307 $categories[$category->id] = $category->name;
308 }
309 $strcategory = get_string("category", "quiz");
310 $strshow = get_string("show", "quiz");
311 $strrename = get_string("rename", "quiz");
312 $strdelete = get_string("delete");
313 $strnew = get_string("new");
314
315 echo "<FORM METHOD=POST ACTION=edit.php>";
316 echo "<B>$strcategory:</B>&nbsp;";
317 choose_from_menu($categories, "cat", "$current");
318 echo "<INPUT TYPE=submit NAME=catshow VALUE=\"$strshow\">";
319 echo "<INPUT TYPE=submit NAME=catrename VALUE=\"$strrename\">";
320 echo "<INPUT TYPE=submit NAME=catdelete VALUE=\"$strdelete\">";
321 echo "<INPUT TYPE=submit NAME=catnew VALUE=\"$strnew\">";
322 echo "</FORM>";
323}
324
325
7bd1aa1d 326function quiz_get_all_question_grades($questionlist, $quizid) {
327// Given a list of question IDs, finds grades or invents them to
328// create an array of matching grades
329
330 $questions = get_records_sql("SELECT * FROM quiz_question_grades
331 WHERE quiz = '$quizid'
332 AND question IN ($questionlist)");
333
334 $list = explode(",", $questionlist);
335 $grades = array();
336
337 foreach ($list as $qid) {
338 if (isset($questions[$qid])) {
339 $grades[$qid] = $questions[$qid]->grade;
340 } else {
341 $grades[$qid] = 1;
342 }
343 }
344 return $grades;
345}
346
347
348function quiz_print_question_list($questionlist, $grades) {
6a952ce7 349// Prints a list of quiz questions in a small layout form with knobs
7bd1aa1d 350// $questionlist is comma-separated list
351// $grades is an array of corresponding grades
6a952ce7 352
353 global $THEME;
354
355 if (!$questionlist) {
356 echo "<P align=center>";
357 print_string("noquestions", "quiz");
358 echo "</P>";
359 return;
360 }
361
362 $order = explode(",", $questionlist);
363
7bd1aa1d 364 if (!$questions = get_records_list("quiz_questions", "id", $questionlist)) {
6a952ce7 365 error("No questions were found!");
366 }
367
368 $strorder = get_string("order");
369 $strquestionname = get_string("questionname", "quiz");
370 $strgrade = get_string("grade");
371 $strdelete = get_string("delete");
372 $stredit = get_string("edit");
373 $strmoveup = get_string("moveup");
374 $strmovedown = get_string("movedown");
375 $strsavegrades = get_string("savegrades", "quiz");
376
377 for ($i=100; $i>=0; $i--) {
7bd1aa1d 378 $gradesmenu[$i] = $i;
6a952ce7 379 }
380 $count = 0;
381 $sumgrade = 0;
382 $total = count($order);
383 echo "<FORM METHOD=post ACTION=edit.php>";
384 echo "<TABLE BORDER=0 CELLPADDING=5 CELLSPACING=2 WIDTH=\"100%\">";
385 echo "<TR><TH COLSPAN=3>$strorder</TH><TH align=left>$strquestionname</TH><TH>$strgrade</TH><TH>$stredit</TH></TR>";
386 foreach ($order as $qnum) {
387 $count++;
388 echo "<TR BGCOLOR=\"$THEME->cellcontent\">";
389 echo "<TD>$count</TD>";
390 echo "<TD>";
391 if ($count != 1) {
392 echo "<A TITLE=\"$strmoveup\" HREF=\"edit.php?up=$qnum\"><IMG
393 SRC=\"../../pix/t/up.gif\" BORDER=0></A>";
394 }
395 echo "</TD>";
396 echo "<TD>";
397 if ($count != $total) {
398 echo "<A TITLE=\"$strmovedown\" HREF=\"edit.php?down=$qnum\"><IMG
399 SRC=\"../../pix/t/down.gif\" BORDER=0></A>";
400 }
401 echo "</TD>";
402 echo "<TD>".$questions[$qnum]->name."</TD>";
403 echo "<TD>";
7bd1aa1d 404 choose_from_menu($gradesmenu, "q$qnum", $grades[$qnum], "");
6a952ce7 405 echo "<TD>";
406 echo "<A TITLE=\"$strdelete\" HREF=\"edit.php?delete=$qnum\"><IMG
407 SRC=\"../../pix/t/delete.gif\" BORDER=0></A>&nbsp;";
408 echo "<A TITLE=\"$stredit\" HREF=\"question.php?id=$qnum\"><IMG
409 SRC=\"../../pix/t/edit.gif\" BORDER=0></A>";
410 echo "</TD>";
411
7bd1aa1d 412 $sumgrade += $grades[$qnum];
6a952ce7 413 }
414 echo "<TR><TD COLSPAN=3><TD ALIGN=right>";
415 echo "<INPUT TYPE=submit VALUE=\"$strsavegrades:\">";
416 echo "<INPUT TYPE=hidden NAME=grade VALUE=\"save\">";
417 echo "<TD ALIGN=LEFT BGCOLOR=\"$THEME->cellcontent\">";
418 echo "<B>$sumgrade</B>";
419 echo "</TD><TD></TD></TR>";
420 echo "</TABLE>";
421 echo "</FORM>";
422}
423
424
425function quiz_print_cat_question_list($categoryid) {
426// Prints a form to choose categories
427
428 global $THEME, $QUIZ_QUESTION_TYPE;
429
430 $strquestion = get_string("question", "quiz");
431 $strnoquestions = get_string("noquestions", "quiz");
432 $strselect = get_string("select", "quiz");
433 $strcreatenewquestion = get_string("createnewquestion", "quiz");
434 $strquestionname = get_string("questionname", "quiz");
435 $strdelete = get_string("delete");
436 $stredit = get_string("edit");
437 $straddselectedtoquiz = get_string("addselectedtoquiz", "quiz");
438
439 if (!$categoryid) {
440 echo "<P align=center>";
441 print_string("selectcategoryabove", "quiz");
442 echo "</P>";
443 return;
444 }
a5e1f35c 445
6a952ce7 446 if (!$category = get_record("quiz_categories", "id", "$categoryid")) {
447 notify("Category not found!");
448 return;
449 }
450 echo "<P>$category->info</P>";
451
452 echo "<FORM METHOD=POST ACTION=question.php>";
453 echo "<B>$strquestion:</B>&nbsp;";
454 choose_from_menu($QUIZ_QUESTION_TYPE, "type", "", "");
455 echo "<INPUT TYPE=hidden NAME=category VALUE=\"$category->id\">";
456 echo "<INPUT TYPE=submit NAME=new VALUE=\"$strcreatenewquestion\">";
457 echo "</FORM>";
458
459 if (!$questions = get_records("quiz_questions", "category", $category->id)) {
460 echo "<P align=center>";
461 print_string("noquestions", "quiz");
462 echo "</P>";
463 return;
464 }
465
466 echo "<FORM METHOD=post ACTION=edit.php>";
467 echo "<TABLE BORDER=0 CELLPADDING=5 CELLSPACING=2 WIDTH=\"100%\">";
468 echo "<TR><TH width=10>$strselect</TH><TH width=* align=left>$strquestionname</TH><TH width=10>$stredit</TH></TR>";
469 foreach ($questions as $question) {
470 echo "<TR BGCOLOR=\"$THEME->cellcontent\">";
471 echo "<TD ALIGN=CENTER>";
472 echo "<INPUT TYPE=CHECKBOX NAME=q$question->id VALUE=\"1\">";
473 echo "</TD>";
474 echo "<TD>".$question->name."</TD>";
475 echo "<TD>";
476 echo "<A TITLE=\"$strdelete\" HREF=\"question.php?delete=$question->id\"><IMG
477 SRC=\"../../pix/t/delete.gif\" BORDER=0></A>&nbsp;";
478 echo "<A TITLE=\"$stredit\" HREF=\"question.php?id=$question->id\"><IMG
479 SRC=\"../../pix/t/edit.gif\" BORDER=0></A>";
480 echo "</TD></TR>";
481 }
482 echo "<TR><TD COLSPAN=3>";
483 echo "<INPUT TYPE=hidden NAME=add VALUE=\"1\">";
484 echo "<INPUT TYPE=submit VALUE=\"<< $straddselectedtoquiz\">";
485 echo "</TD></TR>";
486 echo "</TABLE>";
487 echo "</FORM>";
488}
a5e1f35c 489
3a506ca2 490
491function quiz_get_user_attempts($quizid, $userid) {
a5e1f35c 492// Returns a list of all attempts by a user
3a506ca2 493 return get_records_sql("SELECT * FROM quiz_attempts WHERE quiz = '$quizid' and user = '$userid' ORDER by attempt ASC");
494}
495
a5e1f35c 496function quiz_get_best_grade($quizid, $userid) {
497/// Get the best current grade for a particular user in a quiz
3a506ca2 498 if (!$grade = get_record_sql("SELECT * FROM quiz_grades WHERE quiz = '$quizid' and user = '$userid'")) {
499 return 0;
500 }
501
502 return $grade->grade;
503}
504
579ddad5 505function quiz_get_grade_records($quiz) {
506/// Gets all info required to display the table of quiz results
507/// for report.php
508
509 return get_records_sql("SELECT qg.*, u.firstname, u.lastname, u.picture
510 FROM quiz_grades qg, user u
511 WHERE qg.quiz = '$quiz->id'
512 AND qg.user = u.id");
513}
514
a5e1f35c 515function quiz_save_best_grade($quiz, $user) {
516/// Calculates the best grade out of all attempts at a quiz for a user,
517/// and then saves that grade in the quiz_grades table.
518
519 if (!$attempts = quiz_get_user_attempts($quiz->id, $user->id)) {
520 return false;
521 }
522
523 $bestgrade = quiz_calculate_best_grade($quiz, $attempts);
524 $bestgrade = (($bestgrade / $quiz->sumgrades) * $quiz->grade);
525
526 if ($grade = get_record_sql("SELECT * FROM quiz_grades WHERE quiz='$quiz->id' AND user='$user->id'")) {
527 $grade->grade = $bestgrade;
528 $grade->timemodified = time();
529 if (!update_record("quiz_grades", $grade)) {
530 return false;
531 }
532 } else {
533 $grade->quiz = $quiz->id;
534 $grade->user = $user->id;
535 $grade->grade = $bestgrade;
536 $grade->timemodified = time();
537 if (!insert_record("quiz_grades", $grade)) {
538 return false;
539 }
540 }
541 return true;
542}
543
544
545function quiz_get_answer($question) {
546// Given a question, returns the correct answers and grades
547 switch ($question->type) {
548 case SHORTANSWER; // Could be multiple answers
549 return get_records_sql("SELECT a.*, sa.case, g.grade
550 FROM quiz_shortanswer sa, quiz_answers a, quiz_question_grades g
551 WHERE sa.question = '$question->id'
552 AND sa.question = a.question
553 AND sa.question = g.question");
554 break;
555
556 case TRUEFALSE; // Should be always two answers
557 return get_records_sql("SELECT a.*, g.grade
558 FROM quiz_answers a, quiz_question_grades g
559 WHERE a.question = '$question->id'
560 AND a.question = g.question");
561 break;
562
563 case MULTICHOICE; // Should be multiple answers
564 return get_records_sql("SELECT a.*, mc.single, g.grade
565 FROM quiz_multichoice mc, quiz_answers a, quiz_question_grades g
566 WHERE mc.question = '$question->id'
567 AND mc.question = a.question
568 AND mc.question = g.question");
569 break;
570
571 default:
572 return false;
573 }
574}
575
3a506ca2 576function quiz_calculate_best_grade($quiz, $attempts) {
a5e1f35c 577/// Calculate the best grade for a quiz given a number of attempts by a particular user.
3a506ca2 578
579 switch ($quiz->grademethod) {
a5e1f35c 580
581 case ATTEMPTFIRST:
3a506ca2 582 foreach ($attempts as $attempt) {
a5e1f35c 583 return $attempt->sumgrades;
3a506ca2 584 }
a5e1f35c 585 break;
586
587 case ATTEMPTLAST:
588 foreach ($attempts as $attempt) {
589 $final = $attempt->sumgrades;
590 }
591 return $final;
3a506ca2 592
a5e1f35c 593 case GRADEAVERAGE:
3a506ca2 594 $sum = 0;
595 $count = 0;
596 foreach ($attempts as $attempt) {
a5e1f35c 597 $sum += $attempt->sumgrades;
3a506ca2 598 $count++;
599 }
600 return (float)$sum/$count;
601
3a506ca2 602 default:
a5e1f35c 603 case GRADEHIGHEST:
604 $max = 0;
3a506ca2 605 foreach ($attempts as $attempt) {
a5e1f35c 606 if ($attempt->sumgrades > $max) {
607 $max = $attempt->sumgrades;
608 }
3a506ca2 609 }
a5e1f35c 610 return $max;
611 }
612}
613
614function quiz_save_attempt($quiz, $questions, $result, $attemptnum) {
615/// Given a quiz, a list of attempted questions and a total grade
616/// this function saves EVERYTHING so it can be reconstructed later
617/// if necessary.
618
619 global $USER;
620
621 // First let's save the attempt record itself
622
623 $attempt->quiz = $quiz->id;
624 $attempt->user = $USER->id;
625 $attempt->attempt = $attemptnum;
626 $attempt->sumgrades = $result->sumgrades;
627 $attempt->timemodified = time();
628
629 if (!$attempt->id = insert_record("quiz_attempts", $attempt)) {
630 return false;
631 }
632
633 // Now let's save all the questions for this attempt
634
635 foreach ($questions as $question) {
636 $response->attempt = $attempt->id;
637 $response->question = $question->id;
638 $response->grade = $result->grades[$question->id];
639 if ($question->answer) {
640 $response->answer = implode(",",$question->answer);
641 } else {
642 $response->answer = "";
643 }
644 if (!insert_record("quiz_responses", $response)) {
645 return false;
646 }
3a506ca2 647 }
a5e1f35c 648 return true;
3a506ca2 649}
730fd187 650
a5e1f35c 651
652function quiz_grade_attempt_results($quiz, $questions) {
653/// Given a list of questions (including answers for each one)
654/// this function does all the hard work of calculating the
655/// grades for each question, as well as a total grade for
656/// for the whole quiz. It returns everything in a structure
657/// that looks like:
658/// $result->sumgrades (sum of all grades for all questions)
659/// $result->percentage (Percentage of grades that were correct)
660/// $result->grade (final grade result for the whole quiz)
661/// $result->grades[] (array of grades, indexed by question id)
662/// $result->feedback[] (array of feedback arrays, indexed by question id)
663
664 if (!$questions) {
665 error("No questions!");
666 }
667
668 $result->sumgrades = 0;
669
670 foreach ($questions as $question) {
671 if (!$answers = quiz_get_answer($question)) {
672 error("No answer defined for question id $question->id!");
673 }
674
675 $grade = 0; // default
676 $feedback = array ();
677
678 switch ($question->type) {
679 case SHORTANSWER:
680 if ($question->answer) {
681 $question->answer = $question->answer[0];
682 } else {
683 $question->answer = NULL;
684 }
685 foreach($answers as $answer) { // There might be multiple right answers
686 $feedback[$answer->id] = $answer->feedback;
687 if (!$answer->case) { // Don't compare case
688 $answer->answer = strtolower($answer->answer);
689 $question->answer = strtolower($question->answer);
690 }
691 if ($question->answer == $answer->answer) {
692 $grade = (float)$answer->fraction * $answer->grade;
693 }
694 }
695 break;
696
697
698 case TRUEFALSE:
699 if ($question->answer) {
700 $question->answer = $question->answer[0];
701 } else {
702 $question->answer = NULL;
703 }
704 foreach($answers as $answer) { // There should be two answers (true and false)
705 $feedback[$answer->id] = $answer->feedback;
706 if ($question->answer == $answer->id) {
707 $grade = (float)$answer->fraction * $answer->grade;
708 }
709 }
710 break;
711
712
713 case MULTICHOICE:
714 foreach($answers as $answer) { // There will be multiple answers, perhaps more than one is right
715 $feedback[$answer->id] = $answer->feedback;
716 if ($question->answer) {
717 foreach ($question->answer as $questionanswer) {
718 if ($questionanswer == $answer->id) {
719 if ($answer->single) {
720 $grade = (float)$answer->fraction * $answer->grade;
721 continue;
722 } else {
723 $grade += (float)$answer->fraction * $answer->grade;
724 }
725 }
726 }
727 }
728 }
729 break;
730
731
732 }
733 if ($grade < 0.0) { // No negative grades
734 $grade = 0.0;
735 }
736 $result->grades[$question->id] = $grade;
737 $result->sumgrades += $grade;
738 $result->feedback[$question->id] = $feedback;
739 }
740
741 $result->percentage = ($result->sumgrades / $quiz->sumgrades);
742 $result->grade = $result->percentage * $quiz->grade;
743
744 return $result;
745}
746
730fd187 747?>