MDL-21723 fixed rression, thanks Ben Dailey
[moodle.git] / mod / quiz / report / overview / overview_table.php
CommitLineData
aeb15530 1<?php
c35f3afc 2
3class quiz_report_overview_table extends table_sql {
9cf4a18b 4
c35f3afc 5 var $useridfield = 'userid';
9cf4a18b 6
c35f3afc 7 var $candelete;
8 var $reporturl;
9 var $displayoptions;
f29e6691 10 var $regradedqs = array();
9cf4a18b 11
c35f3afc 12 function quiz_report_overview_table($quiz , $qmsubselect, $groupstudents,
57d6a267 13 $students, $detailedmarks, $questions, $candelete, $reporturl, $displayoptions, $context){
c35f3afc 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;
57d6a267 24 $this->context = $context;
c35f3afc 25 }
26 function build_table(){
9cf4a18b 27 global $CFG, $DB;
c35f3afc 28 if ($this->rawdata) {
29 // Define some things we need later to process raw data from db.
ca2f6fc7 30 $this->strtimeformat = str_replace(',', '', get_string('strftimedatetime'));
c35f3afc 31 parent::build_table();
32 //end of adding data from attempts data to table / download
33 //now add averages at bottom of table :
9cf4a18b 34 $params = array($this->quiz->id);
c35f3afc 35 $averagesql = "SELECT AVG(qg.grade) AS grade " .
9cf4a18b 36 "FROM {quiz_grades} qg " .
37 "WHERE quiz=?";
38
c35f3afc 39 $this->add_separator();
40 if ($this->is_downloading()){
41 $namekey = 'lastname';
42 } else {
43 $namekey = 'fullname';
44 }
45 if ($this->groupstudents){
8673a566 46 list($g_usql, $g_params) = $DB->get_in_or_equal($this->groupstudents);
9cf4a18b 47
48 $groupaveragesql = $averagesql." AND qg.userid $g_usql";
49 $groupaverage = $DB->get_record_sql($groupaveragesql, array_merge($params, $g_params));
c35f3afc 50 $groupaveragerow = array($namekey => get_string('groupavg', 'grades'),
f88fb62c 51 'sumgrades' => quiz_format_grade($this->quiz, $groupaverage->grade),
c35f3afc 52 'feedbacktext'=> strip_tags(quiz_report_feedback_for_grade($groupaverage->grade, $this->quiz->id)));
1dc12a64 53 if($this->detailedmarks && ($this->qmsubselect || $this->quiz->attempts == 1)) {
c35f3afc 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 }
9cf4a18b 59
8673a566 60 list($s_usql, $s_params) = $DB->get_in_or_equal($this->students);
9cf4a18b 61 $overallaverage = $DB->get_record_sql($averagesql." AND qg.userid $s_usql", array_merge($params, $s_params));
c35f3afc 62 $overallaveragerow = array($namekey => get_string('overallaverage', 'grades'),
f88fb62c 63 'sumgrades' => quiz_format_grade($this->quiz, $overallaverage->grade),
c35f3afc 64 'feedbacktext'=> strip_tags(quiz_report_feedback_for_grade($overallaverage->grade, $this->quiz->id)));
1dc12a64 65 if($this->detailedmarks && ($this->qmsubselect || $this->quiz->attempts == 1)) {
c35f3afc 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 }
9cf4a18b 72
c35f3afc 73 function wrap_html_start(){
74 if (!$this->is_downloading()) {
75 if ($this->candelete) {
76 // Start form
6ea66ff3 77 $url = new moodle_url($this->reporturl, $this->displayoptions);
c35f3afc 78 echo '<div id="tablecontainer">';
eb788065 79 echo '<form id="attemptsform" method="post" action="' . $this->reporturl->out_omit_querystring() .'">';
5bb614fe 80 echo '<div style="display: none;">';
6ea66ff3 81 echo html_writer::input_hidden_params($url);
5bb614fe 82 echo '</div>';
c35f3afc 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) {
98f38217 91 $strreallydel = addslashes_js(get_string('deleteattemptcheck','quiz'));
92 echo '<div id="commands">';
c35f3afc 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;';
bbf4f440 98 if (has_capability('mod/quiz:regrade', $this->context)){
314b8a20 99 echo '<input type="submit" name="regrade" value="'.get_string('regradeselected', 'quiz_overview').'"/>';
100 }
98f38217 101 echo '<input type="submit" onclick="return confirm(\''.$strreallydel.'\');" name="delete" value="'.get_string('deleteselected', 'quiz_overview').'"/>';
102 echo '</div>';
c35f3afc 103 // Close form
104 echo '</div>';
105 echo '</form></div>';
106 }
107 }
108 }
109
9cf4a18b 110
c35f3afc 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 }
9cf4a18b 118
c35f3afc 119 function col_picture($attempt){
39e37019 120 global $COURSE, $OUTPUT;
375381a0 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;
812dbaf7 127 return $OUTPUT->user_picture($user);
c35f3afc 128 }
129
9cf4a18b 130
c35f3afc 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 }
9cf4a18b 159
c35f3afc 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()) {
98f38217 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 }
c35f3afc 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 }
b55797b8 201
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 */
c35f3afc 209 function other_cols($colname, $attempt){
ca4b4f8f 210 global $OUTPUT;
98f38217 211
c35f3afc 212 if (preg_match('/^qsgrade([0-9]+)$/', $colname, $matches)){
213 $questionid = $matches[1];
214 $question = $this->questions[$questionid];
98f38217 215 if (isset($this->gradedstatesbyattempt[$attempt->attemptuniqueid][$questionid])){
216 $stateforqinattempt = $this->gradedstatesbyattempt[$attempt->attemptuniqueid][$questionid];
c35f3afc 217 } else {
98f38217 218 $stateforqinattempt = false;
c35f3afc 219 }
98f38217 220 if ($stateforqinattempt && question_state_is_graded($stateforqinattempt)) {
20016444 221 $grade = quiz_rescale_grade($stateforqinattempt->grade, $this->quiz, 'question');
98f38217 222 if (!$this->is_downloading()) {
223 if (isset($this->regradedqs[$attempt->attemptuniqueid][$questionid])){
224 $gradefromdb = $grade;
20016444
FM
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');
98f38217 227
228 $grade = '<del>'.$oldgrade.'</del><br />'.
229 $newgrade;
230 }
ca4b4f8f 231
9bf16314
PS
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));
45c6bf6c 234 $linktopopup = $OUTPUT->action_link($link, $grade, $action, array('title'=>get_string('reviewresponsetoq', 'quiz', $question->formattedname)));
ca4b4f8f 235
1c88737f 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 }
ca4b4f8f 246
98f38217 247 } else {
248 return $grade;
249 }
c35f3afc 250 } else {
98f38217 251 return '--';
9cf4a18b 252 }
c35f3afc 253 } else {
254 return NULL;
255 }
256 }
9cf4a18b 257
c35f3afc 258 function col_feedbacktext($attempt){
259 if ($attempt->timefinish) {
260 if (!$this->is_downloading()) {
20016444 261 return quiz_report_feedback_for_grade(quiz_rescale_grade($attempt->sumgrades, $this->quiz, false), $this->quiz->id);
c35f3afc 262 } else {
20016444 263 return strip_tags(quiz_report_feedback_for_grade(quiz_rescale_grade($attempt->sumgrades, $this->quiz, false), $this->quiz->id));
c35f3afc 264 }
265 } else {
266 return '-';
267 }
9cf4a18b 268
c35f3afc 269 }
98f38217 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 }
8673a566 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 }
f29e6691 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 }
8673a566 310 }
311 }
312 parent::query_db($pagesize, $useinitialsbar);
f29e6691 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;
98f38217 320 }
f29e6691 321 }
322 $this->gradedstatesbyattempt = quiz_get_newgraded_states($attemptids, true, 'qs.id, qs.grade, qs.event, qs.question, qs.attempt');
bbf4f440 323 if (has_capability('mod/quiz:regrade', $this->context)){
98f38217 324 $this->regradedqs = quiz_get_regraded_qs($attemptids);
f29e6691 325 }
326 } else {
327 $this->gradedstatesbyattempt = quiz_get_newgraded_states($this->sql, true, 'qs.id, qs.grade, qs.event, qs.question, qs.attempt');
bbf4f440 328 if (has_capability('mod/quiz:regrade', $this->context)){
98f38217 329 $this->regradedqs = quiz_get_regraded_qs($this->sql);
330 }
331 }
8673a566 332 }
c35f3afc 333}
aeb15530 334