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 core indicators. 19 * 20 * @package core 21 * @category analytics 22 * @copyright 2017 David MonllaĆ³ {@link http://www.davidmonllao.com} 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 require_once (__DIR__ . '/../../analytics/tests/fixtures/test_target_shortname.php'); 29 require_once (__DIR__ . '/../../admin/tool/log/store/standard/tests/fixtures/event.php'); 30 require_once (__DIR__ . '/../../lib/enrollib.php'); 31 32 /** 33 * Unit tests for core indicators. 34 * 35 * @package core 36 * @category analytics 37 * @copyright 2017 David MonllaĆ³ {@link http://www.davidmonllao.com} 38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 39 */ 40 class core_analytics_indicators_testcase extends advanced_testcase { 41 42 /** 43 * Test all core indicators. 44 * 45 * Single method as it is significantly faster (from 13s to 5s) than having separate 46 * methods because of preventResetByRollback. 47 * 48 * @return void 49 */ 50 public function test_core_indicators() { 51 global $DB; 52 53 $this->preventResetByRollback(); 54 $this->resetAfterTest(true); 55 $this->setAdminuser(); 56 57 set_config('enabled_stores', 'logstore_standard', 'tool_log'); 58 set_config('buffersize', 0, 'logstore_standard'); 59 60 $user1 = $this->getDataGenerator()->create_user(); 61 $user2 = $this->getDataGenerator()->create_user(); 62 63 // Test any access after end. 64 $params = array( 65 'startdate' => mktime(0, 0, 0, 10, 24, 2015), 66 'enddate' => mktime(0, 0, 0, 10, 24, 2016) 67 ); 68 $course = $this->getDataGenerator()->create_course($params); 69 $coursecontext = \context_course::instance($course->id); 70 $this->getDataGenerator()->enrol_user($user1->id, $course->id); 71 72 $indicator = new \core\analytics\indicator\any_access_after_end(); 73 74 $sampleids = array($user1->id => $user1->id, $user2->id => $user2->id); 75 $data = array($user1->id => array( 76 'context' => $coursecontext, 77 'course' => $course, 78 'user' => $user1 79 )); 80 $data[$user2->id] = $data[$user1->id]; 81 $data[$user2->id]['user'] = $user2; 82 $indicator->add_sample_data($data); 83 84 list($values, $unused) = $indicator->calculate($sampleids, 'notrelevanthere'); 85 $this->assertEquals($indicator::get_min_value(), $values[$user1->id][0]); 86 $this->assertEquals($indicator::get_min_value(), $values[$user2->id][0]); 87 88 \logstore_standard\event\unittest_executed::create( 89 array('context' => $coursecontext, 'userid' => $user1->id))->trigger(); 90 list($values, $unused) = $indicator->calculate($sampleids, 'notrelevanthere'); 91 $this->assertEquals($indicator::get_max_value(), $values[$user1->id][0]); 92 $this->assertEquals($indicator::get_min_value(), $values[$user2->id][0]); 93 94 // Test any access before start. 95 $params = array( 96 'startdate' => 9999999998, 97 'enddate' => 9999999999 98 ); 99 // Resetting $course var. 100 $course = $this->getDataGenerator()->create_course($params); 101 $coursecontext = \context_course::instance($course->id); 102 $this->getDataGenerator()->enrol_user($user1->id, $course->id); 103 104 $indicator = new \core\analytics\indicator\any_access_before_start(); 105 106 $sampleids = array($user1->id => $user1->id, $user2->id => $user2->id); 107 $data = array($user1->id => array( 108 'context' => $coursecontext, 109 'course' => $course, 110 'user' => $user1 111 )); 112 $data[$user2->id] = $data[$user1->id]; 113 $data[$user2->id]['user'] = $user2; 114 $indicator->add_sample_data($data); 115 116 list($values, $unused) = $indicator->calculate($sampleids, 'notrelevanthere'); 117 $this->assertEquals($indicator::get_min_value(), $values[$user1->id][0]); 118 $this->assertEquals($indicator::get_min_value(), $values[$user2->id][0]); 119 120 \logstore_standard\event\unittest_executed::create( 121 array('context' => $coursecontext, 'userid' => $user1->id))->trigger(); 122 list($values, $unused) = $indicator->calculate($sampleids, 'notrelevanthere'); 123 $this->assertEquals($indicator::get_max_value(), $values[$user1->id][0]); 124 $this->assertEquals($indicator::get_min_value(), $values[$user2->id][0]); 125 126 // Test any course access. 127 $course = $this->getDataGenerator()->create_course($params); 128 $coursecontext = \context_course::instance($course->id); 129 $this->getDataGenerator()->enrol_user($user1->id, $course->id); 130 131 $indicator = new \core\analytics\indicator\any_course_access(); 132 133 $sampleids = array($user1->id => $user1->id); 134 $data = array($user1->id => array( 135 'course' => $course, 136 'user' => $user1 137 )); 138 $indicator->add_sample_data($data); 139 $analysable = new \core_analytics\course($course); 140 141 // Min value if no user_lastaccess records. 142 $indicator->fill_per_analysable_caches($analysable); 143 list($values, $unused) = $indicator->calculate($sampleids, 'notrelevanthere'); 144 $this->assertEquals($indicator::get_min_value(), $values[$user1->id][0]); 145 list($values, $unused) = $indicator->calculate($sampleids, 'notrelevanthere', time() - 10, time() + 10); 146 $this->assertEquals($indicator::get_min_value(), $values[$user1->id][0]); 147 148 // Any access is enough if no time restrictions. 149 $DB->insert_record('user_lastaccess', array('userid' => $user1->id, 150 'courseid' => $course->id, 'timeaccess' => time() - 1)); 151 $indicator->fill_per_analysable_caches($analysable); 152 list($values, $unused) = $indicator->calculate($sampleids, 'notrelevanthere'); 153 $this->assertEquals($indicator::get_max_value(), $values[$user1->id][0]); 154 155 // Min value if the existing records are old. 156 $indicator->fill_per_analysable_caches($analysable); 157 list($values, $unused) = $indicator->calculate($sampleids, 'notrelevanthere', time(), time() + 10); 158 $this->assertEquals($indicator::get_min_value(), $values[$user1->id][0]); 159 list($values, $unused) = $indicator->calculate($sampleids, 'notrelevanthere', time()); 160 $this->assertEquals($indicator::get_min_value(), $values[$user1->id][0]); 161 162 // Max value if the existing records are prior to end. 163 list($values, $unused) = $indicator->calculate($sampleids, 'notrelevanthere', time() - 10, time()); 164 $this->assertEquals($indicator::get_max_value(), $values[$user1->id][0]); 165 list($values, $unused) = $indicator->calculate($sampleids, 'notrelevanthere', false, time()); 166 $this->assertEquals($indicator::get_max_value(), $values[$user1->id][0]); 167 list($values, $unused) = $indicator->calculate($sampleids, 'notrelevanthere', false, time()); 168 $this->assertEquals($indicator::get_max_value(), $values[$user1->id][0]); 169 170 // Max value if no end time and existing user_lastaccess record. 171 list($values, $unused) = $indicator->calculate($sampleids, 'notrelevanthere', time() - 10); 172 $this->assertEquals($indicator::get_max_value(), $values[$user1->id][0]); 173 174 // Rely on logs if the last time access is after the end time. 175 list($values, $unused) = $indicator->calculate($sampleids, 'notrelevanthere', false, time() - 10); 176 // Min value if no logs are found. 177 $this->assertEquals($indicator::get_min_value(), $values[$user1->id][0]); 178 179 \logstore_standard\event\unittest_executed::create( 180 array('context' => \context_course::instance($course->id), 'userid' => $user1->id))->trigger(); 181 // Max value if logs are found before the end time. 182 list($values, $unused) = $indicator->calculate($sampleids, 'notrelevanthere', false, time() + 10); 183 $this->assertEquals($indicator::get_max_value(), $values[$user1->id][0]); 184 185 // Test any write action. 186 $course1 = $this->getDataGenerator()->create_course(); 187 $coursecontext1 = \context_course::instance($course1->id); 188 $course2 = $this->getDataGenerator()->create_course(); 189 $coursecontext2 = \context_course::instance($course2->id); 190 $this->getDataGenerator()->enrol_user($user1->id, $course2->id); 191 192 $indicator = new \core\analytics\indicator\any_write_action(); 193 194 $sampleids = array($user1->id => $user1->id, $user2->id => $user2->id); 195 $data = array($user1->id => array( 196 'context' => $coursecontext1, 197 'course' => $course1, 198 'user' => $user1 199 )); 200 $data[$user2->id] = $data[$user1->id]; 201 $data[$user2->id]['user'] = $user2; 202 $indicator->add_sample_data($data); 203 204 list($values, $unused) = $indicator->calculate($sampleids, 'user'); 205 $this->assertEquals($indicator::get_min_value(), $values[$user1->id][0]); 206 $this->assertEquals($indicator::get_min_value(), $values[$user2->id][0]); 207 208 $beforecourseeventcreate = time(); 209 sleep(1); 210 211 \logstore_standard\event\unittest_executed::create( 212 array('context' => $coursecontext1, 'userid' => $user1->id))->trigger(); 213 list($values, $unused) = $indicator->calculate($sampleids, 'user'); 214 $this->assertEquals($indicator::get_max_value(), $values[$user1->id][0]); 215 $this->assertEquals($indicator::get_min_value(), $values[$user2->id][0]); 216 217 // Now try with course-level samples where user is not available. 218 $sampleids = array($course1->id => $course1->id, $course2->id => $course2->id); 219 $data = array( 220 $course1->id => array( 221 'context' => $coursecontext1, 222 'course' => $course1, 223 ), 224 $course2->id => array( 225 'context' => $coursecontext2, 226 'course' => $course2, 227 ) 228 ); 229 $indicator->clear_sample_data(); 230 $indicator->add_sample_data($data); 231 232 // Limited by time to avoid previous logs interfering as other logs 233 // have been generated by the system. 234 list($values, $unused) = $indicator->calculate($sampleids, 'course', $beforecourseeventcreate); 235 $this->assertEquals($indicator::get_max_value(), $values[$course1->id][0]); 236 $this->assertEquals($indicator::get_min_value(), $values[$course2->id][0]); 237 238 // Test any write action in the course. 239 $course1 = $this->getDataGenerator()->create_course(); 240 $coursecontext1 = \context_course::instance($course1->id); 241 $activity1 = $this->getDataGenerator()->create_module('forum', array('course' => $course1->id)); 242 $activity1context = \context_module::instance($activity1->cmid); 243 $course2 = $this->getDataGenerator()->create_course(); 244 $coursecontext2 = \context_course::instance($course2->id); 245 $this->getDataGenerator()->enrol_user($user1->id, $course2->id); 246 247 $indicator = new \core\analytics\indicator\any_write_action_in_course(); 248 249 $sampleids = array($user1->id => $user1->id, $user2->id => $user2->id); 250 $data = array($user1->id => array( 251 'context' => $coursecontext1, 252 'course' => $course1, 253 'user' => $user1 254 )); 255 $data[$user2->id] = $data[$user1->id]; 256 $data[$user2->id]['user'] = $user2; 257 $indicator->add_sample_data($data); 258 259 list($values, $unused) = $indicator->calculate($sampleids, 'user'); 260 $this->assertEquals($indicator::get_min_value(), $values[$user1->id][0]); 261 $this->assertEquals($indicator::get_min_value(), $values[$user2->id][0]); 262 263 $beforecourseeventcreate = time(); 264 sleep(1); 265 266 \logstore_standard\event\unittest_executed::create( 267 array('context' => $activity1context, 'userid' => $user1->id))->trigger(); 268 list($values, $unused) = $indicator->calculate($sampleids, 'user'); 269 $this->assertEquals($indicator::get_max_value(), $values[$user1->id][0]); 270 $this->assertEquals($indicator::get_min_value(), $values[$user2->id][0]); 271 272 // Now try with course-level samples where user is not available. 273 $sampleids = array($course1->id => $course1->id, $course2->id => $course2->id); 274 $data = array( 275 $course1->id => array( 276 'context' => $coursecontext1, 277 'course' => $course1, 278 ), 279 $course2->id => array( 280 'context' => $coursecontext2, 281 'course' => $course2, 282 ) 283 ); 284 $indicator->clear_sample_data(); 285 $indicator->add_sample_data($data); 286 287 // Limited by time to avoid previous logs interfering as other logs 288 // have been generated by the system. 289 list($values, $unused) = $indicator->calculate($sampleids, 'course', $beforecourseeventcreate); 290 $this->assertEquals($indicator::get_max_value(), $values[$course1->id][0]); 291 $this->assertEquals($indicator::get_min_value(), $values[$course2->id][0]); 292 293 // Test read actions. 294 $course = $this->getDataGenerator()->create_course(); 295 $coursecontext = \context_course::instance($course->id); 296 $this->getDataGenerator()->enrol_user($user1->id, $course->id); 297 298 $indicator = new \core\analytics\indicator\read_actions(); 299 300 $sampleids = array($user1->id => $user1->id, $user2->id => $user2->id); 301 $data = array($user1->id => array( 302 'context' => $coursecontext, 303 'course' => $course, 304 'user' => $user1 305 )); 306 $data[$user2->id] = $data[$user1->id]; 307 $data[$user2->id]['user'] = $user2; 308 $indicator->add_sample_data($data); 309 310 // More or less 4 weeks duration. 311 $startdate = time() - (WEEKSECS * 2); 312 $enddate = time() + (WEEKSECS * 2); 313 314 $this->setAdminUser(); 315 list($values, $unused) = $indicator->calculate($sampleids, 'user'); 316 $this->assertNull($values[$user1->id][0]); 317 $this->assertNull($values[$user1->id][1]); 318 $this->assertNull($values[$user1->id][0]); 319 $this->assertNull($values[$user2->id][1]); 320 321 // Zero score for 0 accesses. 322 list($values, $unused) = $indicator->calculate($sampleids, 'user', $startdate, $enddate); 323 $this->assertEquals($indicator::get_min_value(), $values[$user1->id][0]); 324 $this->assertEquals($indicator::get_min_value(), $values[$user2->id][0]); 325 326 // 1/3 score for more than 0 accesses. 327 \core\event\course_viewed::create( 328 array('context' => $coursecontext, 'userid' => $user1->id))->trigger(); 329 list($values, $unused) = $indicator->calculate($sampleids, 'user', $startdate, $enddate); 330 $this->assertEquals(-0.33, $values[$user1->id][0]); 331 $this->assertEquals($indicator::get_min_value(), $values[$user2->id][0]); 332 333 // 2/3 score for more than 1 access per week. 334 for ($i = 0; $i < 12; $i++) { 335 \core\event\course_viewed::create( 336 array('context' => $coursecontext, 'userid' => $user1->id))->trigger(); 337 } 338 list($values, $unused) = $indicator->calculate($sampleids, 'user', $startdate, $enddate); 339 $this->assertEquals(0.33, $values[$user1->id][0]); 340 $this->assertEquals($indicator::get_min_value(), $values[$user2->id][0]); 341 342 // 100% score for tons of accesses during this period (3 logs per access * 4 weeks * 10 accesses). 343 for ($i = 0; $i < (3 * 10 * 4); $i++) { 344 \core\event\course_viewed::create( 345 array('context' => $coursecontext, 'userid' => $user1->id))->trigger(); 346 } 347 list($values, $unused) = $indicator->calculate($sampleids, 'user', $startdate, $enddate); 348 $this->assertEquals($indicator::get_max_value(), $values[$user1->id][0]); 349 $this->assertEquals($indicator::get_min_value(), $values[$user2->id][0]); 350 351 set_config('enabled_stores', '', 'tool_log'); 352 get_log_manager(true); 353 } 354 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body