MDL-43479 quiz response analysis : suppress break down by variants
[moodle.git] / question / classes / statistics / questions / calculated.php
CommitLineData
515b3ae6
JP
1<?php
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 * Question statistics calculations class. Used in the quiz statistics report but also available for use elsewhere.
19 *
20 * @package core
21 * @subpackage questionbank
22 * @copyright 2013 Open University
23 * @author Jamie Pratt <me@jamiep.org>
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 */
26
27namespace core_question\statistics\questions;
28defined('MOODLE_INTERNAL') || die();
29
30/**
31 * This class is used to return the stats as calculated by {@link \core_question\statistics\questions\calculator}
32 *
33 * @copyright 2013 Open University
34 * @author Jamie Pratt <me@jamiep.org>
35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36 */
37class calculated {
38
39 public $questionid;
40
515b3ae6
JP
41 // These first fields are the final fields cached in the db and shown in reports.
42
43 // See : http://docs.moodle.org/dev/Quiz_statistics_calculations#Position_statistics .
44
45 public $slot = null;
46
1239d287
JP
47 /**
48 * @var null|integer if this property is not null then this is the stats for a variant of a question or when inherited by
49 * calculated_for_subquestion and not null then this is the stats for a variant of a sub question.
50 */
51 public $variant = null;
52
515b3ae6
JP
53 /**
54 * @var bool is this a sub question.
55 */
56 public $subquestion = false;
57
df9ddae6
JP
58 /**
59 * @var string if this stat has been picked as a min, median or maximum facility value then this string says which stat this
c3e2e754 60 * is. Prepended to question name for display.
df9ddae6
JP
61 */
62 public $minmedianmaxnotice = '';
63
515b3ae6
JP
64 /**
65 * @var int total attempts at this question.
66 */
67 public $s = 0;
68
69 /**
70 * @var float effective weight of this question.
71 */
72 public $effectiveweight;
73
74 /**
75 * @var bool is covariance of this questions mark with other question marks negative?
76 */
77 public $negcovar;
78
79 /**
80 * @var float
81 */
82 public $discriminationindex;
83
84 /**
85 * @var float
86 */
87 public $discriminativeefficiency;
88
89 /**
90 * @var float standard deviation
91 */
92 public $sd;
93
94 /**
95 * @var float
96 */
97 public $facility;
98
99 /**
100 * @var float max mark achievable for this question.
101 */
102 public $maxmark;
103
104 /**
105 * @var string comma separated list of the positions in which this question appears.
106 */
107 public $positions;
108
109 /**
110 * @var null|float The average score that students would have got by guessing randomly. Or null if not calculable.
111 */
112 public $randomguessscore = null;
113
114 // End of fields in db.
115
116 protected $fieldsindb = array('questionid', 'slot', 'subquestion', 's', 'effectiveweight', 'negcovar', 'discriminationindex',
1239d287 117 'discriminativeefficiency', 'sd', 'facility', 'subquestions', 'maxmark', 'positions', 'randomguessscore', 'variant');
515b3ae6
JP
118
119 // Fields used for intermediate calculations.
120
121 public $totalmarks = 0;
122
123 public $totalothermarks = 0;
124
8e328617
JP
125 /**
126 * @var float The total of marks achieved for all positions in all attempts where this item was seen.
127 */
128 public $totalsummarks = 0;
129
515b3ae6
JP
130 public $markvariancesum = 0;
131
132 public $othermarkvariancesum = 0;
133
134 public $covariancesum = 0;
135
136 public $covariancemaxsum = 0;
137
138 public $subquestions = '';
139
140 public $covariancewithoverallmarksum = 0;
141
142 public $markarray = array();
143
144 public $othermarksarray = array();
145
146 public $markaverage;
147
148 public $othermarkaverage;
149
8e328617
JP
150 /**
151 * @var float The average for all attempts, of the sum of the marks for all positions in which this item appeared.
152 */
153 public $summarksaverage;
154
515b3ae6
JP
155 public $markvariance;
156 public $othermarkvariance;
157 public $covariance;
158 public $covariancemax;
159 public $covariancewithoverallmark;
160
161 /**
162 * @var object full question data
163 */
164 public $question;
165
1239d287
JP
166 /**
167 * An array of calculated stats for each variant of the question. Even when there is just one variant we still calculate this
168 * data as there is no way to know if there are variants before we have finished going through the attempt data one time.
169 *
170 * @var calculated[] $variants
171 */
172 public $variantstats = array();
173
515b3ae6
JP
174 /**
175 * Set if this record has been retrieved from cache. This is the time that the statistics were calculated.
176 *
177 * @var integer
178 */
179 public $timemodified;
180
c3e2e754
JP
181 /**
182 * Set up a calculated instance ready to store a question's (or a variant of a slot's question's)
183 * stats for one slot in the quiz.
184 *
185 * @param null|object $question
186 * @param null|int $slot
187 * @param null|int $variant
188 */
189 public function __construct($question = null, $slot = null, $variant = null) {
190 if ($question !== null) {
191 $this->questionid = $question->id;
192 $this->maxmark = $question->maxmark;
193 $this->positions = $question->number;
194 $this->question = $question;
195 }
196 if ($slot !== null) {
197 $this->slot = $slot;
198 }
199 if ($variant !== null) {
200 $this->variant = $variant;
201 }
202 }
203
204 /**
205 * @return null|string a string that represents the pool of questions from this question draws if it random or null if not.
206 */
207 public function random_selector_string() {
208 if ($this->question->qtype == 'random') {
209 return $this->question->category .'/'. $this->question->questiontext;
210 } else {
211 return null;
212 }
213 }
214
515b3ae6
JP
215 /**
216 * Cache calculated stats stored in this object in 'question_statistics' table.
217 *
218 * @param \qubaid_condition $qubaids
219 */
220 public function cache($qubaids) {
221 global $DB;
222 $toinsert = new \stdClass();
223 $toinsert->hashcode = $qubaids->get_hash_code();
224 $toinsert->timemodified = time();
225 foreach ($this->fieldsindb as $field) {
226 $toinsert->{$field} = $this->{$field};
227 }
228 $DB->insert_record('question_statistics', $toinsert, false);
1239d287
JP
229
230 if (count($this->variantstats) > 1) {
231 foreach ($this->variantstats as $variantstat) {
232 $variantstat->cache($qubaids);
233 }
234 }
515b3ae6
JP
235 }
236
237 /**
238 * @param object $record Given a record from 'question_statistics' copy stats from record to properties.
239 */
240 public function populate_from_record($record) {
241 foreach ($this->fieldsindb as $field) {
242 $this->$field = $record->$field;
243 }
244 $this->timemodified = $record->timemodified;
245 }
246
c3e2e754
JP
247 public function sort_variants() {
248 ksort($this->variantstats);
249 }
250
251 /**
252 * @return int[] array of sub-question ids or empty array if there are none.
253 */
254 public function get_sub_question_ids() {
255 if ($this->subquestions !== '') {
256 return explode(',', $this->subquestions);
257 } else {
258 return array();
259 }
260 }
51e3ded8
JP
261
262 /**
263 * Array of variants that have appeared in the attempt data for this question.
264 *
265 * @return int[]
266 */
267 public function get_variants() {
268 $variants = array_keys($this->variantstats);
269 if (count($variants) > 1) {
270 return $variants;
271 } else {
272 return array();
273 }
274 }
3d6f2466
JP
275
276 public function break_down_by_variant() {
277 $qtype = \question_bank::get_qtype($this->question->qtype);
278 return $qtype->break_down_stats_and_response_analysis_by_variant($this->question);
279 }
280
281
282 /**
283 * Delete the data structure for storing variant stats.
284 */
285 public function clear_variants() {
286 $this->variantstats = array();
287 }
515b3ae6 288}