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 |
11 | require_once($CFG->dirroot.'/mod/quiz/report/statistics/statistics_form.php'); |
12 | require_once($CFG->dirroot.'/mod/quiz/report/statistics/statistics_table.php'); |
13 | |
c386eaa3 |
14 | class 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 | ?> |