$filename = quiz_report_download_filename($report, $courseshortname, $quiz->name);
$this->table->is_downloading($download, $filename,
get_string('quizstructureanalysis', 'quiz_statistics'));
-
- // Load the questions.
- $questions = quiz_report_get_significant_questions($quiz);
- $questionids = array();
- foreach ($questions as $question) {
- $questionids[] = $question->id;
- }
- $fullquestions = question_load_questions($questionids);
- foreach ($questions as $qno => $question) {
- $q = $fullquestions[$question->id];
- $q->maxmark = $question->maxmark;
- $q->slot = $qno;
- $q->number = $question->number;
- $questions[$qno] = $q;
- }
+ $questions = $this->load_and_initialise_questions_for_calculations($quiz);
// Get the data to be displayed.
list($quizstats, $questions, $subquestions, $s) =
/**
* Compute the quiz statistics.
*
- * @param object $quizid the quiz id.
+ * @param int $quizid the quiz id.
* @param int $currentgroup the current group. 0 for none.
* @param bool $nostudentsingroup true if there a no students.
* @param bool $useallattempts use all attempts, or just first attempts.
return get_string('firstattempts', 'quiz_statistics');
}
}
+
+ /**
+ * @param object $quiz the quiz.
+ * @return array of questions for this quiz.
+ */
+ public function load_and_initialise_questions_for_calculations($quiz) {
+ // Load the questions.
+ $questions = quiz_report_get_significant_questions($quiz);
+ $questionids = array();
+ foreach ($questions as $question) {
+ $questionids[] = $question->id;
+ }
+ $fullquestions = question_load_questions($questionids);
+ foreach ($questions as $qno => $question) {
+ $q = $fullquestions[$question->id];
+ $q->maxmark = $question->maxmark;
+ $q->slot = $qno;
+ $q->number = $question->number;
+ $questions[$qno] = $q;
+ }
+ return $questions;
+ }
}
function quiz_statistics_attempts_sql($quizid, $currentgroup, $groupstudents,
--- /dev/null
+slot,facility,sd,effectiveweight,covariance,markvariance,othermarkvariance,discriminationindex,covariancemax,discriminativeefficiency
+1,0.704,0.4513682901,21.2922742344,-0.022555556,0.2037333333,0.5002777794,-7.0650767526,0.2385555565,-9.4550536967
+2,0.48,0.5099019514,18.8979800309,-0.1172777785,0.26,0.6334555578,-28.8982125772,0.318833334,-36.7834118938
+3,0.973333332,0.13333334,4.443012573,-0.0098888894,0.0177777796,0.6609,-9.1230674268,0.045666669,-21.6545012165
+4,0.68,0.4760952286,18.9347251357,-0.0833888893,0.2266666667,0.5990111128,-22.6306444113,0.2652222232,-31.4411395613
+5,0.52,0.3055050463,11.1450138688,-0.0436944444,0.0933333333,0.6529555563,-17.6997047674,0.2063055556,-21.1794802584
+6,0.64,0.4898979486,9.8081339177,-0.2015555547,0.24,0.8220111101,-45.3785178421,0.3539999995,-56.9365974439
+7,0.62,0.331662479,15.4788602394,-0.0142499998,0.11,0.5774000005,-5.6543166602,0.2190833335,-6.5043742058
--- /dev/null
+slot,type,which,cat,mark
+1,random,,rand,1
+,shortanswer,,rand,1
+,numerical,,rand,1
+2,calculatedsimple,sumwithvariants,maincat,1
+3,match,,maincat,1
+4,truefalse,,maincat,1
+5,multichoice,two_of_four,maincat,1
+6,multichoice,one_of_four,maincat,1
+7,multianswer,,maincat,1
--- /dev/null
+quizattempt,slots.1.mark,slots.2.mark,slots.3.mark,slots.4.mark,slots.5.mark,slots.6.mark,slots.7.mark,summarks
+1,1,1,1,1,1,0,1,6
+2,1,1,1,1,0.5,1,0,5.5
+3,1,1,1,1,0.5,0,1,5.5
+4,0,0,1,1,1,0,0,3
+5,1,0,1,0,0,1,0.5,3.5
+6,1,1,1,1,0.5,0,0.5,5
+7,0,1,1,1,0.5,1,0.5,5
+8,1,1,1,0,0.5,1,0.5,5
+9,1,0,1,1,1,1,1,6
+10,0,1,1,0,0.5,1,0.5,4
+11,0.8,0,1,1,0.5,1,0.5,4.8
+12,1,1,1,0,0.5,1,1,5.5
+13,1,0,1,1,0.5,1,0.5,5
+14,1,0,1,1,1,0,0.5,4.5
+15,0,0,1,1,0,1,1,4
+16,1,0,1,0,0.5,1,1,4.5
+17,0.8,0,1,1,0.5,0,1,4.3
+18,1,0,1,1,0,1,0.5,4.5
+19,1,0,1,1,0.5,0,0.5,4
+20,0,1,1,0,0.5,1,0,3.5
+21,1,1,0.3333333,1,0.5,0,0.5,4.33333
+22,1,1,1,0,0.5,0,0.5,4
+23,1,1,1,1,0,1,0.5,5.5
+24,0,0,1,0,0.5,1,1,3.5
+25,0,0,1,1,1,1,1,5
--- /dev/null
+quizattempt,firstname,lastname,randqs.1,responses.1.answer,variants.2,responses.2.answer,responses.3.0,responses.3.1,responses.3.2,responses.4.answer,responses.5.0,responses.5.1,responses.5.2,responses.5.3,responses.6.answer,responses.7.1.answer,responses.7.2.answer
+1,John,Jones,numerical,3.14,1,9.9,amphibian,mammal,amphibian,1,1,0,1,0,1,Owl,2
+2,John,Smith,shortanswer,frog,1,9.9,amphibian,mammal,amphibian,1,0,1,1,0,0,Dog,0
+3,John,Vicars,numerical,3.14,6,9.4,amphibian,mammal,amphibian,1,0,0,1,1,1,Owl,2
+4,John,Pacino,shortanswer,butterfly,6,-0.1,amphibian,mammal,amphibian,1,1,0,1,0,3,Dog,0
+5,John,Deniro,numerical,3.14,4,0,amphibian,mammal,amphibian,0,0,1,0,1,0,Dog,2
+6,John,Banks,numerical,3.14,1,9.9,amphibian,mammal,amphibian,1,1,0,0,1,1,Owl,1
+7,John,Asimov,numerical,3.142,7,9.1,amphibian,mammal,amphibian,1,0,0,1,1,0,Owl,1
+8,John,Chomsky,numerical,3.14,4,19.4,amphibian,mammal,amphibian,0,0,0,1,1,0,Owl,1
+9,John,Yamaguchi,shortanswer,frog,1,-0.7,amphibian,mammal,amphibian,1,1,0,1,0,0,Owl,2
+10,John,Robbins,numerical,3.1,5,14.2,amphibian,mammal,amphibian,0,0,1,1,0,0,Owl,0
+11,Joe,Jones,shortanswer,toad,6,-0.2,amphibian,mammal,amphibian,1,1,1,0,0,0,Owl,0
+12,Joe,Smith,shortanswer,frog,8,5.7,amphibian,mammal,amphibian,0,0,0,1,1,0,Owl,2
+13,Joe,Vicars,numerical,3.14,8,-0.2,amphibian,mammal,amphibian,1,1,0,0,1,0,wfz9p,2
+14,Joe,Pacino,shortanswer,frog,1,-0.2,amphibian,mammal,amphibian,1,1,0,1,0,1,Owl,0
+15,Joe,Deniro,numerical,3.1,7,-0.9,amphibian,mammal,amphibian,1,0,1,0,1,0,Owl,2
+16,Joe,Banks,shortanswer,frog,10,-0.7,amphibian,mammal,amphibian,0,1,0,0,1,0,Owl,2
+17,Joe,Asimov,shortanswer,toad,4,-0.4,amphibian,mammal,amphibian,1,0,1,1,0,2,Owl,2
+18,Joe,Chomsky,shortanswer,frog,6,-1,amphibian,mammal,amphibian,1,0,1,0,1,0,Pussy-cat,2
+19,Joe,Yamaguchi,numerical,3.14,8,-0.5,amphibian,mammal,amphibian,1,1,1,0,0,1,Owl,0
+20,Joe,Robbins,shortanswer,butterfly,4,19.4,amphibian,mammal,amphibian,0,0,1,1,0,0,"Wiggly worm",0
+21,Roberto,Jones,shortanswer,frog,8,5.7,amphibian,amphibian,mammal,1,0,1,1,0,2,RjUpn,2
+22,Roberto,Smith,shortanswer,frog,5,14.2,amphibian,mammal,amphibian,0,1,0,0,1,1,Dog,2
+23,Roberto,Vicars,shortanswer,frog,5,14.2,amphibian,mammal,amphibian,1,0,1,0,1,0,"Wiggly worm",2
+24,Roberto,Pacino,numerical,3.1,1,-1,amphibian,mammal,amphibian,0,1,1,0,0,0,Owl,2
+25,Roberto,Deniro,numerical,3.1,8,-0.1,amphibian,mammal,amphibian,1,1,0,1,0,0,Owl,2
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Quiz attempt walk through using data from csv file.
+ *
+ * @package mod_quiz
+ * @category phpunit
+ * @copyright 2013 The Open University
+ * @author Jamie Pratt <me@jamiep.org>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->dirroot . '/mod/quiz/tests/attempt_walkthrough_from_csv_test.php');
+require_once($CFG->dirroot . '/mod/quiz/report/default.php');
+require_once($CFG->dirroot . '/mod/quiz/report/statistics/report.php');
+require_once($CFG->dirroot . '/mod/quiz/report/reportlib.php');
+/**
+ * Test helper subclass of quiz_statistics_report
+ *
+ * @copyright 2013 The Open University
+ * @author Jamie Pratt <me@jamiep.org>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class testable_quiz_statistics_report extends quiz_statistics_report {
+
+ public function get_stats($quiz, $useallattempts = true,
+ $currentgroup = 0, $groupstudents = array(), $nostudentsingroup = false) {
+ $this->clear_cached_data($quiz->id, $currentgroup, $useallattempts);
+ $questions = $this->load_and_initialise_questions_for_calculations($quiz);
+ return $this->get_quiz_and_questions_stats($quiz, $currentgroup, $nostudentsingroup,
+ $useallattempts, $groupstudents, $questions);
+ }
+}
+
+/**
+ * Quiz attempt walk through using data from csv file.
+ *
+ * @package mod_quiz
+ * @category phpunit
+ * @copyright 2013 The Open University
+ * @author Jamie Pratt <me@jamiep.org>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class quiz_report_statistics_from_steps extends mod_quiz_attempt_walkthrough_from_csv_testcase {
+
+ /**
+ * @var quiz_statistics_report object to do stats calculations.
+ */
+ protected $report;
+
+ protected function get_full_path_of_csv_file($setname, $test) {
+ // Overridden here so that __DIR__ points to the path of this file.
+ return __DIR__."/fixtures/{$setname}{$test}.csv";
+ }
+
+ protected $files = array('questions', 'steps', 'results', 'qstats');
+
+ /**
+ * 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($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($csvdata['questions']);
+
+ $attemptids = $this->walkthrough_attempts($csvdata['steps']);
+
+ $this->check_attempts_results($csvdata['results'], $attemptids);
+
+ $this->report = new testable_quiz_statistics_report();
+ list($quizstats, $questions, $subquestions, $s) = $this->report->get_stats($this->quiz);
+
+ // 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.2230935542,
+ 'errorratio' => 136.8294900795,
+ 'standarderror' => 1.1106813066
+ );
+
+ foreach ($quizstatsexpected as $statname => $statvalue) {
+ $this->assertEquals($statvalue, $quizstats->$statname, $quizstats->$statname, abs($statvalue) * 1e-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 = 1e-5;
+ break;
+ default :
+ $precision = 1e-6;
+ }
+ $slot = $slotqstats['slot'];
+ $delta = abs($slotqstat) * $precision;
+ $actual = $questions[$slot]->_stats->{$statname};
+ $this->assertEquals(floatval($slotqstat), $actual, "$statname for slot $slot", $delta);
+ }
+ }
+ }
+ }
+}
*/
protected $tests = array('00');
+ protected $files = array('questions', 'steps', 'results');
+
/**
* @var stdClass the quiz record we create.
*/
}
}
- public function get_data_for_walkthrough() {
- $dataset = array();
- foreach ($this->tests as $test) {
- $qs = $this->load_csv_data_file('questions', $test);
- $steps = $this->load_csv_data_file('steps', $test);
- $results = $this->load_csv_data_file('results', $test);
- $dataset[] = array($qs, $steps, $results);
- }
- return $dataset;
- }
-
/**
* Get full path of CSV file.
*
return $parts;
}
+ /**
+ * Data provider method for test_walkthrough_from_csv. Called by PHPUnit.
+ *
+ * @return array One array element for each run of the test. Each element contains an array with the one param for
+ * test_walkthrough_from_csv.
+ */
+ public function get_data_for_walkthrough() {
+ $datasets = array();
+ foreach ($this->tests as $test) {
+ $dataset = array();
+ foreach ($this->files as $file) {
+ $dataset[$file] = $this->load_csv_data_file($file, $test);
+ }
+ $datasets[] = array($dataset);
+ }
+ return $datasets;
+ }
+
/**
* Create a quiz add questions to it, walk through quiz attempts and then check results.
*
- * @param PHPUnit_Extensions_Database_DataSet_ITable $qs questions to add to quiz, read from csv file "questionsXX.csv".
- * @param PHPUnit_Extensions_Database_DataSet_ITable $steps steps to simulate, read from csv file "stepsXX.csv".
- * @param PHPUnit_Extensions_Database_DataSet_ITable $results results expected, read from csv file "resultsXX.csv".
+ * @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($qs, $steps, $results) {
- global $DB;
+ public function test_walkthrough_from_csv($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($qs);
+ $this->create_quiz($csvdata['questions']);
+
+ $attemptids = $this->walkthrough_attempts($csvdata['steps']);
+ $this->check_attempts_results($csvdata['results'], $attemptids);
+ }
+
+ /**
+ * @param $steps PHPUnit_Extensions_Database_DataSet_ITable the step data from the csv file.
+ * @return array attempt no as in csv file => the id of the quiz_attempt as stored in the db.
+ */
+ protected function walkthrough_attempts($steps) {
+ global $DB;
$attemptids = array();
for ($rowno = 0; $rowno < $steps->getRowCount(); $rowno++) {
$step = $this->explode_dot_separated_keys_to_make_subindexs($steps->getRow($rowno));
// Find existing user or make a new user to do the quiz.
$username = array('firstname' => $step['firstname'],
- 'lastname' => $step['lastname']);
+ 'lastname' => $step['lastname']);
if (!$user = $DB->get_record('user', $username)) {
$user = $this->getDataGenerator()->create_user($username);
$attemptobj = quiz_attempt::create($attempt->id);
$attemptobj->process_finish($timenow, false);
-
}
+ return $attemptids;
+ }
+ /**
+ * @param $results PHPUnit_Extensions_Database_DataSet_ITable the results data from the csv file.
+ * @param $attemptids array attempt no as in csv file => the id of the quiz_attempt as stored in the db.
+ */
+ protected function check_attempts_results($results, $attemptids) {
for ($rowno = 0; $rowno < $results->getRowCount(); $rowno++) {
$result = $this->explode_dot_separated_keys_to_make_subindexs($results->getRow($rowno));
// Re-load quiz attempt data.
$attemptobj = quiz_attempt::create($attemptids[$result['quizattempt']]);
$this->check_attempt_results($result, $attemptobj);
-
}
}
break;
default :
throw new coding_exception('Unknown slots sub field column in csv file '
- .s($slotfieldname));
+ .s($slotfieldname));
}
}
}