Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
}