MDL-14216 - change query to work on Postgres.
[moodle.git] / mod / quiz / report / statistics / report.php
CommitLineData
0c1c764e 1<?php
2/**
fb94cd48 3 * This script calculates various statistics about student attempts
0c1c764e 4 *
5 * @version $Id$
fb94cd48 6 * @author Martin Dougiamas, Jamie Pratt, Tim Hunt and others.
0c1c764e 7 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
8 * @package quiz
9 *//** */
10
0c1c764e 11require_once($CFG->dirroot.'/mod/quiz/report/statistics/statistics_form.php');
12require_once($CFG->dirroot.'/mod/quiz/report/statistics/statistics_table.php');
13
c386eaa3 14class quiz_statistics_report extends quiz_default_report {
0c1c764e 15
16 /**
17 * Display the report.
18 */
19 function display($quiz, $cm, $course) {
20 global $CFG, $DB;
21
22 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
23
24 $download = optional_param('download', '', PARAM_ALPHA);
25
26 $pageoptions = array();
27 $pageoptions['id'] = $cm->id;
28 $pageoptions['q'] = $quiz->id;
29 $pageoptions['mode'] = 'statistics';
30
31 $reporturl = new moodle_url($CFG->wwwroot.'/mod/quiz/report.php', $pageoptions);
32
33 $mform = new mod_quiz_report_statistics($reporturl);
34 if ($fromform = $mform->get_data()){
35 $useallattempts = $fromform->useallattempts;
36 if ($fromform->useallattempts){
37 set_user_preference('quiz_report_statistics_useallattempts', $fromform->useallattempts);
38 } else {
39 unset_user_preference('quiz_report_statistics_useallattempts');
40 }
41 } else {
42 $useallattempts = get_user_preferences('quiz_report_statistics_useallattempts', 0);
43 }
44
45 /// find out current groups mode
46 $currentgroup = groups_get_activity_group($cm, true);
47
e72efdd4 48 $nostudentsingroup = false;//true if a group is selected and their is noeone in it.
0c1c764e 49 if (!empty($currentgroup)) {
50 // all users who can attempt quizzes and who are in the currently selected group
e72efdd4 51 $groupstudents = get_users_by_capability($context, 'mod/quiz:attempt','','','','',$currentgroup,'',false);
52 if (!$groupstudents){
53 $nostudentsingroup = true;
0c1c764e 54 }
0c1c764e 55 }
56
6f51ed72 57 $questions = quiz_report_load_questions($quiz);
58 // Load the question type specific information
59 if (!get_question_options($questions)) {
60 print_error('cannotloadquestion', 'question');
61 }
62
c0250840 63 $table = new quiz_report_statistics_table();
0c1c764e 64 $table->is_downloading($download, get_string('reportstatistics','quiz_statistics'),
65 "$course->shortname ".format_string($quiz->name,true));
66 if (!$table->is_downloading()) {
67 // Only print headers if not asked to download data
68 $this->print_header_and_tabs($cm, $course, $quiz, "statistics");
69 }
70
71 if ($groupmode = groups_get_activity_groupmode($cm)) { // Groups are being used
72 if (!$table->is_downloading()) {
73 groups_print_activity_menu($cm, $reporturl->out());
e72efdd4 74 if ($currentgroup && !$groupstudents){
75 notify(get_string('nostudentsingroup', 'quiz_statistics'));
76 }
0c1c764e 77 }
78 }
79
80
81 // Print information on the number of existing attempts
82 if (!$table->is_downloading()) { //do not print notices when downloading
83 print_heading(get_string('quizinformation', 'quiz_statistics'));
84 $quizinformationtable = new object();
85 $quizinformationtable->align = array('center', 'center');
86 $quizinformationtable->width = '60%';
87 $quizinformationtable->class = 'generaltable titlesleft';
88 $quizinformationtable->data = array();
89 $quizinformationtable->data[] = array(get_string('quizname', 'quiz_statistics'), $quiz->name);
90 $quizinformationtable->data[] = array(get_string('coursename', 'quiz_statistics'), $course->fullname);
91 if ($cm->idnumber){
92 $quizinformationtable->data[] = array(get_string('coursename', 'quiz_statistics'), $cm->idnumber);
93 }
94 if ($quiz->timeopen){
95 $quizinformationtable->data[] = array(get_string('quizopen', 'quiz'), userdate($quiz->timeopen));
96 }
97 if ($quiz->timeclose){
98 $quizinformationtable->data[] = array(get_string('quizclose', 'quiz'), userdate($quiz->timeclose));
99 }
100 if ($quiz->timeopen && $quiz->timeclose){
fb94cd48 101 $quizinformationtable->data[] = array(get_string('duration', 'quiz_statistics'), format_time($quiz->timeclose - $quiz->timeopen));
0c1c764e 102 }
103 print_table($quizinformationtable);
104 }
105 if (!$table->is_downloading()) {
106 // Print display options
107 $mform->set_data(array('useallattempts' => $useallattempts));
108 $mform->display();
109 }
08a7ead5 110 $fromqa = '{quiz_attempts} qa ';
e72efdd4 111 $whereqa = 'quiz = :quizid AND preview=0 AND timefinish !=0 ';
112 $qaparams = array('quizid'=>$quiz->id);
113 if (!empty($currentgroup) && $groupstudents) {
114 list($grpsql, $grpparams) = $DB->get_in_or_equal(array_keys($groupstudents), SQL_PARAMS_NAMED, 'u0000');
115 $whereqa .= ' AND qa.userid '.$grpsql.' ';
116 $qaparams += $grpparams;
117 }
a1db71cb 118 $sql = 'SELECT (CASE WHEN attempt=1 THEN 1 ELSE 0 END) AS isfirst, COUNT(1) AS countrecs, SUM(sumgrades) AS total ' .
08a7ead5 119 'FROM '.$fromqa.
120 'WHERE ' .$whereqa.
0c1c764e 121 'GROUP BY (attempt=1)';
122
e72efdd4 123 if (!$nostudentsingroup){
124 //Calculating_MEAN_of_grades_for_all_attempts_by_students
125 //http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Calculating_MEAN_of_grades_for_all_attempts_by_students
126 if (!$attempttotals = $DB->get_records_sql($sql, $qaparams)){
127 print_heading(get_string('noattempts','quiz'));
128 $s = 0;
129 } else {
130 $firstattempt = $attempttotals[1];
131 $allattempts = new object();
132 $allattempts->countrecs = $firstattempt->countrecs +
133 (isset($attempttotals[0])?$attempttotals[0]->countrecs:0);
134 $allattempts->total = $firstattempt->total +
135 (isset($attempttotals[0])?$attempttotals[0]->total:0);
136 if ($useallattempts){
137 $usingattempts = $allattempts;
138 $usingattempts->attempts = get_string('allattempts', 'quiz_statistics');
139 $usingattempts->sql = '';
140 } else {
141 $usingattempts = $firstattempt;
142 $usingattempts->attempts = get_string('firstattempts', 'quiz_statistics');
143 $usingattempts->sql = 'AND qa.attempt=1 ';
144 }
145 $usingattempts->heading = get_string('statsfor', 'quiz_statistics', $usingattempts->attempts);
146 $s = $usingattempts->countrecs;
ea751786 147 $sumgradesavg = $usingattempts->total / $usingattempts->countrecs;
e72efdd4 148 }
0c1c764e 149 } else {
e72efdd4 150 $s = 0;
0c1c764e 151 }
e72efdd4 152
153 if ($s && !$table->is_downloading()) {
0c1c764e 154 print_heading(get_string('quizoverallstatistics', 'quiz_statistics'));
155 $quizoverallstatistics = new object();
156 $quizoverallstatistics->align = array('center', 'center');
157 $quizoverallstatistics->width = '60%';
158 $quizoverallstatistics->class = 'generaltable titlesleft';
159 $quizoverallstatistics->data = array();
160 $quizoverallstatistics->data[] = array(get_string('nooffirstattempts', 'quiz_statistics'), $firstattempt->countrecs);
161 $quizoverallstatistics->data[] = array(get_string('noofallattempts', 'quiz_statistics'), $allattempts->countrecs);
162 $quizoverallstatistics->data[] = array(get_string('firstattemptsavg', 'quiz_statistics'), quiz_report_scale_sumgrades_as_percentage($firstattempt->total / $firstattempt->countrecs, $quiz));
163 $quizoverallstatistics->data[] = array(get_string('allattemptsavg', 'quiz_statistics'), quiz_report_scale_sumgrades_as_percentage($allattempts->total / $allattempts->countrecs, $quiz));
164 print_table($quizoverallstatistics);
165 }
e72efdd4 166 $quizattsstatistics = new object();
167 $quizattsstatistics->align = array('center', 'center');
168 $quizattsstatistics->width = '60%';
169 $quizattsstatistics->class = 'generaltable titlesleft';
170 $quizattsstatistics->data = array();
0c1c764e 171 //get the median
e72efdd4 172 if ($s && !$table->is_downloading()) {
173
0c1c764e 174 print_heading($usingattempts->heading);
e72efdd4 175 if (($s%2)==0){
0c1c764e 176 //even number of attempts
e72efdd4 177 $limitoffset = ($s/2) - 1;
0c1c764e 178 $limit = 2;
179 } else {
e72efdd4 180 $limitoffset = (floor($s/2)) - 1;
0c1c764e 181 $limit = 1;
182 }
183 $sql = 'SELECT id, sumgrades ' .
08a7ead5 184 'FROM ' .$fromqa.
185 'WHERE ' .$whereqa.
0c1c764e 186 $usingattempts->sql.
187 'ORDER BY sumgrades';
e72efdd4 188 if (!$mediangrades = $DB->get_records_sql_menu($sql, $qaparams, $limitoffset, $limit)){
0c1c764e 189 print_error('errormedian', 'quiz_statistics');
190 }
191 if (count($mediangrades)==1){
192 $median = array_shift($mediangrades);
193 } else {
194 $median = array_shift($mediangrades);
195 $median += array_shift($mediangrades);
196 $median = $median /2;
197 }
6f51ed72 198 if ($s>1){
6f51ed72 199 $quizattsstatistics->data[] = array(get_string('median', 'quiz_statistics'), quiz_report_scale_sumgrades_as_percentage($median, $quiz));
200 //fetch sum of squared, cubed and power 4d
201 //differences between grades and mean grade
e72efdd4 202 $mean = $usingattempts->total / $s;
6f51ed72 203 $sql = "SELECT " .
e72efdd4 204 "SUM(POWER((qa.sumgrades - :mean1),2)) AS power2, " .
205 "SUM(POWER((qa.sumgrades - :mean2),3)) AS power3, ".
206 "SUM(POWER((qa.sumgrades - :mean3),4)) AS power4 ".
6f51ed72 207 'FROM ' .$fromqa.
040c36e3 208 'WHERE ' .$whereqa.
6f51ed72 209 $usingattempts->sql;
e72efdd4 210 $params = array('mean1' => $mean, 'mean2' => $mean, 'mean3' => $mean)+$qaparams;
6f51ed72 211 if (!$powers = $DB->get_record_sql($sql, $params)){
212 print_error('errorpowers', 'quiz_statistics');
213 }
214
215 //Standard_Deviation
216 //see http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Standard_Deviation
217
218 $sd = sqrt($powers->power2 / ($s -1));
219 $quizattsstatistics->data[] = array(get_string('standarddeviation', 'quiz_statistics'), quiz_report_scale_sumgrades_as_percentage($sd, $quiz));
220
221
222 //Skewness_and_Kurtosis
223 if ($s>2){
224 //see http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Skewness_and_Kurtosis
225 $m2= $powers->power2 / $s;
226 $m3= $powers->power3 / $s;
227 $m4= $powers->power4 / $s;
228
229 $k2= $s*$m2/($s-1);
230 $k3= $s*$s*$m3/(($s-1)*($s-2));
231
232 $skewness = $k3 / (pow($k2, 2/3));
233 $quizattsstatistics->data[] = array(get_string('skewness', 'quiz_statistics'), $skewness);
234 }
235
236
237 if ($s>3){
238 $k4= (($s*$s*$s)/(($s-1)*($s-2)*($s-3)))*((($s+1)*$m4)-(3*($s-1)*$m2*$m2));
239
240 $kurtosis = $k4 / ($k2*$k2);
241
242 $quizattsstatistics->data[] = array(get_string('kurtosis', 'quiz_statistics'), $kurtosis);
243 }
e72efdd4 244 }
245 }
246 if ($s){
4f5ffac0 247 require_once("$CFG->dirroot/mod/quiz/report/statistics/qstats.php");
248 $qstats = new qstats($questions, $s, $sumgradesavg);
249 $qstats->get_records($fromqa, $whereqa, $usingattempts->sql, $qaparams);
250 set_time_limit(0);
251 $qstats->process_states();
e72efdd4 252 }
253 if (!$table->is_downloading()){
254 if ($s>1){
ea751786 255 $p = count($questions);//no of positions
f44d7d46 256 if ($p > 1){
4f5ffac0 257 $cic = (100 * $p / ($p -1)) * (1 - ($qstats->sum_of_grade_variance())/$k2);
f44d7d46 258 $quizattsstatistics->data[] = array(get_string('cic', 'quiz_statistics'), number_format($cic, $quiz->decimalpoints).' %');
259 $errorratio = 100 * sqrt(1-($cic/100));
260 $quizattsstatistics->data[] = array(get_string('errorratio', 'quiz_statistics'), number_format($errorratio, $quiz->decimalpoints).' %');
261 $standarderror = ($errorratio * $sd / 100);
262 $quizattsstatistics->data[] = array(get_string('standarderror', 'quiz_statistics'),
263 quiz_report_scale_sumgrades_as_percentage($standarderror, $quiz));
264 }
e72efdd4 265 }
266 if ($quizattsstatistics->data){
6f51ed72 267 print_table($quizattsstatistics);
08a7ead5 268 }
c0250840 269 }
270 if (!$table->is_downloading()){
271 print_heading(get_string('quizstructureanalysis', 'quiz_statistics'));
272 }
f8d4e7b1 273 $table->setup($quiz, $cm->id, $reporturl, $s);
4f5ffac0 274 if (isset($qstats)){
051bc0e1 275 while ($question = array_shift($qstats->questions)){
4f5ffac0 276 $table->add_data_keyed($table->format_row($question));
051bc0e1 277 if ($question->qtype == 'random'){
278 $randomselectorstring = $question->category.'/'.$question->questiontext;
279 if ($qstats->questions){
280 $nextquestion = current($qstats->questions);
281 $nextrandomselectorstring = $nextquestion->category.'/'.$nextquestion->questiontext;
282 if ($nextquestion->qtype == 'random' && $randomselectorstring == $nextrandomselectorstring){
283 continue;//next loop iteration
284 }
285 }
286 if (isset($qstats->randomselectors[$randomselectorstring])){
287 foreach ($qstats->randomselectors[$randomselectorstring] as $itemid){
288 $table->add_data_keyed($table->format_row($qstats->subquestions[$itemid]));
289 }
290 }
4f5ffac0 291 }
292 }
08a7ead5 293 }
c0250840 294 $table->finish_output();
0c1c764e 295 return true;
296 }
297
298}
299
300
301?>