3 define('B_QUIZRESULTS_NAME_FORMAT_FULL', 1);
4 define('B_QUIZRESULTS_NAME_FORMAT_ID', 2);
5 define('B_QUIZRESULTS_NAME_FORMAT_ANON', 3);
6 define('B_QUIZRESULTS_GRADE_FORMAT_PCT', 1);
7 define('B_QUIZRESULTS_GRADE_FORMAT_FRA', 2);
8 define('B_QUIZRESULTS_GRADE_FORMAT_ABS', 3);
10 class block_quiz_results extends block_base {
12 $this->title = get_string('formaltitle', 'block_quiz_results');
13 $this->version = 2007101509;
16 function applicable_formats() {
17 return array('course' => true, 'mod-quiz' => true);
21 * If this block belongs to a quiz context, then return that quiz's id.
22 * Otherwise, return 0.
23 * @return integer the quiz id.
25 public function get_owning_quiz() {
26 if (empty($this->instance->parentcontextid)) {
29 $parentcontext = get_context_instance_by_id($this->instance->parentcontextid);
30 if ($parentcontext->contextlevel != CONTEXT_MODULE) {
33 $cm = get_coursemodule_from_id('quiz', $parentcontext->instanceid);
40 function instance_config_save($data) {
41 if (empty($data->quizid)) {
42 $data->quizid = $this->get_owning_quiz();
44 parent::instance_config_save($data);
47 function get_content() {
48 global $USER, $CFG, $DB;
50 if ($this->content !== NULL) {
51 return $this->content;
54 $this->content = new stdClass;
55 $this->content->text = '';
56 $this->content->footer = '';
58 if (empty($this->instance)) {
59 return $this->content;
62 if ($this->page->activityname == 'quiz' && $this->page->context->id == $this->instance->parentcontextid) {
63 $quiz = $this->page->activityrecord;
65 $courseid = $this->page->course->id;
67 } else if (!empty($this->config->quizid)) {
68 $quizid = $this->config->quizid;
69 $quiz = $DB->get_record('quiz', array('id' => $quizid));
71 $this->content->text = get_string('error_emptyquizrecord', 'block_quiz_results');
72 return $this->content;
74 $courseid = $quiz->course;
81 $this->content->text = get_string('error_emptyquizid', 'block_quiz_results');
82 return $this->content;
85 if (empty($this->config->showbest) && empty($this->config->showworst)) {
86 $this->content->text = get_string('configuredtoshownothing', 'block_quiz_results');
87 return $this->content;
90 // Get the grades for this quiz
91 $grades = $DB->get_records('quiz_grades', array('quiz' => $quizid), 'grade, timemodified DESC');
95 // The block will hide itself in this case
96 return $this->content;
99 $groupmode = NOGROUPS;
103 if (!empty($this->config->nameformat)) {
104 $nameformat = $this->config->nameformat;
106 $nameformat = B_QUIZRESULTS_NAME_FORMAT_FULL;
109 if (!empty($this->config->usegroups)) {
111 $cm = $this->page->cm;
112 $context = $this->page->context;
114 $cm = get_coursemodule_from_instance('quiz', $quizid, $courseid);
115 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
117 $groupmode = groups_get_activity_groupmode($cm);
119 if ($groupmode == SEPARATEGROUPS && has_capability('moodle/site:accessallgroups', $context)) {
120 // We 'll make an exception in this case
121 $groupmode = VISIBLEGROUPS;
125 switch ($groupmode) {
127 // Display group-mode results
128 $groups = groups_get_all_groups($courseid);
131 // No groups exist, sorry
132 $this->content->text = get_string('error_nogroupsexist', 'block_quiz_results');
133 return $this->content;
136 // Find out all the userids which have a submitted grade
138 $gradeforuser = array();
139 foreach ($grades as $grade) {
140 $userids[] = $grade->userid;
141 $gradeforuser[$grade->userid] = (float)$grade->grade;
144 // Now find which groups these users belong in
145 list($usertest, $params) = $DB->get_in_or_equal($userids);
146 $params[] = $courseid;
147 $usergroups = $DB->get_records_sql('
148 SELECT gm.id, gm.userid, gm.groupid, g.name
150 LEFT JOIN {groups_members} gm ON g.id = gm.groupid
151 WHERE gm.userid ' . $usertest . ' AND g.courseid = ?', $params);
153 // Now, iterate the grades again and sum them up for each group
154 $groupgrades = array();
155 foreach ($usergroups as $usergroup) {
156 if (!isset($groupgrades[$usergroup->groupid])) {
157 $groupgrades[$usergroup->groupid] = array(
158 'sum' => (float)$gradeforuser[$usergroup->userid],
160 'group' => $usergroup->name);
162 $groupgrades[$usergroup->groupid]['sum'] += $gradeforuser[$usergroup->userid];
163 $groupgrades[$usergroup->groupid]['number'] += 1;
167 foreach($groupgrades as $groupid => $groupgrade) {
168 $groupgrades[$groupid]['average'] = $groupgrades[$groupid]['sum'] / $groupgrades[$groupid]['number'];
171 // Sort groupgrades according to average grade, ascending
172 uasort($groupgrades, create_function('$a, $b', 'if($a["average"] == $b["average"]) return 0; return ($a["average"] > $b["average"] ? 1 : -1);'));
174 // How many groups do we have with graded member submissions to show?
175 $numbest = empty($this->config->showbest) ? 0 : min($this->config->showbest, count($groupgrades));
176 $numworst = empty($this->config->showworst) ? 0 : min($this->config->showworst, count($groupgrades) - $numbest);
178 // Collect all the group results we are going to use in $best and $worst
179 $remaining = $numbest;
180 $groupgrade = end($groupgrades);
181 while ($remaining--) {
182 $best[key($groupgrades)] = $groupgrade['average'];
183 $groupgrade = prev($groupgrades);
186 $remaining = $numworst;
187 $groupgrade = reset($groupgrades);
188 while ($remaining--) {
189 $worst[key($groupgrades)] = $groupgrade['average'];
190 $groupgrade = next($groupgrades);
194 $gradeformat = intval(empty($this->config->gradeformat) ? B_QUIZRESULTS_GRADE_FORMAT_PCT : $this->config->gradeformat);
197 // Don't show header and link to the quiz if we ARE at the quiz...
198 $this->content->text .= '<h1><a href="'.$CFG->wwwroot.'/mod/quiz/view.php?q='.$quizid.'">'.$quiz->name.'</a></h1>';
201 if ($nameformat = B_QUIZRESULTS_NAME_FORMAT_FULL) {
202 if (has_capability('moodle/course:managegroups', $context)) {
203 $grouplink = $CFG->wwwroot.'/group/overview.php?id='.$courseid.'&group=';
204 } else if (has_capability('moodle/course:viewparticipants', $context)) {
205 $grouplink = $CFG->wwwroot.'/user/index.php?id='.$courseid.'&group=';
213 $this->content->text .= '<table class="grades"><caption>';
214 $this->content->text .= ($numbest == 1?get_string('bestgroupgrade', 'block_quiz_results'):get_string('bestgroupgrades', 'block_quiz_results', $numbest));
215 $this->content->text .= '</caption><colgroup class="number" /><colgroup class="name" /><colgroup class="grade" /><tbody>';
216 foreach($best as $groupid => $averagegrade) {
217 switch($nameformat) {
218 case B_QUIZRESULTS_NAME_FORMAT_ANON:
219 case B_QUIZRESULTS_NAME_FORMAT_ID:
220 $thisname = get_string('group');
223 case B_QUIZRESULTS_NAME_FORMAT_FULL:
225 $thisname = '<a href="'.$grouplink.$groupid.'">'.$groupgrades[$groupid]['group'].'</a>';
227 $thisname = $groupgrades[$groupid]['group'];
231 $this->content->text .= '<tr><td>'.(++$rank).'.</td><td>'.$thisname.'</td><td>';
232 switch($gradeformat) {
233 case B_QUIZRESULTS_GRADE_FORMAT_FRA:
234 $this->content->text .= quiz_format_grade($quiz, $averagegrade).'/'.$quiz->grade;
236 case B_QUIZRESULTS_GRADE_FORMAT_ABS:
237 $this->content->text .= quiz_format_grade($quiz, $averagegrade);
240 case B_QUIZRESULTS_GRADE_FORMAT_PCT:
241 $this->content->text .= round((float)$averagegrade / (float)$quiz->grade * 100).'%';
244 $this->content->text .= '</td></tr>';
246 $this->content->text .= '</tbody></table>';
251 $worst = array_reverse($worst, true);
252 $this->content->text .= '<table class="grades"><caption>';
253 $this->content->text .= ($numworst == 1?get_string('worstgroupgrade', 'block_quiz_results'):get_string('worstgroupgrades', 'block_quiz_results', $numworst));
254 $this->content->text .= '</caption><colgroup class="number" /><colgroup class="name" /><colgroup class="grade" /><tbody>';
255 foreach($worst as $groupid => $averagegrade) {
256 switch($nameformat) {
257 case B_QUIZRESULTS_NAME_FORMAT_ANON:
258 case B_QUIZRESULTS_NAME_FORMAT_ID:
259 $thisname = get_string('group');
262 case B_QUIZRESULTS_NAME_FORMAT_FULL:
263 $thisname = '<a href="'.$CFG->wwwroot.'/course/group.php?group='.$groupid.'&id='.$courseid.'">'.$groupgrades[$groupid]['group'].'</a>';
266 $this->content->text .= '<tr><td>'.(++$rank).'.</td><td>'.$thisname.'</td><td>';
267 switch($gradeformat) {
268 case B_QUIZRESULTS_GRADE_FORMAT_FRA:
269 $this->content->text .= quiz_format_grade($quiz, $averagegrade).'/'.$quiz->grade;
271 case B_QUIZRESULTS_GRADE_FORMAT_ABS:
272 $this->content->text .= quiz_format_grade($quiz, $averagegrade);
275 case B_QUIZRESULTS_GRADE_FORMAT_PCT:
276 $this->content->text .= round((float)$averagegrade / (float)$quiz->grade * 100).'%';
279 $this->content->text .= '</td></tr>';
281 $this->content->text .= '</tbody></table>';
287 // This is going to be just like no-groups mode, only we 'll filter
288 // out the grades from people not in our group.
289 if(empty($USER) || empty($USER->id)) {
290 // Not logged in, so show nothing
291 return $this->content;
294 $mygroups = groups_get_all_groups($courseid, $USER->id);
295 if(empty($mygroups)) {
296 // Not member of a group, show nothing
297 return $this->content;
300 // Get users from the same groups as me.
301 list($grouptest, $params) = $DB->get_in_or_equal(array_keys($mygroups));
302 $mygroupsusers = $DB->get_records_sql_menu(
303 'SELECT DISTINCT userid, 1 FROM {groups_members} WHERE groupid ' . $grouptest,
306 // Filter out the grades belonging to other users, and proceed as if there were no groups
307 foreach ($grades as $key => $grade) {
308 if (!isset($mygroupsusers[$grade->userid])) {
309 unset($grades[$key]);
313 // No break, fall through to the default case now we have filtered the $grades array.
317 $numbest = empty($this->config->showbest) ? 0 : min($this->config->showbest, count($grades));
318 $numworst = empty($this->config->showworst) ? 0 : min($this->config->showworst, count($grades) - $numbest);
320 // Collect all the usernames we are going to need
321 $remaining = $numbest;
322 $grade = end($grades);
323 while($remaining--) {
324 $best[$grade->userid] = $grade->id;
325 $grade = prev($grades);
328 $remaining = $numworst;
329 $grade = reset($grades);
330 while($remaining--) {
331 $worst[$grade->userid] = $grade->id;
332 $grade = next($grades);
335 if(empty($best) && empty($worst)) {
336 // Nothing to show, for some reason...
337 return $this->content;
340 // Now grab all the users from the database
341 $userids = array_merge(array_keys($best), array_keys($worst));
342 $users = $DB->get_records_list('user', 'id', $userids, '', 'id, firstname, lastname, idnumber');
346 $gradeformat = intval(empty($this->config->gradeformat) ? B_QUIZRESULTS_GRADE_FORMAT_PCT : $this->config->gradeformat);
349 // Don't show header and link to the quiz if we ARE at the quiz...
350 $this->content->text .= '<h1><a href="'.$CFG->wwwroot.'/mod/quiz/view.php?q='.$quizid.'">'.$quiz->name.'</a></h1>';
355 $this->content->text .= '<table class="grades"><caption>';
356 $this->content->text .= ($numbest == 1?get_string('bestgrade', 'block_quiz_results'):get_string('bestgrades', 'block_quiz_results', $numbest));
357 $this->content->text .= '</caption><colgroup class="number" /><colgroup class="name" /><colgroup class="grade" /><tbody>';
358 foreach($best as $userid => $gradeid) {
359 switch($nameformat) {
360 case B_QUIZRESULTS_NAME_FORMAT_ID:
361 $thisname = get_string('user').' '.$users[$userid]->idnumber;
363 case B_QUIZRESULTS_NAME_FORMAT_ANON:
364 $thisname = get_string('user');
367 case B_QUIZRESULTS_NAME_FORMAT_FULL:
368 $thisname = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$userid.'&course='.$courseid.'">'.fullname($users[$userid]).'</a>';
371 $this->content->text .= '<tr><td>'.(++$rank).'.</td><td>'.$thisname.'</td><td>';
372 switch($gradeformat) {
373 case B_QUIZRESULTS_GRADE_FORMAT_FRA:
374 $this->content->text .= quiz_format_grade($quiz, $grades[$gradeid]->grade).'/'.$quiz->grade;
376 case B_QUIZRESULTS_GRADE_FORMAT_ABS:
377 $this->content->text .= quiz_format_grade($quiz, $grades[$gradeid]->grade);
380 case B_QUIZRESULTS_GRADE_FORMAT_PCT:
382 $this->content->text .= round((float)$grades[$gradeid]->grade / (float)$quiz->grade * 100).'%';
384 $this->content->text .= '--%';
388 $this->content->text .= '</td></tr>';
390 $this->content->text .= '</tbody></table>';
395 $worst = array_reverse($worst, true);
396 $this->content->text .= '<table class="grades"><caption>';
397 $this->content->text .= ($numworst == 1?get_string('worstgrade', 'block_quiz_results'):get_string('worstgrades', 'block_quiz_results', $numworst));
398 $this->content->text .= '</caption><colgroup class="number" /><colgroup class="name" /><colgroup class="grade" /><tbody>';
399 foreach($worst as $userid => $gradeid) {
400 switch($nameformat) {
401 case B_QUIZRESULTS_NAME_FORMAT_ID:
402 $thisname = get_string('user').' '.$users[$userid]->idnumber;
404 case B_QUIZRESULTS_NAME_FORMAT_ANON:
405 $thisname = get_string('user');
408 case B_QUIZRESULTS_NAME_FORMAT_FULL:
409 $thisname = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$userid.'&course='.$courseid.'">'.fullname($users[$userid]).'</a>';
412 $this->content->text .= '<tr><td>'.(++$rank).'.</td><td>'.$thisname.'</td><td>';
413 switch($gradeformat) {
414 case B_QUIZRESULTS_GRADE_FORMAT_FRA:
415 $this->content->text .= quiz_format_grade($quiz, $grades[$gradeid]->grade).'/'.$quiz->grade;
417 case B_QUIZRESULTS_GRADE_FORMAT_ABS:
418 $this->content->text .= quiz_format_grade($quiz, $grades[$gradeid]->grade);
421 case B_QUIZRESULTS_GRADE_FORMAT_PCT:
422 $this->content->text .= round((float)$grades[$gradeid]->grade / (float)$quiz->grade * 100).'%';
425 $this->content->text .= '</td></tr>';
427 $this->content->text .= '</tbody></table>';
432 return $this->content;
435 function instance_allow_multiple() {