2c3968c4 |
1 | <?php |
2 | /** |
3 | * This script lists student attempts |
4 | * |
5 | * @version $Id$ |
6 | * @author Martin Dougiamas, Tim Hunt and others. |
7 | * @license http://www.gnu.org/copyleft/gpl.html GNU Public License |
8 | * @package quiz |
98f38217 |
9 | */ |
2c3968c4 |
10 | |
11 | require_once($CFG->libdir.'/tablelib.php'); |
0217f932 |
12 | require_once($CFG->dirroot.'/mod/quiz/report/overview/overviewsettings_form.php'); |
c35f3afc |
13 | require_once($CFG->dirroot.'/mod/quiz/report/overview/overview_table.php'); |
7bbe08a2 |
14 | |
c386eaa3 |
15 | class quiz_overview_report extends quiz_default_report { |
7bbe08a2 |
16 | |
2c3968c4 |
17 | /** |
18 | * Display the report. |
19 | */ |
20 | function display($quiz, $cm, $course) { |
9cf4a18b |
21 | global $CFG, $COURSE, $DB; |
7bbe08a2 |
22 | |
98f38217 |
23 | $this->context = get_context_instance(CONTEXT_MODULE, $cm->id); |
07a7d859 |
24 | |
c35f3afc |
25 | // Work out some display options - whether there is feedback, and whether scores should be shown. |
26 | $hasfeedback = quiz_has_feedback($quiz->id) && $quiz->grade > 1.e-7 && $quiz->sumgrades > 1.e-7; |
27 | $fakeattempt = new stdClass(); |
28 | $fakeattempt->preview = false; |
29 | $fakeattempt->timefinish = $quiz->timeopen; |
98f38217 |
30 | $reviewoptions = quiz_get_reviewoptions($quiz, $fakeattempt, $this->context); |
c35f3afc |
31 | $showgrades = $quiz->grade && $quiz->sumgrades && $reviewoptions->scores; |
32 | |
33 | $download = optional_param('download', '', PARAM_ALPHA); |
98f38217 |
34 | |
35 | /// find out current groups mode |
36 | $currentgroup = groups_get_activity_group($cm, true); |
37 | if (!$students = get_users_by_capability($this->context, 'mod/quiz:attempt','','','','','','',false)){ |
38 | $students = array(); |
39 | } else { |
40 | $students = array_keys($students); |
41 | } |
7bbe08a2 |
42 | |
98f38217 |
43 | if (empty($currentgroup)) { |
44 | // all users who can attempt quizzes |
45 | $allowed = $students; |
46 | $groupstudents = array(); |
47 | } else { |
48 | // all users who can attempt quizzes and who are in the currently selected group |
49 | if (!$groupstudents = get_users_by_capability($this->context, 'mod/quiz:attempt','','','','',$currentgroup,'',false)){ |
50 | $groupstudents = array(); |
51 | } else { |
52 | $groupstudents = array_keys($groupstudents); |
53 | } |
54 | $allowed = $groupstudents; |
55 | } |
56 | |
57 | if (empty($currentgroup)||$groupstudents) { |
58 | if (optional_param('delete', 0, PARAM_BOOL)){ |
59 | if($attemptids = optional_param('attemptid', array(), PARAM_INT)) { |
60 | //attempts need to be deleted |
61 | $this->delete_selected_attempts($quiz, $cm, $attemptids, $groupstudents); |
62 | //No need for a redirect, any attemptids that do not exist are ignored. |
63 | //So no problem if the user refreshes and tries to delete the same attempts |
64 | //twice. |
65 | } |
66 | } else if (optional_param('regrade', 0, PARAM_BOOL)){ |
67 | if($attemptids = optional_param('attemptid', array(), PARAM_INT)) { |
68 | $this->regrade_selected_attempts($quiz, $attemptids, $groupstudents); |
69 | //No need for a redirect, any attemptids that do not exist are ignored. |
70 | //So no problem if the user refreshes and tries to delete the same attempts |
71 | //twice. |
72 | } |
2fecd85b |
73 | } |
7bbe08a2 |
74 | } |
18dfa593 |
75 | |
01600b51 |
76 | |
0217f932 |
77 | $pageoptions = array(); |
78 | $pageoptions['id'] = $cm->id; |
79 | $pageoptions['q'] = $quiz->id; |
80 | $pageoptions['mode'] = 'overview'; |
81 | |
82 | $reporturl = new moodle_url($CFG->wwwroot.'/mod/quiz/report.php', $pageoptions); |
334edb71 |
83 | $qmsubselect = quiz_report_qm_filter_select($quiz); |
84 | |
85 | |
7660aa80 |
86 | $mform = new mod_quiz_report_overview_settings($reporturl, array('qmsubselect'=> $qmsubselect, 'quiz'=>$quiz, 'currentgroup'=>$currentgroup)); |
0217f932 |
87 | if ($fromform = $mform->get_data()){ |
98f38217 |
88 | $regradeall = false; |
89 | $regradealldry = false; |
90 | $regradealldrydo = false; |
0217f932 |
91 | $attemptsmode = $fromform->attemptsmode; |
4469159e |
92 | if ($qmsubselect){ |
93 | //control is not on the form if |
9cf4a18b |
94 | //the grading method is not set |
4469159e |
95 | //to grade one attempt per user eg. for average attempt grade. |
96 | $qmfilter = $fromform->qmfilter; |
97 | } else { |
98 | $qmfilter = 0; |
99 | } |
98f38217 |
100 | $regradefilter = $fromform->regradefilter; |
0217f932 |
101 | set_user_preference('quiz_report_overview_detailedmarks', $fromform->detailedmarks); |
102 | set_user_preference('quiz_report_pagesize', $fromform->pagesize); |
103 | $detailedmarks = $fromform->detailedmarks; |
104 | $pagesize = $fromform->pagesize; |
105 | } else { |
98f38217 |
106 | $regradeall = optional_param('regradeall', 0, PARAM_BOOL); |
107 | $regradealldry = optional_param('regradealldry', 0, PARAM_BOOL); |
108 | $regradealldrydo = optional_param('regradealldrydo', 0, PARAM_BOOL); |
7660aa80 |
109 | $attemptsmode = optional_param('attemptsmode', null, PARAM_INT); |
98f38217 |
110 | if ($qmsubselect){ |
111 | $qmfilter = optional_param('qmfilter', 0, PARAM_INT); |
112 | } else { |
113 | $qmfilter = 0; |
114 | } |
115 | $regradefilter = optional_param('regradefilter', 0, PARAM_INT); |
7660aa80 |
116 | if ($attemptsmode === null){ |
117 | //default |
118 | $attemptsmode = QUIZ_REPORT_ATTEMPTS_ALL; |
119 | } else if ($currentgroup){ |
120 | //default for when a group is selected |
121 | if ($attemptsmode === null || $attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL){ |
122 | $attemptsmode = QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH; |
123 | } |
124 | } else if (!$currentgroup && $course->id == SITEID) { |
125 | //force report on front page to show all, unless a group is selected. |
126 | $attemptsmode = QUIZ_REPORT_ATTEMPTS_ALL; |
127 | } |
0217f932 |
128 | $detailedmarks = get_user_preferences('quiz_report_overview_detailedmarks', 1); |
129 | $pagesize = get_user_preferences('quiz_report_pagesize', 0); |
130 | } |
ca359748 |
131 | if (!$reviewoptions->scores) { |
132 | $detailedmarks = 0; |
133 | } |
0217f932 |
134 | if ($pagesize < 1) { |
135 | $pagesize = QUIZ_REPORT_DEFAULT_PAGE_SIZE; |
136 | } |
2fecd85b |
137 | // We only want to show the checkbox to delete attempts |
138 | // if the user has permissions and if the report mode is showing attempts. |
98f38217 |
139 | $candelete = has_capability('mod/quiz:deleteattempts', $this->context) |
2fecd85b |
140 | && ($attemptsmode!= QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH_NO); |
141 | |
0217f932 |
142 | |
143 | $displayoptions = array(); |
144 | $displayoptions['attemptsmode'] = $attemptsmode; |
4469159e |
145 | $displayoptions['qmfilter'] = $qmfilter; |
98f38217 |
146 | $displayoptions['regradefilter'] = $regradefilter; |
01600b51 |
147 | |
c35f3afc |
148 | //work out the sql for this table. |
c35f3afc |
149 | if ($detailedmarks) { |
150 | $questions = quiz_report_load_questions($quiz); |
151 | } else { |
152 | $questions = array(); |
153 | } |
8673a566 |
154 | $table = new quiz_report_overview_table($quiz , $qmsubselect, $groupstudents, |
57d6a267 |
155 | $students, $detailedmarks, $questions, $candelete, $reporturl, |
156 | $displayoptions, $this->context); |
c35f3afc |
157 | $table->is_downloading($download, get_string('reportoverview','quiz'), |
158 | "$COURSE->shortname ".format_string($quiz->name,true)); |
159 | if (!$table->is_downloading()) { |
160 | // Only print headers if not asked to download data |
161 | $this->print_header_and_tabs($cm, $course, $quiz, "overview"); |
162 | } |
98f38217 |
163 | |
164 | if ($regradeall){ |
165 | $this->regrade_all(false, $quiz, $groupstudents); |
166 | } else if ($regradealldry){ |
167 | $this->regrade_all(true, $quiz, $groupstudents); |
168 | } else if ($regradealldrydo){ |
169 | $this->regrade_all_needed($quiz, $groupstudents); |
170 | } |
171 | if ($regradeall || $regradealldry || $regradealldrydo){ |
172 | redirect($reporturl->out(false, $displayoptions), '', 5); |
173 | } |
174 | |
5ada3c8e |
175 | if ($groupmode = groups_get_activity_groupmode($cm)) { // Groups are being used |
c35f3afc |
176 | if (!$table->is_downloading()) { |
177 | groups_print_activity_menu($cm, $reporturl->out(false, $displayoptions)); |
970d0fe0 |
178 | } |
2d7617c6 |
179 | } |
98f38217 |
180 | |
181 | |
2a13e454 |
182 | // Print information on the number of existing attempts |
c35f3afc |
183 | if (!$table->is_downloading()) { //do not print notices when downloading |
aa17ea53 |
184 | if ($strattemptnum = quiz_num_attempt_summary($quiz, $cm, true, $currentgroup)) { |
2a13e454 |
185 | echo '<div class="quizattemptcounts">' . $strattemptnum . '</div>'; |
186 | } |
187 | } |
abe67b24 |
188 | $nostudents = false; |
189 | if (!$students){ |
190 | notify(get_string('nostudentsyet')); |
191 | $nostudents = true; |
192 | }else if ($currentgroup && !$groupstudents){ |
193 | notify(get_string('nostudentsingroup')); |
194 | $nostudents = true; |
195 | } |
95758992 |
196 | if (!$table->is_downloading()) { |
197 | // Print display options |
198 | $mform->set_data($displayoptions +compact('detailedmarks', 'pagesize')); |
199 | $mform->display(); |
200 | } |
98f38217 |
201 | |
b6943d37 |
202 | |
98f38217 |
203 | |
b6943d37 |
204 | if (!$table->is_downloading()) { //do not print notices when downloading |
205 | $countregradeneeded = $this->count_regrade_all_needed($quiz, $groupstudents); |
206 | //regrade buttons |
207 | if ($currentgroup){ |
208 | $a= new object(); |
209 | $a->groupname = groups_get_group_name($currentgroup); |
210 | $a->coursestudents = $COURSE->students; |
211 | $a->countregradeneeded = $countregradeneeded; |
212 | $regradealldrydolabel = get_string('regradealldrydogroup', 'quiz_overview', $a); |
213 | $regradealldrylabel = get_string('regradealldrygroup', 'quiz_overview', $a); |
214 | $regradealllabel = get_string('regradeallgroup', 'quiz_overview', $a); |
215 | } else { |
216 | $regradealldrydolabel = get_string('regradealldrydo', 'quiz_overview', $countregradeneeded); |
217 | $regradealldrylabel = get_string('regradealldry', 'quiz_overview'); |
218 | $regradealllabel = get_string('regradeall', 'quiz_overview'); |
219 | } |
220 | if (has_capability('mod/quiz:grade', $this->context)){ |
221 | echo '<div class="mdl-align">'; |
222 | echo '<form action="'.$reporturl->out(true).'">'; |
223 | echo '<div>'; |
224 | echo $reporturl->hidden_params_out(array(), 0, $displayoptions); |
225 | echo '<input type="submit" name="regradeall" value="'.$regradealllabel.'"/>'; |
226 | echo '<input type="submit" name="regradealldry" value="'.$regradealldrylabel.'"/>'; |
227 | if ($countregradeneeded){ |
228 | echo '<input type="submit" name="regradealldrydo" value="'.$regradealldrydolabel.'"/>'; |
229 | } |
230 | echo '</div>'; |
231 | echo '</form>'; |
232 | echo '</div>'; |
314b8a20 |
233 | } |
314b8a20 |
234 | } |
abe67b24 |
235 | if (!$nostudents || ($attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL)){ |
236 | // Print information on the grading method and whether we are displaying |
237 | // |
238 | if (!$table->is_downloading()) { //do not print notices when downloading |
239 | if ($strattempthighlight = quiz_report_highlighting_grading_method($quiz, $qmsubselect, $qmfilter)) { |
240 | echo '<div class="quizattemptcounts">' . $strattempthighlight . '</div>'; |
241 | } |
242 | } |
243 | |
244 | |
245 | $showgrades = $quiz->grade && $quiz->sumgrades && $reviewoptions->scores; |
246 | $hasfeedback = quiz_has_feedback($quiz->id) && $quiz->grade > 1.e-7 && $quiz->sumgrades > 1.e-7; |
247 | |
248 | |
249 | // Construct the SQL |
55caa1d5 |
250 | $fields = $DB->sql_concat('u.id', '\'#\'', 'COALESCE(qa.attempt, \'0\')').' AS uniqueid, '; |
251 | if ($qmsubselect) { |
252 | $fields .= |
253 | "(CASE " . |
254 | " WHEN $qmsubselect THEN 1" . |
255 | " ELSE 0 " . |
256 | "END) AS gradedattempt, "; |
257 | } |
258 | |
259 | $fields .='qa.uniqueid AS attemptuniqueid, qa.id AS attempt, ' . |
375381a0 |
260 | 'u.id AS userid, u.idnumber, u.firstname, u.lastname, u.picture, u.imagealt, '. |
abe67b24 |
261 | 'qa.sumgrades, qa.timefinish, qa.timestart, qa.timefinish - qa.timestart AS duration '; |
98f38217 |
262 | |
abe67b24 |
263 | // This part is the same for all cases - join users and quiz_attempts tables |
264 | $from = '{user} u '; |
265 | $from .= 'LEFT JOIN {quiz_attempts} qa ON qa.userid = u.id AND qa.quiz = :quizid'; |
266 | $params = array('quizid' => $quiz->id); |
267 | |
268 | if ($qmsubselect && $qmfilter){ |
269 | $from .= ' AND '.$qmsubselect; |
270 | } |
98f38217 |
271 | switch ($attemptsmode){ |
abe67b24 |
272 | case QUIZ_REPORT_ATTEMPTS_ALL: |
273 | // Show all attempts, including students who are no longer in the course |
274 | $where = 'qa.id IS NOT NULL AND qa.preview = 0'; |
275 | break; |
276 | case QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH: |
277 | // Show only students with attempts |
278 | list($allowed_usql, $allowed_params) = $DB->get_in_or_equal($allowed, SQL_PARAMS_NAMED, 'u0000'); |
279 | $params += $allowed_params; |
280 | $where = "u.id $allowed_usql AND qa.preview = 0 AND qa.id IS NOT NULL"; |
281 | break; |
282 | case QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH_NO: |
283 | // Show only students without attempts |
284 | list($allowed_usql, $allowed_params) = $DB->get_in_or_equal($allowed, SQL_PARAMS_NAMED, 'u0000'); |
285 | $params += $allowed_params; |
286 | $where = "u.id $allowed_usql AND qa.id IS NULL"; |
287 | break; |
288 | case QUIZ_REPORT_ATTEMPTS_ALL_STUDENTS: |
289 | // Show all students with or without attempts |
290 | list($allowed_usql, $allowed_params) = $DB->get_in_or_equal($allowed, SQL_PARAMS_NAMED, 'u0000'); |
291 | $params += $allowed_params; |
292 | $where = "u.id $allowed_usql AND (qa.preview = 0 OR qa.preview IS NULL)"; |
293 | break; |
294 | } |
295 | |
296 | $table->set_count_sql("SELECT COUNT(1) FROM $from WHERE $where", $params); |
98f38217 |
297 | |
298 | $sqlobject = new object; |
299 | $sqlobject->from = $from; |
300 | $sqlobject->where = $where; |
301 | $sqlobject->params = $params; |
302 | //test to see if there are any regraded attempts to be listed. |
303 | if (quiz_get_regraded_qs($sqlobject, 0, 1)){ |
304 | $regradedattempts = true; |
305 | } else { |
306 | $regradedattempts = false; |
307 | } |
308 | $fields .= ', COALESCE((SELECT MAX(qqr.regraded) FROM {quiz_question_regrade} qqr WHERE qqr.attemptid = qa.uniqueid),-1) AS regraded'; |
309 | if ($regradefilter){ |
310 | $where .= ' AND COALESCE((SELECT MAX(qqr.regraded) FROM {quiz_question_regrade} qqr WHERE qqr.attemptid = qa.uniqueid),-1) !=\'-1\''; |
311 | } |
abe67b24 |
312 | $table->set_sql($fields, $from, $where, $params); |
313 | |
314 | // Define table columns |
315 | $columns = array(); |
316 | $headers = array(); |
317 | |
318 | |
319 | if (!$table->is_downloading() && $candelete) { |
320 | $columns[]= 'checkbox'; |
321 | $headers[]= NULL; |
322 | } |
323 | |
324 | if (!$table->is_downloading() && $CFG->grade_report_showuserimage) { |
325 | $columns[]= 'picture'; |
326 | $headers[]= ''; |
327 | } |
328 | if (!$table->is_downloading()){ |
329 | $columns[]= 'fullname'; |
330 | $headers[]= get_string('name'); |
331 | } else { |
332 | $columns[]= 'lastname'; |
333 | $headers[]= get_string('lastname'); |
334 | $columns[]= 'firstname'; |
335 | $headers[]= get_string('firstname'); |
336 | } |
337 | |
338 | if ($CFG->grade_report_showuseridnumber) { |
339 | $columns[]= 'idnumber'; |
340 | $headers[]= get_string('idnumber'); |
341 | } |
342 | |
343 | $columns[]= 'timestart'; |
344 | $headers[]= get_string('startedon', 'quiz'); |
345 | |
346 | $columns[]= 'timefinish'; |
347 | $headers[]= get_string('timecompleted','quiz'); |
348 | |
349 | $columns[]= 'duration'; |
350 | $headers[]= get_string('attemptduration', 'quiz'); |
351 | |
352 | if ($detailedmarks) { |
353 | foreach ($questions as $id => $question) { |
354 | // Ignore questions of zero length |
355 | $columns[] = 'qsgrade'.$id; |
98f38217 |
356 | $header = '#'.$question->number; |
357 | if (!$table->is_downloading()) { |
358 | $header .='<br />'; |
359 | } else { |
360 | $header .=' '; |
361 | } |
6559096f |
362 | $header .='--/'.quiz_rescale_grade($question->maxgrade, $quiz); |
98f38217 |
363 | $headers[] = $header; |
abe67b24 |
364 | } |
365 | } |
98f38217 |
366 | if ($regradedattempts){ |
367 | $columns[] = 'regraded'; |
368 | $headers[] = get_string('regrade', 'quiz_overview'); |
369 | } |
abe67b24 |
370 | if ($showgrades) { |
371 | $columns[] = 'sumgrades'; |
f88fb62c |
372 | $headers[] = get_string('grade', 'quiz').'/'.quiz_format_grade($quiz, $quiz->grade); |
abe67b24 |
373 | } |
374 | |
375 | if ($hasfeedback) { |
376 | $columns[] = 'feedbacktext'; |
377 | $headers[] = get_string('feedback', 'quiz'); |
c35f3afc |
378 | } |
abe67b24 |
379 | |
380 | $table->define_columns($columns); |
381 | $table->define_headers($headers); |
382 | $table->sortable(true, 'uniqueid'); |
383 | |
384 | // Set up the table |
385 | $table->define_baseurl($reporturl->out(false, $displayoptions)); |
386 | |
98f38217 |
387 | $table->collapsible(false); |
abe67b24 |
388 | |
389 | $table->column_suppress('picture'); |
390 | $table->column_suppress('fullname'); |
391 | $table->column_suppress('idnumber'); |
392 | |
393 | $table->no_sorting('feedbacktext'); |
394 | |
395 | $table->column_class('picture', 'picture'); |
396 | $table->column_class('lastname', 'bold'); |
397 | $table->column_class('firstname', 'bold'); |
398 | $table->column_class('fullname', 'bold'); |
399 | $table->column_class('sumgrades', 'bold'); |
400 | |
401 | $table->set_attribute('id', 'attempts'); |
402 | |
403 | $table->out($pagesize, true); |
7bbe08a2 |
404 | } |
c35f3afc |
405 | if (!$table->is_downloading()) { |
8b2f8253 |
406 | //should be quicker than a COUNT to test if there is at least one record : |
407 | if ($DB->get_records('quiz_grades', array('quiz'=> $quiz->id), '', '*', 0, 1)){ |
c35f3afc |
408 | $imageurl = $CFG->wwwroot.'/mod/quiz/report/overview/overviewgraph.php?id='.$quiz->id; |
409 | print_heading(get_string('overviewreportgraph', 'quiz_overview')); |
410 | echo '<div class="mdl-align"><img src="'.$imageurl.'" alt="'.get_string('overviewreportgraph', 'quiz_overview').'" /></div>'; |
aad5b0fc |
411 | } |
78517b5a |
412 | } |
7bbe08a2 |
413 | return true; |
414 | } |
98f38217 |
415 | /** |
416 | * @param bool changedb whether to change contents of state and grades |
417 | * tables. |
418 | */ |
419 | function regrade_all($dry, $quiz, $groupstudents){ |
420 | global $DB; |
421 | if (!has_capability('mod/quiz:grade', $this->context)) { |
422 | notify(get_string('regradenotallowed', 'quiz')); |
423 | return true; |
424 | } |
425 | // Fetch all attempts |
426 | if ($groupstudents){ |
427 | list($usql, $params) = $DB->get_in_or_equal($groupstudents); |
428 | $select = "userid $usql AND "; |
429 | } else { |
430 | $select = ''; |
431 | $params = array(); |
432 | } |
433 | $select .= "quiz = ? AND preview = 0"; |
434 | $params[] = $quiz->id; |
435 | if (!$attempts = $DB->get_records_select('quiz_attempts', $select, $params)) { |
436 | print_heading(get_string('noattempts', 'quiz')); |
437 | return true; |
438 | } |
439 | |
440 | $this->clear_regrade_table($quiz, $groupstudents); |
441 | |
442 | // Fetch all questions |
16de0ed3 |
443 | $questions = question_load_questions(explode(',',quiz_questions_in_quiz($quiz->questions)), 'qqi.grade AS maxgrade, qqi.id AS instance', |
98f38217 |
444 | '{quiz_question_instances} qqi ON qqi.quiz = ' . $quiz->id . ' AND q.id = qqi.question'); |
445 | |
446 | // Print heading |
447 | print_heading(get_string('regradingquiz', 'quiz', format_string($quiz->name))); |
448 | $qstodo = count($questions); |
449 | $qsdone = 0; |
450 | if ($qstodo > 1){ |
451 | $qpb = new progress_bar('qregradingbar', 500, true); |
452 | $qpb->update($qsdone, $qstodo, "Question $qsdone of $qstodo"); |
453 | } |
454 | $apb = new progress_bar('aregradingbar', 500, true); |
455 | |
456 | // Loop through all questions and all attempts and regrade while printing progress info |
457 | $attemptstodo = count($attempts); |
458 | foreach ($questions as $question) { |
459 | $attemptsdone = 0; |
460 | $apb->restart(); |
461 | echo '<p class="mdl-align"><strong>'.get_string('regradingquestion', 'quiz', $question->name).'</strong></p>'; |
462 | @flush();@ob_flush(); |
463 | foreach ($attempts as $attempt) { |
464 | set_time_limit(30); |
465 | $changed = regrade_question_in_attempt($question, $attempt, $quiz, true, $dry); |
466 | |
467 | $attemptsdone++; |
468 | $a = new object(); |
469 | $a->done = $attemptsdone; |
470 | $a->todo = $attemptstodo; |
471 | $apb->update($attemptsdone, $attemptstodo, get_string('attemptprogress', 'quiz_overview', $a)); |
472 | } |
473 | $qsdone++; |
474 | if (isset($qpb)){ |
475 | $a = new object(); |
476 | $a->done = $qsdone; |
477 | $a->todo = $qstodo; |
478 | $qpb->update($qsdone, $qstodo, get_string('qprogress', 'quiz_overview', $a)); |
479 | } |
480 | // the following makes sure that the output is sent immediately. |
481 | @flush();@ob_flush(); |
482 | } |
483 | |
484 | if (!$dry){ |
485 | $this->check_overall_grades($quiz, $groupstudents); |
486 | } |
487 | } |
488 | function count_regrade_all_needed($quiz, $groupstudents){ |
489 | global $DB; |
490 | // Fetch all attempts that need regrading |
491 | if ($groupstudents){ |
492 | list($usql, $params) = $DB->get_in_or_equal($groupstudents); |
493 | $where = "qa.userid $usql AND "; |
494 | } else { |
495 | $where = ''; |
496 | $params = array(); |
497 | } |
498 | $where .= "qa.quiz = ? AND qa.preview = 0 AND qa.uniqueid = qqr.attemptid AND qqr.regraded = 0"; |
499 | $params[] = $quiz->id; |
500 | return $DB->get_field_sql('SELECT COUNT(1) FROM {quiz_attempts} qa, {quiz_question_regrade} qqr WHERE '. $where, $params); |
501 | } |
502 | function regrade_all_needed($quiz, $groupstudents){ |
503 | global $DB; |
504 | if (!has_capability('mod/quiz:grade', $this->context)) { |
505 | notify(get_string('regradenotallowed', 'quiz')); |
506 | return; |
507 | } |
508 | // Fetch all attempts that need regrading |
509 | if ($groupstudents){ |
510 | list($usql, $params) = $DB->get_in_or_equal($groupstudents); |
511 | $where = "qa.userid $usql AND "; |
512 | } else { |
513 | $where = ''; |
514 | $params = array(); |
515 | } |
516 | $where .= "qa.quiz = ? AND qa.preview = 0 AND qa.uniqueid = qqr.attemptid AND qqr.regraded = 0"; |
517 | $params[] = $quiz->id; |
518 | if (!$attempts = $DB->get_records_sql('SELECT qa.*, qqr.questionid FROM {quiz_attempts} qa, {quiz_question_regrade} qqr WHERE '. $where, $params)) { |
519 | print_heading(get_string('noattemptstoregrade', 'quiz_overview')); |
520 | return true; |
521 | } |
522 | $this->clear_regrade_table($quiz, $groupstudents); |
523 | // Fetch all questions |
16de0ed3 |
524 | $questions = question_load_questions(explode(',',quiz_questions_in_quiz($quiz->questions)), 'qqi.grade AS maxgrade, qqi.id AS instance', |
98f38217 |
525 | '{quiz_question_instances} qqi ON qqi.quiz = ' . $quiz->id . ' AND q.id = qqi.question'); |
526 | |
527 | // Print heading |
528 | print_heading(get_string('regradingquiz', 'quiz', format_string($quiz->name))); |
529 | |
530 | $apb = new progress_bar('aregradingbar', 500, true); |
531 | |
532 | // Loop through all questions and all attempts and regrade while printing progress info |
533 | $attemptstodo = count($attempts); |
534 | $attemptsdone = 0; |
535 | @flush();@ob_flush(); |
536 | $attemptschanged = array(); |
537 | foreach ($attempts as $attempt) { |
538 | $question = $questions[$attempt->questionid]; |
539 | $changed = regrade_question_in_attempt($question, $attempt, $quiz, true); |
540 | if ($changed){ |
541 | $attemptschanged[] = $attempt->uniqueid; |
542 | $usersschanged[] = $attempt->userid; |
543 | } |
544 | if (!empty($apb)){ |
545 | $attemptsdone++; |
546 | $a = new object(); |
547 | $a->done = $attemptsdone; |
548 | $a->todo = $attemptstodo; |
549 | $apb->update($attemptsdone, $attemptstodo, get_string('attemptprogress', 'quiz_overview', $a)); |
550 | } |
551 | } |
552 | $this->check_overall_grades($quiz, array(), $attemptschanged); |
553 | } |
554 | |
555 | function clear_regrade_table($quiz, $groupstudents){ |
556 | global $DB; |
557 | // Fetch all attempts that need regrading |
558 | if ($groupstudents){ |
559 | list($usql, $params) = $DB->get_in_or_equal($groupstudents); |
23277af8 |
560 | $where = "userid $usql AND "; |
98f38217 |
561 | } else { |
562 | $usql = ''; |
563 | $where = ''; |
564 | $params = array(); |
565 | } |
566 | $params[] = $quiz->id; |
23277af8 |
567 | $delsql = 'DELETE FROM {quiz_question_regrade} WHERE attemptid IN |
568 | (SELECT uniqueid FROM {quiz_attempts} WHERE ' . $where . ' quiz = ?)'; |
98f38217 |
569 | if (!$DB->execute($delsql, $params)){ |
570 | print_error('err_failedtodeleteregrades', 'quiz_overview'); |
571 | } |
572 | } |
23277af8 |
573 | |
98f38217 |
574 | function check_overall_grades($quiz, $userids=array(), $attemptids=array()){ |
575 | global $DB; |
576 | //recalculate $attempt->sumgrade |
577 | //already updated in regrade_question_in_attempt |
1e98b864 |
578 | $sql = "UPDATE {quiz_attempts} SET sumgrades= " . |
98f38217 |
579 | "(SELECT SUM(qs.grade) FROM {question_sessions} qns, {question_states} qs " . |
1e98b864 |
580 | "WHERE qns.newgraded = qs.id AND qns.attemptid = {quiz_attempts}.uniqueid ) WHERE "; |
98f38217 |
581 | $attemptsql=''; |
582 | if (!$attemptids){ |
583 | if ($userids){ |
584 | list($usql, $params) = $DB->get_in_or_equal($userids); |
1e98b864 |
585 | $attemptsql .= "{quiz_attempts}.userid $usql AND "; |
98f38217 |
586 | } else { |
587 | $params = array(); |
588 | } |
1e98b864 |
589 | $attemptsql .= "{quiz_attempts}.quiz =? AND preview = 0"; |
98f38217 |
590 | $params[] = $quiz->id; |
591 | } else { |
592 | list($asql, $params) = $DB->get_in_or_equal($attemptids); |
1e98b864 |
593 | $attemptsql .= "{quiz_attempts}.uniqueid $asql"; |
98f38217 |
594 | } |
595 | $sql .= $attemptsql; |
596 | if (!$DB->execute($sql, $params)){ |
597 | print_error('err_failedtorecalculateattemptgrades', 'quiz_overview'); |
598 | } |
599 | |
600 | // Update the overall quiz grades |
601 | if ($attemptids){ |
602 | //make sure we fetch all attempts for users to calculate grade. |
603 | //not just those that have changed. |
daae98e1 |
604 | $sql = "SELECT qa2.* FROM {quiz_attempts} qa2 WHERE qa2.userid IN (SELECT DISTINCT userid FROM {quiz_attempts} WHERE $attemptsql)"; |
98f38217 |
605 | } else { |
1e98b864 |
606 | $sql = "SELECT * FROM {quiz_attempts} WHERE $attemptsql"; |
98f38217 |
607 | } |
608 | if ($attempts = $DB->get_records_sql($sql, $params)) { |
609 | $attemptsbyuser = quiz_report_index_by_keys($attempts, array('userid', 'id')); |
610 | foreach($attemptsbyuser as $userid => $attemptsforuser) { |
611 | quiz_save_best_grade($quiz, $userid, $attemptsforuser); |
612 | } |
613 | } |
614 | } |
615 | function delete_selected_attempts($quiz, $cm, $attemptids, $groupstudents){ |
616 | global $DB, $COURSE; |
617 | require_capability('mod/quiz:deleteattempts', $this->context); |
618 | $attemptids = optional_param('attemptid', array(), PARAM_INT); |
619 | if ($groupstudents){ |
620 | list($usql, $params) = $DB->get_in_or_equal($groupstudents); |
621 | $where = "qa.userid $usql AND "; |
622 | } |
623 | foreach($attemptids as $attemptid) { |
624 | add_to_log($COURSE->id, 'quiz', 'delete attempt', 'report.php?id=' . $cm->id, |
625 | $attemptid, $cm->id); |
626 | quiz_delete_attempt($attemptid, $quiz); |
627 | } |
628 | } |
629 | function regrade_selected_attempts($quiz, $attemptids, $groupstudents){ |
630 | global $DB; |
631 | require_capability('mod/quiz:grade', $this->context); |
632 | if ($groupstudents){ |
633 | list($usql, $params) = $DB->get_in_or_equal($groupstudents); |
634 | $where = "qa.userid $usql AND "; |
635 | } else { |
636 | $params = array(); |
637 | $where = ''; |
638 | } |
639 | list($asql, $aparams) = $DB->get_in_or_equal($attemptids); |
640 | $where = "qa.id $asql AND "; |
641 | $params = array_merge($params, $aparams); |
642 | |
643 | $where .= "qa.quiz = ? AND qa.preview = 0"; |
644 | $params[] = $quiz->id; |
645 | if (!$attempts = $DB->get_records_sql('SELECT qa.* FROM {quiz_attempts} qa WHERE '. $where, $params)) { |
646 | print_error('noattemptstoregrade', 'quiz_overview'); |
647 | } |
648 | |
649 | // Fetch all questions |
16de0ed3 |
650 | $questions = question_load_questions(explode(',',quiz_questions_in_quiz($quiz->questions)), 'qqi.grade AS maxgrade, qqi.id AS instance', |
98f38217 |
651 | '{quiz_question_instances} qqi ON qqi.quiz = ' . $quiz->id . ' AND q.id = qqi.question'); |
652 | $updateoverallgrades = array(); |
653 | foreach($attempts as $attempt) { |
654 | foreach ($questions as $question){ |
655 | $changed = regrade_question_in_attempt($question, $attempt, $quiz, true); |
656 | } |
657 | $updateoverallgrades[] = $attempt->uniqueid; |
658 | } |
659 | $this->check_overall_grades($quiz, array(), $updateoverallgrades); |
660 | } |
c35f3afc |
661 | |
7bbe08a2 |
662 | } |
663 | |
c35f3afc |
664 | |
04419703 |
665 | ?> |