Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

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