MDL-14905 Removed table creation and data insertion from setUp() method, and put...
[moodle.git] / mod / quiz / report / statistics / report.php
CommitLineData
0c1c764e 1<?php
2/**
3 * This script lists student attempts
4 *
5 * @version $Id$
6 * @author Martin Dougiamas, Tim Hunt and others.
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
14class quiz_report extends quiz_default_report {
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
48 if (!empty($currentgroup)) {
49 // all users who can attempt quizzes and who are in the currently selected group
50 if (!$groupstudents = get_users_by_capability($context, 'mod/quiz:attempt','','','','',$currentgroup,'',false)){
51 $groupstudents = array();
52 }
53 $groupstudentslist = join(',', array_keys($groupstudents));
54 $allowedlist = $groupstudentslist;
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());
74 }
75 }
76
77
78 // Print information on the number of existing attempts
79 if (!$table->is_downloading()) { //do not print notices when downloading
80 print_heading(get_string('quizinformation', 'quiz_statistics'));
81 $quizinformationtable = new object();
82 $quizinformationtable->align = array('center', 'center');
83 $quizinformationtable->width = '60%';
84 $quizinformationtable->class = 'generaltable titlesleft';
85 $quizinformationtable->data = array();
86 $quizinformationtable->data[] = array(get_string('quizname', 'quiz_statistics'), $quiz->name);
87 $quizinformationtable->data[] = array(get_string('coursename', 'quiz_statistics'), $course->fullname);
88 if ($cm->idnumber){
89 $quizinformationtable->data[] = array(get_string('coursename', 'quiz_statistics'), $cm->idnumber);
90 }
91 if ($quiz->timeopen){
92 $quizinformationtable->data[] = array(get_string('quizopen', 'quiz'), userdate($quiz->timeopen));
93 }
94 if ($quiz->timeclose){
95 $quizinformationtable->data[] = array(get_string('quizclose', 'quiz'), userdate($quiz->timeclose));
96 }
97 if ($quiz->timeopen && $quiz->timeclose){
98 $quizinformationtable->data[] = array(get_string('duration'), format_time($quiz->timeclose - $quiz->timeopen));
99 }
100 print_table($quizinformationtable);
101 }
102 if (!$table->is_downloading()) {
103 // Print display options
104 $mform->set_data(array('useallattempts' => $useallattempts));
105 $mform->display();
106 }
08a7ead5 107 $fromqa = '{quiz_attempts} qa ';
108 $whereqa = 'quiz = ? AND preview=0 AND timefinish !=0 ';
0c1c764e 109 $sql = 'SELECT (attempt=1) AS isfirst, COUNT(1) AS countrecs, SUM(sumgrades) AS total ' .
08a7ead5 110 'FROM '.$fromqa.
111 'WHERE ' .$whereqa.
0c1c764e 112 'GROUP BY (attempt=1)';
113
040c36e3 114 //Calculating_MEAN_of_grades_for_all_attempts_by_students
115 //http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Calculating_MEAN_of_grades_for_all_attempts_by_students
08a7ead5 116 if (!$attempttotals = $DB->get_records_sql($sql, array($quiz->id))){
0c1c764e 117 print_heading(get_string('noattempts','quiz'));
118 return true;
119 } else {
120 $firstattempt = $attempttotals[1];
121 $allattempts = new object();
122 $allattempts->countrecs = $firstattempt->countrecs +
123 (isset($attempttotals[0])?$attempttotals[0]->countrecs:0);
124 $allattempts->total = $firstattempt->total +
125 (isset($attempttotals[0])?$attempttotals[0]->total:0);
126 }
127
128 if (!$table->is_downloading()) {
129 print_heading(get_string('quizoverallstatistics', 'quiz_statistics'));
130 $quizoverallstatistics = new object();
131 $quizoverallstatistics->align = array('center', 'center');
132 $quizoverallstatistics->width = '60%';
133 $quizoverallstatistics->class = 'generaltable titlesleft';
134 $quizoverallstatistics->data = array();
135 $quizoverallstatistics->data[] = array(get_string('nooffirstattempts', 'quiz_statistics'), $firstattempt->countrecs);
136 $quizoverallstatistics->data[] = array(get_string('noofallattempts', 'quiz_statistics'), $allattempts->countrecs);
137 $quizoverallstatistics->data[] = array(get_string('firstattemptsavg', 'quiz_statistics'), quiz_report_scale_sumgrades_as_percentage($firstattempt->total / $firstattempt->countrecs, $quiz));
138 $quizoverallstatistics->data[] = array(get_string('allattemptsavg', 'quiz_statistics'), quiz_report_scale_sumgrades_as_percentage($allattempts->total / $allattempts->countrecs, $quiz));
139 print_table($quizoverallstatistics);
140 }
141 //get the median
0c1c764e 142 if (!$table->is_downloading()) {
143 if ($useallattempts){
144 $usingattempts = $allattempts;
f8d4e7b1 145 $usingattempts->attempts = get_string('allattempts', 'quiz_statistics');
0c1c764e 146 $usingattempts->sql = '';
147 } else {
148 $usingattempts = $firstattempt;
f8d4e7b1 149 $usingattempts->attempts = get_string('firstattempts', 'quiz_statistics');
08a7ead5 150 $usingattempts->sql = 'AND qa.attempt=1 ';
0c1c764e 151 }
f8d4e7b1 152 $usingattempts->heading = get_string('statsfor', 'quiz_statistics', $usingattempts->attempts);
0c1c764e 153 print_heading($usingattempts->heading);
154 if (($usingattempts->countrecs/2)==floor($usingattempts->countrecs/2)){
155 //even number of attempts
156 $limitoffset = (floor($usingattempts->countrecs/2)) - 1;
157 $limit = 2;
158 } else {
159 $limitoffset = ($usingattempts->countrecs/2) - 1;
160 $limit = 1;
161 }
162 $sql = 'SELECT id, sumgrades ' .
08a7ead5 163 'FROM ' .$fromqa.
164 'WHERE ' .$whereqa.
0c1c764e 165 $usingattempts->sql.
166 'ORDER BY sumgrades';
08a7ead5 167 if (!$mediangrades = $DB->get_records_sql_menu($sql, array($quiz->id), $limitoffset, $limit)){
0c1c764e 168 print_error('errormedian', 'quiz_statistics');
169 }
170 if (count($mediangrades)==1){
171 $median = array_shift($mediangrades);
172 } else {
173 $median = array_shift($mediangrades);
174 $median += array_shift($mediangrades);
175 $median = $median /2;
176 }
0c1c764e 177
178 $s = $usingattempts->countrecs;
6f51ed72 179 if ($s>1){
180 $quizattsstatistics = new object();
181 $quizattsstatistics->align = array('center', 'center');
182 $quizattsstatistics->width = '60%';
183 $quizattsstatistics->class = 'generaltable titlesleft';
184 $quizattsstatistics->data = array();
185 $quizattsstatistics->data[] = array(get_string('median', 'quiz_statistics'), quiz_report_scale_sumgrades_as_percentage($median, $quiz));
186 //fetch sum of squared, cubed and power 4d
187 //differences between grades and mean grade
188 $mean = $usingattempts->total / $usingattempts->countrecs;
189 $sql = "SELECT " .
190 "SUM(POWER((qa.sumgrades - ?),2)) AS power2, " .
191 "SUM(POWER((qa.sumgrades - ?),3)) AS power3, ".
192 "SUM(POWER((qa.sumgrades - ?),4)) AS power4 ".
193 'FROM ' .$fromqa.
040c36e3 194 'WHERE ' .$whereqa.
6f51ed72 195 $usingattempts->sql;
196 $params = array($mean, $mean, $mean, $quiz->id);
197 if (!$powers = $DB->get_record_sql($sql, $params)){
198 print_error('errorpowers', 'quiz_statistics');
199 }
200
201 //Standard_Deviation
202 //see http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Standard_Deviation
203
204 $sd = sqrt($powers->power2 / ($s -1));
205 $quizattsstatistics->data[] = array(get_string('standarddeviation', 'quiz_statistics'), quiz_report_scale_sumgrades_as_percentage($sd, $quiz));
206
207
208 //Skewness_and_Kurtosis
209 if ($s>2){
210 //see http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Skewness_and_Kurtosis
211 $m2= $powers->power2 / $s;
212 $m3= $powers->power3 / $s;
213 $m4= $powers->power4 / $s;
214
215 $k2= $s*$m2/($s-1);
216 $k3= $s*$s*$m3/(($s-1)*($s-2));
217
218 $skewness = $k3 / (pow($k2, 2/3));
219 $quizattsstatistics->data[] = array(get_string('skewness', 'quiz_statistics'), $skewness);
220 }
221
222
223 if ($s>3){
224 $k4= (($s*$s*$s)/(($s-1)*($s-2)*($s-3)))*((($s+1)*$m4)-(3*($s-1)*$m2*$m2));
225
226 $kurtosis = $k4 / ($k2*$k2);
227
228 $quizattsstatistics->data[] = array(get_string('kurtosis', 'quiz_statistics'), $kurtosis);
229 }
230 //CIC, ER and SE.
231 //http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#CIC.2C_ER_and_SE
f8d4e7b1 232 list($qsql, $sqlparams) = $DB->get_in_or_equal(array_keys($questions), SQL_PARAMS_QM);
233 array_unshift($sqlparams, $quiz->id);//put quiz id in at beginning of array
6f51ed72 234 $qgradeavgsql = "SELECT qs.question, AVG(qs.grade) FROM " .
235 "{question_sessions} qns, " .
236 "{question_states} qs, " .
6f51ed72 237 $fromqa.' '.
238 'WHERE ' .$whereqa.
239 'AND qns.attemptid = qa.uniqueid '.
f8d4e7b1 240 'AND qs.question '.$qsql.' ' .
6f51ed72 241 $usingattempts->sql.
242 'AND qns.newgraded = qs.id GROUP BY qs.question';
f8d4e7b1 243 $qgradeavgs = $DB->get_records_sql_menu($qgradeavgsql, $sqlparams);
244
6f51ed72 245 $sum = 0;
f8d4e7b1 246 $sql = 'SELECT COUNT(1) as s,' .
6f51ed72 247 'SUM(POWER((qs.grade - ?),2)) AS power2 ' .
248 'FROM ' .
249 '{question_sessions} qns, ' .
f8d4e7b1 250 '{question_states} qs, '.
6f51ed72 251 $fromqa.' '.
252 'WHERE ' .$whereqa.
253 'AND qns.attemptid = qa.uniqueid '.
254 'AND qs.question = ? ' .
255 $usingattempts->sql.
256 'AND qns.newgraded = qs.id';
257 foreach ($qgradeavgs as $qid => $qgradeavg){
258 $params = array($qgradeavg, $quiz->id, $qid);
f8d4e7b1 259 $fromdb = $DB->get_record_sql($sql, $params);
260 if ($fromdb === false){
6f51ed72 261 print_error('errorpowerquestions', 'quiz_statistics');
262 }
f8d4e7b1 263 $questions[$qid]->facility = $qgradeavg / $questions[$qid]->grade;
264 $questions[$qid]->power = $fromdb->power2;
265 $questions[$qid]->s = $fromdb->s;
266 $questions[$qid]->sd = sqrt($questions[$qid]->power / ($questions[$qid]->s -1));
267 $sum += $questions[$qid]->power;
08a7ead5 268 }
6f51ed72 269 $sumofvarianceforallpositions = $sum / ($usingattempts->countrecs -1);
270 $p = count($qgradeavgs);//no of positions
f44d7d46 271 if ($p > 1){
272 $cic = (100 * $p / ($p -1)) * (1 - ($sumofvarianceforallpositions/$k2));
273 $quizattsstatistics->data[] = array(get_string('cic', 'quiz_statistics'), number_format($cic, $quiz->decimalpoints).' %');
274 $errorratio = 100 * sqrt(1-($cic/100));
275 $quizattsstatistics->data[] = array(get_string('errorratio', 'quiz_statistics'), number_format($errorratio, $quiz->decimalpoints).' %');
276 $standarderror = ($errorratio * $sd / 100);
277 $quizattsstatistics->data[] = array(get_string('standarderror', 'quiz_statistics'),
278 quiz_report_scale_sumgrades_as_percentage($standarderror, $quiz));
279 }
6f51ed72 280 print_table($quizattsstatistics);
08a7ead5 281 }
c0250840 282
283 }
284 if (!$table->is_downloading()){
285 print_heading(get_string('quizstructureanalysis', 'quiz_statistics'));
286 }
f8d4e7b1 287 $table->setup($quiz, $cm->id, $reporturl, $s);
c0250840 288 foreach ($questions as $question){
289 $table->add_data_keyed($table->format_row($question));
08a7ead5 290 }
c0250840 291 $table->finish_output();
0c1c764e 292 return true;
293 }
294
295}
296
297
298?>