course request: MDL-17575 and MDL-6160 further improvements:
[moodle.git] / blocks / quiz_results / block_quiz_results.php
CommitLineData
98ad7484 1<?php //$Id$
2
85311ca8 3define('B_QUIZRESULTS_NAME_FORMAT_FULL', 1);
4define('B_QUIZRESULTS_NAME_FORMAT_ID', 2);
5define('B_QUIZRESULTS_NAME_FORMAT_ANON', 3);
6define('B_QUIZRESULTS_GRADE_FORMAT_PCT', 1);
7define('B_QUIZRESULTS_GRADE_FORMAT_FRA', 2);
8define('B_QUIZRESULTS_GRADE_FORMAT_ABS', 3);
98ad7484 9
10class block_quiz_results extends block_base {
11 function init() {
12 $this->title = get_string('formaltitle', 'block_quiz_results');
433c242f 13 $this->version = 2007101509;
98ad7484 14 }
15
63927672 16 function applicable_formats() {
76d37688 17 return array('course' => true, 'mod-quiz' => true);
63927672 18 }
19
98ad7484 20 function get_content() {
44e1b7d7 21 global $USER, $CFG, $DB;
98ad7484 22
23 if ($this->content !== NULL) {
24 return $this->content;
25 }
98ad7484 26
27 $this->content = new stdClass;
28 $this->content->text = '';
29 $this->content->footer = '';
30
164e3054 31 if (empty($this->instance)) {
32 return $this->content;
33 }
34
09fe3652 35 if($this->instance->pagetype == 'course-view') {
98ad7484 36 // We need to see if we are monitoring a quiz
37 $quizid = empty($this->config->quizid) ? 0 : $this->config->quizid;
38 $courseid = $this->instance->pageid;
39 }
40 else {
41 // Assuming we are displayed in the quiz view page
e0a80e89 42 $quizid = $this->instance->pageid;
0144a0a7 43
44 // A trick to take advantage of instance config and save queries
f28f2d90 45 if (empty($this->config->courseid)) {
46 $modrecord = $DB->get_record('modules', array('name'=>'quiz'));
47 $cmrecord = $DB->get_record('course_modules', array('module'=>$modrecord->id, 'instance'=>$quizid));
0144a0a7 48 $this->config->courseid = intval($cmrecord->course);
49 $this->instance_config_commit();
50 }
51 $courseid = $this->config->courseid;
98ad7484 52 }
53
01199ea9 54 $context = get_context_instance(CONTEXT_COURSE, $courseid);
55
56
f28f2d90 57 if (empty($quizid)) {
6b4dc5a3 58 $this->content->text = get_string('error_emptyquizid', 'block_quiz_results');
98ad7484 59 return $this->content;
60 }
61
62 // Get the quiz record
f28f2d90 63 $quiz = $DB->get_record('quiz', array('id'=>$quizid));
64 if (empty($quiz)) {
6b4dc5a3 65 $this->content->text = get_string('error_emptyquizrecord', 'block_quiz_results');
98ad7484 66 return $this->content;
67 }
68
69 // Get the grades for this quiz
f28f2d90 70 $grades = $DB->get_records('quiz_grades', array('quiz'=>$quizid), 'grade, timemodified DESC');
98ad7484 71
f28f2d90 72 if (empty($grades)) {
98ad7484 73 // No grades, sorry
6b4dc5a3 74 // The block will hide itself in this case
00a4237f 75 return $this->content;
98ad7484 76 }
77
e7c15eca 78 if(empty($this->config->showbest) && empty($this->config->showworst)) {
79 $this->content->text = get_string('configuredtoshownothing', 'block_quiz_results');
80 return $this->content;
81 }
82
6b4dc5a3 83 $groupmode = NOGROUPS;
84 $best = array();
85 $worst = array();
98ad7484 86
85311ca8 87 $nameformat = intval(empty($this->config->nameformat) ? B_QUIZRESULTS_NAME_FORMAT_FULL : $this->config->nameformat);
88
89 // If the block is configured to operate in group mode, or if the name display format
90 // is other than "fullname", then we need to retrieve the full course record
f28f2d90 91 if (!empty($this->config->usegroups) || $nameformat != B_QUIZRESULTS_NAME_FORMAT_FULL) {
1bd72efa 92 $course = $DB->get_record('course', array('id'=>$courseid), 'groupmode, groupmodeforce');
85311ca8 93 }
94
98ad7484 95 if(!empty($this->config->usegroups)) {
f88fb62c 96 $groupmode = groups_get_activity_groupmode(
97 get_coursemodule_from_instance('quiz', $quizid, $course->id), $course);
98ad7484 98 }
6b4dc5a3 99
f28f2d90 100 if (has_capability('moodle/site:accessallgroups', $context) && $groupmode == SEPARATEGROUPS) {
b35bc4d8 101 // We 'll make an exception in this case
102 $groupmode = VISIBLEGROUPS;
103 }
6b4dc5a3 104
b35bc4d8 105 switch($groupmode) {
106 case VISIBLEGROUPS:
107 // Display group-mode results
2c386f82 108 $groups = groups_get_all_groups($courseid);
6b4dc5a3 109
110 if(empty($groups)) {
111 // No groups exist, sorry
112 $this->content->text = get_string('error_nogroupsexist', 'block_quiz_results');
113 return $this->content;
114 }
115
116 // Find out all the userids which have a submitted grade
117 $userids = array();
118 foreach($grades as $grade) {
119 $userids[] = $grade->userid;
120 }
121
122 // Now find which groups these users belong in
f28f2d90 123 $groupofuser = $DB->get_records_sql(
124 'SELECT m.userid, m.groupid, g.name FROM {groups} g LEFT JOIN {groups_members} m ON g.id = m.groupid '.
125 'WHERE g.courseid = ? AND m.userid IN ('.implode(',', $userids).')', array($courseid)
5bf243d1 126 );
6b4dc5a3 127
128 $groupgrades = array();
129
130 // OK... now, iterate the grades again and sum them up for each group
131 foreach($grades as $grade) {
132 if(isset($groupofuser[$grade->userid])) {
133 // Count this result only if the user is in a group
134 $groupid = $groupofuser[$grade->userid]->groupid;
135 if(!isset($groupgrades[$groupid])) {
090cf95a 136 $groupgrades[$groupid] = array('sum' => (float)$grade->grade, 'number' => 1, 'group' => $groupofuser[$grade->userid]->name);
6b4dc5a3 137 }
138 else {
139 $groupgrades[$groupid]['sum'] += $grade->grade;
140 ++$groupgrades[$groupid]['number'];
141 }
142 }
143 }
144
3779fd55 145 foreach($groupgrades as $groupid => $groupgrade) {
146 $groupgrades[$groupid]['average'] = $groupgrades[$groupid]['sum'] / $groupgrades[$groupid]['number'];
147 }
148
149 // Sort groupgrades according to average grade, ascending
150 uasort($groupgrades, create_function('$a, $b', 'if($a["average"] == $b["average"]) return 0; return ($a["average"] > $b["average"] ? 1 : -1);'));
6b4dc5a3 151
152 // How many groups do we have with graded member submissions to show?
153 $numbest = empty($this->config->showbest) ? 0 : min($this->config->showbest, count($groupgrades));
154 $numworst = empty($this->config->showworst) ? 0 : min($this->config->showworst, count($groupgrades) - $numbest);
155
156 // Collect all the group results we are going to use in $best and $worst
157 $remaining = $numbest;
158 $groupgrade = end($groupgrades);
159 while($remaining--) {
3779fd55 160 $best[key($groupgrades)] = $groupgrade['average'];
6b4dc5a3 161 $groupgrade = prev($groupgrades);
162 }
163
164 $remaining = $numworst;
165 $groupgrade = reset($groupgrades);
166 while($remaining--) {
3779fd55 167 $worst[key($groupgrades)] = $groupgrade['average'];
6b4dc5a3 168 $groupgrade = next($groupgrades);
169 }
170
171 // Ready for output!
85311ca8 172 $gradeformat = intval(empty($this->config->gradeformat) ? B_QUIZRESULTS_GRADE_FORMAT_PCT : $this->config->gradeformat);
6b4dc5a3 173
09fe3652 174 if($this->instance->pagetype != 'mod-quiz-view') {
f58a15de 175 // Don't show header and link to the quiz if we ARE at the quiz...
176 $this->content->text .= '<h1><a href="'.$CFG->wwwroot.'/mod/quiz/view.php?q='.$quizid.'">'.$quiz->name.'</a></h1>';
177 }
6b4dc5a3 178
9d6c4e80 179 if ($nameformat = B_QUIZRESULTS_NAME_FORMAT_FULL) {
180 if (has_capability('moodle/course:managegroups', $context)) {
3d05fc13 181 $grouplink = $CFG->wwwroot.'/group/overview.php?id='.$courseid.'&amp;group=';
9d6c4e80 182 } else if (has_capability('moodle/course:viewparticipants', $context)) {
183 $grouplink = $CFG->wwwroot.'/user/index.php?id='.$courseid.'&amp;group=';
184 } else {
185 $grouplink = '';
186 }
187 }
188
6b4dc5a3 189 $rank = 0;
190 if(!empty($best)) {
d9f7e051 191 $this->content->text .= '<table class="grades"><caption>';
192 $this->content->text .= ($numbest == 1?get_string('bestgroupgrade', 'block_quiz_results'):get_string('bestgroupgrades', 'block_quiz_results', $numbest));
193 $this->content->text .= '</caption><colgroup class="number" /><colgroup class="name" /><colgroup class="grade" /><tbody>';
6b4dc5a3 194 foreach($best as $groupid => $averagegrade) {
85311ca8 195 switch($nameformat) {
196 case B_QUIZRESULTS_NAME_FORMAT_ANON:
197 case B_QUIZRESULTS_NAME_FORMAT_ID:
198 $thisname = get_string('group');
199 break;
200 default:
201 case B_QUIZRESULTS_NAME_FORMAT_FULL:
9d6c4e80 202 if ($grouplink) {
203 $thisname = '<a href="'.$grouplink.$groupid.'">'.$groupgrades[$groupid]['group'].'</a>';
204 } else {
205 $thisname = $groupgrades[$groupid]['group'];
206 }
85311ca8 207 break;
208 }
209 $this->content->text .= '<tr><td>'.(++$rank).'.</td><td>'.$thisname.'</td><td>';
6b4dc5a3 210 switch($gradeformat) {
85311ca8 211 case B_QUIZRESULTS_GRADE_FORMAT_FRA:
f88fb62c 212 $this->content->text .= quiz_format_grade($quiz, $averagegrade).'/'.$quiz->grade;
6b4dc5a3 213 break;
85311ca8 214 case B_QUIZRESULTS_GRADE_FORMAT_ABS:
f88fb62c 215 $this->content->text .= quiz_format_grade($quiz, $averagegrade);
6b4dc5a3 216 break;
217 default:
85311ca8 218 case B_QUIZRESULTS_GRADE_FORMAT_PCT:
090cf95a 219 $this->content->text .= round((float)$averagegrade / (float)$quiz->grade * 100).'%';
6b4dc5a3 220 break;
221 }
222 $this->content->text .= '</td></tr>';
223 }
224 $this->content->text .= '</tbody></table>';
225 }
226
227 $rank = 0;
228 if(!empty($worst)) {
229 $worst = array_reverse($worst, true);
d9f7e051 230 $this->content->text .= '<table class="grades"><caption>';
231 $this->content->text .= ($numworst == 1?get_string('worstgroupgrade', 'block_quiz_results'):get_string('worstgroupgrades', 'block_quiz_results', $numworst));
232 $this->content->text .= '</caption><colgroup class="number" /><colgroup class="name" /><colgroup class="grade" /><tbody>';
6b4dc5a3 233 foreach($worst as $groupid => $averagegrade) {
85311ca8 234 switch($nameformat) {
235 case B_QUIZRESULTS_NAME_FORMAT_ANON:
236 case B_QUIZRESULTS_NAME_FORMAT_ID:
237 $thisname = get_string('group');
238 break;
239 default:
240 case B_QUIZRESULTS_NAME_FORMAT_FULL:
241 $thisname = '<a href="'.$CFG->wwwroot.'/course/group.php?group='.$groupid.'&amp;id='.$courseid.'">'.$groupgrades[$groupid]['group'].'</a>';
242 break;
243 }
244 $this->content->text .= '<tr><td>'.(++$rank).'.</td><td>'.$thisname.'</td><td>';
6b4dc5a3 245 switch($gradeformat) {
85311ca8 246 case B_QUIZRESULTS_GRADE_FORMAT_FRA:
f88fb62c 247 $this->content->text .= quiz_format_grade($quiz, $averagegrade).'/'.$quiz->grade;
6b4dc5a3 248 break;
85311ca8 249 case B_QUIZRESULTS_GRADE_FORMAT_ABS:
f88fb62c 250 $this->content->text .= quiz_format_grade($quiz, $averagegrade);
6b4dc5a3 251 break;
252 default:
85311ca8 253 case B_QUIZRESULTS_GRADE_FORMAT_PCT:
090cf95a 254 $this->content->text .= round((float)$averagegrade / (float)$quiz->grade * 100).'%';
6b4dc5a3 255 break;
256 }
257 $this->content->text .= '</td></tr>';
258 }
259 $this->content->text .= '</tbody></table>';
260 }
b35bc4d8 261 break;
6b4dc5a3 262
263
b35bc4d8 264 case SEPARATEGROUPS:
265 // This is going to be just like no-groups mode, only we 'll filter
266 // out the grades from people not in our group.
267 if(empty($USER) || empty($USER->id)) {
268 // Not logged in, so show nothing
269 return $this->content;
270 }
271
2c386f82 272 $mygroups = groups_get_all_groups($courseid, $USER->id);
b35bc4d8 273 if(empty($mygroups)) {
274 // Not member of a group, show nothing
275 return $this->content;
276 }
277
44e1b7d7 278 $mygroupsusers = $DB->get_records_list('groups_members', 'groupid', array_keys($mygroups), '', 'userid, id');
b35bc4d8 279 // There should be at least one user there, ourselves. So no more tests.
280
281 // Just filter out the grades belonging to other users, and proceed as if there were no groups
282 $strallowedusers = implode(',', array_keys($mygroupsusers));
283 $grades = array_filter($grades, create_function('$el', '$allowed = explode(",", "'.$strallowedusers.'"); return in_array($el->userid, $allowed);'));
284
285 // NO break; HERE, JUST GO AHEAD
286 default:
287 case NOGROUPS:
98ad7484 288 // Single user mode
6b4dc5a3 289 $numbest = empty($this->config->showbest) ? 0 : min($this->config->showbest, count($grades));
290 $numworst = empty($this->config->showworst) ? 0 : min($this->config->showworst, count($grades) - $numbest);
98ad7484 291
292 // Collect all the usernames we are going to need
293 $remaining = $numbest;
294 $grade = end($grades);
295 while($remaining--) {
296 $best[$grade->userid] = $grade->id;
297 $grade = prev($grades);
298 }
299
300 $remaining = $numworst;
301 $grade = reset($grades);
302 while($remaining--) {
303 $worst[$grade->userid] = $grade->id;
304 $grade = next($grades);
305 }
306
307 if(empty($best) && empty($worst)) {
308 // Nothing to show, for some reason...
309 return $this->content;
310 }
311
312 // Now grab all the users from the database
313 $userids = array_merge(array_keys($best), array_keys($worst));
44e1b7d7 314 $users = $DB->get_records_list('user', 'id', $userids, '', 'id, firstname, lastname, idnumber');
98ad7484 315
316 // Ready for output!
317
85311ca8 318 $gradeformat = intval(empty($this->config->gradeformat) ? B_QUIZRESULTS_GRADE_FORMAT_PCT : $this->config->gradeformat);
98ad7484 319
09fe3652 320 if($this->instance->pagetype != 'mod-quiz-view') {
f58a15de 321 // Don't show header and link to the quiz if we ARE at the quiz...
322 $this->content->text .= '<h1><a href="'.$CFG->wwwroot.'/mod/quiz/view.php?q='.$quizid.'">'.$quiz->name.'</a></h1>';
323 }
98ad7484 324
325 $rank = 0;
326 if(!empty($best)) {
d9f7e051 327 $this->content->text .= '<table class="grades"><caption>';
328 $this->content->text .= ($numbest == 1?get_string('bestgrade', 'block_quiz_results'):get_string('bestgrades', 'block_quiz_results', $numbest));
329 $this->content->text .= '</caption><colgroup class="number" /><colgroup class="name" /><colgroup class="grade" /><tbody>';
98ad7484 330 foreach($best as $userid => $gradeid) {
85311ca8 331 switch($nameformat) {
332 case B_QUIZRESULTS_NAME_FORMAT_ID:
9101efd3 333 $thisname = get_string('user').' '.intval($users[$userid]->idnumber);
85311ca8 334 break;
335 case B_QUIZRESULTS_NAME_FORMAT_ANON:
9101efd3 336 $thisname = get_string('user');
85311ca8 337 break;
338 default:
339 case B_QUIZRESULTS_NAME_FORMAT_FULL:
340 $thisname = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$userid.'&amp;course='.$courseid.'">'.fullname($users[$userid]).'</a>';
341 break;
342 }
343 $this->content->text .= '<tr><td>'.(++$rank).'.</td><td>'.$thisname.'</td><td>';
98ad7484 344 switch($gradeformat) {
85311ca8 345 case B_QUIZRESULTS_GRADE_FORMAT_FRA:
f88fb62c 346 $this->content->text .= quiz_format_grade($quiz, $grades[$gradeid]->grade).'/'.$quiz->grade;
98ad7484 347 break;
85311ca8 348 case B_QUIZRESULTS_GRADE_FORMAT_ABS:
f88fb62c 349 $this->content->text .= quiz_format_grade($quiz, $grades[$gradeid]->grade);
98ad7484 350 break;
351 default:
85311ca8 352 case B_QUIZRESULTS_GRADE_FORMAT_PCT:
f64fdf3f 353 if ($quiz->grade) {
354 $this->content->text .= round((float)$grades[$gradeid]->grade / (float)$quiz->grade * 100).'%';
355 } else {
356 $this->content->text .= '--%';
357 }
98ad7484 358 break;
359 }
360 $this->content->text .= '</td></tr>';
361 }
362 $this->content->text .= '</tbody></table>';
363 }
364
365 $rank = 0;
366 if(!empty($worst)) {
367 $worst = array_reverse($worst, true);
d9f7e051 368 $this->content->text .= '<table class="grades"><caption>';
369 $this->content->text .= ($numworst == 1?get_string('worstgrade', 'block_quiz_results'):get_string('worstgrades', 'block_quiz_results', $numworst));
370 $this->content->text .= '</caption><colgroup class="number" /><colgroup class="name" /><colgroup class="grade" /><tbody>';
98ad7484 371 foreach($worst as $userid => $gradeid) {
85311ca8 372 switch($nameformat) {
373 case B_QUIZRESULTS_NAME_FORMAT_ID:
9101efd3 374 $thisname = get_string('user').' '.intval($users[$userid]->idnumber);
85311ca8 375 break;
376 case B_QUIZRESULTS_NAME_FORMAT_ANON:
9101efd3 377 $thisname = get_string('user');
85311ca8 378 break;
379 default:
380 case B_QUIZRESULTS_NAME_FORMAT_FULL:
381 $thisname = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$userid.'&amp;course='.$courseid.'">'.fullname($users[$userid]).'</a>';
382 break;
383 }
384 $this->content->text .= '<tr><td>'.(++$rank).'.</td><td>'.$thisname.'</td><td>';
98ad7484 385 switch($gradeformat) {
85311ca8 386 case B_QUIZRESULTS_GRADE_FORMAT_FRA:
f88fb62c 387 $this->content->text .= quiz_format_grade($quiz, $grades[$gradeid]->grade).'/'.$quiz->grade;
98ad7484 388 break;
85311ca8 389 case B_QUIZRESULTS_GRADE_FORMAT_ABS:
f88fb62c 390 $this->content->text .= quiz_format_grade($quiz, $grades[$gradeid]->grade);
98ad7484 391 break;
392 default:
85311ca8 393 case B_QUIZRESULTS_GRADE_FORMAT_PCT:
090cf95a 394 $this->content->text .= round((float)$grades[$gradeid]->grade / (float)$quiz->grade * 100).'%';
98ad7484 395 break;
396 }
397 $this->content->text .= '</td></tr>';
398 }
399 $this->content->text .= '</tbody></table>';
400 }
b35bc4d8 401 break;
98ad7484 402 }
403
98ad7484 404 return $this->content;
405 }
406
63927672 407 function instance_allow_multiple() {
98ad7484 408 return true;
409 }
410}
411
412?>