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