<FIELD NAME="hashcode" TYPE="char" LENGTH="40" NOTNULL="true" SEQUENCE="false" COMMENT="sha1 hash of serialized qubaids_condition class. Unique for every combination of class name and property."/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="questionid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="variant" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="subqid" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="aid" TYPE="char" LENGTH="100" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="response" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
upgrade_main_savepoint(true, 2014012400.00);
}
- if ($oldversion < 2014020500.00) {
+ if ($oldversion < 2014021300.01) {
+ // Delete any cached stats to force recalculation later, then we can be sure that cached records will have the correct
+ // field.
+ $DB->delete_records('question_response_analysis');
+ $DB->delete_records('question_statistics');
+ $DB->delete_records('quiz_statistics');
+
// Define field variant to be added to question_statistics.
$table = new xmldb_table('question_statistics');
$field = new xmldb_field('variant', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'subquestion');
}
// Main savepoint reached.
- upgrade_main_savepoint(true, 2014020500.00);
+ upgrade_main_savepoint(true, 2014021300.01);
+ }
+
+ if ($oldversion < 2014021300.02) {
+
+ // Define field variant to be added to question_response_analysis.
+ $table = new xmldb_table('question_response_analysis');
+ $field = new xmldb_field('variant', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0, 'questionid');
+
+ // Conditionally launch add field variant.
+ if (!$dbman->field_exists($table, $field)) {
+ $dbman->add_field($table, $field);
+ }
+
+ // Main savepoint reached.
+ upgrade_main_savepoint(true, 2014021300.02);
}
return true;
$string['count'] = 'Count';
$string['coursename'] = 'Course name';
$string['detailedanalysis'] = 'More detailed analysis of the responses to this question';
+$string['detailedanalysisforvariant'] = 'More detailed analysis of the responses to variant {$a} of this question';
$string['discrimination_index'] = 'Discrimination index';
$string['discriminative_efficiency'] = 'Discriminative efficiency';
$string['downloadeverything'] = 'Download full report as {$a->formatsmenu} {$a->downloadbutton}';
$string['statisticsreportgraph'] = 'Statistics for question positions';
$string['statistics:view'] = 'View statistics report';
$string['statsfor'] = 'Quiz statistics (for {$a})';
+$string['variant'] = 'Variant';
+$string['variantno'] = 'Variant {$a}';
+
// A qid paramter indicates we should display the detailed analysis of a sub question.
$qid = optional_param('qid', 0, PARAM_INT);
$slot = optional_param('slot', 0, PARAM_INT);
+ $variantno = optional_param('variant', null, PARAM_INT);
$whichattempts = optional_param('whichattempts', $quiz->grademethod, PARAM_INT);
$pageoptions = array();
echo $OUTPUT->notification(get_string('noattempts', 'quiz'));
}
- foreach($questionstats->any_error_messages() as $errormessage) {
+ foreach ($questionstats->any_error_messages() as $errormessage) {
echo $OUTPUT->notification($errormessage);
}
$this->output_statistics_graph($quiz->id, $currentgroup, $whichattempts);
}
- foreach ($questions as $slot => $question) {
- if (question_bank::get_qtype(
- $question->qtype, false)->can_analyse_responses()) {
- $this->output_individual_question_response_analysis(
- $question, $questionstats->for_slot($slot)->s, $reporturl, $qubaids);
-
- } else if ($subqids = $questionstats->for_slot($slot)->get_sub_question_ids()) {
- foreach ($subqids as $subqid) {
- $this->output_individual_question_response_analysis($questionstats->for_subq($subqid)->question,
- $questionstats->for_subq($subqid)->s,
- $reporturl,
- $qubaids);
- }
- }
- }
+ $this->output_all_question_response_analysis($qubaids, $questions, $questionstats, $reporturl);
}
$this->table->export_class_instance()->finish_document();
+ } else if ($qid) {
+ // Report on an individual sub-question indexed questionid.
+ if (is_null($questionstats->for_subq($qid, $variantno))) {
+ print_error('questiondoesnotexist', 'question');
+ }
+
+ $this->output_individual_question_data($quiz, $questionstats->for_subq($qid, $variantno));
+ $this->output_individual_question_response_analysis($questionstats->for_subq($qid, $variantno)->question,
+ $variantno,
+ $questionstats->for_subq($qid, $variantno)->s,
+ $reporturl,
+ $qubaids);
+ // Back to overview link.
+ echo $OUTPUT->box('<a href="' . $reporturl->out() . '">' .
+ get_string('backtoquizreport', 'quiz_statistics') . '</a>',
+ 'boxaligncenter generalbox boxwidthnormal mdl-align');
} else if ($slot) {
// Report on an individual question indexed by position.
if (!isset($questions[$slot])) {
print_error('questiondoesnotexist', 'question');
}
- if ($questionstats->for_slot($slot)->get_sub_question_ids() || $questionstats->for_slot($slot)->get_variants()) {
+ if ($variantno === null &&
+ ($questionstats->for_slot($slot)->get_sub_question_ids()
+ || $questionstats->for_slot($slot)->get_variants())) {
if (!$this->table->is_downloading()) {
$number = $questionstats->for_slot($slot)->question->number;
echo $OUTPUT->heading(get_string('slotstructureanalysis', 'quiz_statistics', $number), 3);
$this->table->define_baseurl(new moodle_url($reporturl, array('slot' => $slot)));
$this->table->format_and_add_array_of_rows($questionstats->structure_analysis_for_one_slot($slot));
} else {
- $this->output_individual_question_data($quiz, $questionstats->for_slot($slot));
+ $this->output_individual_question_data($quiz, $questionstats->for_slot($slot, $variantno));
$this->output_individual_question_response_analysis($questions[$slot],
- $questionstats->for_slot($slot)->s,
+ $variantno,
+ $questionstats->for_slot($slot, $variantno)->s,
$reporturl,
$qubaids);
-
}
if (!$this->table->is_downloading()) {
// Back to overview link.
$this->table->finish_output();
}
- } else if ($qid) {
- // Report on an individual sub-question indexed questionid.
- if (is_null($questionstats->for_subq($qid))) {
- print_error('questiondoesnotexist', 'question');
- }
-
- $this->output_individual_question_data($quiz, $questionstats->for_subq($qid));
- $this->output_individual_question_response_analysis($questionstats->for_subq($qid)->question,
- $questionstats->for_subq($qid)->s,
- $reporturl,
- $qubaids);
-
- // Back to overview link.
- echo $OUTPUT->box('<a href="' . $reporturl->out() . '">' .
- get_string('backtoquizreport', 'quiz_statistics') . '</a>',
- 'boxaligncenter generalbox boxwidthnormal mdl-align');
-
} else if ($this->table->is_downloading()) {
// Downloading overview report.
$quizinfo = $quizstats->get_formatted_quiz_info_data($course, $cm, $quiz);
$questioninfotable->data[] = array(get_string('modulename', 'quiz'), $quiz->name);
$questioninfotable->data[] = array(get_string('questionname', 'quiz_statistics'),
$questionstat->question->name.' '.$datumfromtable['actions']);
+
+ if ($questionstat->variant !== null) {
+ $questioninfotable->data[] = array(get_string('variant', 'quiz_statistics'), $questionstat->variant);
+
+ }
$questioninfotable->data[] = array(get_string('questiontype', 'quiz_statistics'),
$datumfromtable['icon'] . ' ' .
question_bank::get_qtype($questionstat->question->qtype, false)->menu_name() . ' ' .
/**
* Display the response analysis for a question.
+ *
* @param object $question the question to report on.
+ * @param int|null $variantno the variant
* @param int $s
* @param moodle_url $reporturl the URL to redisplay this report.
* @param qubaid_condition $qubaids
*/
- protected function output_individual_question_response_analysis($question, $s, $reporturl, $qubaids) {
+ protected function output_individual_question_response_analysis($question, $variantno, $s, $reporturl, $qubaids) {
global $OUTPUT;
if (!question_bank::get_qtype($question->qtype, false)->can_analyse_responses()) {
if (!empty($question->number)) {
$questiontabletitle = '(' . $question->number . ') ' . $questiontabletitle;
}
+ if (!is_null($variantno)) {
+ $questiontabletitle .= ' '.get_string('variantno', 'quiz_statistics', $variantno);
+ }
if ($this->table->is_downloading() == 'xhtml') {
$questiontabletitle = get_string('analysisofresponsesfor', 'quiz_statistics', $questiontabletitle);
}
if ($this->table->is_downloading()) {
$exportclass->output_headers($qtable->headers);
}
- foreach ($responseanalysis->get_subpart_ids() as $partid) {
- $subpart = $responseanalysis->get_subpart($partid);
+
+ // Where no variant no is specified the variant no is actually one.
+ if ($variantno === null) {
+ $variantno = 1;
+ }
+ foreach ($responseanalysis->get_subpart_ids($variantno) as $partid) {
+ $subpart = $responseanalysis->get_analysis_for_subpart($variantno, $partid);
foreach ($subpart->get_response_class_ids() as $responseclassid) {
$responseclass = $subpart->get_response_class($responseclassid);
$tabledata = $responseclass->data_for_question_response_table($subpart->has_multiple_response_classes(), $partid);
protected function get_progress_trace_instance() {
if ($this->progress === null) {
if (!$this->table->is_downloading()) {
- $this->progress = new \core\progress\display_if_slow(get_string('calculatingallstats', 'quiz_statistics'));
+ $this->progress = new \core\progress\display_if_slow(get_string('calculatingallstats', 'quiz_statistics'));
$this->progress->set_display_names();
} else {
$this->progress = new \core\progress\null();
}
return $questions;
}
-}
+ /**
+ * Output all response analysis for all questions, sub-questions and variants. For download in a number of formats.
+ *
+ * @param $qubaids
+ * @param $questions
+ * @param $questionstats
+ * @param $reporturl
+ */
+ protected function output_all_question_response_analysis($qubaids, $questions, $questionstats, $reporturl) {
+ foreach ($questions as $slot => $question) {
+ if (question_bank::get_qtype(
+ $question->qtype, false)->can_analyse_responses()
+ ) {
+ if ($questionstats->for_slot($slot)->get_variants()) {
+ foreach ($questionstats->for_slot($slot)->get_variants() as $variantno) {
+ $this->output_individual_question_response_analysis($question,
+ $variantno,
+ $questionstats->for_slot($slot, $variantno)->s,
+ $reporturl,
+ $qubaids);
+ }
+ } else {
+ $this->output_individual_question_response_analysis($question,
+ null,
+ $questionstats->for_slot($slot)->s,
+ $reporturl,
+ $qubaids);
+ }
+ } else if ($subqids = $questionstats->for_slot($slot)->get_sub_question_ids()) {
+ foreach ($subqids as $subqid) {
+ if ($variants = $questionstats->for_subq($subqid)->get_variants()) {
+ foreach ($variants as $variantno) {
+ $this->output_individual_question_response_analysis(
+ $questionstats->for_subq($subqid, $variantno)->question,
+ $variantno,
+ $questionstats->for_subq($subqid, $variantno)->s,
+ $reporturl,
+ $qubaids);
+ }
+ } else {
+ $this->output_individual_question_response_analysis(
+ $questionstats->for_subq($subqid)->question,
+ null,
+ $questionstats->for_subq($subqid)->s,
+ $reporturl,
+ $qubaids);
+
+ }
+ }
+ }
+ }
+ }
+}
}
$baseurl = new moodle_url($this->baseurl);
- if (is_null($questionstat->variant)) {
+ if (!is_null($questionstat->variant)) {
+ if ($questionstat->subquestion) {
+ // Variant of a sub-question.
+ $url = new moodle_url($baseurl, array('qid' => $questionstat->questionid, 'variant' => $questionstat->variant));
+ $name = html_writer::link($url, $name, array('title' => get_string('detailedanalysisforvariant',
+ 'quiz_statistics',
+ $questionstat->variant)));
+ } else if ($questionstat->slot) {
+ // Variant of a question in a slot.
+ $url = new moodle_url($baseurl, array('slot' => $questionstat->slot, 'variant' => $questionstat->variant));
+ $name = html_writer::link($url, $name, array('title' => get_string('detailedanalysisforvariant',
+ 'quiz_statistics',
+ $questionstat->variant)));
+ }
+ } else {
if ($questionstat->subquestion && !$questionstat->get_variants()) {
+ // Sub question without variants.
$url = new moodle_url($baseurl, array('qid' => $questionstat->questionid));
$name = html_writer::link($url, $name, array('title' => get_string('detailedanalysis', 'quiz_statistics')));
} else if ($baseurl->param('slot') === null && $questionstat->slot) {
+ // 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;
$url = new moodle_url($baseurl, array('slot' => $questionstat->slot));
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 {
+ // Question cannot be broken down into sub-questions or variants. Link will show response analysis page.
$name = html_writer::link($url,
$name,
array('title' => get_string('detailedanalysis', 'quiz_statistics')));
--- /dev/null
+slot,randq,variant,subpart,modelresponse,actualresponse,count
+1,numerical,1,1,"3.142 (3.142..3.142)",3.142,1
+1,numerical,1,1,"3.14 (3.14..3.14)",3.14,7
+1,numerical,1,1,"3.1 (3.1..3.1)",3.1,4
+1,shortanswer,1,1,frog,frog,9
+1,shortanswer,1,1,*,butterfly,2
+1,shortanswer,1,1,toad,toad,2
+2,,1,1,{a} + {b} (±0.01 Relative),9.9,3
+2,,1,1,[NO MATCH],-0.7,1
+2,,1,1,[NO MATCH],-0.2,1
+2,,1,1,[NO MATCH],-1,1
+2,,4,1,{a} + {b} (±0.01 Relative),19.4,2
+2,,4,1,[NO RESPONSE],,1
+2,,4,1,[NO MATCH],-0.4,1
+3,,1,1,frog: amphibian,amphibian,25
+3,,1,2,cat: mammal,mammal,24
+3,,1,2,cat: amphibian,amphibian,1
+3,,1,3,newt: amphibian,amphibian,24
+3,,1,3,newt: mammal,mammal,1
return __DIR__."/fixtures/{$setname}{$test}.csv";
}
- protected $files = array('questions', 'steps', 'results', 'qstats');
+ protected $files = array('questions', 'steps', 'results', 'qstats', 'responsecounts');
/**
* Create a quiz add questions to it, walk through quiz attempts and then check results.
$qubaids = quiz_statistics_qubaids_condition($this->quiz->id, $groupstudents, $whichattempts);
// We will create some quiz and question stat calculator instances and some response analyser instances, just in order
- // to check the time of the
+ // to check the last analysed time then returned.
$quizcalc = new \quiz_statistics\calculator();
// Should not be a delay of more than one second between the calculation of stats above and here.
$this->assertTimeCurrent($quizcalc->get_last_calculated_time($qubaids));
$this->assertTimeCurrent($qcalc->get_last_calculated_time($qubaids));
foreach ($questions as $question) {
- if (!question_bank::get_qtype($question->qtype, false)->can_analyse_responses()) {
+ $qtypeobj = question_bank::get_qtype($question->qtype, false);
+ if (!$qtypeobj->can_analyse_responses()) {
continue;
}
$responesstats = new \core_question\statistics\responses\analyser($question);
$this->assertTimeCurrent($responesstats->get_last_analysed_time($qubaids));
}
- // These quiz stats and the question stats found in qstats00.csv were calculated independently in spreadsheet which is
+ for ($rowno = 0; $rowno < $csvdata['responsecounts']->getRowCount(); $rowno++) {
+ $responsecount = $csvdata['responsecounts']->getRow($rowno);
+ if ($responsecount['randq'] == '') {
+ $question = $questions[$responsecount['slot']];
+ } else {
+ $qid = $this->randqids[$responsecount['slot']][$responsecount['randq']];
+ $question = question_finder::get_instance()->load_question_data($qid);
+ }
+ $this->assert_response_count_equals($question, $qubaids, $responsecount);
+ }
+
+ // These quiz stats and the question stats found in qstats00.csv were calculated independently in spreadsheet which is
// available in open document or excel format here :
// https://github.com/jamiepratt/moodle-quiz-tools/tree/master/statsspreadsheet
$quizstatsexpected = array(
$this->assert_stat_equals($questionstats, 1, null, 'numerical', $statname, $expected);
}
-
-
// These variant's stats are calculated in stats_for_variant_1.xls and stats_for_variant_8.xls
// The calculations in the spreadsheets are the same but applied just to the attempts where the variants appeared.
'slot' => 2,
'subquestion' => false));
foreach ($statsforslot2variants as $variant => $stats) {
- foreach ($stats as $statname => $expected) {
- $this->assert_stat_equals($questionstats, 2, $variant, null, $statname, $expected);
- }
+ foreach ($stats as $statname => $expected) {
+ $this->assert_stat_equals($questionstats, 2, $variant, null, $statname, $expected);
+ }
+ }
+ }
+
+ protected function assert_response_count_equals($question, $qubaids, $responsecount) {
+ $responesstats = new \core_question\statistics\responses\analyser($question);
+ $analysis = $responesstats->load_cached($qubaids);
+ if (!isset($responsecount['subpart'])) {
+ $subpart = 1;
+ } else {
+ $subpart = $responsecount['subpart'];
+ }
+ list($subpartid, $responseclassid) = $this->get_response_subpart_and_class_id($question,
+ $subpart,
+ $responsecount['modelresponse']);
+
+ $subpartanalysis = $analysis->get_analysis_for_subpart($responsecount['variant'], $subpartid);
+ $responseclassanalysis = $subpartanalysis->get_response_class($responseclassid);
+ $actualresponsecounts = $responseclassanalysis->data_for_question_response_table('', '');
+ if ($responsecount['modelresponse'] !== '[NO RESPONSE]') {
+ foreach ($actualresponsecounts as $actualresponsecount) {
+ if ($actualresponsecount->response == $responsecount['actualresponse']) {
+ $this->assertEquals($responsecount['count'], $actualresponsecount->count);
+ return;
+ }
+ }
+ throw new coding_exception("Actual response '{$responsecount['actualresponse']}' not found.");
+ } else {
+ $actualresponsecount = array_pop($actualresponsecounts);
+ $this->assertEquals($responsecount['count'], $actualresponsecount->count);
+ }
+ }
+
+ protected function get_response_subpart_and_class_id($question, $subpart, $modelresponse) {
+ $qtypeobj = question_bank::get_qtype($question->qtype, false);
+ $possibleresponses = $qtypeobj->get_possible_responses($question);
+ $possibleresponsesubpartids = array_keys($possibleresponses);
+ if (!isset($possibleresponsesubpartids[$subpart - 1])) {
+ throw new coding_exception("Subpart '{$subpart}' not found.");
+ }
+ $subpartid = $possibleresponsesubpartids[$subpart - 1];
+
+ if ($modelresponse == '[NO RESPONSE]') {
+ return array($subpartid, null);
+
+ } else if ($modelresponse == '[NO MATCH]') {
+ return array($subpartid, 0);
+ }
+
+ $modelresponses = array();
+ foreach ($possibleresponses[$subpartid] as $responseclassid => $subpartpossibleresponse) {
+ $modelresponses[$responseclassid] = $subpartpossibleresponse->responseclass;
}
+ $this->assertContains($modelresponse, $modelresponses);
+ $responseclassid = array_search($modelresponse, $modelresponses);
+ return array($subpartid, $responseclassid);
}
/**
public $analysis;
/**
- * @var array Two index array first index is unique for each sub question part, the second index is the 'class' that this sub
- * question part can be classified into. This is the return value from {@link \question_type::get_possible_responses()}
+ * @var array Two index array first index is unique string for each sub question part, the second string index is the 'class'
+ * that sub-question part can be classified into.
+ *
+ * This is the return value from {@link \question_type::get_possible_responses()} see that method for fuller documentation.
*/
public $responseclasses = array();
// Analyse it.
foreach ($questionattempts as $qa) {
$responseparts = $qa->classify_response();
- $this->analysis->count_response_parts($responseparts);
+ $this->analysis->count_response_parts($qa->get_variant(), $responseparts);
}
$this->analysis->cache($qubaids, $this->questiondata->id);
return $this->analysis;
}
foreach ($rows as $row) {
- $class = $this->analysis->get_subpart($row->subqid)->get_response_class($row->aid);
+ $class = $this->analysis->get_analysis_for_subpart($row->variant, $row->subqid)->get_response_class($row->aid);
$class->add_response_and_count($row->response, $row->credit, $row->rcount);
}
return $this->analysis;
$this->count++;
}
-
/**
* @param \qubaid_condition $qubaids
* @param int $questionid the question id
+ * @param int $variantno
* @param string $subpartid
* @param string $responseclassid
*/
- public function cache($qubaids, $questionid, $subpartid, $responseclassid) {
+ public function cache($qubaids, $questionid, $variantno, $subpartid, $responseclassid) {
global $DB;
$row = new \stdClass();
$row->hashcode = $qubaids->get_hash_code();
$row->questionid = $questionid;
+ $row->variant = $variantno;
$row->subqid = $subpartid;
if ($responseclassid === '') {
$row->aid = null;
/**
* @param \qubaid_condition $qubaids
* @param int $questionid the question id
+ * @param int $variantno
* @param string $subpartid
*/
- public function cache($qubaids, $questionid, $subpartid) {
+ public function cache($qubaids, $questionid, $variantno, $subpartid) {
foreach ($this->actualresponses as $response => $actualresponse) {
- $actualresponse->cache($qubaids, $questionid, $subpartid, $this->responseclassid, $response);
+ $actualresponse->cache($qubaids, $questionid, $variantno, $subpartid, $this->responseclassid, $response);
}
}
class analysis_for_question {
/**
- * Takes either a two index array as a parameter with keys subpartid and classid and values possible_response.
- * Or takes an array of {@link responses_for_classes} objects.
+ * Constructor method.
*
- * @param $subparts[string]array[]\question_possible_response $array
+ * @param array[] Two index array, first index is unique string for each sub question part,
+ * the second string index is the 'class' that sub-question part can be classified into.
+ * Value in array is instance of {@link \question_possible_response}
+ * This is the return value from {@link \question_type::get_possible_responses()}
+ * see that method for fuller documentation.
*/
- public function __construct(array $subparts = null) {
- if (!is_null($subparts)) {
- foreach ($subparts as $subpartid => $classes) {
- $this->subparts[$subpartid] = new analysis_for_subpart($classes);
- }
+ public function __construct(array $possiblereponses = null) {
+ if ($possiblereponses !== null) {
+ $this->possibleresponses = $possiblereponses;
+ }
+ }
+
+ /**
+ * @var array[] See description above in constructor method.
+ */
+ protected $possibleresponses = array();
+
+ /**
+ * A multidimensional array whose first index is variant no and second index is subpart id, array contents are of type
+ * {@link analysis_for_subpart}.
+ *
+ * @var array[]
+ */
+ protected $subparts = array();
+
+ /**
+ * Initialise data structure for response analysis of one variant.
+ *
+ * @param int $variantno
+ */
+ protected function initialise_stats_for_variant($variantno) {
+ $this->subparts[$variantno] = array();
+ foreach ($this->possibleresponses as $subpartid => $classes) {
+ $this->subparts[$variantno][$subpartid] = new analysis_for_subpart($classes);
}
}
/**
- * @var analysis_for_subpart[]
+ * Variant nos found in this question's attempt data.
+ *
+ * @return int[]
*/
- protected $subparts;
+ public function get_variant_nos() {
+ return array_keys($this->subparts);
+ }
/**
* Unique ids for sub parts.
*
+ * @param int $variantno
* @return string[]
*/
- public function get_subpart_ids() {
- return array_keys($this->subparts);
+ public function get_subpart_ids($variantno) {
+ return array_keys($this->subparts[$variantno]);
}
/**
+ * Get the response counts etc. for variant $variantno, question sub part $subpartid.
+ *
+ * Or if there is no recorded analysis yet then initialise the data structure for that part of the analysis and return the
+ * initialised analysis objects.
+ *
+ * @param int $variantno
* @param string $subpartid id for sub part.
* @return analysis_for_subpart
*/
- public function get_subpart($subpartid) {
- return $this->subparts[$subpartid];
+ public function get_analysis_for_subpart($variantno, $subpartid) {
+ if (!isset($this->subparts[$variantno])) {
+ $this->initialise_stats_for_variant($variantno);
+ }
+ return $this->subparts[$variantno][$subpartid];
}
/**
* @return bool whether this question has (a subpart with) more than one response class.
*/
public function has_multiple_response_classes() {
- foreach ($this->subparts as $subpart) {
- if ($subpart->has_multiple_response_classes()) {
- return true;
+ foreach ($this->get_variant_nos() as $variantno) {
+ foreach ($this->get_subpart_ids($variantno) as $subpartid) {
+ if ($this->get_analysis_for_subpart($variantno, $subpartid)->has_multiple_response_classes()) {
+ return true;
+ }
}
}
return false;
* @return bool whether this analysis has more than one subpart.
*/
public function has_subparts() {
- return count($this->subparts) > 1;
+ foreach ($this->get_variant_nos() as $variantno) {
+ if (count($this->get_subpart_ids($variantno)) > 1) {
+ return true;
+ }
+ }
+ return false;
}
/**
* Takes an array of {@link \question_classified_response} and adds counts of the responses to the sub parts and classes.
*
- * @var \question_classified_response[] $responseparts keys are sub-part id.
+ * @param int $variantno
+ * @param \question_classified_response[] $responseparts keys are sub-part id.
*/
- public function count_response_parts($responseparts) {
+ public function count_response_parts($variantno, $responseparts) {
foreach ($responseparts as $subpartid => $responsepart) {
- $this->get_subpart($subpartid)->count_response($responsepart);
+ $this->get_analysis_for_subpart($variantno, $subpartid)->count_response($responsepart);
}
}
* @param int $questionid the question id
*/
public function cache($qubaids, $questionid) {
- foreach ($this->subparts as $subpartid => $subpart) {
- $subpart->cache($qubaids, $questionid, $subpartid);
+ foreach ($this->get_variant_nos() as $variantno) {
+ foreach ($this->get_subpart_ids($variantno) as $subpartid) {
+ $this->get_analysis_for_subpart($variantno, $subpartid)->cache($qubaids, $questionid, $variantno, $subpartid);
+ }
}
}
* the model response.
*/
public function has_actual_responses() {
- foreach ($this->subparts as $subpartid => $subpart) {
- if ($subpart->has_actual_responses()) {
- return true;
+ foreach ($this->get_variant_nos() as $variantno) {
+ foreach ($this->get_subpart_ids($variantno) as $subpartid) {
+ if ($this->get_analysis_for_subpart($variantno, $subpartid)->has_actual_responses()) {
+ return true;
+ }
}
}
return false;
/**
* @param \qubaid_condition $qubaids
* @param int $questionid the question id
+ * @param int $variantno
* @param string $subpartid
*/
- public function cache($qubaids, $questionid, $subpartid) {
+ public function cache($qubaids, $questionid, $variantno, $subpartid) {
foreach ($this->responseclasses as $responseclassid => $responseclass) {
- $responseclass->cache($qubaids, $questionid, $subpartid, $responseclassid);
+ $responseclass->cache($qubaids, $questionid, $variantno, $subpartid, $responseclassid);
}
}
defined('MOODLE_INTERNAL') || die();
-$version = 2014021300.00; // YYYYMMDD = weekly release date of this DEV branch.
+$version = 2014021300.02; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.