MDL-21723 fixed rression, thanks Ben Dailey
[moodle.git] / mod / quiz / report / overview / overview_table.php
1 <?php
3 class quiz_report_overview_table extends table_sql {
5     var $useridfield = 'userid';
7     var $candelete;
8     var $reporturl;
9     var $displayoptions;
10     var $regradedqs = array();
12     function quiz_report_overview_table($quiz , $qmsubselect, $groupstudents,
13                 $students, $detailedmarks, $questions, $candelete, $reporturl, $displayoptions, $context){
14         parent::table_sql('mod-quiz-report-overview-report');
15         $this->quiz = $quiz;
16         $this->qmsubselect = $qmsubselect;
17         $this->groupstudents = $groupstudents;
18         $this->students = $students;
19         $this->detailedmarks = $detailedmarks;
20         $this->questions = $questions;
21         $this->candelete = $candelete;
22         $this->reporturl = $reporturl;
23         $this->displayoptions = $displayoptions;
24         $this->context = $context;
25     }
26     function build_table(){
27         global $CFG, $DB;
28         if ($this->rawdata) {
29             // Define some things we need later to process raw data from db.
30             $this->strtimeformat = str_replace(',', '', get_string('strftimedatetime'));
31             parent::build_table();
32             //end of adding data from attempts data to table / download
33             //now add averages at bottom of table :
34             $params = array($this->quiz->id);
35             $averagesql = "SELECT AVG(qg.grade) AS grade " .
36                     "FROM {quiz_grades} qg " .
37                     "WHERE quiz=?";
39             $this->add_separator();
40             if ($this->is_downloading()){
41                 $namekey = 'lastname';
42             } else {
43                 $namekey = 'fullname';
44             }
45             if ($this->groupstudents){
46                 list($g_usql, $g_params) = $DB->get_in_or_equal($this->groupstudents);
48                 $groupaveragesql = $averagesql." AND qg.userid $g_usql";
49                 $groupaverage = $DB->get_record_sql($groupaveragesql, array_merge($params, $g_params));
50                 $groupaveragerow = array($namekey => get_string('groupavg', 'grades'),
51                         'sumgrades' => quiz_format_grade($this->quiz, $groupaverage->grade),
52                         'feedbacktext'=> strip_tags(quiz_report_feedback_for_grade($groupaverage->grade, $this->quiz->id)));
53                 if($this->detailedmarks && ($this->qmsubselect || $this->quiz->attempts == 1)) {
54                     $avggradebyq = quiz_get_average_grade_for_questions($this->quiz, $this->groupstudents);
55                     $groupaveragerow += quiz_format_average_grade_for_questions($avggradebyq, $this->questions, $this->quiz, $this->is_downloading());
56                 }
57                 $this->add_data_keyed($groupaveragerow);
58             }
60             list($s_usql, $s_params) = $DB->get_in_or_equal($this->students);
61             $overallaverage = $DB->get_record_sql($averagesql." AND qg.userid $s_usql", array_merge($params, $s_params));
62             $overallaveragerow = array($namekey => get_string('overallaverage', 'grades'),
63                         'sumgrades' => quiz_format_grade($this->quiz, $overallaverage->grade),
64                         'feedbacktext'=> strip_tags(quiz_report_feedback_for_grade($overallaverage->grade, $this->quiz->id)));
65             if($this->detailedmarks && ($this->qmsubselect || $this->quiz->attempts == 1)) {
66                 $avggradebyq = quiz_get_average_grade_for_questions($this->quiz, $this->students);
67                 $overallaveragerow += quiz_format_average_grade_for_questions($avggradebyq, $this->questions, $this->quiz, $this->is_downloading());
68             }
69             $this->add_data_keyed($overallaveragerow);
70         }
71     }
73     function wrap_html_start(){
74         if (!$this->is_downloading()) {
75             if ($this->candelete) {
76                 // Start form
77                 $url = new moodle_url($this->reporturl, $this->displayoptions);
78                 echo '<div id="tablecontainer">';
79                 echo '<form id="attemptsform" method="post" action="' . $this->reporturl->out_omit_querystring() .'">';
80                 echo '<div style="display: none;">';
81                 echo html_writer::input_hidden_params($url);
82                 echo '</div>';
83                 echo '<div>';
84             }
85         }
86     }
87     function wrap_html_finish(){
88         if (!$this->is_downloading()) {
89             // Print "Select all" etc.
90             if ($this->candelete) {
91                 $strreallydel  = addslashes_js(get_string('deleteattemptcheck','quiz'));
92                 echo '<div id="commands">';
93                 echo '<a href="javascript:select_all_in(\'DIV\',null,\'tablecontainer\');">'.
94                         get_string('selectall', 'quiz').'</a> / ';
95                 echo '<a href="javascript:deselect_all_in(\'DIV\',null,\'tablecontainer\');">'.
96                         get_string('selectnone', 'quiz').'</a> ';
97                 echo '&nbsp;&nbsp;';
98                 if (has_capability('mod/quiz:regrade', $this->context)){
99                     echo '<input type="submit" name="regrade" value="'.get_string('regradeselected', 'quiz_overview').'"/>';
100                 }
101                 echo '<input type="submit" onclick="return confirm(\''.$strreallydel.'\');" name="delete" value="'.get_string('deleteselected', 'quiz_overview').'"/>';
102                 echo '</div>';
103                 // Close form
104                 echo '</div>';
105                 echo '</form></div>';
106             }
107         }
108     }
111     function col_checkbox($attempt){
112         if ($attempt->attempt){
113             return '<input type="checkbox" name="attemptid[]" value="'.$attempt->attempt.'" />';
114         } else {
115             return '';
116         }
117     }
119     function col_picture($attempt){
120         global $COURSE, $OUTPUT;
121         $user = new object();
122         $user->id = $attempt->userid;
123         $user->lastname = $attempt->lastname;
124         $user->firstname = $attempt->firstname;
125         $user->imagealt = $attempt->imagealt;
126         $user->picture = $attempt->picture;
127         return $OUTPUT->user_picture($user);
128     }
131     function col_timestart($attempt){
132         if ($attempt->attempt) {
133             $startdate = userdate($attempt->timestart, $this->strtimeformat);
134             if (!$this->is_downloading()) {
135                 return  '<a href="review.php?q='.$this->quiz->id.'&amp;attempt='.$attempt->attempt.'">'.$startdate.'</a>';
136             } else {
137                 return  $startdate;
138             }
139         } else {
140             return  '-';
141         }
142     }
143     function col_timefinish($attempt){
144         if ($attempt->attempt) {
145             if ($attempt->timefinish) {
146                 $timefinish = userdate($attempt->timefinish, $this->strtimeformat);
147                 if (!$this->is_downloading()) {
148                     return '<a href="review.php?q='.$this->quiz->id.'&amp;attempt='.$attempt->attempt.'">'.$timefinish.'</a>';
149                 } else {
150                     return $timefinish;
151                 }
152             } else {
153                 return  '-';
154             }
155         } else {
156             return  '-';
157         }
158     }
160     function col_duration($attempt){
161         if ($attempt->timefinish) {
162             return format_time($attempt->duration);
163         } elseif ($attempt->timestart) {
164             return get_string('unfinished', 'quiz');
165         } else {
166             return '-';
167         }
168     }
169     function col_sumgrades($attempt){
170         if ($attempt->timefinish) {
171             $grade = quiz_rescale_grade($attempt->sumgrades, $this->quiz);
172             if (!$this->is_downloading()) {
173                 if (isset($this->regradedqs[$attempt->attemptuniqueid])){
174                     $newsumgrade = 0;
175                     $oldsumgrade = 0;
176                     foreach ($this->questions as $question){
177                         if (isset($this->regradedqs[$attempt->attemptuniqueid][$question->id])){
178                             $newsumgrade += $this->regradedqs[$attempt->attemptuniqueid][$question->id]->newgrade;
179                             $oldsumgrade += $this->regradedqs[$attempt->attemptuniqueid][$question->id]->oldgrade;
180                         } else {
181                             $newsumgrade += $this->gradedstatesbyattempt[$attempt->attemptuniqueid][$question->id]->grade;
182                             $oldsumgrade += $this->gradedstatesbyattempt[$attempt->attemptuniqueid][$question->id]->grade;
183                         }
184                     }
185                     $newsumgrade = quiz_rescale_grade($newsumgrade, $this->quiz);
186                     $oldsumgrade = quiz_rescale_grade($oldsumgrade, $this->quiz);
187                     $grade = "<del>$oldsumgrade</del><br />$newsumgrade";
188                 }
189                 $gradehtml = '<a href="review.php?q='.$this->quiz->id.'&amp;attempt='.$attempt->attempt.'">'.$grade.'</a>';
190                 if ($this->qmsubselect && $attempt->gradedattempt){
191                     $gradehtml = '<div class="highlight">'.$gradehtml.'</div>';
192                 }
193                 return $gradehtml;
194             } else {
195                 return $grade;
196             }
197         } else {
198             return '-';
199         }
200     }
202     /**
203      * @param string $colname the name of the column.
204      * @param object $attempt the row of data - see the SQL in display() in
205      * mod/quiz/report/overview/report.php to see what fields are present,
206      * and what they are called.
207      * @return string the contents of the cell.
208      */
209     function other_cols($colname, $attempt){
210         global $OUTPUT;
212         if (preg_match('/^qsgrade([0-9]+)$/', $colname, $matches)){
213             $questionid = $matches[1];
214             $question = $this->questions[$questionid];
215             if (isset($this->gradedstatesbyattempt[$attempt->attemptuniqueid][$questionid])){
216                 $stateforqinattempt = $this->gradedstatesbyattempt[$attempt->attemptuniqueid][$questionid];
217             } else {
218                 $stateforqinattempt = false;
219             }
220             if ($stateforqinattempt && question_state_is_graded($stateforqinattempt)) {
221                 $grade = quiz_rescale_grade($stateforqinattempt->grade, $this->quiz, 'question');
222                 if (!$this->is_downloading()) {
223                     if (isset($this->regradedqs[$attempt->attemptuniqueid][$questionid])){
224                         $gradefromdb = $grade;
225                         $newgrade = quiz_rescale_grade($this->regradedqs[$attempt->attemptuniqueid][$questionid]->newgrade, $this->quiz, 'question');
226                         $oldgrade = quiz_rescale_grade($this->regradedqs[$attempt->attemptuniqueid][$questionid]->oldgrade, $this->quiz, 'question');
228                         $grade = '<del>'.$oldgrade.'</del><br />'.
229                                 $newgrade;
230                     }
232                     $link = new moodle_url("/mod/quiz/reviewquestion.php?attempt=$attempt->attempt&question=$question->id");
233                     $action = new popup_action('click', $link, 'reviewquestion', array('height' => 450, 'width' => 650));
234                     $linktopopup = $OUTPUT->action_link($link, $grade, $action, array('title'=>get_string('reviewresponsetoq', 'quiz', $question->formattedname)));
236                     if (($this->questions[$questionid]->maxgrade != 0)){
237                         $fractionofgrade = $stateforqinattempt->grade
238                                         / $this->questions[$questionid]->maxgrade;
239                         $qclass = question_get_feedback_class($fractionofgrade);
240                         $feedbackimg = question_get_feedback_image($fractionofgrade);
241                         $questionclass = "que";
242                         return "<span class=\"$questionclass\"><span class=\"$qclass\">".$linktopopup."</span></span>$feedbackimg";
243                     } else {
244                         return $linktopopup;
245                     }
247                 } else {
248                     return $grade;
249                 }
250             } else {
251                 return '--';
252             }
253         } else {
254             return NULL;
255         }
256     }
258     function col_feedbacktext($attempt){
259         if ($attempt->timefinish) {
260             if (!$this->is_downloading()) {
261                 return quiz_report_feedback_for_grade(quiz_rescale_grade($attempt->sumgrades, $this->quiz, false), $this->quiz->id);
262             } else {
263                 return strip_tags(quiz_report_feedback_for_grade(quiz_rescale_grade($attempt->sumgrades, $this->quiz, false), $this->quiz->id));
264             }
265         } else {
266             return '-';
267         }
269     }
270     function col_regraded($attempt){
271         if ($attempt->regraded == '') {
272             return '';
273         } else if ($attempt->regraded == 0) {
274             return get_string('needed', 'quiz_overview');
275         } else if ($attempt->regraded == 1) {
276             return get_string('done', 'quiz_overview');
277         }
278     }
279     function query_db($pagesize, $useinitialsbar=true){
280         // Add table joins so we can sort by question grade
281         // unfortunately can't join all tables necessary to fetch all grades
282         // to get the state for one question per attempt row we must join two tables
283         // and there is a limit to how many joins you can have in one query. In MySQL it
284         // is 61. This means that when having more than 29 questions the query will fail.
285         // So we join just the tables needed to sort the attempts.
286         if($sort = $this->get_sql_sort()) {
287             if ($this->detailedmarks) {
288                 $this->sql->from .= ' ';
289                 $sortparts    = explode(',', $sort);
290                 $matches = array();
291                 foreach($sortparts as $sortpart) {
292                     $sortpart = trim($sortpart);
293                     if (preg_match('/^qsgrade([0-9]+)/', $sortpart, $matches)){
294                         $qid = intval($matches[1]);
295                         $this->sql->fields .=  ", qs$qid.grade AS qsgrade$qid, qs$qid.event AS qsevent$qid, qs$qid.id AS qsid$qid";
296                         $this->sql->from .= "LEFT JOIN {question_sessions} qns$qid ON qns$qid.attemptid = qa.uniqueid AND qns$qid.questionid = :qid$qid ";
297                         $this->sql->from .=  "LEFT JOIN  {question_states} qs$qid ON qs$qid.id = qns$qid.newgraded ";
298                         $this->sql->params['qid'.$qid] = $qid;
299                     }
300                 }
301             } else {
302                 //unset any sort columns that sort on question grade as the
303                 //grades are not being fetched as fields
304                 $sess = &$this->sess;
305                 foreach($sess->sortby as $column => $order) {
306                     if (preg_match('/^qsgrade([0-9]+)/', trim($column))){
307                         unset($sess->sortby[$column]);
308                     }
309                 }
310             }
311         }
312         parent::query_db($pagesize, $useinitialsbar);
313         //get all the attempt ids we want to display on this page
314         //or to export for download.
315         if (!$this->is_downloading()) {
316             $attemptids = array();
317             foreach ($this->rawdata as $attempt){
318                 if ($attempt->attemptuniqueid > 0){
319                     $attemptids[] = $attempt->attemptuniqueid;
320                 }
321             }
322             $this->gradedstatesbyattempt = quiz_get_newgraded_states($attemptids, true, 'qs.id, qs.grade, qs.event, qs.question, qs.attempt');
323             if (has_capability('mod/quiz:regrade', $this->context)){
324                 $this->regradedqs = quiz_get_regraded_qs($attemptids);
325             }
326         } else {
327             $this->gradedstatesbyattempt = quiz_get_newgraded_states($this->sql, true, 'qs.id, qs.grade, qs.event, qs.question, qs.attempt');
328             if (has_capability('mod/quiz:regrade', $this->context)){
329                 $this->regradedqs = quiz_get_regraded_qs($this->sql);
330             }
331         }
332     }