730fd187 |
1 | <?PHP // $Id$ |
2 | |
a5e1f35c |
3 | /// Library of function for module quiz |
730fd187 |
4 | |
a5e1f35c |
5 | /// CONSTANTS /////////////////////////////////////////////////////////////////// |
730fd187 |
6 | |
a5e1f35c |
7 | define("GRADEHIGHEST", "1"); |
8 | define("GRADEAVERAGE", "2"); |
9 | define("ATTEMPTFIRST", "3"); |
10 | define("ATTEMPTLAST", "4"); |
11 | $QUIZ_GRADE_METHOD = array ( GRADEHIGHEST => get_string("gradehighest", "quiz"), |
12 | GRADEAVERAGE => get_string("gradeaverage", "quiz"), |
13 | ATTEMPTFIRST => get_string("attemptfirst", "quiz"), |
14 | ATTEMPTLAST => get_string("attemptlast", "quiz")); |
15 | |
16 | define("SHORTANSWER", "1"); |
17 | define("TRUEFALSE", "2"); |
18 | define("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 | |
27 | function 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 | |
55 | function 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 | |
97 | function 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 | |
117 | function 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 | |
127 | function 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 | |
134 | function 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 | |
145 | function 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 |
160 | function 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: "; |
208 | echo "<INPUT TYPE=RADIO NAME=\"q$question->id\" VALUE=\"$true->id\">$true->answer"; |
209 | echo " "; |
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: </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 |
252 | function 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 | |
277 | function 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 | |
297 | function 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> "; |
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 |
326 | function 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 | |
348 | function 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> "; |
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 | |
425 | function 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> "; |
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> "; |
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 | |
491 | function 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 |
496 | function 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 |
505 | function 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 |
515 | function 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 | |
545 | function 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 |
576 | function 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 | |
614 | function 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 | |
652 | function 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 | ?> |