MDL-41758 quiz statistics : link to full break down of stats for slots
[moodle.git] / mod / quiz / report / statistics / statistics_table.php
CommitLineData
aeb15530 1<?php
04853f27
TH
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Quiz statistics report, table for showing statistics of each question in the quiz.
19 *
8d76124c
TH
20 * @package quiz_statistics
21 * @copyright 2008 Jamie Pratt
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
04853f27
TH
23 */
24
a17b297d
TH
25defined('MOODLE_INTERNAL') || die();
26
6f51ed72 27require_once($CFG->libdir.'/tablelib.php');
b3db3620 28
04853f27
TH
29/**
30 * This table has one row for each question in the quiz, with sub-rows when
c3e2e754
JP
31 * random questions and variants appear.
32 *
33 * There are columns for the various item and position statistics.
04853f27 34 *
8d76124c
TH
35 * @copyright 2008 Jamie Pratt
36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
04853f27 37 */
59ea8176 38class quiz_statistics_table extends flexible_table {
04853f27
TH
39 /** @var object the quiz settings. */
40 protected $quiz;
aeb15530 41
04853f27
TH
42 /** @var integer the quiz course_module id. */
43 protected $cmid;
aeb15530 44
04853f27
TH
45 /**
46 * Constructor.
47 */
c7df5006 48 public function __construct() {
04853f27 49 parent::__construct('mod-quiz-report-statistics-report');
b3db3620 50 }
07e68609 51
c0250840 52 /**
90fbad64 53 * Set up the columns and headers and other properties of the table and then
c0250840 54 * call flexible_table::setup() method.
04853f27
TH
55 *
56 * @param object $quiz the quiz settings
f7970e3c 57 * @param int $cmid the quiz course_module id
04853f27 58 * @param moodle_url $reporturl the URL to redisplay this report.
f7970e3c 59 * @param int $s number of attempts included in the statistics.
c0250840 60 */
90fbad64 61 public function statistics_setup($quiz, $cmid, $reporturl, $s) {
c0250840 62 $this->quiz = $quiz;
63 $this->cmid = $cmid;
04853f27 64
768a7588 65 // Define the table columns.
c0250840 66 $columns = array();
67 $headers = array();
aeb15530 68
04853f27
TH
69 $columns[] = 'number';
70 $headers[] = get_string('questionnumber', 'quiz_statistics');
aeb15530 71
04853f27
TH
72 if (!$this->is_downloading()) {
73 $columns[] = 'icon';
74 $headers[] = '';
75 $columns[] = 'actions';
76 $headers[] = '';
b3db3620 77 } else {
04853f27
TH
78 $columns[] = 'qtype';
79 $headers[] = get_string('questiontype', 'quiz_statistics');
b3db3620 80 }
aeb15530 81
04853f27
TH
82 $columns[] = 'name';
83 $headers[] = get_string('questionname', 'quiz');
84
85 $columns[] = 's';
86 $headers[] = get_string('attempts', 'quiz_statistics');
f8d4e7b1 87
04853f27
TH
88 if ($s > 1) {
89 $columns[] = 'facility';
90 $headers[] = get_string('facility', 'quiz_statistics');
aeb15530 91
04853f27
TH
92 $columns[] = 'sd';
93 $headers[] = get_string('standarddeviationq', 'quiz_statistics');
f8d4e7b1 94 }
aeb15530 95
04853f27
TH
96 $columns[] = 'random_guess_score';
97 $headers[] = get_string('random_guess_score', 'quiz_statistics');
aeb15530 98
04853f27
TH
99 $columns[] = 'intended_weight';
100 $headers[] = get_string('intended_weight', 'quiz_statistics');
aeb15530 101
04853f27
TH
102 $columns[] = 'effective_weight';
103 $headers[] = get_string('effective_weight', 'quiz_statistics');
aeb15530 104
04853f27
TH
105 $columns[] = 'discrimination_index';
106 $headers[] = get_string('discrimination_index', 'quiz_statistics');
107
108 $columns[] = 'discriminative_efficiency';
109 $headers[] = get_string('discriminative_efficiency', 'quiz_statistics');
43ec99aa 110
c0250840 111 $this->define_columns($columns);
112 $this->define_headers($headers);
113 $this->sortable(false);
114
74af927e 115 $this->column_class('s', 'numcol');
04853f27
TH
116 $this->column_class('facility', 'numcol');
117 $this->column_class('sd', 'numcol');
74af927e 118 $this->column_class('random_guess_score', 'numcol');
119 $this->column_class('intended_weight', 'numcol');
ea751786 120 $this->column_class('effective_weight', 'numcol');
ea751786 121 $this->column_class('discrimination_index', 'numcol');
122 $this->column_class('discriminative_efficiency', 'numcol');
74af927e 123
768a7588 124 // Set up the table.
c0250840 125 $this->define_baseurl($reporturl->out());
126
127 $this->collapsible(true);
128
c0250840 129 $this->set_attribute('id', 'questionstatistics');
130 $this->set_attribute('class', 'generaltable generalbox boxaligncenter');
131
132 parent::setup();
b3db3620 133 }
134
04853f27
TH
135 /**
136 * The question number.
515b3ae6 137 * @param \core_question\statistics\questions\calculated $questionstat stats for the question.
04853f27
TH
138 * @return string contents of this table cell.
139 */
515b3ae6 140 protected function col_number($questionstat) {
c3e2e754
JP
141 if (!isset($questionstat->question->number)) {
142 return '';
143 }
1239d287
JP
144 $number = $questionstat->question->number;
145
df9ddae6 146 if (isset($questionstat->subqdisplayorder)) {
1239d287
JP
147 $number = $number . '.'.$questionstat->subqdisplayorder;
148 }
149
150 if ($questionstat->question->qtype != 'random' && !is_null($questionstat->variant)) {
151 $number = $number . '.'.$questionstat->variant;
43ec99aa 152 }
c0250840 153
1239d287 154 return $number;
b3db3620 155 }
d3e99e85 156
157 /**
04853f27 158 * The question type icon.
515b3ae6 159 * @param \core_question\statistics\questions\calculated $questionstat stats for the question.
04853f27 160 * @return string contents of this table cell.
d3e99e85 161 */
515b3ae6
JP
162 protected function col_icon($questionstat) {
163 return print_question_icon($questionstat->question, true);
d3e99e85 164 }
aeb15530 165
04853f27
TH
166 /**
167 * Actions that can be performed on the question by this user (e.g. edit or preview).
515b3ae6 168 * @param \core_question\statistics\questions\calculated $questionstat stats for the question.
04853f27
TH
169 * @return string contents of this table cell.
170 */
515b3ae6
JP
171 protected function col_actions($questionstat) {
172 return quiz_question_action_icons($this->quiz, $this->cmid, $questionstat->question, $this->baseurl);
b3db3620 173 }
aeb15530 174
04853f27
TH
175 /**
176 * The question type name.
515b3ae6
JP
177 *
178 * @param \core_question\statistics\questions\calculated $questionstat stats for the question.
04853f27
TH
179 * @return string contents of this table cell.
180 */
515b3ae6
JP
181 protected function col_qtype($questionstat) {
182 return question_bank::get_qtype_name($questionstat->question->qtype);
04853f27
TH
183 }
184
185 /**
186 * The question name.
515b3ae6
JP
187 *
188 * @param \core_question\statistics\questions\calculated $questionstat stats for the question.
04853f27
TH
189 * @return string contents of this table cell.
190 */
515b3ae6
JP
191 protected function col_name($questionstat) {
192 $name = $questionstat->question->name;
04853f27 193
1239d287
JP
194 if (!is_null($questionstat->variant)) {
195 $a = new stdClass();
196 $a->name = $name;
197 $a->variant = $questionstat->variant;
198 $name = get_string('nameforvariant', 'quiz_statistics', $a);
199 }
200
04853f27
TH
201 if ($this->is_downloading()) {
202 return $name;
4f5ffac0 203 }
04853f27 204
51e3ded8
JP
205 $baseurl = new moodle_url($this->baseurl);
206 if (is_null($questionstat->variant)) {
207 if ($questionstat->subquestion && !$questionstat->get_variants()) {
208 $url = new moodle_url($baseurl, array('qid' => $questionstat->questionid));
209 $name = html_writer::link($url, $name, array('title' => get_string('detailedanalysis', 'quiz_statistics')));
210 } else if ($baseurl->param('slot') === null && $questionstat->slot) {
211 $number = $questionstat->question->number;
212 $url = new moodle_url($baseurl, array('slot' => $questionstat->slot));
213 if ($questionstat->get_variants() || $questionstat->get_sub_question_ids()) {
214 $name = html_writer::link($url,
215 $name,
216 array('title' => get_string('slotstructureanalysis', 'quiz_statistics', $number)));
217 } else {
218 $name = html_writer::link($url,
219 $name,
220 array('title' => get_string('detailedanalysis', 'quiz_statistics')));
221 }
222 }
04853f27
TH
223 }
224
04853f27 225
515b3ae6 226 if ($this->is_dubious_question($questionstat)) {
39153237 227 $name = html_writer::tag('div', $name, array('class' => 'dubious'));
04853f27
TH
228 }
229
df9ddae6 230 if (!empty($questionstat->minmedianmaxnotice)) {
c3e2e754 231 $name = get_string($questionstat->minmedianmaxnotice, 'quiz_statistics') . '<br />' . $name;
df9ddae6
JP
232 }
233
04853f27 234 return $name;
b3db3620 235 }
04853f27
TH
236
237 /**
238 * The number of attempts at this question.
515b3ae6
JP
239 *
240 * @param \core_question\statistics\questions\calculated $questionstat stats for the question.
04853f27
TH
241 * @return string contents of this table cell.
242 */
515b3ae6
JP
243 protected function col_s($questionstat) {
244 if (!isset($questionstat->s)) {
04853f27 245 return 0;
4f5ffac0 246 }
04853f27 247
515b3ae6 248 return $questionstat->s;
ea751786 249 }
04853f27
TH
250
251 /**
252 * The facility index (average fraction).
515b3ae6 253 * @param \core_question\statistics\questions\calculated $questionstat stats for the question.
04853f27
TH
254 * @return string contents of this table cell.
255 */
515b3ae6
JP
256 protected function col_facility($questionstat) {
257 if (is_null($questionstat->facility)) {
04853f27 258 return '';
ea751786 259 }
04853f27 260
515b3ae6 261 return number_format($questionstat->facility*100, 2) . '%';
ea751786 262 }
04853f27
TH
263
264 /**
265 * The standard deviation of the fractions.
515b3ae6 266 * @param \core_question\statistics\questions\calculated $questionstat stats for the question.
04853f27
TH
267 * @return string contents of this table cell.
268 */
515b3ae6
JP
269 protected function col_sd($questionstat) {
270 if (is_null($questionstat->sd) || $questionstat->maxmark == 0) {
4f5ffac0 271 return '';
272 }
04853f27 273
515b3ae6 274 return number_format($questionstat->sd*100 / $questionstat->maxmark, 2) . '%';
ea751786 275 }
04853f27
TH
276
277 /**
278 * An estimate of the fraction a student would get by guessing randomly.
515b3ae6 279 * @param \core_question\statistics\questions\calculated $questionstat stats for the question.
04853f27
TH
280 * @return string contents of this table cell.
281 */
515b3ae6
JP
282 protected function col_random_guess_score($questionstat) {
283 if (is_null($questionstat->randomguessscore)) {
04853f27 284 return '';
455c3efa 285 }
04853f27 286
515b3ae6 287 return number_format($questionstat->randomguessscore * 100, 2).'%';
6f51ed72 288 }
aeb15530 289
04853f27
TH
290 /**
291 * The intended question weight. Maximum mark for the question as a percentage
292 * of maximum mark for the quiz. That is, the indended influence this question
293 * on the student's overall mark.
515b3ae6 294 * @param \core_question\statistics\questions\calculated $questionstat stats for the question.
04853f27
TH
295 * @return string contents of this table cell.
296 */
515b3ae6
JP
297 protected function col_intended_weight($questionstat) {
298 return quiz_report_scale_summarks_as_percentage($questionstat->maxmark, $this->quiz);
04853f27
TH
299 }
300
301 /**
302 * The effective question weight. That is, an estimate of the actual
303 * influence this question has on the student's overall mark.
515b3ae6 304 * @param \core_question\statistics\questions\calculated $questionstat stats for the question.
04853f27
TH
305 * @return string contents of this table cell.
306 */
515b3ae6 307 protected function col_effective_weight($questionstat) {
442a3c0b
TH
308 global $OUTPUT;
309
51e3ded8 310 if (is_null($questionstat->effectiveweight)) {
aa3133bc 311 return '';
312 }
04853f27 313
515b3ae6 314 if ($questionstat->negcovar) {
04853f27
TH
315 $negcovar = get_string('negcovar', 'quiz_statistics');
316
317 if (!$this->is_downloading()) {
442a3c0b
TH
318 $negcovar = html_writer::tag('div',
319 $negcovar . $OUTPUT->help_icon('negcovar', 'quiz_statistics'),
320 array('class' => 'negcovar'));
04853f27
TH
321 }
322
323 return $negcovar;
324 }
325
515b3ae6 326 return number_format($questionstat->effectiveweight, 2) . '%';
f8d4e7b1 327 }
04853f27
TH
328
329 /**
330 * Discrimination index. This is the product moment correlation coefficient
515b3ae6 331 * between the fraction for this question, and the average fraction for the
04853f27 332 * other questions in this quiz.
515b3ae6 333 * @param \core_question\statistics\questions\calculated $questionstat stats for the question.
04853f27
TH
334 * @return string contents of this table cell.
335 */
515b3ae6
JP
336 protected function col_discrimination_index($questionstat) {
337 if (!is_numeric($questionstat->discriminationindex)) {
338 return $questionstat->discriminationindex;
e72efdd4 339 }
04853f27 340
515b3ae6 341 return number_format($questionstat->discriminationindex, 2) . '%';
f8d4e7b1 342 }
04853f27
TH
343
344 /**
345 * Discrimination efficiency, similar to, but different from, the Discrimination index.
515b3ae6
JP
346 *
347 * @param \core_question\statistics\questions\calculated $questionstat stats for the question.
04853f27
TH
348 * @return string contents of this table cell.
349 */
515b3ae6
JP
350 protected function col_discriminative_efficiency($questionstat) {
351 if (!is_numeric($questionstat->discriminativeefficiency)) {
1f23bda7 352 return '';
353 }
04853f27 354
515b3ae6 355 return number_format($questionstat->discriminativeefficiency, 2) . '%';
04853f27
TH
356 }
357
358 /**
359 * This method encapsulates the test for wheter a question should be considered dubious.
515b3ae6 360 * @param \core_question\statistics\questions\calculated $questionstat stats for the question.
f7970e3c 361 * @return bool is this question possibly not pulling it's weight?
04853f27 362 */
515b3ae6
JP
363 protected function is_dubious_question($questionstat) {
364 if (!is_numeric($questionstat->discriminativeefficiency)) {
04853f27
TH
365 return false;
366 }
367
515b3ae6 368 return $questionstat->discriminativeefficiency < 15;
f8d4e7b1 369 }
04853f27 370
9db17898 371 public function wrap_html_start() {
04853f27 372 // Horrible Moodle 2.0 wide-content work-around.
9db17898 373 if (!$this->is_downloading()) {
04853f27
TH
374 echo html_writer::start_tag('div', array('id' => 'tablecontainer',
375 'class' => 'statistics-tablecontainer'));
9db17898
SH
376 }
377 }
04853f27 378
9db17898
SH
379 public function wrap_html_finish() {
380 if (!$this->is_downloading()) {
04853f27 381 echo html_writer::end_tag('div');
9db17898
SH
382 }
383 }
b3db3620 384}