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