From 29cc55079acbbc8f8f4667fcf9e9a0de1adf954f Mon Sep 17 00:00:00 2001 From: Ryan Wyllie Date: Thu, 12 Apr 2018 13:18:58 +0800 Subject: [PATCH] MDL-63186 question: include random questions in statistics calculator Part of MDL-62610 --- .../statistics/lang/en/quiz_statistics.php | 1 + .../report/statistics/statistics_table.php | 46 +++++++++++-- .../all_calculated_for_qubaid_condition.php | 67 +++++++++++++------ .../calculated_random_question_summary.php | 55 +++++++++++++++ 4 files changed, 146 insertions(+), 23 deletions(-) create mode 100644 question/classes/statistics/questions/calculated_random_question_summary.php diff --git a/mod/quiz/report/statistics/lang/en/quiz_statistics.php b/mod/quiz/report/statistics/lang/en/quiz_statistics.php index 38895d02e13..4a7c9b10bb9 100644 --- a/mod/quiz/report/statistics/lang/en/quiz_statistics.php +++ b/mod/quiz/report/statistics/lang/en/quiz_statistics.php @@ -115,4 +115,5 @@ $string['statisticsreportgraph'] = 'Statistics for question positions'; $string['statistics:view'] = 'View statistics report'; $string['statsfor'] = 'Quiz statistics (for {$a})'; $string['variant'] = 'Variant'; +$string['viewdisplayedquestions'] = 'View displayed questions'; $string['whichtries'] = 'Analyze responses for'; diff --git a/mod/quiz/report/statistics/statistics_table.php b/mod/quiz/report/statistics/statistics_table.php index 348532fb839..80c97dc40eb 100644 --- a/mod/quiz/report/statistics/statistics_table.php +++ b/mod/quiz/report/statistics/statistics_table.php @@ -26,6 +26,7 @@ defined('MOODLE_INTERNAL') || die(); require_once($CFG->libdir.'/tablelib.php'); +use \core_question\statistics\questions\calculated_random_question_summary; /** * This table has one row for each question in the quiz, with sub-rows when * random questions and variants appear. @@ -160,7 +161,11 @@ class quiz_statistics_table extends flexible_table { * @return string contents of this table cell. */ protected function col_icon($questionstat) { - return print_question_icon($questionstat->question, true); + if ($this->is_random_question_summary($questionstat)) { + return ''; + } else { + return print_question_icon($questionstat->question, true); + } } /** @@ -169,8 +174,12 @@ class quiz_statistics_table extends flexible_table { * @return string contents of this table cell. */ protected function col_actions($questionstat) { - return quiz_question_action_icons($this->quiz, $this->cmid, - $questionstat->question, $this->baseurl, $questionstat->variant); + if ($this->is_random_question_summary($questionstat)) { + return ''; + } else { + return quiz_question_action_icons($this->quiz, $this->cmid, + $questionstat->question, $this->baseurl, $questionstat->variant); + } } /** @@ -227,8 +236,19 @@ class quiz_statistics_table extends flexible_table { // Question in a slot, we are not on a page showing structural analysis of one slot, // we don't want linking on those pages. $number = $questionstat->question->number; + $israndomquestion = $questionstat->question->qtype == 'random'; $url = new moodle_url($baseurl, array('slot' => $questionstat->slot)); - if ($questionstat->get_variants() || $questionstat->get_sub_question_ids()) { + if ($israndomquestion) { + if ($this->is_random_question_summary($questionstat)) { + // Only make the random question summary row name link to the slot structure + // analysis page with specific text to clearly indicate the link to the user. + // Random question rows will render the name without a link to improve clarity + // in the UI. + $name = html_writer::link($url, + get_string('viewdisplayedquestions', 'quiz_statistics'), + array('title' => get_string('slotstructureanalysis', 'quiz_statistics', $number))); + } + } else if ($questionstat->get_variants() || $questionstat->get_sub_question_ids()) { // Question can be broken down into sub-questions or variants. Link will show structural analysis page. $name = html_writer::link($url, $name, @@ -261,6 +281,10 @@ class quiz_statistics_table extends flexible_table { * @return string contents of this table cell. */ protected function col_s($questionstat) { + if ($this->is_random_question_summary($questionstat)) { + return ''; + } + if (!isset($questionstat->s)) { return 0; } @@ -315,6 +339,10 @@ class quiz_statistics_table extends flexible_table { * @return string contents of this table cell. */ protected function col_intended_weight($questionstat) { + if ($this->is_random_question_summary($questionstat)) { + return ''; + } + return quiz_report_scale_summarks_as_percentage($questionstat->maxmark, $this->quiz); } @@ -388,6 +416,16 @@ class quiz_statistics_table extends flexible_table { return $questionstat->discriminativeefficiency < 15; } + /** + * Check if the given stats object is an instance of calculated_random_question_summary. + * + * @param \core_question\statistics\questions\calculated $questionstat Stats object + * @return bool + */ + protected function is_random_question_summary($questionstat) { + return $questionstat instanceof calculated_random_question_summary; + } + public function wrap_html_start() { // Horrible Moodle 2.0 wide-content work-around. if (!$this->is_downloading()) { diff --git a/question/classes/statistics/questions/all_calculated_for_qubaid_condition.php b/question/classes/statistics/questions/all_calculated_for_qubaid_condition.php index 592c12642e4..8c664351161 100644 --- a/question/classes/statistics/questions/all_calculated_for_qubaid_condition.php +++ b/question/classes/statistics/questions/all_calculated_for_qubaid_condition.php @@ -410,29 +410,41 @@ class all_calculated_for_qubaid_condition { protected function all_subq_and_variant_stats_for_slot($slot, $limited) { // Random question in this slot? if ($this->for_slot($slot)->get_sub_question_ids()) { - if ($limited) { - $subqvariantstats = $this->all_subq_variants_for_one_slot($slot); - if ($this->too_many_subq_and_or_variant_rows($subqvariantstats)) { - // Too many variants from randomly selected questions. - return $this->find_min_median_and_max_facility_stats_objects($subqvariantstats); - } - $subqstats = $this->all_subqs_for_one_slot($slot); - if ($this->too_many_subq_and_or_variant_rows($subqstats)) { - // Too many randomly selected questions. - return $this->find_min_median_and_max_facility_stats_objects($subqstats); - } - } $toreturn = array(); - $displaynumber = 1; - foreach ($this->for_slot($slot)->get_sub_question_ids() as $subqid) { - $toreturn[] = $this->make_new_subq_stat_for($displaynumber, $slot, $subqid); - if ($variants = $this->for_subq($subqid)->get_variants()) { - foreach ($variants as $variant) { - $toreturn[] = $this->make_new_subq_stat_for($displaynumber, $slot, $subqid, $variant); + + if ($limited) { + $randomquestioncalculated = $this->for_slot($slot); + // If we're showing a limited view of the statistics then add a + // random question summary stat rather than a stat for each + // subquestion. + $toreturn[] = $this->make_new_random_question_summary_stat($randomquestioncalculated); + + if ($subqvariantstats = $this->all_subq_variants_for_one_slot($slot)) { + $subqvariantfacilitystats = $this->find_min_median_and_max_facility_stats_objects($subqvariantstats); + $toreturn = array_merge($toreturn, $subqvariantfacilitystats); + } + + if ($subqstats = $this->all_subqs_for_one_slot($slot)) { + $subqfacilitystats = $this->find_min_median_and_max_facility_stats_objects($subqstats); + $toreturn = array_merge($toreturn, $subqfacilitystats); + } + + foreach ($toreturn as $index => $calculated) { + $calculated->subqdisplayorder = $index + 1; + } + } else { + $displaynumber = 1; + foreach ($this->for_slot($slot)->get_sub_question_ids() as $subqid) { + $toreturn[] = $this->make_new_subq_stat_for($displaynumber, $slot, $subqid); + if ($variants = $this->for_subq($subqid)->get_variants()) { + foreach ($variants as $variant) { + $toreturn[] = $this->make_new_subq_stat_for($displaynumber, $slot, $subqid, $variant); + } } + $displaynumber++; } - $displaynumber++; } + return $toreturn; } else { $variantstats = $this->all_variant_stats_for_one_slot($slot); @@ -460,4 +472,21 @@ class all_calculated_for_qubaid_condition { $slotstat->subqdisplayorder = $displaynumber; return $slotstat; } + + /** + * Create a summary calculated object for a random question. This is used as a placeholder + * to indicate that a random question has sub questions to show rather than listing each + * subquestion directly. + * + * @param calculated $randomquestioncalculated The calculated instance for the random question slot. + * @return calculated_random_question_summary + */ + protected function make_new_random_question_summary_stat($randomquestioncalculated) { + $question = $randomquestioncalculated->question; + $slot = $randomquestioncalculated->slot; + $calculatedsummary = new calculated_random_question_summary($question, $slot); + $calculatedsummary->subquestions = $randomquestioncalculated->subquestions; + + return $calculatedsummary; + } } diff --git a/question/classes/statistics/questions/calculated_random_question_summary.php b/question/classes/statistics/questions/calculated_random_question_summary.php new file mode 100644 index 00000000000..91d62c1e581 --- /dev/null +++ b/question/classes/statistics/questions/calculated_random_question_summary.php @@ -0,0 +1,55 @@ +. + +/** + * Question statistics calculations class. Used in the quiz statistics report. + * + * @package core_question + * @copyright 2018 Ryan Wyllie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_question\statistics\questions; +defined('MOODLE_INTERNAL') || die(); + +/** + * Class calculated_random_question_summary + * + * This class is used to indicate the statistics for a random question slot should + * be rendered with a link to a summary of the displayed questions. + * + * It's used in the limited view of the statistics calculation in lieu of adding + * the stats for each subquestion individually. + * + * @copyright 2018 Ryan Wyllie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class calculated_random_question_summary extends calculated { + + /** + * @var int only set immediately before display in the table. The order of display in the table. + */ + public $subqdisplayorder; + + /** + * This is a summary stat so never breakdown by variant. + * + * @return bool + */ + public function break_down_by_variant() { + return false; + } +}