Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Unit tests for the parts of {@link question_engine_data_mapper} related to reporting. 19 * 20 * @package core_question 21 * @category test 22 * @copyright 2013 The Open University 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 global $CFG; 30 require_once (__DIR__ . '/../lib.php'); 31 require_once (__DIR__ . '/helpers.php'); 32 33 34 /** 35 * Unit tests for the parts of {@link question_engine_data_mapper} related to reporting. 36 * 37 * @copyright 2013 The Open University 38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 39 */ 40 class question_engine_data_mapper_reporting_testcase extends qbehaviour_walkthrough_test_base { 41 42 /** @var question_engine_data_mapper */ 43 protected $dm; 44 45 /** @var qtype_shortanswer_question */ 46 protected $sa; 47 48 /** @var qtype_essay_question */ 49 protected $essay; 50 51 /** @var array */ 52 protected $usageids = array(); 53 54 /** @var qubaid_condition */ 55 protected $bothusages; 56 57 /** @var array */ 58 protected $allslots = array(); 59 60 /** 61 * Test the various methods that load data for reporting. 62 * 63 * Since these methods need an expensive set-up, and then only do read-only 64 * operations on the data, we use a single method to do the set-up, which 65 * calls diffents methods to test each query. 66 */ 67 public function test_reporting_queries() { 68 // We create two usages, each with two questions, a short-answer marked 69 // out of 5, and and essay marked out of 10. 70 // 71 // In the first usage, the student answers the short-answer 72 // question correctly, and enters something in the essay. 73 // 74 // In the second useage, the student answers the short-answer question 75 // wrongly, and leaves the essay blank. 76 $this->resetAfterTest(); 77 $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); 78 $cat = $generator->create_question_category(); 79 $this->sa = $generator->create_question('shortanswer', null, 80 array('category' => $cat->id)); 81 $this->essay = $generator->create_question('essay', null, 82 array('category' => $cat->id)); 83 84 $this->usageids = array(); 85 86 // Create the first usage. 87 $q = question_bank::load_question($this->sa->id); 88 $this->start_attempt_at_question($q, 'interactive', 5); 89 $this->allslots[] = $this->slot; 90 $this->process_submission(array('answer' => 'cat')); 91 $this->process_submission(array('answer' => 'frog', '-submit' => 1)); 92 93 $q = question_bank::load_question($this->essay->id); 94 $this->start_attempt_at_question($q, 'interactive', 10); 95 $this->allslots[] = $this->slot; 96 $this->process_submission(array('answer' => '<p>The cat sat on the mat.</p>', 'answerformat' => FORMAT_HTML)); 97 98 $this->finish(); 99 $this->save_quba(); 100 $this->usageids[] = $this->quba->get_id(); 101 102 // Create the second usage. 103 $this->quba = question_engine::make_questions_usage_by_activity('unit_test', 104 context_system::instance()); 105 106 $q = question_bank::load_question($this->sa->id); 107 $this->start_attempt_at_question($q, 'interactive', 5); 108 $this->process_submission(array('answer' => 'fish')); 109 110 $q = question_bank::load_question($this->essay->id); 111 $this->start_attempt_at_question($q, 'interactive', 10); 112 113 $this->finish(); 114 $this->save_quba(); 115 $this->usageids[] = $this->quba->get_id(); 116 117 // Set up some things the tests will need. 118 $this->dm = new question_engine_data_mapper(); 119 $this->bothusages = new qubaid_list($this->usageids); 120 121 // Now test the various queries. 122 $this->dotest_load_questions_usages_latest_steps($this->allslots); 123 $this->dotest_load_questions_usages_latest_steps(null); 124 $this->dotest_load_questions_usages_question_state_summary($this->allslots); 125 $this->dotest_load_questions_usages_question_state_summary(null); 126 $this->dotest_load_questions_usages_where_question_in_state(); 127 $this->dotest_load_average_marks($this->allslots); 128 $this->dotest_load_average_marks(null); 129 $this->dotest_sum_usage_marks_subquery(); 130 $this->dotest_question_attempt_latest_state_view(); 131 } 132 133 /** 134 * This test is executed by {@link test_reporting_queries()}. 135 * 136 * @param array|null $slots list of slots to use in the call. 137 */ 138 protected function dotest_load_questions_usages_latest_steps($slots) { 139 $rawstates = $this->dm->load_questions_usages_latest_steps($this->bothusages, $slots, 140 'qa.id AS questionattemptid, qa.questionusageid, qa.slot, ' . 141 'qa.questionid, qa.maxmark, qas.sequencenumber, qas.state'); 142 143 $states = array(); 144 foreach ($rawstates as $state) { 145 $states[$state->questionusageid][$state->slot] = $state; 146 unset($state->questionattemptid); 147 unset($state->questionusageid); 148 unset($state->slot); 149 } 150 151 $state = $states[$this->usageids[0]][$this->allslots[0]]; 152 $this->assertEquals((object) array( 153 'questionid' => $this->sa->id, 154 'maxmark' => 5.0, 155 'sequencenumber' => 2, 156 'state' => (string) question_state::$gradedright, 157 ), $state); 158 159 $state = $states[$this->usageids[0]][$this->allslots[1]]; 160 $this->assertEquals((object) array( 161 'questionid' => $this->essay->id, 162 'maxmark' => 10.0, 163 'sequencenumber' => 2, 164 'state' => (string) question_state::$needsgrading, 165 ), $state); 166 167 $state = $states[$this->usageids[1]][$this->allslots[0]]; 168 $this->assertEquals((object) array( 169 'questionid' => $this->sa->id, 170 'maxmark' => 5.0, 171 'sequencenumber' => 2, 172 'state' => (string) question_state::$gradedwrong, 173 ), $state); 174 175 $state = $states[$this->usageids[1]][$this->allslots[1]]; 176 $this->assertEquals((object) array( 177 'questionid' => $this->essay->id, 178 'maxmark' => 10.0, 179 'sequencenumber' => 1, 180 'state' => (string) question_state::$gaveup, 181 ), $state); 182 } 183 184 /** 185 * This test is executed by {@link test_reporting_queries()}. 186 * 187 * @param array|null $slots list of slots to use in the call. 188 */ 189 protected function dotest_load_questions_usages_question_state_summary($slots) { 190 $summary = $this->dm->load_questions_usages_question_state_summary( 191 $this->bothusages, $slots); 192 193 $this->assertEquals($summary[$this->allslots[0] . ',' . $this->sa->id], 194 (object) array( 195 'slot' => $this->allslots[0], 196 'questionid' => $this->sa->id, 197 'name' => $this->sa->name, 198 'inprogress' => 0, 199 'needsgrading' => 0, 200 'autograded' => 2, 201 'manuallygraded' => 0, 202 'all' => 2, 203 )); 204 $this->assertEquals($summary[$this->allslots[1] . ',' . $this->essay->id], 205 (object) array( 206 'slot' => $this->allslots[1], 207 'questionid' => $this->essay->id, 208 'name' => $this->essay->name, 209 'inprogress' => 0, 210 'needsgrading' => 1, 211 'autograded' => 1, 212 'manuallygraded' => 0, 213 'all' => 2, 214 )); 215 } 216 217 /** 218 * This test is executed by {@link test_reporting_queries()}. 219 */ 220 protected function dotest_load_questions_usages_where_question_in_state() { 221 $this->assertEquals( 222 array(array($this->usageids[0], $this->usageids[1]), 2), 223 $this->dm->load_questions_usages_where_question_in_state($this->bothusages, 224 'all', $this->allslots[1], null, 'questionusageid')); 225 226 $this->assertEquals( 227 array(array($this->usageids[0], $this->usageids[1]), 2), 228 $this->dm->load_questions_usages_where_question_in_state($this->bothusages, 229 'autograded', $this->allslots[0], null, 'questionusageid')); 230 231 $this->assertEquals( 232 array(array($this->usageids[0]), 1), 233 $this->dm->load_questions_usages_where_question_in_state($this->bothusages, 234 'needsgrading', $this->allslots[1], null, 'questionusageid')); 235 } 236 237 /** 238 * This test is executed by {@link test_reporting_queries()}. 239 * 240 * @param array|null $slots list of slots to use in the call. 241 */ 242 protected function dotest_load_average_marks($slots) { 243 $averages = $this->dm->load_average_marks($this->bothusages, $slots); 244 245 $this->assertEquals(array( 246 $this->allslots[0] => (object) array( 247 'slot' => $this->allslots[0], 248 'averagefraction' => 0.5, 249 'numaveraged' => 2, 250 ), 251 $this->allslots[1] => (object) array( 252 'slot' => $this->allslots[1], 253 'averagefraction' => 0, 254 'numaveraged' => 1, 255 ), 256 ), $averages); 257 } 258 259 /** 260 * This test is executed by {@link test_reporting_queries()}. 261 */ 262 protected function dotest_sum_usage_marks_subquery() { 263 global $DB; 264 265 $totals = $DB->get_records_sql_menu("SELECT qu.id, ({$this->dm->sum_usage_marks_subquery('qu.id')}) AS totalmark 266 FROM {question_usages} qu 267 WHERE qu.id IN ({$this->usageids[0]}, {$this->usageids[1]})"); 268 269 $this->assertNull($totals[$this->usageids[0]]); // Since a question requires grading. 270 271 $this->assertNotNull($totals[$this->usageids[1]]); // Grrr! PHP null == 0 makes this hard. 272 $this->assertEquals(0, $totals[$this->usageids[1]]); 273 } 274 275 /** 276 * This test is executed by {@link test_reporting_queries()}. 277 */ 278 protected function dotest_question_attempt_latest_state_view() { 279 global $DB; 280 281 list($inlineview, $viewparams) = $this->dm->question_attempt_latest_state_view( 282 'lateststate', $this->bothusages); 283 284 $rawstates = $DB->get_records_sql(" 285 SELECT lateststate.questionattemptid, 286 qu.id AS questionusageid, 287 lateststate.slot, 288 lateststate.questionid, 289 lateststate.maxmark, 290 lateststate.sequencenumber, 291 lateststate.state 292 FROM {question_usages} qu 293 LEFT JOIN $inlineview ON lateststate.questionusageid = qu.id 294 WHERE qu.id IN ({$this->usageids[0]}, {$this->usageids[1]})", $viewparams); 295 296 $states = array(); 297 foreach ($rawstates as $state) { 298 $states[$state->questionusageid][$state->slot] = $state; 299 unset($state->questionattemptid); 300 unset($state->questionusageid); 301 unset($state->slot); 302 } 303 304 $state = $states[$this->usageids[0]][$this->allslots[0]]; 305 $this->assertEquals((object) array( 306 'questionid' => $this->sa->id, 307 'maxmark' => 5.0, 308 'sequencenumber' => 2, 309 'state' => (string) question_state::$gradedright, 310 ), $state); 311 312 $state = $states[$this->usageids[0]][$this->allslots[1]]; 313 $this->assertEquals((object) array( 314 'questionid' => $this->essay->id, 315 'maxmark' => 10.0, 316 'sequencenumber' => 2, 317 'state' => (string) question_state::$needsgrading, 318 ), $state); 319 320 $state = $states[$this->usageids[1]][$this->allslots[0]]; 321 $this->assertEquals((object) array( 322 'questionid' => $this->sa->id, 323 'maxmark' => 5.0, 324 'sequencenumber' => 2, 325 'state' => (string) question_state::$gradedwrong, 326 ), $state); 327 328 $state = $states[$this->usageids[1]][$this->allslots[1]]; 329 $this->assertEquals((object) array( 330 'questionid' => $this->essay->id, 331 'maxmark' => 10.0, 332 'sequencenumber' => 1, 333 'state' => (string) question_state::$gaveup, 334 ), $state); 335 } 336 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body