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