MDL-6751 improve the usability of the page a bit.
[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
71a2b878 9 **/
0c1c764e 10
71a2b878 11define('QUIZ_REPORT_TIME_TO_CACHE_STATS', MINSECS * 15);
0c1c764e 12require_once($CFG->dirroot.'/mod/quiz/report/statistics/statistics_form.php');
13require_once($CFG->dirroot.'/mod/quiz/report/statistics/statistics_table.php');
14
c386eaa3 15class quiz_statistics_report extends quiz_default_report {
43ec99aa 16
17 /**
18 * @var object instance of table class used for main questions stats table.
19 */
20 var $table;
0c1c764e 21
22 /**
23 * Display the report.
24 */
25 function display($quiz, $cm, $course) {
26 global $CFG, $DB;
27
28 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
29
30 $download = optional_param('download', '', PARAM_ALPHA);
d1789d5d 31 $recalculate = optional_param('recalculate', 0, PARAM_BOOL);
43ec99aa 32 //pass the question id for detailed analysis question
33 $qid = optional_param('qid', 0, PARAM_INT);
0c1c764e 34 $pageoptions = array();
35 $pageoptions['id'] = $cm->id;
36 $pageoptions['q'] = $quiz->id;
37 $pageoptions['mode'] = 'statistics';
71a2b878 38
39 $questions = quiz_report_load_questions($quiz);
40 // Load the question type specific information
41 if (!get_question_options($questions)) {
42 print_error('cannotloadquestion', 'question');
43 }
0c1c764e 44
71a2b878 45
0c1c764e 46 $reporturl = new moodle_url($CFG->wwwroot.'/mod/quiz/report.php', $pageoptions);
47
48 $mform = new mod_quiz_report_statistics($reporturl);
49 if ($fromform = $mform->get_data()){
50 $useallattempts = $fromform->useallattempts;
51 if ($fromform->useallattempts){
52 set_user_preference('quiz_report_statistics_useallattempts', $fromform->useallattempts);
53 } else {
54 unset_user_preference('quiz_report_statistics_useallattempts');
55 }
56 } else {
57 $useallattempts = get_user_preferences('quiz_report_statistics_useallattempts', 0);
58 }
59
60 /// find out current groups mode
61 $currentgroup = groups_get_activity_group($cm, true);
43ec99aa 62
0c1c764e 63
e72efdd4 64 $nostudentsingroup = false;//true if a group is selected and their is noeone in it.
0c1c764e 65 if (!empty($currentgroup)) {
66 // all users who can attempt quizzes and who are in the currently selected group
e72efdd4 67 $groupstudents = get_users_by_capability($context, 'mod/quiz:attempt','','','','',$currentgroup,'',false);
68 if (!$groupstudents){
69 $nostudentsingroup = true;
0c1c764e 70 }
71a2b878 71 } else {
72 $groupstudents = array();
0c1c764e 73 }
6f51ed72 74
43ec99aa 75 if ($recalculate){
76 if ($todelete = $DB->get_records_menu('quiz_statistics', array('quizid' => $quiz->id, 'groupid'=> (int)$currentgroup, 'allattempts'=>$useallattempts))){
77 list($todeletesql, $todeleteparams) = $DB->get_in_or_equal(array_keys($todelete));
78 if (!$DB->delete_records_select('quiz_statistics', "id $todeletesql", $todeleteparams)){
79 print_error('errordeletingquizstats', 'quiz_statistics');
80 }
81 if (!$DB->delete_records_select('quiz_question_statistics', "quizstatisticsid $todeletesql", $todeleteparams)){
82 print_error('errordeletingqstats', 'quiz_statistics');
83 }
84 }
85 redirect($reporturl->out());
86 }
87
88
89 $this->table = new quiz_report_statistics_table();
90 $filename = "$course->shortname-".format_string($quiz->name,true);
91 $this->table->is_downloading($download, $filename, get_string('quizstructureanalysis', 'quiz_statistics'));
92 if (!$this->table->is_downloading()) {
0c1c764e 93 // Only print headers if not asked to download data
94 $this->print_header_and_tabs($cm, $course, $quiz, "statistics");
95 }
96
97 if ($groupmode = groups_get_activity_groupmode($cm)) { // Groups are being used
43ec99aa 98 if (!$this->table->is_downloading()) {
0c1c764e 99 groups_print_activity_menu($cm, $reporturl->out());
43ec99aa 100 echo '<br />';
e72efdd4 101 if ($currentgroup && !$groupstudents){
102 notify(get_string('nostudentsingroup', 'quiz_statistics'));
103 }
0c1c764e 104 }
105 }
106
43ec99aa 107 if (!$this->table->is_downloading()) {
71a2b878 108 // Print display options
109 $mform->set_data(array('useallattempts' => $useallattempts));
110 $mform->display();
111 }
71a2b878 112
43ec99aa 113 list($quizstats, $questions, $subquestions, $s, $usingattemptsstring)
114 = $this->quiz_questions_stats($quiz, $currentgroup, $nostudentsingroup,
115 $useallattempts, $groupstudents, $questions);
116
117 if (!$this->table->is_downloading()){
118 if ($s==0){
119 print_heading(get_string('noattempts','quiz'));
71a2b878 120 }
43ec99aa 121 }
122 if ($s){
123 $this->table->setup($quiz, $cm->id, $reporturl, $s);
124 }
125
5153422c 126 if (!$qid){//main page
43ec99aa 127 $this->output_quiz_stats_table($course, $cm, $quiz, $quizstats, $usingattemptsstring, $currentgroup, $groupstudents, $useallattempts, $download, $reporturl);
128 $this->output_question_stats_table($s, $questions, $subquestions);
5153422c 129 $imageurl = $CFG->wwwroot.'/mod/quiz/report/statistics/statistics_graph.php?id='.$quizstats->id;
130 print_heading(get_string('statisticsreportgraph', 'quiz_statistics'));
131 echo '<div class="mdl-align"><img src="'.$imageurl.'" alt="'.get_string('statisticsreportgraph', 'quiz_statistics').'" /></div>';
132 } else {//individual question page
43ec99aa 133 $thisquestion = false;
134 if (isset($questions[$qid])){
135 $thisquestion = $questions[$qid];
136 } else if (isset($subquestions[$qid])){
137 $thisquestion = $subquestions[$qid];
71a2b878 138 } else {
43ec99aa 139 print_error('questiondoesnotexist', 'question');
71a2b878 140 }
43ec99aa 141 $this->output_question_info_table($quiz, $thisquestion);
142 }
143 return true;
144 }
145
146 function output_question_info_table($quiz, $question){
147 $datumfromtable = $this->table->format_row($question);
148
149 $questioninfotable = new object();
150 $questioninfotable->align = array('center', 'center');
151 $questioninfotable->width = '60%';
152 $questioninfotable->class = 'generaltable titlesleft';
153
154 $questioninfotable->data = array();
155 $questioninfotable->data[] = array(get_string('modulename', 'quiz'), $quiz->name);
156 $questioninfotable->data[] = array(get_string('questionname', 'quiz_statistics'), $question->name.'&nbsp;'.$datumfromtable['actions']);
157 $questioninfotable->data[] = array(get_string('questiontype', 'quiz_statistics'), $question->qtype.'&nbsp;'.$datumfromtable['icon']);
158 $questioninfotable->data[] = array(get_string('positions', 'quiz_statistics'), $question->_stats->positions);
159
160 $questionstatstable = new object();
161 $questionstatstable->align = array('center', 'center');
162 $questionstatstable->width = '60%';
163 $questionstatstable->class = 'generaltable titlesleft';
164
165 unset($datumfromtable['number']);
166 unset($datumfromtable['icon']);
167 $actions = $datumfromtable['actions'];
168 unset($datumfromtable['actions']);
169 unset($datumfromtable['name']);
170 $labels = array('s' => get_string('attempts', 'quiz_statistics'),
171 'facility' => get_string('facility', 'quiz_statistics'),
172 'sd' => get_string('standarddeviationq', 'quiz_statistics'),
173 'random_guess_score' => get_string('random_guess_score', 'quiz_statistics'),
174 'intended_weight'=> get_string('intended_weight', 'quiz_statistics'),
175 'effective_weight'=> get_string('effective_weight', 'quiz_statistics'),
176 'discrimination_index'=> get_string('discrimination_index', 'quiz_statistics'),
177 'discriminative_efficiency'=> get_string('discriminative_efficiency', 'quiz_statistics'));
178 foreach ($datumfromtable as $item => $value){
179 $questionstatstable->data[] = array($labels[$item], $value);
180 }
181
182 print_heading(get_string('questioninformation', 'quiz_statistics'));
183 print_table($questioninfotable);
184
185 print_box(format_text($question->questiontext).$actions, 'boxaligncenter generalbox boxwidthnormal mdl-align');
186
187 print_heading(get_string('questionstatistics', 'quiz_statistics'));
188 print_table($questionstatstable);
189
190 print_heading(get_string('analysisofresponses', 'quiz_statistics'));
191
192 }
193
194 function output_question_stats_table($s, $questions, $subquestions){
195 if (!$this->table->is_downloading()){
196 print_heading(get_string('quizstructureanalysis', 'quiz_statistics'));
197 }
198 if ($s){
199 foreach ($questions as $question){
200 $this->table->add_data_keyed($this->table->format_row($question));
201 if (!empty($question->_stats->subquestions)){
202 $subitemstodisplay = explode(',', $question->_stats->subquestions);
203 foreach ($subitemstodisplay as $subitemid){
204 $subquestions[$subitemid]->maxgrade = $question->maxgrade;
205 $this->table->add_data_keyed($this->table->format_row($subquestions[$subitemid]));
206 }
71a2b878 207 }
71a2b878 208 }
43ec99aa 209
210 $this->table->finish_output();
0c1c764e 211 }
43ec99aa 212 }
213
214 function output_quiz_stats_table($course, $cm, $quiz, $quizstats, $usingattemptsstring, $currentgroup, $groupstudents, $useallattempts, $download, $reporturl){
215 global $DB;
216 // Print information on the number of existing attempts
217 $quizinformationtablehtml = print_heading(get_string('quizinformation', 'quiz_statistics'), '', 2, 'main', true);
218 $quizinformationtable = new object();
219 $quizinformationtable->align = array('center', 'center');
220 $quizinformationtable->width = '60%';
221 $quizinformationtable->class = 'generaltable titlesleft';
222 $quizinformationtable->data = array();
223 $quizinformationtable->data[] = array(get_string('quizname', 'quiz_statistics'), $quiz->name);
224 $quizinformationtable->data[] = array(get_string('coursename', 'quiz_statistics'), $course->fullname);
225 if ($cm->idnumber){
226 $quizinformationtable->data[] = array(get_string('coursename', 'quiz_statistics'), $cm->idnumber);
227 }
228 if ($quiz->timeopen){
229 $quizinformationtable->data[] = array(get_string('quizopen', 'quiz'), userdate($quiz->timeopen));
230 }
231 if ($quiz->timeclose){
232 $quizinformationtable->data[] = array(get_string('quizclose', 'quiz'), userdate($quiz->timeclose));
233 }
234 if ($quiz->timeopen && $quiz->timeclose){
235 $quizinformationtable->data[] = array(get_string('duration', 'quiz_statistics'), format_time($quiz->timeclose - $quiz->timeopen));
236 }
237 $format = array('firstattemptscount' => '',
238 'allattemptscount' => '',
239 'firstattemptsavg' => 'sumgrades_as_percentage',
240 'allattemptsavg' => 'sumgrades_as_percentage',
241 'median' => 'sumgrades_as_percentage',
242 'standarddeviation' => 'sumgrades_as_percentage',
243 'skewness' => '',
244 'kurtosis' => '',
245 'cic' => 'number_format',
246 'errorratio' => 'number_format',
247 'standarderror' => 'sumgrades_as_percentage');
248 foreach ($quizstats as $property => $value){
249 if (!isset($format[$property])){
250 continue;
71a2b878 251 }
43ec99aa 252 if (!is_null($value)){
71a2b878 253 switch ($format[$property]){
254 case 'sumgrades_as_percentage' :
255 $formattedvalue = quiz_report_scale_sumgrades_as_percentage($value, $quiz);
256 break;
257 case 'number_format' :
f88fb62c 258 $formattedvalue = quiz_format_grade($quiz, $value).'%';
71a2b878 259 break;
260 default :
261 $formattedvalue = $value;
262 }
263 $quizinformationtable->data[] = array(get_string($property, 'quiz_statistics', $usingattemptsstring), $formattedvalue);
264 }
43ec99aa 265 }
266 if (!$this->table->is_downloading()){
d1789d5d 267 if (isset($quizstats->timemodified)){
268 list($fromqa, $whereqa, $qaparams) = quiz_report_attempts_sql($quiz->id, $currentgroup, $groupstudents, $useallattempts);
269 $sql = 'SELECT COUNT(1) ' .
270 'FROM ' .$fromqa.' '.
271 'WHERE ' .$whereqa.' AND qa.timefinish > :time';
272 $a = new object();
273 $a->lastcalculated = format_time(time() - $quizstats->timemodified);
274 if (!$a->count = $DB->count_records_sql($sql, array('time'=>$quizstats->timemodified)+$qaparams)){
275 $a->count = 0;
43ec99aa 276 }
277 $quizinformationtablehtml .= print_box_start('boxaligncenter generalbox boxwidthnormal mdl-align', '', true);
278 $quizinformationtablehtml .= get_string('lastcalculated', 'quiz_statistics', $a);
279 $quizinformationtablehtml .= print_single_button($reporturl->out(true), $reporturl->params()+array('recalculate'=>1),
280 get_string('recalculatenow', 'quiz_statistics'), 'post', '', true);
281 $quizinformationtablehtml .= print_box_end(true);
d1789d5d 282 }
e72efdd4 283 }
43ec99aa 284 $quizinformationtablehtml .= print_table($quizinformationtable, true);
285 if (!$this->table->is_downloading()){
286 echo $quizinformationtablehtml;
287 } else {
288 $exportclass =& $this->table->export_class_instance();
289 if ($download == 'xhtml'){
290 echo $quizinformationtablehtml;
291 } else {
292 $exportclass->start_table(get_string('quizinformation', 'quiz_statistics'));
293 $headers = array();
294 $row = array();
295 foreach ($quizinformationtable->data as $data){
296 $headers[]= $data[0];
297 $row[] = $data[1];
71a2b878 298 }
43ec99aa 299 $exportclass->output_headers($headers);
300 $exportclass->add_data($row);
301 $exportclass->finish_table();
71a2b878 302 }
71a2b878 303 }
71a2b878 304 }
43ec99aa 305
71a2b878 306 function quiz_stats($nostudentsingroup, $quizid, $currentgroup, $groupstudents, $questions, $useallattempts){
307 global $CFG, $DB;
e72efdd4 308 if (!$nostudentsingroup){
309 //Calculating_MEAN_of_grades_for_all_attempts_by_students
310 //http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Calculating_MEAN_of_grades_for_all_attempts_by_students
71a2b878 311
312 list($fromqa, $whereqa, $qaparams) = quiz_report_attempts_sql($quizid, $currentgroup, $groupstudents);
313
314 $sql = 'SELECT (CASE WHEN attempt=1 THEN 1 ELSE 0 END) AS isfirst, COUNT(1) AS countrecs, SUM(sumgrades) AS total ' .
315 'FROM '.$fromqa.
316 'WHERE ' .$whereqa.
317 'GROUP BY (attempt=1)';
e72efdd4 318 if (!$attempttotals = $DB->get_records_sql($sql, $qaparams)){
e72efdd4 319 $s = 0;
320 } else {
321 $firstattempt = $attempttotals[1];
322 $allattempts = new object();
323 $allattempts->countrecs = $firstattempt->countrecs +
324 (isset($attempttotals[0])?$attempttotals[0]->countrecs:0);
325 $allattempts->total = $firstattempt->total +
326 (isset($attempttotals[0])?$attempttotals[0]->total:0);
327 if ($useallattempts){
328 $usingattempts = $allattempts;
329 $usingattempts->attempts = get_string('allattempts', 'quiz_statistics');
330 $usingattempts->sql = '';
331 } else {
332 $usingattempts = $firstattempt;
333 $usingattempts->attempts = get_string('firstattempts', 'quiz_statistics');
334 $usingattempts->sql = 'AND qa.attempt=1 ';
335 }
71a2b878 336 $usingattemptsstring = $usingattempts->attempts;
e72efdd4 337 $s = $usingattempts->countrecs;
ea751786 338 $sumgradesavg = $usingattempts->total / $usingattempts->countrecs;
e72efdd4 339 }
0c1c764e 340 } else {
e72efdd4 341 $s = 0;
0c1c764e 342 }
71a2b878 343 $quizstats = new object();
344 if ($s == 0){
345 $quizstats->firstattemptscount = 0;
346 $quizstats->allattemptscount = 0;
347 } else {
348 $quizstats->firstattemptscount = $firstattempt->countrecs;
349 $quizstats->allattemptscount = $allattempts->countrecs;
350 $quizstats->firstattemptsavg = $firstattempt->total / $firstattempt->countrecs;
351 $quizstats->allattemptsavg = $allattempts->total / $allattempts->countrecs;
0c1c764e 352 }
71a2b878 353 //recalculate sql again this time possibly including test for first attempt.
354 list($fromqa, $whereqa, $qaparams) = quiz_report_attempts_sql($quizid, $currentgroup, $groupstudents, $useallattempts);
355
0c1c764e 356 //get the median
71a2b878 357 if ($s) {
e72efdd4 358
e72efdd4 359 if (($s%2)==0){
0c1c764e 360 //even number of attempts
e72efdd4 361 $limitoffset = ($s/2) - 1;
0c1c764e 362 $limit = 2;
363 } else {
06d13248 364 $limitoffset = (floor($s/2));
0c1c764e 365 $limit = 1;
366 }
367 $sql = 'SELECT id, sumgrades ' .
08a7ead5 368 'FROM ' .$fromqa.
369 'WHERE ' .$whereqa.
0c1c764e 370 'ORDER BY sumgrades';
e72efdd4 371 if (!$mediangrades = $DB->get_records_sql_menu($sql, $qaparams, $limitoffset, $limit)){
0c1c764e 372 print_error('errormedian', 'quiz_statistics');
373 }
374 if (count($mediangrades)==1){
71a2b878 375 $quizstats->median = array_shift($mediangrades);
0c1c764e 376 } else {
377 $median = array_shift($mediangrades);
378 $median += array_shift($mediangrades);
71a2b878 379 $quizstats->median = $median /2;
0c1c764e 380 }
6f51ed72 381 if ($s>1){
6f51ed72 382 //fetch sum of squared, cubed and power 4d
383 //differences between grades and mean grade
e72efdd4 384 $mean = $usingattempts->total / $s;
6f51ed72 385 $sql = "SELECT " .
e72efdd4 386 "SUM(POWER((qa.sumgrades - :mean1),2)) AS power2, " .
387 "SUM(POWER((qa.sumgrades - :mean2),3)) AS power3, ".
388 "SUM(POWER((qa.sumgrades - :mean3),4)) AS power4 ".
6f51ed72 389 'FROM ' .$fromqa.
71a2b878 390 'WHERE ' .$whereqa;
e72efdd4 391 $params = array('mean1' => $mean, 'mean2' => $mean, 'mean3' => $mean)+$qaparams;
6f51ed72 392 if (!$powers = $DB->get_record_sql($sql, $params)){
393 print_error('errorpowers', 'quiz_statistics');
394 }
395
396 //Standard_Deviation
397 //see http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Standard_Deviation
398
71a2b878 399 $quizstats->standarddeviation = sqrt($powers->power2 / ($s -1));
400
6f51ed72 401
402
403 //Skewness_and_Kurtosis
404 if ($s>2){
405 //see http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Skewness_and_Kurtosis
406 $m2= $powers->power2 / $s;
407 $m3= $powers->power3 / $s;
408 $m4= $powers->power4 / $s;
409
410 $k2= $s*$m2/($s-1);
411 $k3= $s*$s*$m3/(($s-1)*($s-2));
43ec99aa 412 if ($k2){
413 $quizstats->skewness = $k3 / (pow($k2, 2/3));
414 }
6f51ed72 415 }
416
417
418 if ($s>3){
419 $k4= (($s*$s*$s)/(($s-1)*($s-2)*($s-3)))*((($s+1)*$m4)-(3*($s-1)*$m2*$m2));
43ec99aa 420 if ($k2){
421 $quizstats->kurtosis = $k4 / ($k2*$k2);
422 }
6f51ed72 423 }
e72efdd4 424 }
425 }
426 if ($s){
4f5ffac0 427 require_once("$CFG->dirroot/mod/quiz/report/statistics/qstats.php");
428 $qstats = new qstats($questions, $s, $sumgradesavg);
71a2b878 429 $qstats->get_records($quizid, $currentgroup, $groupstudents, $useallattempts);
4f5ffac0 430 $qstats->process_states();
71a2b878 431 } else {
432 $qstats = false;
e72efdd4 433 }
71a2b878 434 if ($s>1){
435 $p = count($qstats->questions);//no of positions
436 if ($p > 1){
43ec99aa 437 if ($k2){
438 $quizstats->cic = (100 * $p / ($p -1)) * (1 - ($qstats->sum_of_grade_variance())/$k2);
439 $quizstats->errorratio = 100 * sqrt(1-($quizstats->cic/100));
440 $quizstats->standarderror = ($quizstats->errorratio * $quizstats->standarddeviation / 100);
441 }
4f5ffac0 442 }
08a7ead5 443 }
71a2b878 444 return array($s, $usingattemptsstring, $quizstats, $qstats);
0c1c764e 445 }
43ec99aa 446
447 function quiz_questions_stats($quiz, $currentgroup, $nostudentsingroup, $useallattempts, $groupstudents, $questions){
448 global $DB;
449 $timemodified = time() - QUIZ_REPORT_TIME_TO_CACHE_STATS;
450 $params = array('quizid'=>$quiz->id, 'groupid'=>(int)$currentgroup, 'allattempts'=>$useallattempts, 'timemodified'=>$timemodified);
451 if (!$quizstats = $DB->get_record_select('quiz_statistics', 'quizid = :quizid AND groupid = :groupid AND allattempts = :allattempts AND timemodified > :timemodified', $params, '*', true)){
452 list($s, $usingattemptsstring, $quizstats, $qstats) = $this->quiz_stats($nostudentsingroup, $quiz->id, $currentgroup, $groupstudents, $questions, $useallattempts);
453 $toinsert = (object)((array)$quizstats + $params);
454 $toinsert->timemodified = time();
5153422c 455 $quizstats->id = $DB->insert_record('quiz_statistics', $toinsert);
43ec99aa 456 foreach ($qstats->questions as $question){
5153422c 457 $question->_stats->quizstatisticsid = $quizstats->id;
43ec99aa 458 $DB->insert_record('quiz_question_statistics', $question->_stats, false, true);
459 }
460 foreach ($qstats->subquestions as $subquestion){
5153422c 461 $subquestion->_stats->quizstatisticsid = $quizstats->id;
43ec99aa 462 $DB->insert_record('quiz_question_statistics', $subquestion->_stats, false, true);
463 }
464 if (isset($qstats)){
465 $questions = $qstats->questions;
466 $subquestions = $qstats->subquestions;
467 } else {
468 $questions = array();
469 $subquestions = array();
470 }
471 } else {
472 //use cached results
473 if ($useallattempts){
474 $usingattemptsstring = get_string('allattempts', 'quiz_statistics');
475 $s = $quizstats->allattemptscount;
476 } else {
477 $usingattemptsstring = get_string('firstattempts', 'quiz_statistics');
478 $s = $quizstats->firstattemptscount;
479 }
480 $questionstats = $DB->get_records('quiz_question_statistics', array('quizstatisticsid'=>$quizstats->id), 'subquestion ASC');
481 $questionstats = quiz_report_index_by_keys($questionstats, array('subquestion', 'questionid'));
482 if (1 < count($questionstats)){
483 list($mainquestionstats, $subquestionstats) = $questionstats;
484 $subqstofetch = array_keys($subquestionstats);
485 $subquestions = question_load_questions($subqstofetch);
486 foreach (array_keys($subquestions) as $subqid){
487 $subquestions[$subqid]->_stats = $subquestionstats[$subqid];
488 }
489 } else {
490 $mainquestionstats = $questionstats[0];
491 $subquestions = array();
492 }
493 foreach (array_keys($questions) as $qid){
494 $questions[$qid]->_stats = $mainquestionstats[$qid];
495 }
496 }
497 return array($quizstats, $questions, $subquestions, $s, $usingattemptsstring);
498 }
0c1c764e 499}
71a2b878 500function quiz_report_attempts_sql($quizid, $currentgroup, $groupstudents, $allattempts = true){
06d13248 501 global $DB;
71a2b878 502 $fromqa = '{quiz_attempts} qa ';
d1789d5d 503 $whereqa = 'qa.quiz = :quizid AND qa.preview=0 AND qa.timefinish !=0 ';
71a2b878 504 $qaparams = array('quizid'=>$quizid);
505 if (!empty($currentgroup) && $groupstudents) {
506 list($grpsql, $grpparams) = $DB->get_in_or_equal(array_keys($groupstudents), SQL_PARAMS_NAMED, 'u0000');
507 $whereqa .= 'AND qa.userid '.$grpsql.' ';
508 $qaparams += $grpparams;
509 }
510 if (!$allattempts){
511 $whereqa .= 'AND qa.attempt=1 ';
512 }
513 return array($fromqa, $whereqa, $qaparams);
514}
43ec99aa 515function quiz_report_safe_divider($dividend, $divisor){
516 if ($divisor == 0){
517 return null;
518 }
519 return $dividend / $divisor;
520}
0c1c764e 521?>