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