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 | |
11 | require_once($CFG->libdir.'/tablelib.php'); |
12 | require_once($CFG->dirroot.'/mod/quiz/report/statistics/statistics_form.php'); |
13 | require_once($CFG->dirroot.'/mod/quiz/report/statistics/statistics_table.php'); |
14 | |
15 | class quiz_report extends quiz_default_report { |
16 | |
17 | /** |
18 | * Display the report. |
19 | */ |
20 | function display($quiz, $cm, $course) { |
21 | global $CFG, $DB; |
22 | |
23 | $context = get_context_instance(CONTEXT_MODULE, $cm->id); |
24 | |
25 | $download = optional_param('download', '', PARAM_ALPHA); |
26 | |
27 | $pageoptions = array(); |
28 | $pageoptions['id'] = $cm->id; |
29 | $pageoptions['q'] = $quiz->id; |
30 | $pageoptions['mode'] = 'statistics'; |
31 | |
32 | $reporturl = new moodle_url($CFG->wwwroot.'/mod/quiz/report.php', $pageoptions); |
33 | |
34 | $mform = new mod_quiz_report_statistics($reporturl); |
35 | if ($fromform = $mform->get_data()){ |
36 | $useallattempts = $fromform->useallattempts; |
37 | if ($fromform->useallattempts){ |
38 | set_user_preference('quiz_report_statistics_useallattempts', $fromform->useallattempts); |
39 | } else { |
40 | unset_user_preference('quiz_report_statistics_useallattempts'); |
41 | } |
42 | } else { |
43 | $useallattempts = get_user_preferences('quiz_report_statistics_useallattempts', 0); |
44 | } |
45 | |
46 | /// find out current groups mode |
47 | $currentgroup = groups_get_activity_group($cm, true); |
48 | |
49 | if (!empty($currentgroup)) { |
50 | // all users who can attempt quizzes and who are in the currently selected group |
51 | if (!$groupstudents = get_users_by_capability($context, 'mod/quiz:attempt','','','','',$currentgroup,'',false)){ |
52 | $groupstudents = array(); |
53 | } |
54 | $groupstudentslist = join(',', array_keys($groupstudents)); |
55 | $allowedlist = $groupstudentslist; |
56 | } |
57 | |
c0250840 |
58 | $questions = question_load_questions(quiz_questions_in_quiz($quiz->questions)); |
0c1c764e |
59 | |
c0250840 |
60 | $table = new quiz_report_statistics_table(); |
0c1c764e |
61 | $table->is_downloading($download, get_string('reportstatistics','quiz_statistics'), |
62 | "$course->shortname ".format_string($quiz->name,true)); |
63 | if (!$table->is_downloading()) { |
64 | // Only print headers if not asked to download data |
65 | $this->print_header_and_tabs($cm, $course, $quiz, "statistics"); |
66 | } |
67 | |
68 | if ($groupmode = groups_get_activity_groupmode($cm)) { // Groups are being used |
69 | if (!$table->is_downloading()) { |
70 | groups_print_activity_menu($cm, $reporturl->out()); |
71 | } |
72 | } |
73 | |
74 | |
75 | // Print information on the number of existing attempts |
76 | if (!$table->is_downloading()) { //do not print notices when downloading |
77 | print_heading(get_string('quizinformation', 'quiz_statistics')); |
78 | $quizinformationtable = new object(); |
79 | $quizinformationtable->align = array('center', 'center'); |
80 | $quizinformationtable->width = '60%'; |
81 | $quizinformationtable->class = 'generaltable titlesleft'; |
82 | $quizinformationtable->data = array(); |
83 | $quizinformationtable->data[] = array(get_string('quizname', 'quiz_statistics'), $quiz->name); |
84 | $quizinformationtable->data[] = array(get_string('coursename', 'quiz_statistics'), $course->fullname); |
85 | if ($cm->idnumber){ |
86 | $quizinformationtable->data[] = array(get_string('coursename', 'quiz_statistics'), $cm->idnumber); |
87 | } |
88 | if ($quiz->timeopen){ |
89 | $quizinformationtable->data[] = array(get_string('quizopen', 'quiz'), userdate($quiz->timeopen)); |
90 | } |
91 | if ($quiz->timeclose){ |
92 | $quizinformationtable->data[] = array(get_string('quizclose', 'quiz'), userdate($quiz->timeclose)); |
93 | } |
94 | if ($quiz->timeopen && $quiz->timeclose){ |
95 | $quizinformationtable->data[] = array(get_string('duration'), format_time($quiz->timeclose - $quiz->timeopen)); |
96 | } |
97 | print_table($quizinformationtable); |
98 | } |
99 | if (!$table->is_downloading()) { |
100 | // Print display options |
101 | $mform->set_data(array('useallattempts' => $useallattempts)); |
102 | $mform->display(); |
103 | } |
08a7ead5 |
104 | $fromqa = '{quiz_attempts} qa '; |
105 | $whereqa = 'quiz = ? AND preview=0 AND timefinish !=0 '; |
0c1c764e |
106 | $sql = 'SELECT (attempt=1) AS isfirst, COUNT(1) AS countrecs, SUM(sumgrades) AS total ' . |
08a7ead5 |
107 | 'FROM '.$fromqa. |
108 | 'WHERE ' .$whereqa. |
0c1c764e |
109 | 'GROUP BY (attempt=1)'; |
110 | |
040c36e3 |
111 | //Calculating_MEAN_of_grades_for_all_attempts_by_students |
112 | //http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Calculating_MEAN_of_grades_for_all_attempts_by_students |
08a7ead5 |
113 | if (!$attempttotals = $DB->get_records_sql($sql, array($quiz->id))){ |
0c1c764e |
114 | print_heading(get_string('noattempts','quiz')); |
115 | return true; |
116 | } else { |
117 | $firstattempt = $attempttotals[1]; |
118 | $allattempts = new object(); |
119 | $allattempts->countrecs = $firstattempt->countrecs + |
120 | (isset($attempttotals[0])?$attempttotals[0]->countrecs:0); |
121 | $allattempts->total = $firstattempt->total + |
122 | (isset($attempttotals[0])?$attempttotals[0]->total:0); |
123 | } |
124 | |
125 | if (!$table->is_downloading()) { |
126 | print_heading(get_string('quizoverallstatistics', 'quiz_statistics')); |
127 | $quizoverallstatistics = new object(); |
128 | $quizoverallstatistics->align = array('center', 'center'); |
129 | $quizoverallstatistics->width = '60%'; |
130 | $quizoverallstatistics->class = 'generaltable titlesleft'; |
131 | $quizoverallstatistics->data = array(); |
132 | $quizoverallstatistics->data[] = array(get_string('nooffirstattempts', 'quiz_statistics'), $firstattempt->countrecs); |
133 | $quizoverallstatistics->data[] = array(get_string('noofallattempts', 'quiz_statistics'), $allattempts->countrecs); |
134 | $quizoverallstatistics->data[] = array(get_string('firstattemptsavg', 'quiz_statistics'), quiz_report_scale_sumgrades_as_percentage($firstattempt->total / $firstattempt->countrecs, $quiz)); |
135 | $quizoverallstatistics->data[] = array(get_string('allattemptsavg', 'quiz_statistics'), quiz_report_scale_sumgrades_as_percentage($allattempts->total / $allattempts->countrecs, $quiz)); |
136 | print_table($quizoverallstatistics); |
137 | } |
138 | //get the median |
0c1c764e |
139 | if (!$table->is_downloading()) { |
140 | if ($useallattempts){ |
141 | $usingattempts = $allattempts; |
142 | $usingattempts->heading = get_string('statsforallattempts', 'quiz_statistics'); |
143 | $usingattempts->sql = ''; |
144 | } else { |
145 | $usingattempts = $firstattempt; |
146 | $usingattempts->heading = get_string('statsforfirstattempts', 'quiz_statistics'); |
08a7ead5 |
147 | $usingattempts->sql = 'AND qa.attempt=1 '; |
0c1c764e |
148 | } |
149 | print_heading($usingattempts->heading); |
150 | if (($usingattempts->countrecs/2)==floor($usingattempts->countrecs/2)){ |
151 | //even number of attempts |
152 | $limitoffset = (floor($usingattempts->countrecs/2)) - 1; |
153 | $limit = 2; |
154 | } else { |
155 | $limitoffset = ($usingattempts->countrecs/2) - 1; |
156 | $limit = 1; |
157 | } |
158 | $sql = 'SELECT id, sumgrades ' . |
08a7ead5 |
159 | 'FROM ' .$fromqa. |
160 | 'WHERE ' .$whereqa. |
0c1c764e |
161 | $usingattempts->sql. |
162 | 'ORDER BY sumgrades'; |
08a7ead5 |
163 | if (!$mediangrades = $DB->get_records_sql_menu($sql, array($quiz->id), $limitoffset, $limit)){ |
0c1c764e |
164 | print_error('errormedian', 'quiz_statistics'); |
165 | } |
166 | if (count($mediangrades)==1){ |
167 | $median = array_shift($mediangrades); |
168 | } else { |
169 | $median = array_shift($mediangrades); |
170 | $median += array_shift($mediangrades); |
171 | $median = $median /2; |
172 | } |
173 | //fetch sum of squared, cubed and power 4d |
174 | //differences between grades and mean grade |
175 | $mean = $usingattempts->total / $usingattempts->countrecs; |
176 | $sql = "SELECT " . |
08a7ead5 |
177 | "SUM(POWER((qa.sumgrades - ?),2)) AS power2, " . |
178 | "SUM(POWER((qa.sumgrades - ?),3)) AS power3, ". |
179 | "SUM(POWER((qa.sumgrades - ?),4)) AS power4 ". |
180 | 'FROM ' .$fromqa. |
181 | 'WHERE ' .$whereqa. |
182 | $usingattempts->sql; |
0c1c764e |
183 | $params = array($mean, $mean, $mean, $quiz->id); |
184 | if (!$powers = $DB->get_record_sql($sql, $params)){ |
185 | print_error('errorpowers', 'quiz_statistics'); |
186 | } |
187 | |
188 | $s = $usingattempts->countrecs; |
189 | |
040c36e3 |
190 | //Standard_Deviation |
191 | //see http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Standard_Deviation |
192 | |
0c1c764e |
193 | $sd = sqrt($powers->power2 / ($s -1)); |
194 | |
040c36e3 |
195 | //Skewness_and_Kurtosis |
196 | //see http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Skewness_and_Kurtosis |
0c1c764e |
197 | $m2= $powers->power2 / $s; |
198 | $m3= $powers->power3 / $s; |
199 | $m4= $powers->power4 / $s; |
200 | |
201 | $k2= $s*$m2/($s-1); |
202 | $k3= $s*$s*$m3/(($s-1)*($s-2)); |
040c36e3 |
203 | $k4= (($s*$s*$s)/(($s-1)*($s-2)*($s-3)))*((($s+1)*$m4)-(3*($s-1)*$m2*$m2)); |
0c1c764e |
204 | |
205 | $skewness = $k3 / (pow($k2, 2/3)); |
206 | $kurtosis = $k4 / ($k2*$k2); |
207 | |
208 | $quizattsstatistics = new object(); |
209 | $quizattsstatistics->align = array('center', 'center'); |
210 | $quizattsstatistics->width = '60%'; |
211 | $quizattsstatistics->class = 'generaltable titlesleft'; |
212 | $quizattsstatistics->data = array(); |
213 | |
214 | $quizattsstatistics->data[] = array(get_string('median', 'quiz_statistics'), quiz_report_scale_sumgrades_as_percentage($median, $quiz)); |
215 | $quizattsstatistics->data[] = array(get_string('standarddeviation', 'quiz_statistics'), quiz_report_scale_sumgrades_as_percentage($sd, $quiz)); |
216 | $quizattsstatistics->data[] = array(get_string('skewness', 'quiz_statistics'), $skewness); |
217 | $quizattsstatistics->data[] = array(get_string('kurtosis', 'quiz_statistics'), $kurtosis); |
08a7ead5 |
218 | |
040c36e3 |
219 | //CIC, ER and SE. |
220 | //http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#CIC.2C_ER_and_SE |
08a7ead5 |
221 | $qgradeavgsql = "SELECT qs.question, AVG(qs.grade) FROM " . |
222 | "{question_sessions} qns, " . |
223 | "{question_states} qs, " . |
224 | "{question} q, " . |
225 | $fromqa.' '. |
226 | 'WHERE ' .$whereqa. |
227 | 'AND qns.attemptid = qa.uniqueid '. |
228 | 'AND qs.question = q.id ' . |
229 | 'AND q.length > 0 '. |
230 | $usingattempts->sql. |
231 | 'AND qns.newgraded = qs.id GROUP BY qs.question'; |
232 | $qgradeavgs = $DB->get_records_sql_menu($qgradeavgsql, array($quiz->id)); |
233 | $sum = 0; |
040c36e3 |
234 | $sql = 'SELECT ' . |
235 | 'SUM(POWER((qs.grade - ?),2)) AS power2 ' . |
236 | 'FROM ' . |
237 | '{question_sessions} qns, ' . |
238 | '{question_states} qs, ' . |
239 | '{question} q, ' . |
240 | $fromqa.' '. |
241 | 'WHERE ' .$whereqa. |
242 | 'AND qns.attemptid = qa.uniqueid '. |
243 | 'AND qs.question = ? ' . |
244 | $usingattempts->sql. |
245 | 'AND qns.newgraded = qs.id'; |
08a7ead5 |
246 | foreach ($qgradeavgs as $qid => $qgradeavg){ |
08a7ead5 |
247 | $params = array($qgradeavg, $quiz->id, $qid); |
248 | $power = $DB->get_field_sql($sql, $params); |
249 | if ($power === false){ |
250 | print_error('errorpowerquestions', 'quiz_statistics'); |
251 | } |
252 | $sum += $power; |
253 | } |
254 | $sumofvarianceforallpositions = $sum / ($usingattempts->countrecs -1); |
255 | $p = count($qgradeavgs);//no of positions |
256 | $cic = (100 * $p / ($p -1)) * (1 - ($sumofvarianceforallpositions/$k2)); |
257 | $quizattsstatistics->data[] = array(get_string('cic', 'quiz_statistics'), number_format($cic, $quiz->decimalpoints).' %'); |
258 | $errorratio = 100 * sqrt(1-($cic/100)); |
259 | $quizattsstatistics->data[] = array(get_string('errorratio', 'quiz_statistics'), number_format($errorratio, $quiz->decimalpoints).' %'); |
260 | $standarderror = ($errorratio * $sd / 100); |
261 | $quizattsstatistics->data[] = array(get_string('standarderror', 'quiz_statistics'), |
262 | quiz_report_scale_sumgrades_as_percentage($standarderror, $quiz)); |
0c1c764e |
263 | print_table($quizattsstatistics); |
c0250840 |
264 | |
265 | } |
266 | if (!$table->is_downloading()){ |
267 | print_heading(get_string('quizstructureanalysis', 'quiz_statistics')); |
268 | } |
269 | $table->setup($quiz, $cm->id, $reporturl); |
270 | foreach ($questions as $question){ |
271 | $table->add_data_keyed($table->format_row($question)); |
08a7ead5 |
272 | } |
c0250840 |
273 | $table->finish_output(); |
0c1c764e |
274 | return true; |
275 | } |
276 | |
277 | } |
278 | |
279 | |
280 | ?> |