MDL-41752 question statistics class moved and improved
[moodle.git] / mod / quiz / report / statistics / report.php
CommitLineData
0c1c764e 1<?php
04853f27
TH
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
0c1c764e 17/**
04853f27 18 * Quiz statistics report class.
0c1c764e 19 *
8d76124c
TH
20 * @package quiz_statistics
21 * @copyright 2008 Jamie Pratt
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
04853f27
TH
23 */
24
a17b297d
TH
25defined('MOODLE_INTERNAL') || die();
26
04853f27
TH
27require_once($CFG->dirroot . '/mod/quiz/report/statistics/statistics_form.php');
28require_once($CFG->dirroot . '/mod/quiz/report/statistics/statistics_table.php');
29require_once($CFG->dirroot . '/mod/quiz/report/statistics/statistics_question_table.php');
e68e4ccf 30require_once($CFG->dirroot . '/mod/quiz/report/statistics/statisticslib.php');
04853f27
TH
31/**
32 * The quiz statistics report provides summary information about each question in
33 * a quiz, compared to the whole quiz. It also provides a drill-down to more
34 * detailed information about each question.
35 *
8d76124c
TH
36 * @copyright 2008 Jamie Pratt
37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
04853f27 38 */
c386eaa3 39class quiz_statistics_report extends quiz_default_report {
7de1e35b
JP
40
41 /**
42 * @var context_module
43 */
44 protected $context;
3b1d5cc4 45
515b3ae6 46 /** @var quiz_statistics_table instance of table class used for main questions stats table. */
04853f27 47 protected $table;
0c1c764e 48
49 /**
50 * Display the report.
51 */
04853f27
TH
52 public function display($quiz, $cm, $course) {
53 global $CFG, $DB, $OUTPUT, $PAGE;
0c1c764e 54
26aded55 55 $this->context = context_module::instance($cm->id);
0c1c764e 56
04853f27 57 // Work out the display options.
0c1c764e 58 $download = optional_param('download', '', PARAM_ALPHA);
869309b8 59 $everything = optional_param('everything', 0, PARAM_BOOL);
d1789d5d 60 $recalculate = optional_param('recalculate', 0, PARAM_BOOL);
04853f27 61 // A qid paramter indicates we should display the detailed analysis of a question.
43ec99aa 62 $qid = optional_param('qid', 0, PARAM_INT);
04853f27
TH
63 $slot = optional_param('slot', 0, PARAM_INT);
64
0c1c764e 65 $pageoptions = array();
66 $pageoptions['id'] = $cm->id;
0c1c764e 67 $pageoptions['mode'] = 'statistics';
68
a6855934 69 $reporturl = new moodle_url('/mod/quiz/report.php', $pageoptions);
0c1c764e 70
6b4e2d76 71 $mform = new quiz_statistics_settings_form($reporturl);
04853f27 72 if ($fromform = $mform->get_data()) {
0c1c764e 73 $useallattempts = $fromform->useallattempts;
04853f27 74 if ($fromform->useallattempts) {
25a03faa
TH
75 set_user_preference('quiz_report_statistics_useallattempts',
76 $fromform->useallattempts);
0c1c764e 77 } else {
78 unset_user_preference('quiz_report_statistics_useallattempts');
79 }
04853f27 80
0c1c764e 81 } else {
82 $useallattempts = get_user_preferences('quiz_report_statistics_useallattempts', 0);
83 }
84
768a7588 85 // Find out current groups mode.
490668bb 86 $currentgroup = $this->get_current_group($cm, $course, $this->context);
04853f27
TH
87 $nostudentsingroup = false; // True if a group is selected and there is no one in it.
88 if (empty($currentgroup)) {
89 $currentgroup = 0;
90 $groupstudents = array();
3b1d5cc4 91
e4977ba5
TH
92 } else if ($currentgroup == self::NO_GROUPS_ALLOWED) {
93 $groupstudents = array();
94 $nostudentsingroup = true;
95
04853f27 96 } else {
768a7588 97 // All users who can attempt quizzes and who are in the currently selected group.
fdb5bc03 98 $groupstudents = get_users_by_capability($this->context,
04853f27
TH
99 array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'),
100 '', '', '', '', $currentgroup, '', false);
101 if (!$groupstudents) {
e72efdd4 102 $nostudentsingroup = true;
0c1c764e 103 }
0c1c764e 104 }
3b1d5cc4 105
e68e4ccf
JP
106 $qubaids = quiz_statistics_qubaids_condition($quiz->id, $currentgroup, $groupstudents, $useallattempts);
107
04853f27 108 // If recalculate was requested, handle that.
db77f410 109 if ($recalculate && confirm_sesskey()) {
e68e4ccf 110 $this->clear_cached_data($qubaids);
e42f153c 111 redirect($reporturl);
43ec99aa 112 }
3b1d5cc4 113
04853f27 114 // Set up the main table.
59ea8176 115 $this->table = new quiz_statistics_table();
04853f27
TH
116 if ($everything) {
117 $report = get_string('completestatsfilename', 'quiz_statistics');
118 } else {
119 $report = get_string('questionstatsfilename', 'quiz_statistics');
120 }
0eafc988 121 $courseshortname = format_string($course->shortname, true,
26aded55 122 array('context' => context_course::instance($course->id)));
8ebbb06a 123 $filename = quiz_report_download_filename($report, $courseshortname, $quiz->name);
25a03faa
TH
124 $this->table->is_downloading($download, $filename,
125 get_string('quizstructureanalysis', 'quiz_statistics'));
3652dddd 126 $questions = $this->load_and_initialise_questions_for_calculations($quiz);
04853f27 127
7de1e35b
JP
128 if (!$nostudentsingroup) {
129 // Get the data to be displayed.
515b3ae6 130 list($quizstats, $questionstats, $subquestionstats) =
7de1e35b
JP
131 $this->get_quiz_and_questions_stats($quiz, $currentgroup, $useallattempts, $groupstudents, $questions);
132 } else {
133 // Or create empty stats containers.
134 $quizstats = new quiz_statistics_calculated($useallattempts);
515b3ae6
JP
135 $questionstats = array();
136 $subquestionstats = array();
7de1e35b
JP
137 }
138 $quizinfo = $quizstats->get_formatted_quiz_info_data($course, $cm, $quiz);
04853f27 139
04853f27 140 // Set up the table, if there is data.
7de1e35b
JP
141 if ($quizstats->s()) {
142 $this->table->statistics_setup($quiz, $cm->id, $reporturl, $quizstats->s());
43ec99aa 143 }
3b1d5cc4 144
04853f27
TH
145 // Print the page header stuff (if not downloading.
146 if (!$this->table->is_downloading()) {
147 $this->print_header_and_tabs($cm, $course, $quiz, 'statistics');
148
e4977ba5 149 if (groups_get_activity_groupmode($cm)) {
04853f27
TH
150 groups_print_activity_menu($cm, $reporturl->out());
151 if ($currentgroup && !$groupstudents) {
152 $OUTPUT->notification(get_string('nostudentsingroup', 'quiz_statistics'));
b0e4fa41
TH
153 }
154 }
155
3c6185e9 156 if (!quiz_questions_in_quiz($quiz->questions)) {
fdb5bc03 157 echo quiz_no_questions_message($quiz, $cm, $this->context);
7de1e35b 158 } else if (!$this->table->is_downloading() && $quizstats->s() == 0) {
3c6185e9
TH
159 echo $OUTPUT->notification(get_string('noattempts', 'quiz'));
160 }
161
04853f27
TH
162 // Print display options form.
163 $mform->set_data(array('useallattempts' => $useallattempts));
164 $mform->display();
165 }
166
167 if ($everything) { // Implies is downloading.
168 // Overall report, then the analysis of each question.
169 $this->download_quiz_info_table($quizinfo);
170
7de1e35b 171 if ($quizstats->s()) {
515b3ae6 172 $this->output_quiz_structure_analysis_table($quizstats->s(), $questionstats, $subquestionstats);
04853f27 173
7de1e35b 174 if ($this->table->is_downloading() == 'xhtml' && $quizstats->s() != 0) {
e68e4ccf 175 $this->output_statistics_graph($quiz->id, $currentgroup, $useallattempts);
869309b8 176 }
04853f27 177
515b3ae6 178 foreach ($questions as $slot => $question) {
25a03faa
TH
179 if (question_bank::get_qtype(
180 $question->qtype, false)->can_analyse_responses()) {
04853f27 181 $this->output_individual_question_response_analysis(
515b3ae6 182 $question, $questionstats[$slot]->s, $reporturl, $qubaids);
04853f27 183
515b3ae6
JP
184 } else if (!empty($questionstats[$slot]->subquestions)) {
185 $subitemstodisplay = explode(',', $questionstats[$slot]->subquestions);
04853f27
TH
186 foreach ($subitemstodisplay as $subitemid) {
187 $this->output_individual_question_response_analysis(
515b3ae6 188 $subquestionstats[$subitemid]->question, $subquestionstats[$subitemid]->s, $reporturl, $qubaids);
869309b8 189 }
190 }
869309b8 191 }
192 }
04853f27
TH
193
194 $this->table->export_class_instance()->finish_document();
195
196 } else if ($slot) {
197 // Report on an individual question indexed by position.
198 if (!isset($questions[$slot])) {
199 print_error('questiondoesnotexist', 'question');
869309b8 200 }
04853f27 201
515b3ae6
JP
202 $this->output_individual_question_data($quiz, $questionstats[$slot]);
203 $this->output_individual_question_response_analysis($questions[$slot], $questionstats[$slot]->s, $reporturl, $qubaids);
04853f27
TH
204
205 // Back to overview link.
206 echo $OUTPUT->box('<a href="' . $reporturl->out() . '">' .
207 get_string('backtoquizreport', 'quiz_statistics') . '</a>',
2f1ba2ae 208 'backtomainstats boxaligncenter generalbox boxwidthnormal mdl-align');
04853f27
TH
209
210 } else if ($qid) {
211 // Report on an individual sub-question indexed questionid.
212 if (!isset($subquestions[$qid])) {
43ec99aa 213 print_error('questiondoesnotexist', 'question');
71a2b878 214 }
04853f27 215
515b3ae6
JP
216 $this->output_individual_question_data($quiz, $subquestionstats[$qid]);
217 $this->output_individual_question_response_analysis($subquestionstats[$qid]->question,
218 $subquestionstats[$qid]->s, $reporturl, $qubaids);
04853f27
TH
219
220 // Back to overview link.
221 echo $OUTPUT->box('<a href="' . $reporturl->out() . '">' .
222 get_string('backtoquizreport', 'quiz_statistics') . '</a>',
223 'boxaligncenter generalbox boxwidthnormal mdl-align');
224
225 } else if ($this->table->is_downloading()) {
226 // Downloading overview report.
227 $this->download_quiz_info_table($quizinfo);
515b3ae6 228 $this->output_quiz_structure_analysis_table($quizstats->s(), $questionstats, $subquestionstats);
04853f27
TH
229 $this->table->finish_output();
230
231 } else {
232 // On-screen display of overview report.
233 echo $OUTPUT->heading(get_string('quizinformation', 'quiz_statistics'));
25a03faa
TH
234 echo $this->output_caching_info($quizstats, $quiz->id, $currentgroup,
235 $groupstudents, $useallattempts, $reporturl);
04853f27
TH
236 echo $this->everything_download_options();
237 echo $this->output_quiz_info_table($quizinfo);
7de1e35b 238 if ($quizstats->s()) {
04853f27 239 echo $OUTPUT->heading(get_string('quizstructureanalysis', 'quiz_statistics'));
515b3ae6 240 $this->output_quiz_structure_analysis_table($quizstats->s(), $questionstats, $subquestionstats);
e68e4ccf 241 $this->output_statistics_graph($quiz->id, $currentgroup, $useallattempts);
b0e4fa41 242 }
43ec99aa 243 }
04853f27 244
43ec99aa 245 return true;
246 }
3b1d5cc4 247
04853f27
TH
248 /**
249 * Display the statistical and introductory information about a question.
250 * Only called when not downloading.
251 * @param object $quiz the quiz settings.
515b3ae6 252 * @param \core_question\statistics\questions\calculated $questionstat the question to report on.
04853f27
TH
253 * @param moodle_url $reporturl the URL to resisplay this report.
254 * @param object $quizstats Holds the quiz statistics.
255 */
515b3ae6 256 protected function output_individual_question_data($quiz, $questionstat) {
04853f27
TH
257 global $OUTPUT;
258
259 // On-screen display. Show a summary of the question's place in the quiz,
260 // and the question statistics.
515b3ae6 261 $datumfromtable = $this->table->format_row($questionstat);
04853f27
TH
262
263 // Set up the question info table.
264 $questioninfotable = new html_table();
265 $questioninfotable->align = array('center', 'center');
266 $questioninfotable->width = '60%';
267 $questioninfotable->attributes['class'] = 'generaltable titlesleft';
268
269 $questioninfotable->data = array();
270 $questioninfotable->data[] = array(get_string('modulename', 'quiz'), $quiz->name);
271 $questioninfotable->data[] = array(get_string('questionname', 'quiz_statistics'),
515b3ae6 272 $questionstat->question->name.'&nbsp;'.$datumfromtable['actions']);
04853f27
TH
273 $questioninfotable->data[] = array(get_string('questiontype', 'quiz_statistics'),
274 $datumfromtable['icon'] . '&nbsp;' .
515b3ae6 275 question_bank::get_qtype($questionstat->question->qtype, false)->menu_name() . '&nbsp;' .
04853f27
TH
276 $datumfromtable['icon']);
277 $questioninfotable->data[] = array(get_string('positions', 'quiz_statistics'),
515b3ae6 278 $questionstat->positions);
04853f27
TH
279
280 // Set up the question statistics table.
281 $questionstatstable = new html_table();
282 $questionstatstable->align = array('center', 'center');
283 $questionstatstable->width = '60%';
284 $questionstatstable->attributes['class'] = 'generaltable titlesleft';
285
286 unset($datumfromtable['number']);
287 unset($datumfromtable['icon']);
288 $actions = $datumfromtable['actions'];
289 unset($datumfromtable['actions']);
290 unset($datumfromtable['name']);
25a03faa
TH
291 $labels = array(
292 's' => get_string('attempts', 'quiz_statistics'),
293 'facility' => get_string('facility', 'quiz_statistics'),
294 'sd' => get_string('standarddeviationq', 'quiz_statistics'),
295 'random_guess_score' => get_string('random_guess_score', 'quiz_statistics'),
296 'intended_weight' => get_string('intended_weight', 'quiz_statistics'),
297 'effective_weight' => get_string('effective_weight', 'quiz_statistics'),
298 'discrimination_index' => get_string('discrimination_index', 'quiz_statistics'),
299 'discriminative_efficiency' =>
300 get_string('discriminative_efficiency', 'quiz_statistics')
301 );
04853f27
TH
302 foreach ($datumfromtable as $item => $value) {
303 $questionstatstable->data[] = array($labels[$item], $value);
869309b8 304 }
04853f27
TH
305
306 // Display the various bits.
307 echo $OUTPUT->heading(get_string('questioninformation', 'quiz_statistics'));
308 echo html_writer::table($questioninfotable);
515b3ae6 309 echo $this->render_question_text($questionstat->question);
04853f27
TH
310 echo $OUTPUT->heading(get_string('questionstatistics', 'quiz_statistics'));
311 echo html_writer::table($questionstatstable);
869309b8 312 }
3b1d5cc4 313
edfa0d80
TH
314 /**
315 * @param object $question question data.
316 * @return string HTML of question text, ready for display.
317 */
fdb5bc03 318 protected function render_question_text($question) {
edfa0d80 319 global $OUTPUT;
fdb5bc03 320
68d2f6a0
TH
321 $text = question_rewrite_question_preview_urls($question->questiontext, $question->id,
322 $question->contextid, 'question', 'questiontext', $question->id,
323 $this->context->id, 'quiz_statistics');
fdb5bc03
TH
324
325 return $OUTPUT->box(format_text($text, $question->questiontextformat,
326 array('noclean' => true, 'para' => false, 'overflowdiv' => true)),
edfa0d80
TH
327 'questiontext boxaligncenter generalbox boxwidthnormal mdl-align');
328 }
329
04853f27
TH
330 /**
331 * Display the response analysis for a question.
e68e4ccf 332 * @param object $question the question to report on.
04853f27 333 * @param moodle_url $reporturl the URL to resisplay this report.
e68e4ccf 334 * @param qubaid_condition $qubaids
04853f27 335 */
515b3ae6 336 protected function output_individual_question_response_analysis($question, $s, $reporturl, $qubaids) {
04853f27 337 global $OUTPUT;
3b1d5cc4 338
04853f27
TH
339 if (!question_bank::get_qtype($question->qtype, false)->can_analyse_responses()) {
340 return;
341 }
3b1d5cc4 342
59ea8176 343 $qtable = new quiz_statistics_question_table($question->id);
04853f27
TH
344 $exportclass = $this->table->export_class_instance();
345 $qtable->export_class_instance($exportclass);
346 if (!$this->table->is_downloading()) {
347 // Output an appropriate title.
348 echo $OUTPUT->heading(get_string('analysisofresponses', 'quiz_statistics'));
3b1d5cc4 349
869309b8 350 } else {
04853f27
TH
351 // Work out an appropriate title.
352 $questiontabletitle = '"' . $question->name . '"';
353 if (!empty($question->number)) {
354 $questiontabletitle = '(' . $question->number . ') ' . $questiontabletitle;
355 }
356 if ($this->table->is_downloading() == 'xhtml') {
25a03faa
TH
357 $questiontabletitle = get_string('analysisofresponsesfor',
358 'quiz_statistics', $questiontabletitle);
869309b8 359 }
04853f27
TH
360
361 // Set up the table.
869309b8 362 $exportclass->start_table($questiontabletitle);
edfa0d80
TH
363
364 if ($this->table->is_downloading() == 'xhtml') {
365 echo $this->render_question_text($question);
366 }
869309b8 367 }
3b1d5cc4 368
515b3ae6 369 $responesstats = new \core_question\statistics\responses\analyser($question);
e68e4ccf 370 $responesstats->load_cached($qubaids);
04853f27 371
515b3ae6 372 $qtable->question_setup($reporturl, $question, $s, $responesstats);
04853f27
TH
373 if ($this->table->is_downloading()) {
374 $exportclass->output_headers($qtable->headers);
375 }
376
377 foreach ($responesstats->responseclasses as $partid => $partclasses) {
0ff4bd08 378 $rowdata = new stdClass();
04853f27
TH
379 $rowdata->part = $partid;
380 foreach ($partclasses as $responseclassid => $responseclass) {
381 $rowdata->responseclass = $responseclass->responseclass;
382
383 $responsesdata = $responesstats->responses[$partid][$responseclassid];
384 if (empty($responsesdata)) {
385 if (!array_key_exists('responseclass', $qtable->columns)) {
386 $rowdata->response = $responseclass->responseclass;
869309b8 387 } else {
04853f27 388 $rowdata->response = '';
869309b8 389 }
04853f27
TH
390 $rowdata->fraction = $responseclass->fraction;
391 $rowdata->count = 0;
392 $qtable->add_data_keyed($qtable->format_row($rowdata));
393 continue;
394 }
395
396 foreach ($responsesdata as $response => $data) {
397 $rowdata->response = $response;
398 $rowdata->fraction = $data->fraction;
399 $rowdata->count = $data->count;
400 $qtable->add_data_keyed($qtable->format_row($rowdata));
869309b8 401 }
869309b8 402 }
43ec99aa 403 }
04853f27
TH
404
405 $qtable->finish_output(!$this->table->is_downloading());
869309b8 406 }
3b1d5cc4 407
04853f27
TH
408 /**
409 * Output the table that lists all the questions in the quiz with their statistics.
f7970e3c 410 * @param int $s number of attempts.
515b3ae6
JP
411 * @param \core_question\statistics\questions\calculated[] $questionstats the stats for the main questions in the quiz.
412 * @param \core_question\statistics\questions\calculated_for_subquestion[] $subquestionstats the stats of any random questions.
04853f27 413 */
515b3ae6 414 protected function output_quiz_structure_analysis_table($s, $questionstats, $subquestionstats) {
04853f27
TH
415 if (!$s) {
416 return;
417 }
418
515b3ae6
JP
419 foreach ($questionstats as $questionstat) {
420 // Output the data for these question statistics.
421 $this->table->add_data_keyed($this->table->format_row($questionstat));
04853f27 422
515b3ae6 423 if (empty($questionstat->subquestions)) {
04853f27 424 continue;
71a2b878 425 }
43ec99aa 426
04853f27 427 // And its subquestions, if it has any.
515b3ae6 428 $subitemstodisplay = explode(',', $questionstat->subquestions);
04853f27 429 foreach ($subitemstodisplay as $subitemid) {
515b3ae6
JP
430 $subquestionstats[$subitemid]->maxmark = $questionstat->maxmark;
431 $this->table->add_data_keyed($this->table->format_row($subquestionstats[$subitemid]));
04853f27 432 }
0c1c764e 433 }
04853f27
TH
434
435 $this->table->finish_output(!$this->table->is_downloading());
43ec99aa 436 }
3b1d5cc4 437
04853f27
TH
438 /**
439 * Output the table of overall quiz statistics.
440 * @param array $quizinfo as returned by {@link get_formatted_quiz_info_data()}.
441 * @return string the HTML.
442 */
443 protected function output_quiz_info_table($quizinfo) {
444
445 $quizinfotable = new html_table();
446 $quizinfotable->align = array('center', 'center');
447 $quizinfotable->width = '60%';
448 $quizinfotable->attributes['class'] = 'generaltable titlesleft';
449 $quizinfotable->data = array();
450
451 foreach ($quizinfo as $heading => $value) {
452 $quizinfotable->data[] = array($heading, $value);
71a2b878 453 }
04853f27
TH
454
455 return html_writer::table($quizinfotable);
71a2b878 456 }
43ec99aa 457
04853f27
TH
458 /**
459 * Download the table of overall quiz statistics.
460 * @param array $quizinfo as returned by {@link get_formatted_quiz_info_data()}.
461 */
462 protected function download_quiz_info_table($quizinfo) {
463 global $OUTPUT;
3b1d5cc4 464
04853f27
TH
465 // XHTML download is a special case.
466 if ($this->table->is_downloading() == 'xhtml') {
467 echo $OUTPUT->heading(get_string('quizinformation', 'quiz_statistics'));
468 echo $this->output_quiz_info_table($quizinfo);
469 return;
470 }
3b1d5cc4 471
04853f27
TH
472 // Reformat the data ready for output.
473 $headers = array();
474 $row = array();
475 foreach ($quizinfo as $heading => $value) {
476 $headers[] = $heading;
477 $row[] = $value;
478 }
3b1d5cc4 479
04853f27
TH
480 // Do the output.
481 $exportclass = $this->table->export_class_instance();
482 $exportclass->start_table(get_string('quizinformation', 'quiz_statistics'));
483 $exportclass->output_headers($headers);
484 $exportclass->add_data($row);
485 $exportclass->finish_table();
486 }
487
488 /**
489 * Output the HTML needed to show the statistics graph.
e68e4ccf
JP
490 * @param $quizid
491 * @param $currentgroup
492 * @param $useallattempts
04853f27 493 */
e68e4ccf 494 protected function output_statistics_graph($quizid, $currentgroup, $useallattempts) {
2cdcb905 495 global $PAGE;
04853f27 496
2cdcb905 497 $output = $PAGE->get_renderer('mod_quiz');
04853f27 498 $imageurl = new moodle_url('/mod/quiz/report/statistics/statistics_graph.php',
e68e4ccf 499 compact('quizid', 'currentgroup', 'useallattempts'));
2cdcb905
TH
500 $graphname = get_string('statisticsreportgraph', 'quiz_statistics');
501 echo $output->graph($imageurl, $graphname);
04853f27
TH
502 }
503
04853f27
TH
504 /**
505 * Get the quiz and question statistics, either by loading the cached results,
506 * or by recomputing them.
507 *
508 * @param object $quiz the quiz settings.
f7970e3c 509 * @param int $currentgroup the current group. 0 for none.
f7970e3c 510 * @param bool $useallattempts use all attempts, or just first attempts.
04853f27
TH
511 * @param array $groupstudents students in this group.
512 * @param array $questions question definitions.
513 * @return array with 4 elements:
514 * - $quizstats The statistics for overall attempt scores.
515b3ae6
JP
515 * - $questionstats array of \core_question\statistics\questions\calculated objects keyed by slot.
516 * - $subquestionstats array of \core_question\statistics\questions\calculated_for_subquestion objects keyed by question id.
04853f27 517 */
7de1e35b 518 protected function get_quiz_and_questions_stats($quiz, $currentgroup, $useallattempts, $groupstudents, $questions) {
04853f27 519
e68e4ccf 520 $qubaids = quiz_statistics_qubaids_condition($quiz->id, $currentgroup, $groupstudents, $useallattempts);
04853f27 521
515b3ae6 522 $qcalc = new \core_question\statistics\questions\calculator($questions);
04853f27 523
7de1e35b
JP
524 $quizcalc = new quiz_statistics_calculator();
525
526 if ($quizcalc->get_last_calculated_time($qubaids) === false) {
e68e4ccf 527 // Recalculate now.
515b3ae6 528 list($questionstats, $subquestionstats) = $qcalc->calculate($qubaids);
04853f27 529
7de1e35b 530 $quizstats = $quizcalc->calculate($quiz->id, $currentgroup, $useallattempts,
515b3ae6 531 $groupstudents, count($questions), $qcalc->get_sum_of_mark_variance());
e68e4ccf 532
7de1e35b 533 if ($quizstats->s()) {
515b3ae6 534 $this->calculate_responses_for_all_questions_and_subquestions($qubaids, $questions, $subquestionstats);
e68e4ccf
JP
535 }
536 } else {
7de1e35b 537 $quizstats = $quizcalc->get_cached($qubaids);
515b3ae6 538 list($questionstats, $subquestionstats) = $qcalc->get_cached($qubaids);
04853f27
TH
539 }
540
515b3ae6 541 return array($quizstats, $questionstats, $subquestionstats);
04853f27
TH
542 }
543
515b3ae6 544 protected function calculate_responses_for_all_questions_and_subquestions($qubaids, $questions, $subquestionstats) {
04853f27
TH
545
546 $done = array();
547 foreach ($questions as $question) {
548 if (!question_bank::get_qtype($question->qtype, false)->can_analyse_responses()) {
549 continue;
43ec99aa 550 }
04853f27
TH
551 $done[$question->id] = 1;
552
515b3ae6 553 $responesstats = new \core_question\statistics\responses\analyser($question);
e68e4ccf 554 $responesstats->calculate($qubaids);
04853f27
TH
555 }
556
515b3ae6
JP
557 foreach ($subquestionstats as $subquestionstat) {
558 if (!question_bank::get_qtype($subquestionstat->question->qtype, false)->can_analyse_responses() ||
559 isset($done[$subquestionstat->question->id])) {
04853f27 560 continue;
43ec99aa 561 }
515b3ae6 562 $done[$subquestionstat->question->id] = 1;
04853f27 563
515b3ae6 564 $responesstats = new \core_question\statistics\responses\analyser($subquestionstat->question);
e68e4ccf 565 $responesstats->calculate($qubaids);
04853f27
TH
566 }
567 }
568
569 /**
570 * @return string HTML snipped for the Download full report as UI.
571 */
572 protected function everything_download_options() {
573 $downloadoptions = $this->table->get_download_menu();
574
0465ef6e
RT
575 $downloadelements = new stdClass();
576 $downloadelements->formatsmenu = html_writer::select($downloadoptions, 'download',
577 $this->table->defaultdownloadformat, false);
578 $downloadelements->downloadbutton = '<input type="submit" value="' .
579 get_string('download') . '"/>';
580
04853f27
TH
581 $output = '<form action="'. $this->table->baseurl .'" method="post">';
582 $output .= '<div class="mdl-align">';
583 $output .= '<input type="hidden" name="everything" value="1"/>';
0465ef6e 584 $output .= html_writer::tag('label', get_string('downloadeverything', 'quiz_statistics', $downloadelements));
04853f27
TH
585 $output .= '</div></form>';
586
587 return $output;
588 }
589
590 /**
591 * Generate the snipped of HTML that says when the stats were last caculated,
592 * with a recalcuate now button.
e68e4ccf
JP
593 * @param object $quizstats the overall quiz statistics.
594 * @param int $quizid the quiz id.
595 * @param int $currentgroup the id of the currently selected group, or 0.
596 * @param array $groupstudents ids of students in the group.
597 * @param bool $useallattempts whether to use all attempts, instead of just
598 * first attempts.
599 * @param moodle_url $reporturl url for this report
25a03faa
TH
600 * @return string a HTML snipped saying when the stats were last computed,
601 * or blank if that is not appropriate.
04853f27
TH
602 */
603 protected function output_caching_info($quizstats, $quizid, $currentgroup,
604 $groupstudents, $useallattempts, $reporturl) {
605 global $DB, $OUTPUT;
606
607 if (empty($quizstats->timemodified)) {
608 return '';
609 }
610
611 // Find the number of attempts since the cached statistics were computed.
612 list($fromqa, $whereqa, $qaparams) = quiz_statistics_attempts_sql(
613 $quizid, $currentgroup, $groupstudents, $useallattempts, true);
614 $count = $DB->count_records_sql("
615 SELECT COUNT(1)
616 FROM $fromqa
617 WHERE $whereqa
618 AND quiza.timefinish > {$quizstats->timemodified}", $qaparams);
619
620 if (!$count) {
621 $count = 0;
622 }
623
624 // Generate the output.
0ff4bd08 625 $a = new stdClass();
04853f27
TH
626 $a->lastcalculated = format_time(time() - $quizstats->timemodified);
627 $a->count = $count;
628
25a03faa
TH
629 $recalcualteurl = new moodle_url($reporturl,
630 array('recalculate' => 1, 'sesskey' => sesskey()));
04853f27 631 $output = '';
25a03faa
TH
632 $output .= $OUTPUT->box_start(
633 'boxaligncenter generalbox boxwidthnormal mdl-align', 'cachingnotice');
04853f27 634 $output .= get_string('lastcalculated', 'quiz_statistics', $a);
25a03faa
TH
635 $output .= $OUTPUT->single_button($recalcualteurl,
636 get_string('recalculatenow', 'quiz_statistics'));
04853f27
TH
637 $output .= $OUTPUT->box_end(true);
638
639 return $output;
640 }
641
642 /**
643 * Clear the cached data for a particular report configuration. This will
644 * trigger a re-computation the next time the report is displayed.
e68e4ccf 645 * @param $qubaids qubaid_condition
04853f27 646 */
e68e4ccf 647 protected function clear_cached_data($qubaids) {
04853f27 648 global $DB;
e68e4ccf
JP
649 $DB->delete_records('quiz_statistics', array('hashcode' => $qubaids->get_hash_code()));
650 $DB->delete_records('question_statistics', array('hashcode' => $qubaids->get_hash_code()));
651 $DB->delete_records('question_response_analysis', array('hashcode' => $qubaids->get_hash_code()));
04853f27
TH
652 }
653
3652dddd
JP
654 /**
655 * @param object $quiz the quiz.
656 * @return array of questions for this quiz.
657 */
658 public function load_and_initialise_questions_for_calculations($quiz) {
659 // Load the questions.
660 $questions = quiz_report_get_significant_questions($quiz);
661 $questionids = array();
662 foreach ($questions as $question) {
663 $questionids[] = $question->id;
664 }
665 $fullquestions = question_load_questions($questionids);
666 foreach ($questions as $qno => $question) {
667 $q = $fullquestions[$question->id];
668 $q->maxmark = $question->maxmark;
669 $q->slot = $qno;
670 $q->number = $question->number;
671 $questions[$qno] = $q;
672 }
673 return $questions;
674 }
0c1c764e 675}
04853f27 676