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