MDL-41754 (2) quiz statistics : using Sam's new progress classes
[moodle.git] / mod / quiz / report / statistics / classes / calculated.php
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/>.
17 namespace quiz_statistics;
19 defined('MOODLE_INTERNAL') || die();
21 /**
22  * The statistics calculator returns an instance of this class which contains the calculated statistics.
23  *
24  * These quiz statistics calculations are described here :
25  *
26  * http://docs.moodle.org/dev/Quiz_statistics_calculations#Test_statistics
27  *
28  * @package    quiz_statistics
29  * @copyright  2013 The Open University
30  * @author     James Pratt me@jamiep.org
31  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
32  */
33 class calculated {
35     /**
36      * @param  string $whichattempts which attempts to use, represented internally as one of the constants as used in
37      *                                   $quiz->grademethod ie.
38      *                                   QUIZ_GRADEAVERAGE, QUIZ_GRADEHIGHEST, QUIZ_ATTEMPTLAST or QUIZ_ATTEMPTFIRST
39      *                                   we calculate stats based on which attempts would affect the grade for each student,
40      *                                   the default null value is used when constructing an instance whose values will be
41      *                                   populated from a db record.
42      */
43     public function __construct($whichattempts = null) {
44         if ($whichattempts !== null) {
45             $this->whichattempts = $whichattempts;
46         }
47     }
49     /**
50      * @var int which attempts we are calculating calculate stats from.
51      */
52     public $whichattempts;
54     /* Following stats all described here : http://docs.moodle.org/dev/Quiz_statistics_calculations#Test_statistics  */
56     public $firstattemptscount = 0;
58     public $allattemptscount = 0;
60     public $lastattemptscount = 0;
62     public $highestattemptscount = 0;
64     public $firstattemptsavg;
66     public $allattemptsavg;
68     public $lastattemptsavg;
70     public $highestattemptsavg;
72     public $median;
74     public $standarddeviation;
76     public $skewness;
78     public $kurtosis;
80     public $cic;
82     public $errorratio;
84     public $standarderror;
86     /**
87      * @var int time these stats where calculated and cached.
88      */
89     public $timemodified;
91     /**
92      * Count of attempts selected by $this->whichattempts
93      *
94      * @return int
95      */
96     public function s() {
97         return $this->get_field('count');
98     }
100     /**
101      * Average grade for the attempts selected by $this->whichattempts
102      *
103      * @return float
104      */
105     public function avg() {
106         return $this->get_field('avg');
107     }
109     /**
110      * Get the right field name to fetch a stat for these attempts that is calculated for more than one $whichattempts (count or
111      * avg).
112      *
113      * @param string $field name of field
114      * @return int|float
115      */
116     protected function get_field($field) {
117         $fieldname = calculator::using_attempts_string_id($this->whichattempts).$field;
118         return $this->{$fieldname};
119     }
121     /**
122      * @param $course
123      * @param $cm
124      * @param $quiz
125      * @return array to display in table or spreadsheet.
126      */
127     public function get_formatted_quiz_info_data($course, $cm, $quiz) {
129         // You can edit this array to control which statistics are displayed.
130         $todisplay = array('firstattemptscount' => 'number',
131                            'allattemptscount' => 'number',
132                            'firstattemptsavg' => 'summarks_as_percentage',
133                            'allattemptsavg' => 'summarks_as_percentage',
134                            'lastattemptsavg' => 'summarks_as_percentage',
135                            'highestattemptsavg' => 'summarks_as_percentage',
136                            'median' => 'summarks_as_percentage',
137                            'standarddeviation' => 'summarks_as_percentage',
138                            'skewness' => 'number_format',
139                            'kurtosis' => 'number_format',
140                            'cic' => 'number_format_percent',
141                            'errorratio' => 'number_format_percent',
142                            'standarderror' => 'summarks_as_percentage');
144         // General information about the quiz.
145         $quizinfo = array();
146         $quizinfo[get_string('quizname', 'quiz_statistics')] = format_string($quiz->name);
147         $quizinfo[get_string('coursename', 'quiz_statistics')] = format_string($course->fullname);
148         if ($cm->idnumber) {
149             $quizinfo[get_string('idnumbermod')] = $cm->idnumber;
150         }
151         if ($quiz->timeopen) {
152             $quizinfo[get_string('quizopen', 'quiz')] = userdate($quiz->timeopen);
153         }
154         if ($quiz->timeclose) {
155             $quizinfo[get_string('quizclose', 'quiz')] = userdate($quiz->timeclose);
156         }
157         if ($quiz->timeopen && $quiz->timeclose) {
158             $quizinfo[get_string('duration', 'quiz_statistics')] =
159                 format_time($quiz->timeclose - $quiz->timeopen);
160         }
162         // The statistics.
163         foreach ($todisplay as $property => $format) {
164             if (!isset($this->$property) || !$format) {
165                 continue;
166             }
167             $value = $this->$property;
169             switch ($format) {
170                 case 'summarks_as_percentage':
171                     $formattedvalue = quiz_report_scale_summarks_as_percentage($value, $quiz);
172                     break;
173                 case 'number_format_percent':
174                     $formattedvalue = quiz_format_grade($quiz, $value) . '%';
175                     break;
176                 case 'number_format':
177                     // 2 extra decimal places, since not a percentage,
178                     // and we want the same number of sig figs.
179                     $formattedvalue = format_float($value, $quiz->decimalpoints + 2);
180                     break;
181                 case 'number':
182                     $formattedvalue = $value + 0;
183                     break;
184                 default:
185                     $formattedvalue = $value;
186             }
188             $quizinfo[get_string($property, 'quiz_statistics',
189                                  calculator::using_attempts_lang_string($this->whichattempts))] = $formattedvalue;
190         }
192         return $quizinfo;
193     }
195     /**
196      * @var array of names of properties of this class that are cached in db record.
197      */
198     protected $fieldsindb = array('whichattempts', 'firstattemptscount', 'allattemptscount', 'firstattemptsavg', 'allattemptsavg',
199                                     'lastattemptscount', 'highestattemptscount', 'lastattemptsavg', 'highestattemptsavg',
200                                     'median', 'standarddeviation', 'skewness',
201                                     'kurtosis', 'cic', 'errorratio', 'standarderror');
203     /**
204      * Cache the stats contained in this class.
205      *
206      * @param $qubaids \qubaid_condition
207      */
208     public function cache($qubaids) {
209         global $DB;
211         $toinsert = new \stdClass();
213         foreach ($this->fieldsindb as $field) {
214             $toinsert->{$field} = $this->{$field};
215         }
217         $toinsert->hashcode = $qubaids->get_hash_code();
218         $toinsert->timemodified = time();
220         // Fix up some dodgy data.
221         if (isset($toinsert->errorratio) && is_nan($toinsert->errorratio)) {
222             $toinsert->errorratio = null;
223         }
224         if (isset($toinsert->standarderror) && is_nan($toinsert->standarderror)) {
225             $toinsert->standarderror = null;
226         }
228         // Store the data.
229         $DB->insert_record('quiz_statistics', $toinsert);
231     }
233     /**
234      * Given a record from 'quiz_statistics' table load the data into the properties of this class.
235      *
236      * @param $record \stdClass from db.
237      */
238     public function populate_from_record($record) {
239         foreach ($this->fieldsindb as $field) {
240             $this->$field = $record->$field;
241         }
242         $this->timemodified = $record->timemodified;
243     }