Automatic installer.php lang files by installer_builder (20070726)
[moodle-linuxchix.git] / mod / hotpot / report / fullstat / report.php
blob42f387dc23d87b76c856c438c1dcb9329535c909
1 <?php // $Id$
2 /// Overview report just displays a big table of all the attempts
3 class hotpot_report extends hotpot_default_report {
4 function display(&$hotpot, &$cm, &$course, &$users, &$attempts, &$questions, &$options) {
5 global $CFG;
6 // create the tables
7 $tables = array();
8 $this->create_responses_table($hotpot, $course, $users, $attempts, $questions, $options, $tables);
9 $this->create_analysis_table($users, $attempts, $questions, $options, $tables);
10 // print report
11 $this->print_report($course, $hotpot, $tables, $options);
12 return true;
14 function create_responses_table(&$hotpot, &$course, &$users, &$attempts, &$questions, &$options, &$tables) {
15 global $CFG;
16 $is_html = ($options['reportformat']=='htm');
17 // shortcuts for font tags
18 $br = $is_html ? "<br />\n" : "\n";
19 $blank = $is_html ? '&nbsp;' : "";
20 $font_end = $is_html ? '</font>' : '';
21 $font_red = $is_html ? '<font color="red">' : '';
22 $font_blue = $is_html ? '<font color="blue">' : '';
23 $font_brown = $is_html ? '<font color="brown">' : '';
24 $font_green = $is_html ? '<font color="green">' : '';
25 $font_small = $is_html ? '<font size="-2">' : '';
26 $nobr_start = $is_html ? '<nobr>' : '';
27 $nobr_end = $is_html ? '</nobr>' : '';
28 // is review allowed? (do this once here, to save time later)
29 $allow_review = ($is_html && (has_capability('mod/hotpot:viewreport',get_context_instance(CONTEXT_COURSE, $course->id)) || $hotpot->review));
30 // assume penalties column is NOT required
31 $show_penalties = false;
32 // initialize $table
33 unset($table);
34 $table->border = 1;
35 $table->width = '100%';
36 // initialize legend, if necessary
37 if (!empty($options['reportshowlegend'])) {
38 $table->legend = array();
40 // headings for name, attempt number, score/grade and penalties
41 $table->head = array(
42 get_string("name"),
43 hotpot_grade_heading($hotpot, $options),
44 get_string('attempt', 'quiz'),
46 $table->align = array('left', 'center', 'center');
47 $table->size = array(150, 80, 10);
48 $table->wrap = array(0, 0, 0);
49 $table->fontsize = array(0, 0, 0);
50 // question headings
51 $this->add_question_headings($questions, $table, 'left', 0, false, 2);
52 // penalties (not always needed) and raw score
53 array_push($table->head,
54 get_string('penalties', 'hotpot'),
55 get_string('score', 'quiz')
57 array_push($table->align, 'center', 'center');
58 array_push($table->size, 50, 50);
59 array_push($table->wrap, 0, 0);
60 array_push($table->fontsize, 0, 0);
61 // message strings
62 $strnoresponse = get_string('noresponse', 'quiz');
63 // array to map columns onto question ids ($col => $id)
64 $questionids = array_keys($questions);
65 // add details of users' responses
66 foreach ($users as $user) {
67 // shortcut to user info held in first attempt record
68 $u = &$user->attempts[0];
69 if (function_exists("fullname")) {
70 $name = fullname($u);
71 } else {
72 $name = "$u->firstname $u->lastname";
74 if ($is_html) {
75 $name = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$u->userid.'&course='.$course->id.'">'.$name.'</a>';
77 $grade = isset($user->grade) ? $user->grade : $blank;
78 foreach ($user->attempts as $attempt) {
79 $attemptnumber = $attempt->attempt;
80 if ($allow_review) {
81 $attemptnumber = ' <a href="review.php?hp='.$hotpot->id.'&attempt='.$attempt->id.'">'.$attemptnumber.'</a>';
83 $cells = array ($name, $grade, $attemptnumber);
84 // $name and $grade are only printed on first line per user
85 $name = $blank;
86 $grade = $blank;
87 $start_col = count($cells);
88 foreach ($questionids as $col => $id) {
89 $cells[$start_col + $col] = "$font_brown($strnoresponse)$font_end";
91 if (isset($attempt->penalties)) {
92 $show_penalties = true;
93 $penalties = $attempt->penalties;
94 } else {
95 $penalties = $blank;
97 array_push($cells, $penalties, hotpot_format_score($attempt));
98 // get responses to questions in this attempt
99 foreach ($attempt->responses as $response) {
100 // check this question id is OK (should be)
101 $col = array_search($response->question, $questionids);
102 if (is_numeric($col)) {
103 // correct
104 if ($value = hotpot_strings($response->correct)) {
105 $this->set_legend($table, $col, $value, $questions[$response->question]);
106 } else {
107 $value = "($strnoresponse)";
109 $cell = $font_red.$value.$font_end;
110 // wrong
111 if ($value = hotpot_strings($response->wrong)) {
112 if (isset($table->legend)) {
113 $values = array();
114 foreach (explode(',', $value) as $v) {
115 $this->set_legend($table, $col, $v, $questions[$response->question]);
116 $values[] = $v;
118 $value = implode(',', $values);
120 $cell .= $br.$font_blue.$value.$font_end;
122 // ignored
123 if ($value = hotpot_strings($response->ignored)) {
124 if (isset($table->legend)) {
125 $values = array();
126 foreach (explode(',', $value) as $v) {
127 $this->set_legend($table, $col, $v, $questions[$response->question]);
128 $values[] = $v;
130 $value = implode(',', $values);
132 $cell .= $br.$font_brown.$value.$font_end;
134 // numeric
135 if (is_numeric($response->score)) {
136 if (empty($table->caption)) {
137 $table->caption = get_string('indivresp', 'quiz');
138 if ($is_html) {
139 $table->caption .= helpbutton('responsestable', $table->caption, 'hotpot', true, false, '', true);
142 $hints = empty($response->hints) ? 0 : $response->hints;
143 $clues = empty($response->clues) ? 0 : $response->clues;
144 $checks = empty($response->checks) ? 0 : $response->checks;
145 $numeric = $response->score.'% '.$blank.' ('.$hints.','.$clues.','.$checks.')';
146 $cell .= $br.$nobr_start.$font_green.$numeric.$font_end.$nobr_end;
148 $cells[$start_col + $col] = $cell;
151 $table->data[] = $cells;
153 // insert 'tabledivider' between users
154 $table->data[] = 'hr';
155 } // end foreach $users
156 // remove final 'hr' from data rows
157 array_pop($table->data);
158 if (!$show_penalties) {
159 $col = 3 + count($questionids);
160 $this->remove_column($table, $col);
162 $tables[] = &$table;
164 function create_analysis_table(&$users, &$attempts, &$questions, &$options, &$tables) {
165 $is_html = ($options['reportformat']=='htm');
166 // the fields we are interested in, in the order we want them
167 $fields = array('correct', 'wrong', 'ignored', 'hints', 'clues', 'checks', 'weighting');
168 $string_fields = array('correct', 'wrong', 'ignored');
169 $q = array(); // statistics about the $q(uestions)
170 $f = array(); // statistics about the $f(ields)
171 ////////////////////////////////////////////
172 // compile the statistics about the questions
173 ////////////////////////////////////////////
174 foreach ($questions as $id=>$question) {
175 // extract scores for attempts at this question
176 $scores = array();
177 foreach ($question->attempts as $attempt) {
178 $scores[] = $attempt->score;
180 // sort scores values (in ascending order)
181 asort($scores);
182 // get the borderline high and low scores
183 $count = count($scores);
184 switch ($count) {
185 case 0:
186 $lo_score = 0;
187 $hi_score = 0;
188 break;
189 case 1:
190 $lo_score = 0;
191 $hi_score = $scores[0];
192 break;
193 default:
194 $lo_score = $scores[round($count*1/3)];
195 $hi_score = $scores[round($count*2/3)];
196 break;
198 // get statistics for each attempt which includes this question
199 foreach ($question->attempts as $attempt) {
200 $is_hi_score = ($attempt->score >= $hi_score);
201 $is_lo_score = ($attempt->score < $lo_score);
202 // reference to the response to the current question
203 $response = &$attempt->responses[$id];
204 // update statistics for fields in this response
205 foreach($fields as $field) {
206 if (!isset($q[$id])) {
207 $q[$id] = array();
209 if (!isset($f[$field])) {
210 $f[$field] = array('count' => 0);
212 if (!isset($q[$id][$field])) {
213 $q[$id][$field] = array('count' => 0);
215 $values = explode(',', $response->$field);
216 $values = array_unique($values);
217 foreach($values as $value) {
218 // $value should be an integer (string_id or count)
219 if (is_numeric($value)) {
220 $f[$field]['count']++;
221 if (!isset($q[$id][$field][$value])) {
222 $q[$id][$field][$value] = 0;
224 $q[$id][$field]['count']++;
225 $q[$id][$field][$value]++;
228 } // end foreach $field
229 // initialize counters for this question, if necessary
230 if (!isset($q[$id]['count'])) {
231 $q[$id]['count'] = array('hi'=>0, 'lo'=>0, 'correct'=>0, 'total'=>0, 'sum'=>0);
233 // increment counters
234 $q[$id]['count']['sum'] += $response->score;
235 $q[$id]['count']['total']++;
236 if ($response->score==100) {
237 $q[$id]['count']['correct']++;
238 if ($is_hi_score) {
239 $q[$id]['count']['hi']++;
240 } else if ($is_lo_score) {
241 $q[$id]['count']['lo']++;
244 } // end foreach attempt
245 } // end foreach question
246 // check we have some details
247 if (count($q)) {
248 $showhideid = 'showhide';
249 // shortcuts for html tags
250 $bold_start = $is_html ? '<strong>' : "";
251 $bold_end = $is_html ? '</strong>' : "";
252 $div_start = $is_html ? '<div id="'.$showhideid.'">' : "";
253 $div_end = $is_html ? '</div>' : "";
254 $font_red = $is_html ? '<font color="red" size="-2">' : '';
255 $font_blue = $is_html ? '<font color="blue" size="-2">' : '';
256 $font_green = $is_html ? '<font color="green" size="-2">' : '';
257 $font_brown = $is_html ? '<font color="brown" size="-2">' : '';
258 $font_end = $is_html ? '</font>'."\n" : '';
259 $br = $is_html ? '<br />' : "\n";
260 $space = $is_html ? '&nbsp;' : "";
261 $no_value = $is_html ? '--' : "";
262 $help_button = $is_html ? helpbutton("discrimination", "", "quiz", true, false, "", true) : "";
263 // table properties
264 unset($table);
265 $table->border = 1;
266 $table->width = '100%';
267 $table->caption = get_string('itemanal', 'quiz');
268 if ($is_html) {
269 $table->caption .= helpbutton('analysistable', $table->caption, 'hotpot', true, false, '', true);
271 // initialize legend, if necessary
272 if (!empty($options['reportshowlegend'])) {
273 if (empty($tables) || empty($tables[0]->legend)) {
274 $table->legend = array();
275 } else {
276 $table->legend = $tables[0]->legend;
277 unset($tables[0]->legend);
280 // headings for name, attempt number and score/grade
281 $table->head = array($space);
282 $table->align = array('right');
283 $table->size = array(80);
284 // question headings
285 $this->add_question_headings($questions, $table, 'left', 0);
286 // initialize statistics
287 $table->stat = array();
288 $table->statheadercols = array(0);
289 // add headings for the $foot of the $table
290 $table->foot = array();
291 $table->foot[0] = array(get_string('average', 'hotpot'));
292 $table->foot[1] = array(get_string('percentcorrect', 'quiz'));
293 $table->foot[2] = array(get_string('discrimination', 'quiz').$help_button);
294 // maximum discrimination index (also default the default value)
295 $max_d_index = 10;
296 ////////////////////////////////////////////
297 // format the statistics into the $table
298 ////////////////////////////////////////////
299 // add $stat(istics) and $foot of $table
300 $questionids = array_keys($q);
301 foreach ($questionids as $col => $id) {
302 $row = 0;
303 // print the question text if there is no legend
304 if (empty($table->legend)) {
305 // add button to show/hide question text
306 if (!isset($table->stat[0])) {
307 $button = $is_html ? hotpot_showhide_button($showhideid) : "";
308 $table->stat[0] = array(get_string('question', 'quiz').$button);
310 // add the question name/text
311 $name = hotpot_get_question_name($questions[$id]);
312 $table->stat[$row++][$col+1] = $div_start.$bold_start.$name.$bold_end.$div_end.$space;
314 // add details about each field
315 foreach ($fields as $field) {
316 // check this row is required
317 if ($f[$field]['count']) {
318 $values = array();
319 $string_type = array_search($field, $string_fields);
320 // get the value of each response to this field
321 // and the count of that value
322 foreach ($q[$id][$field] as $value => $count) {
323 if (is_numeric($value) && $count) {
324 if (is_numeric($string_type)) {
325 $value = hotpot_string($value);
326 $this->set_legend($table, $col, $value, $questions[$id]);
327 switch ($string_type) {
328 case 0: // correct
329 $font_start = $font_red;
330 break;
331 case 1: // wrong
332 $font_start = $font_blue;
333 break;
334 case 2: // ignored
335 $font_start = $font_brown;
336 break;
338 } else { // numeric field
339 $font_start = $font_green;
341 $values[] = $font_start.round(100*$count/$q[$id]['count']['total']).'%'.$font_end.' '.$value;
343 } // end foreach $value => $count
344 // initialize stat(istics) row for this field, if required
345 if (!isset($table->stat[$row])) {
346 $table->stat[$row] = array(get_string($field, 'hotpot'));
348 // sort the values by frequency (using user-defined function)
349 usort($values, "hotpot_sort_stat_values");
350 // add stat(istics) values for this field
351 $table->stat[$row++][$col+1] = count($values) ? implode($br, $values) : $space;
353 } // end foreach field
354 // default percent correct and discrimination index for this question
355 $average = $no_value;
356 $percent = $no_value;
357 $d_index = $no_value;
358 if (isset($q[$id]['count'])) {
359 // average and percent correct
360 if ($q[$id]['count']['total']) {
361 $average = round($q[$id]['count']['sum'] / $q[$id]['count']['total']).'%';
362 $percent = round(100*$q[$id]['count']['correct'] / $q[$id]['count']['total']).'%';
363 $percent .= ' ('.$q[$id]['count']['correct'].'/'.$q[$id]['count']['total'].')';
365 // discrimination index
366 if ($q[$id]['count']['lo']) {
367 $d_index = min($max_d_index, round($q[$id]['count']['hi'] / $q[$id]['count']['lo'], 1));
368 } else {
369 $d_index = $q[$id]['count']['hi'] ? $max_d_index : 0;
371 $d_index .= ' ('.$q[$id]['count']['hi'].'/'.$q[$id]['count']['lo'].')';
373 $table->foot[0][$col+1] = $average;
374 $table->foot[1][$col+1] = $percent;
375 $table->foot[2][$col+1] = $d_index;
376 } // end foreach $question ($col)
377 // add javascript to show/hide question text
378 if (isset($table->stat[0]) && $is_html && empty($table->legend)) {
379 $i = count($table->stat[0]);
380 $table->stat[0][$i-1] .= hotpot_showhide_set($showhideid);
382 $tables[] = &$table;
383 $this->create_legend_table($tables, $table);
384 } // end if (empty($q)
385 } // end function
386 } // end class
387 function hotpot_sort_stat_values($a, $b) {
388 // sorts in descending order
389 // assumes first chars in $a and $b are a percentage
390 $a_val = intval(strip_tags($a));
391 $b_val = intval(strip_tags($b));
392 return ($a_val<$b_val) ? 1 : ($a_val==$b_val ? 0 : -1);
394 function hotpot_showhide_button($id) {
395 $show = get_string('show');
396 $hide = get_string('hide');
397 $pref = '1';
398 $text = ($pref=='1' ? $hide : $show);
399 return <<<SHOWHIDE_BUTTON
400 <script type="text/javascript">
401 //<![CDATA[
402 function showhide (id, toggle) {
403 var show = true;
404 obj = document.getElementById(id+'pref');
405 if (obj) {
406 show = (obj.value=='1');
407 if (toggle) {
408 show = !show;
409 obj.value = (show ? '1' : '0');
412 obj = document.getElementById(id+'button');
413 if (obj) {
414 obj.value = (show ? '$hide' : '$show');
416 obj = document.getElementsByName(id);
417 var i_max = obj.length;
418 for (var i=0; i<i_max; i++) {
419 obj[i].style.display = (show ? 'block' : 'none');
422 var showhide_allowed = (document.getElementById && document.getElementsByName);
423 if (showhide_allowed) {
424 var html = '';
425 html += '<form onsubmit="return false">';
426 html += '<input type="button" value="$text" id="{$id}button" onClick="javascript: return showhide(\\'$id\\', true);" />';
427 html += '<input type="hidden" name="{$id}pref" id="{$id}pref" value="$pref" />';
428 html += '</form>';
429 document.writeln(html);
431 //]]>
432 </script>
433 SHOWHIDE_BUTTON
436 function hotpot_showhide_set($id) {
437 return <<<SHOWHIDE_SET
438 <script type="text/javascript">
439 //<![CDATA[
440 if (showhide_allowed) {
441 showhide('$id');
443 //]]>
444 </script>
445 SHOWHIDE_SET