MDL-41751 changes to api of question_response_analyser
[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);
d50b05e6 61 // A qid paramter indicates we should display the detailed analysis of a sub 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 137 }
04853f27 138
04853f27 139 // Set up the table, if there is data.
7de1e35b
JP
140 if ($quizstats->s()) {
141 $this->table->statistics_setup($quiz, $cm->id, $reporturl, $quizstats->s());
43ec99aa 142 }
3b1d5cc4 143
04853f27
TH
144 // Print the page header stuff (if not downloading.
145 if (!$this->table->is_downloading()) {
146 $this->print_header_and_tabs($cm, $course, $quiz, 'statistics');
147
e4977ba5 148 if (groups_get_activity_groupmode($cm)) {
04853f27
TH
149 groups_print_activity_menu($cm, $reporturl->out());
150 if ($currentgroup && !$groupstudents) {
151 $OUTPUT->notification(get_string('nostudentsingroup', 'quiz_statistics'));
b0e4fa41
TH
152 }
153 }
154
3c6185e9 155 if (!quiz_questions_in_quiz($quiz->questions)) {
fdb5bc03 156 echo quiz_no_questions_message($quiz, $cm, $this->context);
7de1e35b 157 } else if (!$this->table->is_downloading() && $quizstats->s() == 0) {
3c6185e9
TH
158 echo $OUTPUT->notification(get_string('noattempts', 'quiz'));
159 }
160
04853f27
TH
161 // Print display options form.
162 $mform->set_data(array('useallattempts' => $useallattempts));
163 $mform->display();
164 }
165
166 if ($everything) { // Implies is downloading.
167 // Overall report, then the analysis of each question.
d50b05e6 168 $quizinfo = $quizstats->get_formatted_quiz_info_data($course, $cm, $quiz);
04853f27
TH
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.
d50b05e6 212 if (!isset($subquestionstats[$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.
d50b05e6 227 $quizinfo = $quizstats->get_formatted_quiz_info_data($course, $cm, $quiz);
04853f27 228 $this->download_quiz_info_table($quizinfo);
515b3ae6 229 $this->output_quiz_structure_analysis_table($quizstats->s(), $questionstats, $subquestionstats);
04853f27
TH
230 $this->table->finish_output();
231
232 } else {
233 // On-screen display of overview report.
234 echo $OUTPUT->heading(get_string('quizinformation', 'quiz_statistics'));
25a03faa
TH
235 echo $this->output_caching_info($quizstats, $quiz->id, $currentgroup,
236 $groupstudents, $useallattempts, $reporturl);
04853f27 237 echo $this->everything_download_options();
d50b05e6 238 $quizinfo = $quizstats->get_formatted_quiz_info_data($course, $cm, $quiz);
04853f27 239 echo $this->output_quiz_info_table($quizinfo);
7de1e35b 240 if ($quizstats->s()) {
04853f27 241 echo $OUTPUT->heading(get_string('quizstructureanalysis', 'quiz_statistics'));
515b3ae6 242 $this->output_quiz_structure_analysis_table($quizstats->s(), $questionstats, $subquestionstats);
e68e4ccf 243 $this->output_statistics_graph($quiz->id, $currentgroup, $useallattempts);
b0e4fa41 244 }
43ec99aa 245 }
04853f27 246
43ec99aa 247 return true;
248 }
3b1d5cc4 249
04853f27
TH
250 /**
251 * Display the statistical and introductory information about a question.
252 * Only called when not downloading.
d50b05e6 253 * @param object $quiz the quiz settings.
515b3ae6 254 * @param \core_question\statistics\questions\calculated $questionstat the question to report on.
04853f27 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.
d50b05e6
JP
332 * @param object $question the question to report on.
333 * @param int $s
334 * @param moodle_url $reporturl the URL to redisplay this report.
e68e4ccf 335 * @param qubaid_condition $qubaids
04853f27 336 */
515b3ae6 337 protected function output_individual_question_response_analysis($question, $s, $reporturl, $qubaids) {
04853f27 338 global $OUTPUT;
3b1d5cc4 339
04853f27
TH
340 if (!question_bank::get_qtype($question->qtype, false)->can_analyse_responses()) {
341 return;
342 }
3b1d5cc4 343
59ea8176 344 $qtable = new quiz_statistics_question_table($question->id);
04853f27
TH
345 $exportclass = $this->table->export_class_instance();
346 $qtable->export_class_instance($exportclass);
347 if (!$this->table->is_downloading()) {
348 // Output an appropriate title.
349 echo $OUTPUT->heading(get_string('analysisofresponses', 'quiz_statistics'));
3b1d5cc4 350
869309b8 351 } else {
04853f27
TH
352 // Work out an appropriate title.
353 $questiontabletitle = '"' . $question->name . '"';
354 if (!empty($question->number)) {
355 $questiontabletitle = '(' . $question->number . ') ' . $questiontabletitle;
356 }
357 if ($this->table->is_downloading() == 'xhtml') {
d50b05e6 358 $questiontabletitle = get_string('analysisofresponsesfor', '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
d50b05e6
JP
369 $responesanalyser = new \core_question\statistics\responses\analyser($question);
370 $responseanalysis = $responesanalyser->load_cached($qubaids);
04853f27 371
d50b05e6 372 $qtable->question_setup($reporturl, $question, $s, $responseanalysis);
04853f27
TH
373 if ($this->table->is_downloading()) {
374 $exportclass->output_headers($qtable->headers);
375 }
d50b05e6
JP
376 foreach ($responseanalysis->get_subpart_ids() as $partid) {
377 $subpart = $responseanalysis->get_subpart($partid);
378 foreach ($subpart->get_response_class_ids() as $responseclassid) {
379 $responseclass = $subpart->get_response_class($responseclassid);
380 $tabledata = $responseclass->data_for_question_response_table($subpart->has_multiple_response_classes(), $partid);
381 foreach ($tabledata as $row) {
382 $qtable->add_data_keyed($qtable->format_row($row));
869309b8 383 }
869309b8 384 }
43ec99aa 385 }
04853f27
TH
386
387 $qtable->finish_output(!$this->table->is_downloading());
869309b8 388 }
3b1d5cc4 389
04853f27
TH
390 /**
391 * Output the table that lists all the questions in the quiz with their statistics.
f7970e3c 392 * @param int $s number of attempts.
515b3ae6
JP
393 * @param \core_question\statistics\questions\calculated[] $questionstats the stats for the main questions in the quiz.
394 * @param \core_question\statistics\questions\calculated_for_subquestion[] $subquestionstats the stats of any random questions.
04853f27 395 */
515b3ae6 396 protected function output_quiz_structure_analysis_table($s, $questionstats, $subquestionstats) {
04853f27
TH
397 if (!$s) {
398 return;
399 }
400
515b3ae6
JP
401 foreach ($questionstats as $questionstat) {
402 // Output the data for these question statistics.
403 $this->table->add_data_keyed($this->table->format_row($questionstat));
04853f27 404
515b3ae6 405 if (empty($questionstat->subquestions)) {
04853f27 406 continue;
71a2b878 407 }
43ec99aa 408
04853f27 409 // And its subquestions, if it has any.
515b3ae6 410 $subitemstodisplay = explode(',', $questionstat->subquestions);
04853f27 411 foreach ($subitemstodisplay as $subitemid) {
515b3ae6
JP
412 $subquestionstats[$subitemid]->maxmark = $questionstat->maxmark;
413 $this->table->add_data_keyed($this->table->format_row($subquestionstats[$subitemid]));
04853f27 414 }
0c1c764e 415 }
04853f27
TH
416
417 $this->table->finish_output(!$this->table->is_downloading());
43ec99aa 418 }
3b1d5cc4 419
04853f27
TH
420 /**
421 * Output the table of overall quiz statistics.
422 * @param array $quizinfo as returned by {@link get_formatted_quiz_info_data()}.
423 * @return string the HTML.
424 */
425 protected function output_quiz_info_table($quizinfo) {
426
427 $quizinfotable = new html_table();
428 $quizinfotable->align = array('center', 'center');
429 $quizinfotable->width = '60%';
430 $quizinfotable->attributes['class'] = 'generaltable titlesleft';
431 $quizinfotable->data = array();
432
433 foreach ($quizinfo as $heading => $value) {
434 $quizinfotable->data[] = array($heading, $value);
71a2b878 435 }
04853f27
TH
436
437 return html_writer::table($quizinfotable);
71a2b878 438 }
43ec99aa 439
04853f27
TH
440 /**
441 * Download the table of overall quiz statistics.
442 * @param array $quizinfo as returned by {@link get_formatted_quiz_info_data()}.
443 */
444 protected function download_quiz_info_table($quizinfo) {
445 global $OUTPUT;
3b1d5cc4 446
04853f27
TH
447 // XHTML download is a special case.
448 if ($this->table->is_downloading() == 'xhtml') {
449 echo $OUTPUT->heading(get_string('quizinformation', 'quiz_statistics'));
450 echo $this->output_quiz_info_table($quizinfo);
451 return;
452 }
3b1d5cc4 453
04853f27
TH
454 // Reformat the data ready for output.
455 $headers = array();
456 $row = array();
457 foreach ($quizinfo as $heading => $value) {
458 $headers[] = $heading;
459 $row[] = $value;
460 }
3b1d5cc4 461
04853f27
TH
462 // Do the output.
463 $exportclass = $this->table->export_class_instance();
464 $exportclass->start_table(get_string('quizinformation', 'quiz_statistics'));
465 $exportclass->output_headers($headers);
466 $exportclass->add_data($row);
467 $exportclass->finish_table();
468 }
469
470 /**
471 * Output the HTML needed to show the statistics graph.
e68e4ccf
JP
472 * @param $quizid
473 * @param $currentgroup
474 * @param $useallattempts
04853f27 475 */
e68e4ccf 476 protected function output_statistics_graph($quizid, $currentgroup, $useallattempts) {
2cdcb905 477 global $PAGE;
04853f27 478
2cdcb905 479 $output = $PAGE->get_renderer('mod_quiz');
04853f27 480 $imageurl = new moodle_url('/mod/quiz/report/statistics/statistics_graph.php',
e68e4ccf 481 compact('quizid', 'currentgroup', 'useallattempts'));
2cdcb905
TH
482 $graphname = get_string('statisticsreportgraph', 'quiz_statistics');
483 echo $output->graph($imageurl, $graphname);
04853f27
TH
484 }
485
04853f27
TH
486 /**
487 * Get the quiz and question statistics, either by loading the cached results,
488 * or by recomputing them.
489 *
490 * @param object $quiz the quiz settings.
f7970e3c 491 * @param int $currentgroup the current group. 0 for none.
f7970e3c 492 * @param bool $useallattempts use all attempts, or just first attempts.
04853f27
TH
493 * @param array $groupstudents students in this group.
494 * @param array $questions question definitions.
495 * @return array with 4 elements:
496 * - $quizstats The statistics for overall attempt scores.
515b3ae6
JP
497 * - $questionstats array of \core_question\statistics\questions\calculated objects keyed by slot.
498 * - $subquestionstats array of \core_question\statistics\questions\calculated_for_subquestion objects keyed by question id.
04853f27 499 */
7de1e35b 500 protected function get_quiz_and_questions_stats($quiz, $currentgroup, $useallattempts, $groupstudents, $questions) {
04853f27 501
e68e4ccf 502 $qubaids = quiz_statistics_qubaids_condition($quiz->id, $currentgroup, $groupstudents, $useallattempts);
04853f27 503
515b3ae6 504 $qcalc = new \core_question\statistics\questions\calculator($questions);
04853f27 505
7de1e35b
JP
506 $quizcalc = new quiz_statistics_calculator();
507
508 if ($quizcalc->get_last_calculated_time($qubaids) === false) {
e68e4ccf 509 // Recalculate now.
515b3ae6 510 list($questionstats, $subquestionstats) = $qcalc->calculate($qubaids);
04853f27 511
7de1e35b 512 $quizstats = $quizcalc->calculate($quiz->id, $currentgroup, $useallattempts,
515b3ae6 513 $groupstudents, count($questions), $qcalc->get_sum_of_mark_variance());
e68e4ccf 514
7de1e35b 515 if ($quizstats->s()) {
d50b05e6 516 $this->analyse_responses_for_all_questions_and_subquestions($qubaids, $questions, $subquestionstats);
e68e4ccf
JP
517 }
518 } else {
7de1e35b 519 $quizstats = $quizcalc->get_cached($qubaids);
515b3ae6 520 list($questionstats, $subquestionstats) = $qcalc->get_cached($qubaids);
04853f27
TH
521 }
522
515b3ae6 523 return array($quizstats, $questionstats, $subquestionstats);
04853f27
TH
524 }
525
d50b05e6 526 protected function analyse_responses_for_all_questions_and_subquestions($qubaids, $questions, $subquestionstats) {
04853f27
TH
527
528 $done = array();
529 foreach ($questions as $question) {
530 if (!question_bank::get_qtype($question->qtype, false)->can_analyse_responses()) {
531 continue;
43ec99aa 532 }
04853f27
TH
533 $done[$question->id] = 1;
534
515b3ae6 535 $responesstats = new \core_question\statistics\responses\analyser($question);
e68e4ccf 536 $responesstats->calculate($qubaids);
04853f27
TH
537 }
538
515b3ae6
JP
539 foreach ($subquestionstats as $subquestionstat) {
540 if (!question_bank::get_qtype($subquestionstat->question->qtype, false)->can_analyse_responses() ||
541 isset($done[$subquestionstat->question->id])) {
04853f27 542 continue;
43ec99aa 543 }
515b3ae6 544 $done[$subquestionstat->question->id] = 1;
04853f27 545
515b3ae6 546 $responesstats = new \core_question\statistics\responses\analyser($subquestionstat->question);
e68e4ccf 547 $responesstats->calculate($qubaids);
04853f27
TH
548 }
549 }
550
551 /**
552 * @return string HTML snipped for the Download full report as UI.
553 */
554 protected function everything_download_options() {
555 $downloadoptions = $this->table->get_download_menu();
556
0465ef6e
RT
557 $downloadelements = new stdClass();
558 $downloadelements->formatsmenu = html_writer::select($downloadoptions, 'download',
559 $this->table->defaultdownloadformat, false);
560 $downloadelements->downloadbutton = '<input type="submit" value="' .
561 get_string('download') . '"/>';
562
04853f27
TH
563 $output = '<form action="'. $this->table->baseurl .'" method="post">';
564 $output .= '<div class="mdl-align">';
565 $output .= '<input type="hidden" name="everything" value="1"/>';
0465ef6e 566 $output .= html_writer::tag('label', get_string('downloadeverything', 'quiz_statistics', $downloadelements));
04853f27
TH
567 $output .= '</div></form>';
568
569 return $output;
570 }
571
572 /**
573 * Generate the snipped of HTML that says when the stats were last caculated,
574 * with a recalcuate now button.
e68e4ccf
JP
575 * @param object $quizstats the overall quiz statistics.
576 * @param int $quizid the quiz id.
577 * @param int $currentgroup the id of the currently selected group, or 0.
578 * @param array $groupstudents ids of students in the group.
579 * @param bool $useallattempts whether to use all attempts, instead of just
580 * first attempts.
581 * @param moodle_url $reporturl url for this report
25a03faa
TH
582 * @return string a HTML snipped saying when the stats were last computed,
583 * or blank if that is not appropriate.
04853f27
TH
584 */
585 protected function output_caching_info($quizstats, $quizid, $currentgroup,
586 $groupstudents, $useallattempts, $reporturl) {
587 global $DB, $OUTPUT;
588
589 if (empty($quizstats->timemodified)) {
590 return '';
591 }
592
593 // Find the number of attempts since the cached statistics were computed.
594 list($fromqa, $whereqa, $qaparams) = quiz_statistics_attempts_sql(
595 $quizid, $currentgroup, $groupstudents, $useallattempts, true);
596 $count = $DB->count_records_sql("
597 SELECT COUNT(1)
598 FROM $fromqa
599 WHERE $whereqa
600 AND quiza.timefinish > {$quizstats->timemodified}", $qaparams);
601
602 if (!$count) {
603 $count = 0;
604 }
605
606 // Generate the output.
0ff4bd08 607 $a = new stdClass();
04853f27
TH
608 $a->lastcalculated = format_time(time() - $quizstats->timemodified);
609 $a->count = $count;
610
25a03faa
TH
611 $recalcualteurl = new moodle_url($reporturl,
612 array('recalculate' => 1, 'sesskey' => sesskey()));
04853f27 613 $output = '';
25a03faa
TH
614 $output .= $OUTPUT->box_start(
615 'boxaligncenter generalbox boxwidthnormal mdl-align', 'cachingnotice');
04853f27 616 $output .= get_string('lastcalculated', 'quiz_statistics', $a);
25a03faa
TH
617 $output .= $OUTPUT->single_button($recalcualteurl,
618 get_string('recalculatenow', 'quiz_statistics'));
04853f27
TH
619 $output .= $OUTPUT->box_end(true);
620
621 return $output;
622 }
623
624 /**
625 * Clear the cached data for a particular report configuration. This will
626 * trigger a re-computation the next time the report is displayed.
e68e4ccf 627 * @param $qubaids qubaid_condition
04853f27 628 */
e68e4ccf 629 protected function clear_cached_data($qubaids) {
04853f27 630 global $DB;
e68e4ccf
JP
631 $DB->delete_records('quiz_statistics', array('hashcode' => $qubaids->get_hash_code()));
632 $DB->delete_records('question_statistics', array('hashcode' => $qubaids->get_hash_code()));
633 $DB->delete_records('question_response_analysis', array('hashcode' => $qubaids->get_hash_code()));
04853f27
TH
634 }
635
3652dddd
JP
636 /**
637 * @param object $quiz the quiz.
638 * @return array of questions for this quiz.
639 */
640 public function load_and_initialise_questions_for_calculations($quiz) {
641 // Load the questions.
642 $questions = quiz_report_get_significant_questions($quiz);
643 $questionids = array();
644 foreach ($questions as $question) {
645 $questionids[] = $question->id;
646 }
647 $fullquestions = question_load_questions($questionids);
648 foreach ($questions as $qno => $question) {
649 $q = $fullquestions[$question->id];
650 $q->maxmark = $question->maxmark;
651 $q->slot = $qno;
652 $q->number = $question->number;
653 $questions[$qno] = $q;
654 }
655 return $questions;
656 }
0c1c764e 657}
04853f27 658