MDL-63390 quiz_statistics, question: Handling variants as well

Variants should also be treated like random questions in the sense that
a summary row
should be displayed for them as well.
This commit deals with handling variants.
Also the name of the calculated_random_question_summary class has
changed to
calculated_question_summary to be more generic as the summary row is not
specific to random questions only.
Also removed the protected function too_many_subq_and_or_variant_rows
and the const SUBQ_AND_VARIANT_ROW_LIMIT as they were not being used
anymore.

Part of MDL-62610
This commit is contained in:
Shamim Rezaie 2018-09-28 16:09:32 +10:00 committed by Simey Lameze
parent f4bc55871c
commit 84140b9137
5 changed files with 63 additions and 74 deletions

View File

@ -117,5 +117,4 @@ $string['statistics:view'] = 'View statistics report';
$string['statsfor'] = 'Quiz statistics (for {$a})';
$string['variant'] = 'Variant';
$string['viewanalysis'] = 'View analysis of these questions';
$string['viewanalysishint'] = 'View analysis of the random question(s) above';
$string['whichtries'] = 'Analyze responses for';

View File

@ -26,7 +26,8 @@ defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/tablelib.php');
use \core_question\statistics\questions\calculated_random_question_summary;
use \core_question\statistics\questions\calculated_question_summary;
/**
* This table has one row for each question in the quiz, with sub-rows when
* random questions and variants appear.
@ -139,7 +140,7 @@ class quiz_statistics_table extends flexible_table {
* @return string contents of this table cell.
*/
protected function col_number($questionstat) {
if ($this->is_random_question_summary($questionstat)) {
if ($this->is_calculated_question_summary($questionstat)) {
return '';
}
if (!isset($questionstat->question->number)) {
@ -164,7 +165,7 @@ class quiz_statistics_table extends flexible_table {
* @return string contents of this table cell.
*/
protected function col_icon($questionstat) {
if ($this->is_random_question_summary($questionstat)) {
if ($this->is_calculated_question_summary($questionstat)) {
return '';
} else {
return print_question_icon($questionstat->question, true);
@ -177,7 +178,7 @@ class quiz_statistics_table extends flexible_table {
* @return string contents of this table cell.
*/
protected function col_actions($questionstat) {
if ($this->is_random_question_summary($questionstat)) {
if ($this->is_calculated_question_summary($questionstat)) {
return '';
} else {
return quiz_question_action_icons($this->quiz, $this->cmid,
@ -241,22 +242,14 @@ class quiz_statistics_table extends flexible_table {
$number = $questionstat->question->number;
$israndomquestion = $questionstat->question->qtype == 'random';
$url = new moodle_url($baseurl, array('slot' => $questionstat->slot));
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('viewanalysis', 'quiz_statistics'),
array('title' => get_string('viewanalysishint', '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,
array('title' => get_string('slotstructureanalysis', 'quiz_statistics', $number)));
} else {
if ($this->is_calculated_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 and variant question rows will render the name without a link to improve clarity
// in the UI.
$name = html_writer::link($url, get_string('viewanalysis', 'quiz_statistics'));
} else if (!$israndomquestion && !$questionstat->get_variants() && !$questionstat->get_sub_question_ids()) {
// Question cannot be broken down into sub-questions or variants. Link will show response analysis page.
$name = html_writer::link($url,
$name,
@ -284,7 +277,7 @@ 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)) {
if ($this->is_calculated_question_summary($questionstat)) {
list($min, $max) = $questionstat->get_min_max_of('s');
$a = new stdClass();
$a->min = $min ?: 0;
@ -303,7 +296,7 @@ class quiz_statistics_table extends flexible_table {
* @return string contents of this table cell.
*/
protected function col_facility($questionstat) {
if ($this->is_random_question_summary($questionstat)) {
if ($this->is_calculated_question_summary($questionstat)) {
list($min, $max) = $questionstat->get_min_max_of('facility');
if (is_null($min) && is_null($max)) {
@ -327,7 +320,7 @@ class quiz_statistics_table extends flexible_table {
* @return string contents of this table cell.
*/
protected function col_sd($questionstat) {
if ($this->is_random_question_summary($questionstat)) {
if ($this->is_calculated_question_summary($questionstat)) {
list($min, $max) = $questionstat->get_min_max_of('sd');
if (is_null($min) && is_null($max)) {
@ -351,7 +344,7 @@ class quiz_statistics_table extends flexible_table {
* @return string contents of this table cell.
*/
protected function col_random_guess_score($questionstat) {
if ($this->is_random_question_summary($questionstat)) {
if ($this->is_calculated_question_summary($questionstat)) {
list($min, $max) = $questionstat->get_min_max_of('randomguessscore');
if (is_null($min) && is_null($max)) {
@ -378,7 +371,7 @@ 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)) {
if ($this->is_calculated_question_summary($questionstat)) {
list($min, $max) = $questionstat->get_min_max_of('maxmark');
if (is_null($min) && is_null($max)) {
@ -403,7 +396,7 @@ class quiz_statistics_table extends flexible_table {
protected function col_effective_weight($questionstat) {
global $OUTPUT;
if ($this->is_random_question_summary($questionstat)) {
if ($this->is_calculated_question_summary($questionstat)) {
list($min, $max) = $questionstat->get_min_max_of('effectiveweight');
if (is_null($min) && is_null($max)) {
@ -444,7 +437,7 @@ class quiz_statistics_table extends flexible_table {
* @return string contents of this table cell.
*/
protected function col_discrimination_index($questionstat) {
if ($this->is_random_question_summary($questionstat)) {
if ($this->is_calculated_question_summary($questionstat)) {
list($min, $max) = $questionstat->get_min_max_of('discriminationindex');
if (is_numeric($min)) {
@ -472,7 +465,7 @@ class quiz_statistics_table extends flexible_table {
* @return string contents of this table cell.
*/
protected function col_discriminative_efficiency($questionstat) {
if ($this->is_random_question_summary($questionstat)) {
if ($this->is_calculated_question_summary($questionstat)) {
list($min, $max) = $questionstat->get_min_max_of('discriminativeefficiency');
if (!is_numeric($min) && !is_numeric($max)) {
@ -496,7 +489,7 @@ class quiz_statistics_table extends flexible_table {
* @return bool is this question possibly not pulling it's weight?
*/
protected function is_dubious_question($questionstat) {
if ($this->is_random_question_summary($questionstat)) {
if ($this->is_calculated_question_summary($questionstat)) {
// We only care about the minimum value here.
// If the minimum value is less than the threshold, then we know that there is at least one value below the threshold.
list($discriminativeefficiency) = $questionstat->get_min_max_of('discriminativeefficiency');
@ -512,13 +505,13 @@ class quiz_statistics_table extends flexible_table {
}
/**
* Check if the given stats object is an instance of calculated_random_question_summary.
* Check if the given stats object is an instance of calculated_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;
protected function is_calculated_question_summary($questionstat) {
return $questionstat instanceof calculated_question_summary;
}
public function wrap_html_start() {

View File

@ -39,12 +39,6 @@ class all_calculated_for_qubaid_condition {
/** @var int Time after which statistics are automatically recomputed. */
const TIME_TO_CACHE = 900; // 15 minutes.
/**
* The limit of rows of sub-question and variants rows to display on main page of report before switching to showing min,
* median and max variants.
*/
const SUBQ_AND_VARIANT_ROW_LIMIT = 10;
/**
* @var object[]
*/
@ -311,16 +305,6 @@ class all_calculated_for_qubaid_condition {
return $errors;
}
/**
* Are there too many rows of sub-questions and / or variant rows.
*
* @param array $rows the rows we intend to add.
* @return bool Are there too many?
*/
protected function too_many_subq_and_or_variant_rows($rows) {
return (count($rows) > static::SUBQ_AND_VARIANT_ROW_LIMIT);
}
/**
* From a number of calculated instances find the three instances with min, median and maximum facility index values.
*
@ -405,7 +389,7 @@ class all_calculated_for_qubaid_condition {
*
* @param int $slot the slot no
* @param bool $limited limit number of variants and sub-questions displayed?
* @return calculated|calculated_for_subquestion|calculated_random_question_summary[] stats to display
* @return calculated|calculated_for_subquestion|calculated_question_summary[] stats to display
*/
protected function all_subq_and_variant_stats_for_slot($slot, $limited) {
// Random question in this slot?
@ -416,20 +400,26 @@ class all_calculated_for_qubaid_condition {
$randomquestioncalculated = $this->for_slot($slot);
if ($subqvariantstats = $this->all_subq_variants_for_one_slot($slot)) {
// There are some variants from randomly selected questions.
$subqvariantfacilitystats = $this->find_min_median_and_max_facility_stats_objects($subqvariantstats);
$toreturn = array_merge($toreturn, $subqvariantfacilitystats);
// If we're showing a limited view of the statistics then add a question summary stat
// rather than a stat for each subquestion.
$summarystat = $this->make_new_calculated_question_summary_stat($randomquestioncalculated, $subqvariantstats);
$toreturn = array_merge($toreturn, [$summarystat], $subqvariantfacilitystats);
}
if ($subqstats = $this->all_subqs_for_one_slot($slot)) {
// There are some randomly selected questions.
$subqfacilitystats = $this->find_min_median_and_max_facility_stats_objects($subqstats);
$toreturn = array_merge($toreturn, $subqfacilitystats);
}
// If we're showing a limited view of the statistics then add a
// random question summary stat rather than a stat for each
// subquestion.
$summarystat = $this->make_new_random_question_summary_stat($randomquestioncalculated, $subqstats);
$toreturn = array_merge([$summarystat], $toreturn);
// If we're showing a limited view of the statistics then add a question summary stat
// rather than a stat for each subquestion.
$summarystat = $this->make_new_calculated_question_summary_stat($randomquestioncalculated, $subqstats);
$toreturn = array_merge($toreturn, [$summarystat], $subqfacilitystats);
}
foreach ($toreturn as $index => $calculated) {
$calculated->subqdisplayorder = $index;
@ -450,8 +440,15 @@ class all_calculated_for_qubaid_condition {
return $toreturn;
} else {
$variantstats = $this->all_variant_stats_for_one_slot($slot);
if ($limited && $this->too_many_subq_and_or_variant_rows($variantstats)) {
return $this->find_min_median_and_max_facility_stats_objects($variantstats);
if ($limited && $variantstats) {
$variantquestioncalculated = $this->for_slot($slot);
$variantfacilitystats = $this->find_min_median_and_max_facility_stats_objects($variantstats);
// If we're showing a limited view of the statistics then add a question summary stat
// rather than a stat for each variation.
$summarystat = $this->make_new_calculated_question_summary_stat($variantquestioncalculated, $variantstats);
return array_merge([$summarystat], $variantfacilitystats);
} else {
return $variantstats;
}
@ -476,18 +473,18 @@ class all_calculated_for_qubaid_condition {
}
/**
* 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.
* Create a summary calculated object for a calculated question. This is used as a placeholder
* to indicate that a calculated question has sub questions or variations to show rather than listing each
* subquestion or variation directly.
*
* @param calculated $randomquestioncalculated The calculated instance for the random question slot.
* @param calculated[] $subquestionstats The instances of the calculated stats of the questions that are being summarised.
* @return calculated_random_question_summary
* @return calculated_question_summary
*/
protected function make_new_random_question_summary_stat($randomquestioncalculated, $subquestionstats) {
protected function make_new_calculated_question_summary_stat($randomquestioncalculated, $subquestionstats) {
$question = $randomquestioncalculated->question;
$slot = $randomquestioncalculated->slot;
$calculatedsummary = new calculated_random_question_summary($question, $slot, $subquestionstats);
$calculatedsummary = new calculated_question_summary($question, $slot, $subquestionstats);
return $calculatedsummary;
}

View File

@ -26,7 +26,7 @@ namespace core_question\statistics\questions;
defined('MOODLE_INTERNAL') || die();
/**
* Class calculated_random_question_summary
* Class calculated_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.
@ -37,7 +37,7 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 2018 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class calculated_random_question_summary extends calculated {
class calculated_question_summary extends calculated {
/**
* @var int only set immediately before display in the table. The order of display in the table.
@ -50,7 +50,7 @@ class calculated_random_question_summary extends calculated {
protected $subqstats;
/**
* calculated_random_question_summary constructor.
* calculated_question_summary constructor.
*
* @param \stdClass $question
* @param int $slot

View File

@ -25,15 +25,15 @@
defined('MOODLE_INTERNAL') || die();
use core_question\statistics\questions\calculated_random_question_summary;
use core_question\statistics\questions\calculated_question_summary;
/**
* Class core_question_calculated_random_question_summary_testcase
* Class core_question_calculated_question_summary_testcase
*
* @copyright 2018 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_question_calculated_random_question_summary_testcase extends advanced_testcase {
class core_question_calculated_question_summary_testcase extends advanced_testcase {
/**
* Provider for test_get_min_max_of.
@ -100,7 +100,7 @@ class core_question_calculated_random_question_summary_testcase extends advanced
* @dataProvider get_min_max_provider
*/
public function test_get_min_max_of($subqstats, $expected) {
$calculatedsummary = new calculated_random_question_summary(null, null, $subqstats);
$calculatedsummary = new calculated_question_summary(null, null, $subqstats);
$res = $calculatedsummary->get_min_max_of('index');
$this->assertEquals($expected, $res);
}
@ -151,7 +151,7 @@ class core_question_calculated_random_question_summary_testcase extends advanced
* @dataProvider get_sd_min_max_provider
*/
public function test_get_min_max_of_sd($subqstats, $expected) {
$calculatedsummary = new calculated_random_question_summary(null, null, $subqstats);
$calculatedsummary = new calculated_question_summary(null, null, $subqstats);
$res = $calculatedsummary->get_min_max_of('sd');
$this->assertEquals($expected, $res);
}