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);
20 function get_content() {
23 if ($this->content
!== NULL) {
24 return $this->content
;
27 $this->content
= new stdClass
;
28 $this->content
->text
= '';
29 $this->content
->footer
= '';
31 if (empty($this->instance
)) {
32 return $this->content
;
35 if($this->instance
->pagetype
== 'course-view') {
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
;
41 // Assuming we are displayed in the quiz view page
42 $quizid = $this->instance
->pageid
;
44 // A trick to take advantage of instance config and save queries
45 if(empty($this->config
->courseid
)) {
46 $modrecord = get_record('modules', 'name', 'quiz');
47 $cmrecord = get_record('course_modules', 'module', $modrecord->id
, 'instance', $quizid);
48 $this->config
->courseid
= intval($cmrecord->course
);
49 $this->instance_config_commit();
51 $courseid = $this->config
->courseid
;
54 $context = get_context_instance(CONTEXT_COURSE
, $courseid);
58 $this->content
->text
= get_string('error_emptyquizid', 'block_quiz_results');
59 return $this->content
;
62 // Get the quiz record
63 $quiz = get_record('quiz', 'id', $quizid);
65 $this->content
->text
= get_string('error_emptyquizrecord', 'block_quiz_results');
66 return $this->content
;
69 // Get the grades for this quiz
70 $grades = get_records('quiz_grades', 'quiz', $quizid, 'grade, timemodified DESC');
74 // The block will hide itself in this case
75 return $this->content
;
78 if(empty($this->config
->showbest
) && empty($this->config
->showworst
)) {
79 $this->content
->text
= get_string('configuredtoshownothing', 'block_quiz_results');
80 return $this->content
;
83 $groupmode = NOGROUPS
;
87 $nameformat = intval(empty($this->config
->nameformat
) ? B_QUIZRESULTS_NAME_FORMAT_FULL
: $this->config
->nameformat
);
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
91 if(!empty($this->config
->usegroups
) ||
$nameformat != B_QUIZRESULTS_NAME_FORMAT_FULL
) {
92 $course = get_record_select('course', 'id = '.$courseid, 'groupmode, groupmodeforce, student');
95 if(!empty($this->config
->usegroups
)) {
96 // The block was configured to operate in group mode
97 if($course->groupmodeforce
) {
98 $groupmode = $course->groupmode
;
101 $module = get_record_sql('SELECT cm.groupmode FROM '.$CFG->prefix
.'modules m LEFT JOIN '.$CFG->prefix
.'course_modules cm ON m.id = cm.module WHERE m.name = \'quiz\' AND cm.instance = '.$quizid);
102 $groupmode = $module->groupmode
;
104 // The actual groupmode for the quiz is now known to be $groupmode
107 if(has_capability('moodle/site:accessallgroups', $context) && $groupmode == SEPARATEGROUPS
) {
108 // We 'll make an exception in this case
109 $groupmode = VISIBLEGROUPS
;
114 // Display group-mode results
115 $groups = groups_get_all_groups($courseid);
118 // No groups exist, sorry
119 $this->content
->text
= get_string('error_nogroupsexist', 'block_quiz_results');
120 return $this->content
;
123 // Find out all the userids which have a submitted grade
125 foreach($grades as $grade) {
126 $userids[] = $grade->userid
;
129 // Now find which groups these users belong in
130 $groupofuser = get_records_sql(
131 'SELECT m.userid, m.groupid, g.name FROM '.$CFG->prefix
.'groups g LEFT JOIN '.$CFG->prefix
.'groups_members m ON g.id = m.groupid '.
132 'WHERE g.courseid = '.$courseid.' AND m.userid IN ('.implode(',', $userids).')'
135 $groupgrades = array();
137 // OK... now, iterate the grades again and sum them up for each group
138 foreach($grades as $grade) {
139 if(isset($groupofuser[$grade->userid
])) {
140 // Count this result only if the user is in a group
141 $groupid = $groupofuser[$grade->userid
]->groupid
;
142 if(!isset($groupgrades[$groupid])) {
143 $groupgrades[$groupid] = array('sum' => (float)$grade->grade
, 'number' => 1, 'group' => $groupofuser[$grade->userid
]->name
);
146 $groupgrades[$groupid]['sum'] +
= $grade->grade
;
147 ++
$groupgrades[$groupid]['number'];
152 foreach($groupgrades as $groupid => $groupgrade) {
153 $groupgrades[$groupid]['average'] = $groupgrades[$groupid]['sum'] / $groupgrades[$groupid]['number'];
156 // Sort groupgrades according to average grade, ascending
157 uasort($groupgrades, create_function('$a, $b', 'if($a["average"] == $b["average"]) return 0; return ($a["average"] > $b["average"] ? 1 : -1);'));
159 // How many groups do we have with graded member submissions to show?
160 $numbest = empty($this->config
->showbest
) ?
0 : min($this->config
->showbest
, count($groupgrades));
161 $numworst = empty($this->config
->showworst
) ?
0 : min($this->config
->showworst
, count($groupgrades) - $numbest);
163 // Collect all the group results we are going to use in $best and $worst
164 $remaining = $numbest;
165 $groupgrade = end($groupgrades);
166 while($remaining--) {
167 $best[key($groupgrades)] = $groupgrade['average'];
168 $groupgrade = prev($groupgrades);
171 $remaining = $numworst;
172 $groupgrade = reset($groupgrades);
173 while($remaining--) {
174 $worst[key($groupgrades)] = $groupgrade['average'];
175 $groupgrade = next($groupgrades);
179 $gradeformat = intval(empty($this->config
->gradeformat
) ? B_QUIZRESULTS_GRADE_FORMAT_PCT
: $this->config
->gradeformat
);
181 if($this->instance
->pagetype
!= 'mod-quiz-view') {
182 // Don't show header and link to the quiz if we ARE at the quiz...
183 $this->content
->text
.= '<h1><a href="'.$CFG->wwwroot
.'/mod/quiz/view.php?q='.$quizid.'">'.$quiz->name
.'</a></h1>';
186 if ($nameformat = B_QUIZRESULTS_NAME_FORMAT_FULL
) {
187 if (has_capability('moodle/course:managegroups', $context)) {
188 $grouplink = $CFG->wwwroot
.'/group/overview.php?courseid='.$courseid.'&id=';
189 } else if (has_capability('moodle/course:viewparticipants', $context)) {
190 $grouplink = $CFG->wwwroot
.'/user/index.php?id='.$courseid.'&group=';
198 $this->content
->text
.= '<table class="grades"><caption>';
199 $this->content
->text
.= ($numbest == 1?
get_string('bestgroupgrade', 'block_quiz_results'):get_string('bestgroupgrades', 'block_quiz_results', $numbest));
200 $this->content
->text
.= '</caption><colgroup class="number" /><colgroup class="name" /><colgroup class="grade" /><tbody>';
201 foreach($best as $groupid => $averagegrade) {
202 switch($nameformat) {
203 case B_QUIZRESULTS_NAME_FORMAT_ANON
:
204 case B_QUIZRESULTS_NAME_FORMAT_ID
:
205 $thisname = get_string('group');
208 case B_QUIZRESULTS_NAME_FORMAT_FULL
:
210 $thisname = '<a href="'.$grouplink.$groupid.'">'.$groupgrades[$groupid]['group'].'</a>';
212 $thisname = $groupgrades[$groupid]['group'];
216 $this->content
->text
.= '<tr><td>'.(++
$rank).'.</td><td>'.$thisname.'</td><td>';
217 switch($gradeformat) {
218 case B_QUIZRESULTS_GRADE_FORMAT_FRA
:
219 $this->content
->text
.= (format_float($averagegrade,$quiz->decimalpoints
).'/'.$quiz->grade
);
221 case B_QUIZRESULTS_GRADE_FORMAT_ABS
:
222 $this->content
->text
.= format_float($averagegrade,$quiz->decimalpoints
);
225 case B_QUIZRESULTS_GRADE_FORMAT_PCT
:
226 $this->content
->text
.= round((float)$averagegrade / (float)$quiz->grade
* 100).'%';
229 $this->content
->text
.= '</td></tr>';
231 $this->content
->text
.= '</tbody></table>';
236 $worst = array_reverse($worst, true);
237 $this->content
->text
.= '<table class="grades"><caption>';
238 $this->content
->text
.= ($numworst == 1?
get_string('worstgroupgrade', 'block_quiz_results'):get_string('worstgroupgrades', 'block_quiz_results', $numworst));
239 $this->content
->text
.= '</caption><colgroup class="number" /><colgroup class="name" /><colgroup class="grade" /><tbody>';
240 foreach($worst as $groupid => $averagegrade) {
241 switch($nameformat) {
242 case B_QUIZRESULTS_NAME_FORMAT_ANON
:
243 case B_QUIZRESULTS_NAME_FORMAT_ID
:
244 $thisname = get_string('group');
247 case B_QUIZRESULTS_NAME_FORMAT_FULL
:
248 $thisname = '<a href="'.$CFG->wwwroot
.'/course/group.php?group='.$groupid.'&id='.$courseid.'">'.$groupgrades[$groupid]['group'].'</a>';
251 $this->content
->text
.= '<tr><td>'.(++
$rank).'.</td><td>'.$thisname.'</td><td>';
252 switch($gradeformat) {
253 case B_QUIZRESULTS_GRADE_FORMAT_FRA
:
254 $this->content
->text
.= (format_float($averagegrade,$quiz->decimalpoints
).'/'.$quiz->grade
);
256 case B_QUIZRESULTS_GRADE_FORMAT_ABS
:
257 $this->content
->text
.= format_float($averagegrade,$quiz->decimalpoints
);
260 case B_QUIZRESULTS_GRADE_FORMAT_PCT
:
261 $this->content
->text
.= round((float)$averagegrade / (float)$quiz->grade
* 100).'%';
264 $this->content
->text
.= '</td></tr>';
266 $this->content
->text
.= '</tbody></table>';
272 // This is going to be just like no-groups mode, only we 'll filter
273 // out the grades from people not in our group.
274 if(empty($USER) ||
empty($USER->id
)) {
275 // Not logged in, so show nothing
276 return $this->content
;
279 $mygroups = groups_get_all_groups($courseid, $USER->id
);
280 if(empty($mygroups)) {
281 // Not member of a group, show nothing
282 return $this->content
;
285 $mygroupsusers = get_records_list('groups_members', 'groupid', implode(',', array_keys($mygroups)), '', 'userid, id');
286 // There should be at least one user there, ourselves. So no more tests.
288 // Just filter out the grades belonging to other users, and proceed as if there were no groups
289 $strallowedusers = implode(',', array_keys($mygroupsusers));
290 $grades = array_filter($grades, create_function('$el', '$allowed = explode(",", "'.$strallowedusers.'"); return in_array($el->userid, $allowed);'));
292 // NO break; HERE, JUST GO AHEAD
296 $numbest = empty($this->config
->showbest
) ?
0 : min($this->config
->showbest
, count($grades));
297 $numworst = empty($this->config
->showworst
) ?
0 : min($this->config
->showworst
, count($grades) - $numbest);
299 // Collect all the usernames we are going to need
300 $remaining = $numbest;
301 $grade = end($grades);
302 while($remaining--) {
303 $best[$grade->userid
] = $grade->id
;
304 $grade = prev($grades);
307 $remaining = $numworst;
308 $grade = reset($grades);
309 while($remaining--) {
310 $worst[$grade->userid
] = $grade->id
;
311 $grade = next($grades);
314 if(empty($best) && empty($worst)) {
315 // Nothing to show, for some reason...
316 return $this->content
;
319 // Now grab all the users from the database
320 $userids = array_merge(array_keys($best), array_keys($worst));
321 $users = get_records_list('user', 'id', implode(',',$userids), '', 'id, firstname, lastname, idnumber');
325 $gradeformat = intval(empty($this->config
->gradeformat
) ? B_QUIZRESULTS_GRADE_FORMAT_PCT
: $this->config
->gradeformat
);
327 if($this->instance
->pagetype
!= 'mod-quiz-view') {
328 // Don't show header and link to the quiz if we ARE at the quiz...
329 $this->content
->text
.= '<h1><a href="'.$CFG->wwwroot
.'/mod/quiz/view.php?q='.$quizid.'">'.$quiz->name
.'</a></h1>';
334 $this->content
->text
.= '<table class="grades"><caption>';
335 $this->content
->text
.= ($numbest == 1?
get_string('bestgrade', 'block_quiz_results'):get_string('bestgrades', 'block_quiz_results', $numbest));
336 $this->content
->text
.= '</caption><colgroup class="number" /><colgroup class="name" /><colgroup class="grade" /><tbody>';
337 foreach($best as $userid => $gradeid) {
338 switch($nameformat) {
339 case B_QUIZRESULTS_NAME_FORMAT_ID
:
340 $thisname = $course->student
.' '.intval($users[$userid]->idnumber
);
342 case B_QUIZRESULTS_NAME_FORMAT_ANON
:
343 $thisname = $course->student
;
346 case B_QUIZRESULTS_NAME_FORMAT_FULL
:
347 $thisname = '<a href="'.$CFG->wwwroot
.'/user/view.php?id='.$userid.'&course='.$courseid.'">'.fullname($users[$userid]).'</a>';
350 $this->content
->text
.= '<tr><td>'.(++
$rank).'.</td><td>'.$thisname.'</td><td>';
351 switch($gradeformat) {
352 case B_QUIZRESULTS_GRADE_FORMAT_FRA
:
353 $this->content
->text
.= (format_float($grades[$gradeid]->grade
,$quiz->decimalpoints
).'/'.$quiz->grade
);
355 case B_QUIZRESULTS_GRADE_FORMAT_ABS
:
356 $this->content
->text
.= format_float($grades[$gradeid]->grade
,$quiz->decimalpoints
);
359 case B_QUIZRESULTS_GRADE_FORMAT_PCT
:
361 $this->content
->text
.= round((float)$grades[$gradeid]->grade
/ (float)$quiz->grade
* 100).'%';
363 $this->content
->text
.= '--%';
367 $this->content
->text
.= '</td></tr>';
369 $this->content
->text
.= '</tbody></table>';
374 $worst = array_reverse($worst, true);
375 $this->content
->text
.= '<table class="grades"><caption>';
376 $this->content
->text
.= ($numworst == 1?
get_string('worstgrade', 'block_quiz_results'):get_string('worstgrades', 'block_quiz_results', $numworst));
377 $this->content
->text
.= '</caption><colgroup class="number" /><colgroup class="name" /><colgroup class="grade" /><tbody>';
378 foreach($worst as $userid => $gradeid) {
379 switch($nameformat) {
380 case B_QUIZRESULTS_NAME_FORMAT_ID
:
381 $thisname = $course->student
.' '.intval($users[$userid]->idnumber
);
383 case B_QUIZRESULTS_NAME_FORMAT_ANON
:
384 $thisname = $course->student
;
387 case B_QUIZRESULTS_NAME_FORMAT_FULL
:
388 $thisname = '<a href="'.$CFG->wwwroot
.'/user/view.php?id='.$userid.'&course='.$courseid.'">'.fullname($users[$userid]).'</a>';
391 $this->content
->text
.= '<tr><td>'.(++
$rank).'.</td><td>'.$thisname.'</td><td>';
392 switch($gradeformat) {
393 case B_QUIZRESULTS_GRADE_FORMAT_FRA
:
394 $this->content
->text
.= (format_float($grades[$gradeid]->grade
,$quiz->decimalpoints
).'/'.$quiz->grade
);
396 case B_QUIZRESULTS_GRADE_FORMAT_ABS
:
397 $this->content
->text
.= format_float($grades[$gradeid]->grade
,$quiz->decimalpoints
);
400 case B_QUIZRESULTS_GRADE_FORMAT_PCT
:
401 $this->content
->text
.= round((float)$grades[$gradeid]->grade
/ (float)$quiz->grade
* 100).'%';
404 $this->content
->text
.= '</td></tr>';
406 $this->content
->text
.= '</tbody></table>';
411 return $this->content
;
414 function instance_allow_multiple() {