Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are 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  /**
  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  }