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