/**
  * Create a quiz add questions to it, walk through quiz attempts and then check results.
  *
  * @param PHPUnit_Extensions_Database_DataSet_ITable[] of data read from csv file "questionsXX.csv",
  *                                                                                  "stepsXX.csv" and "resultsXX.csv".
  * @dataProvider get_data_for_walkthrough
  */
 public function test_walkthrough_from_csv($quizsettings, $csvdata)
 {
     // CSV data files for these tests were generated using :
     // https://github.com/jamiepratt/moodle-quiz-tools/tree/master/responsegenerator
     $this->resetAfterTest(true);
     question_bank::get_qtype('random')->clear_caches_before_testing();
     $this->create_quiz($quizsettings, $csvdata['questions']);
     $attemptids = $this->walkthrough_attempts($csvdata['steps']);
     $this->check_attempts_results($csvdata['results'], $attemptids);
     $this->report = new quiz_statistics_report();
     $whichattempts = QUIZ_GRADEAVERAGE;
     $groupstudents = array();
     $questions = $this->report->load_and_initialise_questions_for_calculations($this->quiz);
     list($quizstats, $questionstats, $subquestionstats) = $this->report->get_quiz_and_questions_stats($this->quiz, $whichattempts, $groupstudents, $questions);
     $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
     $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));
     $qcalc = new \core_question\statistics\questions\calculator($questions);
     $this->assertTimeCurrent($qcalc->get_last_calculated_time($qubaids));
     foreach ($questions as $question) {
         if (!question_bank::get_qtype($question->qtype, false)->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
     // available in open document or excel format here :
     // https://github.com/jamiepratt/moodle-quiz-tools/tree/master/statsspreadsheet
     $quizstatsexpected = array('median' => 4.5, 'firstattemptsavg' => 4.617333332, 'allattemptsavg' => 4.617333332, 'firstattemptscount' => 25, 'allattemptscount' => 25, 'standarddeviation' => 0.8117265554, 'skewness' => -0.092502502, 'kurtosis' => -0.7073968557, 'cic' => -87.22309355420001, 'errorratio' => 136.8294900795, 'standarderror' => 1.1106813066);
     foreach ($quizstatsexpected as $statname => $statvalue) {
         $this->assertEquals($statvalue, $quizstats->{$statname}, $quizstats->{$statname}, abs($statvalue) * 1.5E-5);
     }
     for ($rowno = 0; $rowno < $csvdata['qstats']->getRowCount(); $rowno++) {
         $slotqstats = $csvdata['qstats']->getRow($rowno);
         foreach ($slotqstats as $statname => $slotqstat) {
             if ($statname !== 'slot') {
                 switch ($statname) {
                     case 'covariance':
                     case 'discriminationindex':
                     case 'discriminativeefficiency':
                     case 'effectiveweight':
                         $precision = 1.0E-5;
                         break;
                     default:
                         $precision = 1.0E-6;
                 }
                 $slot = $slotqstats['slot'];
                 $delta = abs($slotqstat) * $precision;
                 $actual = $questionstats[$slot]->{$statname};
                 $this->assertEquals(floatval($slotqstat), $actual, "{$statname} for slot {$slot}", $delta);
             }
         }
     }
 }
Exemple #2
0
 /**
  * Analyse responses for an array of questions or sub questions.
  *
  * @param object[] $questions  as returned by self::load_and_initialise_questions_for_calculations.
  * @param qubaid_condition $qubaids the question usages whose responses to analyse.
  * @param string $whichtries which tries to analyse \question_attempt::FIRST_TRY, LAST_TRY or ALL_TRIES.
  * @param null|\core\progress\base $progress Used to indicate progress of task.
  * @param int[] $done array keys are ids of questions that have been analysed before calling method.
  * @return array array keys are ids of questions that were analysed after this method call.
  */
 protected function analyse_responses_for_questions($questions, $qubaids, $whichtries, $progress = null, $done = array())
 {
     $countquestions = count($questions);
     if (!$countquestions) {
         return array();
     }
     if ($progress === null) {
         $progress = new \core\progress\none();
     }
     $progress->start_progress('', $countquestions, $countquestions);
     foreach ($questions as $question) {
         $progress->increment_progress();
         if (question_bank::get_qtype($question->qtype, false)->can_analyse_responses() && !isset($done[$question->id])) {
             $responesstats = new \core_question\statistics\responses\analyser($question, $whichtries);
             if ($responesstats->get_last_analysed_time($qubaids, $whichtries) === false) {
                 $responesstats->calculate($qubaids, $whichtries);
             }
         }
         $done[$question->id] = 1;
     }
     $progress->end_progress();
     return $done;
 }
Exemple #3
0
 protected function analyse_responses_for_all_questions_and_subquestions($qubaids, $questions, $subquestionstats)
 {
     $done = array();
     foreach ($questions as $question) {
         if (!question_bank::get_qtype($question->qtype, false)->can_analyse_responses()) {
             continue;
         }
         $done[$question->id] = 1;
         $responesstats = new \core_question\statistics\responses\analyser($question);
         $responesstats->calculate($qubaids);
     }
     foreach ($subquestionstats as $subquestionstat) {
         if (!question_bank::get_qtype($subquestionstat->question->qtype, false)->can_analyse_responses() || isset($done[$subquestionstat->question->id])) {
             continue;
         }
         $done[$subquestionstat->question->id] = 1;
         $responesstats = new \core_question\statistics\responses\analyser($subquestionstat->question);
         $responesstats->calculate($qubaids);
     }
 }
 /**
  * @param $questions
  * @param $questionstats
  * @param $whichtries
  * @param $qubaids
  */
 protected function check_variants_count_for_quiz_00($questions, $questionstats, $whichtries, $qubaids)
 {
     $expectedvariantcounts = array(2 => array(1 => 6, 4 => 4, 5 => 3, 6 => 4, 7 => 2, 8 => 5, 10 => 1));
     foreach ($questions as $slot => $question) {
         if (!question_bank::get_qtype($question->qtype, false)->can_analyse_responses()) {
             continue;
         }
         $responesstats = new \core_question\statistics\responses\analyser($question);
         $this->assertTimeCurrent($responesstats->get_last_analysed_time($qubaids, $whichtries));
         $analysis = $responesstats->load_cached($qubaids, $whichtries);
         $variantsnos = $analysis->get_variant_nos();
         if (isset($expectedvariantcounts[$slot])) {
             // Compare contents, ignore ordering of array, using canonicalize parameter of assertEquals.
             $this->assertEquals(array_keys($expectedvariantcounts[$slot]), $variantsnos, '', 0, 10, true);
         } else {
             $this->assertEquals(array(1), $variantsnos);
         }
         $totalspervariantno = array();
         foreach ($variantsnos as $variantno) {
             $subpartids = $analysis->get_subpart_ids($variantno);
             foreach ($subpartids as $subpartid) {
                 if (!isset($totalspervariantno[$subpartid])) {
                     $totalspervariantno[$subpartid] = array();
                 }
                 $totalspervariantno[$subpartid][$variantno] = 0;
                 $subpartanalysis = $analysis->get_analysis_for_subpart($variantno, $subpartid);
                 $classids = $subpartanalysis->get_response_class_ids();
                 foreach ($classids as $classid) {
                     $classanalysis = $subpartanalysis->get_response_class($classid);
                     $actualresponsecounts = $classanalysis->data_for_question_response_table('', '');
                     foreach ($actualresponsecounts as $actualresponsecount) {
                         $totalspervariantno[$subpartid][$variantno] += $actualresponsecount->totalcount;
                     }
                 }
             }
         }
         // Count all counted responses for each part of question and confirm that counted responses, for most question types
         // are the number of attempts at the question for each question part.
         if ($slot != 5) {
             // Slot 5 holds a multi-choice multiple question.
             // Multi-choice multiple is slightly strange. Actual answer counts given for each sub part do not add up to the
             // total attempt count.
             // This is because each option is counted as a sub part and each option can be off or on in each attempt. Off is
             // not counted in response analysis for this question type.
             foreach ($totalspervariantno as $totalpervariantno) {
                 if (isset($expectedvariantcounts[$slot])) {
                     // If we know how many attempts there are at each variant we can check
                     // that we have counted the correct amount of responses for each variant.
                     $this->assertEquals($expectedvariantcounts[$slot], $totalpervariantno, "Totals responses do not add up in response analysis for slot {$slot}.", 0, 10, true);
                 } else {
                     $this->assertEquals(25, array_sum($totalpervariantno), "Totals responses do not add up in response analysis for slot {$slot}.");
                 }
             }
         }
     }
     foreach ($expectedvariantcounts as $slot => $expectedvariantcount) {
         foreach ($expectedvariantcount as $variantno => $s) {
             $this->assertEquals($s, $questionstats->for_slot($slot, $variantno)->s);
         }
     }
 }