Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403]
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 27 namespace core_question\statistics\questions; 28 defined('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 */ 37 class calculated { 38 39 public $questionid; 40 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 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 53 /** 54 * @var bool is this a sub question. 55 */ 56 public $subquestion = false; 57 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 60 * is. Prepended to question name for display. 61 */ 62 public $minmedianmaxnotice = ''; 63 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', 117 'discriminativeefficiency', 'sd', 'facility', 'subquestions', 'maxmark', 'positions', 'randomguessscore', 'variant'); 118 119 // Fields used for intermediate calculations. 120 121 public $totalmarks = 0; 122 123 public $totalothermarks = 0; 124 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 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 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 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 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 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 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 * Used to determine which random questions pull sub questions from the same pools. Where pool means category and possibly 206 * all the sub categories of that category. 207 * 208 * @return null|string represents the pool of questions from which this question draws if it is random, or null if not. 209 */ 210 public function random_selector_string() { 211 if ($this->question->qtype == 'random') { 212 return $this->question->category .'/'. $this->question->questiontext; 213 } else { 214 return null; 215 } 216 } 217 218 /** 219 * Cache calculated stats stored in this object in 'question_statistics' table. 220 * 221 * @param \qubaid_condition $qubaids 222 * @param int|null $timemodified the modified time to store. Defaults to the current time. 223 */ 224 public function cache($qubaids, $timemodified = null) { 225 global $DB; 226 $toinsert = new \stdClass(); 227 $toinsert->hashcode = $qubaids->get_hash_code(); 228 $toinsert->timemodified = $timemodified ?? time(); 229 foreach ($this->fieldsindb as $field) { 230 $toinsert->{$field} = $this->{$field}; 231 } 232 $DB->insert_record('question_statistics', $toinsert, false); 233 234 if ($this->get_variants()) { 235 foreach ($this->variantstats as $variantstat) { 236 $variantstat->cache($qubaids, $timemodified); 237 } 238 } 239 } 240 241 /** 242 * Load properties of this class from db record. 243 * 244 * @param object $record Given a record from 'question_statistics' copy stats from record to properties. 245 */ 246 public function populate_from_record($record) { 247 foreach ($this->fieldsindb as $field) { 248 $this->$field = $record->$field; 249 } 250 $this->timemodified = $record->timemodified; 251 } 252 253 /** 254 * Sort the variants of this question by variant number. 255 */ 256 public function sort_variants() { 257 ksort($this->variantstats); 258 } 259 260 /** 261 * Get any sub question ids for this question. 262 * 263 * @return int[] array of sub-question ids or empty array if there are none. 264 */ 265 public function get_sub_question_ids() { 266 if ($this->subquestions !== '') { 267 return explode(',', $this->subquestions); 268 } else { 269 return array(); 270 } 271 } 272 273 /** 274 * Array of variants that have appeared in the attempt data for this question. Or an empty array if there is only one variant. 275 * 276 * @return int[] the variant nos. 277 */ 278 public function get_variants() { 279 $variants = array_keys($this->variantstats); 280 if (count($variants) > 1 || reset($variants) != 1) { 281 return $variants; 282 } else { 283 return array(); 284 } 285 } 286 287 /** 288 * Do we break down the stats for this question by variant or not? 289 * 290 * @return bool Do we? 291 */ 292 public function break_down_by_variant() { 293 $qtype = \question_bank::get_qtype($this->question->qtype); 294 return $qtype->break_down_stats_and_response_analysis_by_variant($this->question); 295 } 296 297 298 /** 299 * Delete the data structure for storing variant stats. 300 */ 301 public function clear_variants() { 302 $this->variantstats = array(); 303 } 304 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body