Merge branch 'MDL-73883-master' of https://github.com/andrewnicols/moodle
[moodle.git] / lib / tests / indicators_test.php
blobe804dff48ee6d6e1c6ca57ad71287dcdd62606b7
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.
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/>.
17 /**
18 * Unit tests for core indicators.
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
26 defined('MOODLE_INTERNAL') || die();
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');
32 /**
33 * Unit tests for core indicators.
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
40 class core_analytics_indicators_testcase extends advanced_testcase {
42 /**
43 * Test all core indicators.
45 * Single method as it is significantly faster (from 13s to 5s) than having separate
46 * methods because of preventResetByRollback.
48 * @return void
50 public function test_core_indicators() {
51 global $DB;
53 $this->preventResetByRollback();
54 $this->resetAfterTest(true);
55 $this->setAdminuser();
57 set_config('enabled_stores', 'logstore_standard', 'tool_log');
58 set_config('buffersize', 0, 'logstore_standard');
60 $user1 = $this->getDataGenerator()->create_user();
61 $user2 = $this->getDataGenerator()->create_user();
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)
68 $course = $this->getDataGenerator()->create_course($params);
69 $coursecontext = \context_course::instance($course->id);
70 $this->getDataGenerator()->enrol_user($user1->id, $course->id);
72 $indicator = new \core\analytics\indicator\any_access_after_end();
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);
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]);
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]);
94 // Test any access before start.
95 $params = array(
96 'startdate' => 9999999998,
97 'enddate' => 9999999999
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);
104 $indicator = new \core\analytics\indicator\any_access_before_start();
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
112 $data[$user2->id] = $data[$user1->id];
113 $data[$user2->id]['user'] = $user2;
114 $indicator->add_sample_data($data);
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]);
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]);
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);
131 $indicator = new \core\analytics\indicator\any_course_access();
133 $sampleids = array($user1->id => $user1->id);
134 $data = array($user1->id => array(
135 'course' => $course,
136 'user' => $user1
138 $indicator->add_sample_data($data);
139 $analysable = new \core_analytics\course($course);
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]);
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]);
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]);
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]);
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]);
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]);
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]);
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);
192 $indicator = new \core\analytics\indicator\any_write_action();
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
200 $data[$user2->id] = $data[$user1->id];
201 $data[$user2->id]['user'] = $user2;
202 $indicator->add_sample_data($data);
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]);
208 $beforecourseeventcreate = time();
209 sleep(1);
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]);
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,
224 $course2->id => array(
225 'context' => $coursecontext2,
226 'course' => $course2,
229 $indicator->clear_sample_data();
230 $indicator->add_sample_data($data);
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]);
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);
247 $indicator = new \core\analytics\indicator\any_write_action_in_course();
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
255 $data[$user2->id] = $data[$user1->id];
256 $data[$user2->id]['user'] = $user2;
257 $indicator->add_sample_data($data);
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]);
263 $beforecourseeventcreate = time();
264 sleep(1);
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]);
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,
279 $course2->id => array(
280 'context' => $coursecontext2,
281 'course' => $course2,
284 $indicator->clear_sample_data();
285 $indicator->add_sample_data($data);
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]);
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);
298 $indicator = new \core\analytics\indicator\read_actions();
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
306 $data[$user2->id] = $data[$user1->id];
307 $data[$user2->id]['user'] = $user2;
308 $indicator->add_sample_data($data);
310 // More or less 4 weeks duration.
311 $startdate = time() - (WEEKSECS * 2);
312 $enddate = time() + (WEEKSECS * 2);
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]);
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]);
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]);
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();
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]);
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();
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]);
351 set_config('enabled_stores', '', 'tool_log');
352 get_log_manager(true);