[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/ -> statslib.php (source)

   1  <?php
   2  
   3  // This file is part of Moodle - http://moodle.org/
   4  //
   5  // Moodle is free software: you can redistribute it and/or modify
   6  // it under the terms of the GNU General Public License as published by
   7  // the Free Software Foundation, either version 3 of the License, or
   8  // (at your option) any later version.
   9  //
  10  // Moodle is distributed in the hope that it will be useful,
  11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13  // GNU General Public License for more details.
  14  //
  15  // You should have received a copy of the GNU General Public License
  16  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  17  
  18  /**
  19   * @package    core
  20   * @subpackage stats
  21   * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  defined('MOODLE_INTERNAL') || die();
  26  
  27  /** THESE CONSTANTS ARE USED FOR THE REPORTING PAGE. */
  28  
  29  define('STATS_REPORT_LOGINS',1); // double impose logins and unique logins on a line graph. site course only.
  30  define('STATS_REPORT_READS',2); // double impose student reads and teacher reads on a line graph.
  31  define('STATS_REPORT_WRITES',3); // double impose student writes and teacher writes on a line graph.
  32  define('STATS_REPORT_ACTIVITY',4); // 2+3 added up, teacher vs student.
  33  define('STATS_REPORT_ACTIVITYBYROLE',5); // all activity, reads vs writes, selected by role.
  34  
  35  // user level stats reports.
  36  define('STATS_REPORT_USER_ACTIVITY',7);
  37  define('STATS_REPORT_USER_ALLACTIVITY',8);
  38  define('STATS_REPORT_USER_LOGINS',9);
  39  define('STATS_REPORT_USER_VIEW',10);  // this is the report you see on the user profile.
  40  
  41  // admin only ranking stats reports
  42  define('STATS_REPORT_ACTIVE_COURSES',11);
  43  define('STATS_REPORT_ACTIVE_COURSES_WEIGHTED',12);
  44  define('STATS_REPORT_PARTICIPATORY_COURSES',13);
  45  define('STATS_REPORT_PARTICIPATORY_COURSES_RW',14);
  46  
  47  // start after 0 = show dailies.
  48  define('STATS_TIME_LASTWEEK',1);
  49  define('STATS_TIME_LAST2WEEKS',2);
  50  define('STATS_TIME_LAST3WEEKS',3);
  51  define('STATS_TIME_LAST4WEEKS',4);
  52  
  53  // start after 10 = show weeklies
  54  define('STATS_TIME_LAST2MONTHS',12);
  55  
  56  define('STATS_TIME_LAST3MONTHS',13);
  57  define('STATS_TIME_LAST4MONTHS',14);
  58  define('STATS_TIME_LAST5MONTHS',15);
  59  define('STATS_TIME_LAST6MONTHS',16);
  60  
  61  // start after 20 = show monthlies
  62  define('STATS_TIME_LAST7MONTHS',27);
  63  define('STATS_TIME_LAST8MONTHS',28);
  64  define('STATS_TIME_LAST9MONTHS',29);
  65  define('STATS_TIME_LAST10MONTHS',30);
  66  define('STATS_TIME_LAST11MONTHS',31);
  67  define('STATS_TIME_LASTYEAR',32);
  68  
  69  // different modes for what reports to offer
  70  define('STATS_MODE_GENERAL',1);
  71  define('STATS_MODE_DETAILED',2);
  72  define('STATS_MODE_RANKED',3); // admins only - ranks courses
  73  
  74  // Output string when nodebug is on
  75  define('STATS_PLACEHOLDER_OUTPUT', '.');
  76  
  77  /**
  78   * Print daily cron progress
  79   * @param string $ident
  80   */
  81  function stats_progress($ident) {
  82      static $start = 0;
  83      static $init  = 0;
  84  
  85      if ($ident == 'init') {
  86          $init = $start = microtime(true);
  87          return;
  88      }
  89  
  90      $elapsed = round(microtime(true) - $start);
  91      $start   = microtime(true);
  92  
  93      if (debugging('', DEBUG_ALL)) {
  94          mtrace("$ident:$elapsed ", '');
  95      } else {
  96          mtrace(STATS_PLACEHOLDER_OUTPUT, '');
  97      }
  98  }
  99  
 100  /**
 101   * Execute individual daily statistics queries
 102   *
 103   * @param string $sql The query to run
 104   * @return boolean success
 105   */
 106  function stats_run_query($sql, $parameters = array()) {
 107      global $DB;
 108  
 109      try {
 110          $DB->execute($sql, $parameters);
 111      } catch (dml_exception $e) {
 112  
 113         if (debugging('', DEBUG_ALL)) {
 114             mtrace($e->getMessage());
 115         }
 116         return false;
 117      }
 118      return true;
 119  }
 120  
 121  /**
 122   * Execute daily statistics gathering
 123   *
 124   * @param int $maxdays maximum number of days to be processed
 125   * @return boolean success
 126   */
 127  function stats_cron_daily($maxdays=1) {
 128      global $CFG, $DB;
 129      require_once($CFG->libdir.'/adminlib.php');
 130  
 131      $now = time();
 132  
 133      $fpcontext = context_course::instance(SITEID, MUST_EXIST);
 134  
 135      // read last execution date from db
 136      if (!$timestart = get_config(NULL, 'statslastdaily')) {
 137          $timestart = stats_get_base_daily(stats_get_start_from('daily'));
 138          set_config('statslastdaily', $timestart);
 139      }
 140  
 141      // calculate scheduled time
 142      $scheduledtime = stats_get_base_daily() + $CFG->statsruntimestarthour*60*60 + $CFG->statsruntimestartminute*60;
 143  
 144      // Note: This will work fine for sites running cron each 4 hours or less (hopefully, 99.99% of sites). MDL-16709
 145      // check to make sure we're due to run, at least 20 hours after last run
 146      if (isset($CFG->statslastexecution) && ((time() - 20*60*60) < $CFG->statslastexecution)) {
 147          mtrace("...preventing stats to run, last execution was less than 20 hours ago.");
 148          return false;
 149      // also check that we are a max of 4 hours after scheduled time, stats won't run after that
 150      } else if (time() > $scheduledtime + 4*60*60) {
 151          mtrace("...preventing stats to run, more than 4 hours since scheduled time.");
 152          return false;
 153      } else {
 154          set_config('statslastexecution', time()); /// Grab this execution as last one
 155      }
 156  
 157      $nextmidnight = stats_get_next_day_start($timestart);
 158  
 159      // are there any days that need to be processed?
 160      if ($now < $nextmidnight) {
 161          return true; // everything ok and up-to-date
 162      }
 163  
 164  
 165      $timeout = empty($CFG->statsmaxruntime) ? 60*60*24 : $CFG->statsmaxruntime;
 166  
 167      if (!set_cron_lock('statsrunning', $now + $timeout)) {
 168          return false;
 169      }
 170  
 171      // first delete entries that should not be there yet
 172      $DB->delete_records_select('stats_daily',      "timeend > $timestart");
 173      $DB->delete_records_select('stats_user_daily', "timeend > $timestart");
 174  
 175      // Read in a few things we'll use later
 176      $viewactions = stats_get_action_names('view');
 177      $postactions = stats_get_action_names('post');
 178  
 179      $guest           = (int)$CFG->siteguest;
 180      $guestrole       = (int)$CFG->guestroleid;
 181      $defaultfproleid = (int)$CFG->defaultfrontpageroleid;
 182  
 183      mtrace("Running daily statistics gathering, starting at $timestart:");
 184      cron_trace_time_and_memory();
 185  
 186      $days  = 0;
 187      $total = 0;
 188      $failed  = false; // failed stats flag
 189      $timeout = false;
 190  
 191      if (!stats_temp_table_create()) {
 192          $days = 1;
 193          $failed = true;
 194      }
 195      mtrace('Temporary tables created');
 196  
 197      if(!stats_temp_table_setup()) {
 198          $days = 1;
 199          $failed = true;
 200      }
 201      mtrace('Enrolments calculated');
 202  
 203      $totalactiveusers = $DB->count_records('user', array('deleted' => '0'));
 204  
 205      while (!$failed && ($now > $nextmidnight)) {
 206          if ($days >= $maxdays) {
 207              $timeout = true;
 208              break;
 209          }
 210  
 211          $days++;
 212          core_php_time_limit::raise($timeout - 200);
 213  
 214          if ($days > 1) {
 215              // move the lock
 216              set_cron_lock('statsrunning', time() + $timeout, true);
 217          }
 218  
 219          $daystart = time();
 220  
 221          stats_progress('init');
 222  
 223          if (!stats_temp_table_fill($timestart, $nextmidnight)) {
 224              $failed = true;
 225              break;
 226          }
 227  
 228          // Find out if any logs available for this day
 229          $sql = "SELECT 'x' FROM {temp_log1} l";
 230          $logspresent = $DB->get_records_sql($sql, null, 0, 1);
 231  
 232          if ($logspresent) {
 233              // Insert blank record to force Query 10 to generate additional row when no logs for
 234              // the site with userid 0 exist.  Added for backwards compatibility.
 235              $DB->insert_record('temp_log1', array('userid' => 0, 'course' => SITEID, 'action' => ''));
 236          }
 237  
 238          // Calculate the number of active users today
 239          $sql = 'SELECT COUNT(DISTINCT u.id)
 240                    FROM {user} u
 241                    JOIN {temp_log1} l ON l.userid = u.id
 242                   WHERE u.deleted = 0';
 243          $dailyactiveusers = $DB->count_records_sql($sql);
 244  
 245          stats_progress('0');
 246  
 247          // Process login info first
 248          // Note: PostgreSQL doesn't like aliases in HAVING clauses
 249          $sql = "INSERT INTO {temp_stats_user_daily}
 250                              (stattype, timeend, courseid, userid, statsreads)
 251  
 252                  SELECT 'logins', $nextmidnight AS timeend, ".SITEID." AS courseid,
 253                          userid, COUNT(id) AS statsreads
 254                    FROM {temp_log1} l
 255                   WHERE action = 'login'
 256                GROUP BY userid
 257                  HAVING COUNT(id) > 0";
 258  
 259          if ($logspresent && !stats_run_query($sql)) {
 260              $failed = true;
 261              break;
 262          }
 263          $DB->update_temp_table_stats();
 264  
 265          stats_progress('1');
 266  
 267          $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
 268  
 269                  SELECT 'logins' AS stattype, $nextmidnight AS timeend, ".SITEID." AS courseid, 0,
 270                         COALESCE(SUM(statsreads), 0) as stat1, COUNT('x') as stat2
 271                    FROM {temp_stats_user_daily}
 272                   WHERE stattype = 'logins' AND timeend = $nextmidnight";
 273  
 274          if ($logspresent && !stats_run_query($sql)) {
 275              $failed = true;
 276              break;
 277          }
 278          stats_progress('2');
 279  
 280  
 281          // Enrolments and active enrolled users
 282          //
 283          // Unfortunately, we do not know how many users were registered
 284          // at given times in history :-(
 285          // - stat1: enrolled users
 286          // - stat2: enrolled users active in this period
 287          // - SITEID is special case here, because it's all about default enrolment
 288          //   in that case, we'll count non-deleted users.
 289          //
 290  
 291          $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
 292  
 293                  SELECT 'enrolments' as stattype, $nextmidnight as timeend, courseid, roleid,
 294                          COUNT(DISTINCT userid) as stat1, 0 as stat2
 295                    FROM {temp_enroled}
 296                GROUP BY courseid, roleid";
 297  
 298          if (!stats_run_query($sql)) {
 299              $failed = true;
 300              break;
 301          }
 302          stats_progress('3');
 303  
 304          // Set stat2 to the number distinct users with role assignments in the course that were active
 305          // using table alias in UPDATE does not work in pg < 8.2
 306          $sql = "UPDATE {temp_stats_daily}
 307                     SET stat2 = (
 308  
 309                      SELECT COUNT(DISTINCT userid)
 310                        FROM {temp_enroled} te
 311                       WHERE roleid = {temp_stats_daily}.roleid
 312                         AND courseid = {temp_stats_daily}.courseid
 313                         AND EXISTS (
 314  
 315                          SELECT 'x'
 316                            FROM {temp_log1} l
 317                           WHERE l.course = {temp_stats_daily}.courseid
 318                             AND l.userid = te.userid
 319                                    )
 320                                 )
 321                   WHERE {temp_stats_daily}.stattype = 'enrolments'
 322                     AND {temp_stats_daily}.timeend = $nextmidnight
 323                     AND {temp_stats_daily}.courseid IN (
 324  
 325                      SELECT DISTINCT course FROM {temp_log2})";
 326  
 327          if ($logspresent && !stats_run_query($sql, array('courselevel'=>CONTEXT_COURSE))) {
 328              $failed = true;
 329              break;
 330          }
 331          stats_progress('4');
 332  
 333          // Now get course total enrolments (roleid==0) - except frontpage
 334          $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
 335  
 336                  SELECT 'enrolments', $nextmidnight AS timeend, te.courseid AS courseid, 0 AS roleid,
 337                         COUNT(DISTINCT userid) AS stat1, 0 AS stat2
 338                    FROM {temp_enroled} te
 339                GROUP BY courseid
 340                  HAVING COUNT(DISTINCT userid) > 0";
 341  
 342          if ($logspresent && !stats_run_query($sql)) {
 343              $failed = true;
 344              break;
 345          }
 346          stats_progress('5');
 347  
 348          // Set stat 2 to the number of enrolled users who were active in the course
 349          $sql = "UPDATE {temp_stats_daily}
 350                     SET stat2 = (
 351  
 352                      SELECT COUNT(DISTINCT te.userid)
 353                        FROM {temp_enroled} te
 354                       WHERE te.courseid = {temp_stats_daily}.courseid
 355                         AND EXISTS (
 356  
 357                          SELECT 'x'
 358                            FROM {temp_log1} l
 359                           WHERE l.course = {temp_stats_daily}.courseid
 360                             AND l.userid = te.userid
 361                                    )
 362                                 )
 363  
 364                   WHERE {temp_stats_daily}.stattype = 'enrolments'
 365                     AND {temp_stats_daily}.timeend = $nextmidnight
 366                     AND {temp_stats_daily}.roleid = 0
 367                     AND {temp_stats_daily}.courseid IN (
 368  
 369                      SELECT l.course
 370                        FROM {temp_log2} l
 371                       WHERE l.course <> ".SITEID.")";
 372  
 373          if ($logspresent && !stats_run_query($sql, array())) {
 374              $failed = true;
 375              break;
 376          }
 377          stats_progress('6');
 378  
 379          // Frontpage(==site) enrolments total
 380          $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
 381  
 382                  SELECT 'enrolments', $nextmidnight, ".SITEID.", 0, $totalactiveusers AS stat1,
 383                         $dailyactiveusers AS stat2" .
 384                  $DB->sql_null_from_clause();
 385  
 386          if ($logspresent && !stats_run_query($sql)) {
 387              $failed = true;
 388              break;
 389          }
 390          // The steps up until this point, all add to {temp_stats_daily} and don't use new tables.
 391          // There is no point updating statistics as they won't be used until the DELETE below.
 392          $DB->update_temp_table_stats();
 393  
 394          stats_progress('7');
 395  
 396          // Default frontpage role enrolments are all site users (not deleted)
 397          if ($defaultfproleid) {
 398              // first remove default frontpage role counts if created by previous query
 399              $sql = "DELETE
 400                        FROM {temp_stats_daily}
 401                       WHERE stattype = 'enrolments'
 402                         AND courseid = ".SITEID."
 403                         AND roleid = $defaultfproleid
 404                         AND timeend = $nextmidnight";
 405  
 406              if ($logspresent && !stats_run_query($sql)) {
 407                  $failed = true;
 408                  break;
 409              }
 410              stats_progress('8');
 411  
 412              $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
 413  
 414                      SELECT 'enrolments', $nextmidnight, ".SITEID.", $defaultfproleid,
 415                             $totalactiveusers AS stat1, $dailyactiveusers AS stat2" .
 416                      $DB->sql_null_from_clause();
 417  
 418              if ($logspresent && !stats_run_query($sql)) {
 419                  $failed = true;
 420                  break;
 421              }
 422              stats_progress('9');
 423  
 424          } else {
 425              stats_progress('x');
 426              stats_progress('x');
 427          }
 428  
 429  
 430          /// individual user stats (including not-logged-in) in each course, this is slow - reuse this data if possible
 431          list($viewactionssql, $params1) = $DB->get_in_or_equal($viewactions, SQL_PARAMS_NAMED, 'view');
 432          list($postactionssql, $params2) = $DB->get_in_or_equal($postactions, SQL_PARAMS_NAMED, 'post');
 433          $sql = "INSERT INTO {temp_stats_user_daily} (stattype, timeend, courseid, userid, statsreads, statswrites)
 434  
 435                  SELECT 'activity' AS stattype, $nextmidnight AS timeend, course AS courseid, userid,
 436                         SUM(CASE WHEN action $viewactionssql THEN 1 ELSE 0 END) AS statsreads,
 437                         SUM(CASE WHEN action $postactionssql THEN 1 ELSE 0 END) AS statswrites
 438                    FROM {temp_log1} l
 439                GROUP BY userid, course";
 440  
 441          if ($logspresent && !stats_run_query($sql, array_merge($params1, $params2))) {
 442              $failed = true;
 443              break;
 444          }
 445          stats_progress('10');
 446  
 447  
 448          /// How many view/post actions in each course total
 449          $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
 450  
 451                  SELECT 'activity' AS stattype, $nextmidnight AS timeend, c.id AS courseid, 0,
 452                         SUM(CASE WHEN l.action $viewactionssql THEN 1 ELSE 0 END) AS stat1,
 453                         SUM(CASE WHEN l.action $postactionssql THEN 1 ELSE 0 END) AS stat2
 454                    FROM {course} c, {temp_log1} l
 455                   WHERE l.course = c.id
 456                GROUP BY c.id";
 457  
 458          if ($logspresent && !stats_run_query($sql, array_merge($params1, $params2))) {
 459              $failed = true;
 460              break;
 461          }
 462          stats_progress('11');
 463  
 464  
 465          /// how many view actions for each course+role - excluding guests and frontpage
 466  
 467          $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
 468  
 469                  SELECT 'activity', $nextmidnight AS timeend, courseid, roleid, SUM(statsreads), SUM(statswrites)
 470                    FROM (
 471  
 472                      SELECT pl.courseid, pl.roleid, sud.statsreads, sud.statswrites
 473                        FROM {temp_stats_user_daily} sud, (
 474  
 475                          SELECT DISTINCT te.userid, te.roleid, te.courseid
 476                            FROM {temp_enroled} te
 477                           WHERE te.roleid <> $guestrole
 478                             AND te.userid <> $guest
 479                                                          ) pl
 480  
 481                       WHERE sud.userid = pl.userid
 482                         AND sud.courseid = pl.courseid
 483                         AND sud.timeend = $nextmidnight
 484                         AND sud.stattype='activity'
 485                         ) inline_view
 486  
 487                GROUP BY courseid, roleid
 488                  HAVING SUM(statsreads) > 0 OR SUM(statswrites) > 0";
 489  
 490          if ($logspresent && !stats_run_query($sql, array('courselevel'=>CONTEXT_COURSE))) {
 491              $failed = true;
 492              break;
 493          }
 494          stats_progress('12');
 495  
 496          /// how many view actions from guests only in each course - excluding frontpage
 497          /// normal users may enter course with temporary guest access too
 498  
 499          $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
 500  
 501                  SELECT 'activity', $nextmidnight AS timeend, courseid, $guestrole AS roleid,
 502                         SUM(statsreads), SUM(statswrites)
 503                    FROM (
 504  
 505                      SELECT sud.courseid, sud.statsreads, sud.statswrites
 506                        FROM {temp_stats_user_daily} sud
 507                       WHERE sud.timeend = $nextmidnight
 508                         AND sud.courseid <> ".SITEID."
 509                         AND sud.stattype='activity'
 510                         AND (sud.userid = $guest OR sud.userid NOT IN (
 511  
 512                          SELECT userid
 513                            FROM {temp_enroled} te
 514                           WHERE te.courseid = sud.courseid
 515                                                                       ))
 516                         ) inline_view
 517  
 518                GROUP BY courseid
 519                  HAVING SUM(statsreads) > 0 OR SUM(statswrites) > 0";
 520  
 521          if ($logspresent && !stats_run_query($sql, array())) {
 522              $failed = true;
 523              break;
 524          }
 525          stats_progress('13');
 526  
 527  
 528          /// How many view actions for each role on frontpage - excluding guests, not-logged-in and default frontpage role
 529          $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
 530  
 531                  SELECT 'activity', $nextmidnight AS timeend, courseid, roleid,
 532                         SUM(statsreads), SUM(statswrites)
 533                    FROM (
 534                      SELECT pl.courseid, pl.roleid, sud.statsreads, sud.statswrites
 535                        FROM {temp_stats_user_daily} sud, (
 536  
 537                          SELECT DISTINCT ra.userid, ra.roleid, c.instanceid AS courseid
 538                            FROM {role_assignments} ra
 539                            JOIN {context} c ON c.id = ra.contextid
 540                           WHERE ra.contextid = :fpcontext
 541                             AND ra.roleid <> $defaultfproleid
 542                             AND ra.roleid <> $guestrole
 543                             AND ra.userid <> $guest
 544                                                     ) pl
 545                       WHERE sud.userid = pl.userid
 546                         AND sud.courseid = pl.courseid
 547                         AND sud.timeend = $nextmidnight
 548                         AND sud.stattype='activity'
 549                         ) inline_view
 550  
 551                GROUP BY courseid, roleid
 552                  HAVING SUM(statsreads) > 0 OR SUM(statswrites) > 0";
 553  
 554          if ($logspresent && !stats_run_query($sql, array('fpcontext'=>$fpcontext->id))) {
 555              $failed = true;
 556              break;
 557          }
 558          stats_progress('14');
 559  
 560  
 561          // How many view actions for default frontpage role on frontpage only
 562          $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
 563  
 564                  SELECT 'activity', timeend, courseid, $defaultfproleid AS roleid,
 565                         SUM(statsreads), SUM(statswrites)
 566                    FROM (
 567                      SELECT sud.timeend AS timeend, sud.courseid, sud.statsreads, sud.statswrites
 568                        FROM {temp_stats_user_daily} sud
 569                       WHERE sud.timeend = :nextm
 570                         AND sud.courseid = :siteid
 571                         AND sud.stattype='activity'
 572                         AND sud.userid <> $guest
 573                         AND sud.userid <> 0
 574                         AND sud.userid NOT IN (
 575  
 576                          SELECT ra.userid
 577                            FROM {role_assignments} ra
 578                           WHERE ra.roleid <> $guestrole
 579                             AND ra.roleid <> $defaultfproleid
 580                             AND ra.contextid = :fpcontext)
 581                         ) inline_view
 582  
 583                GROUP BY timeend, courseid
 584                  HAVING SUM(statsreads) > 0 OR SUM(statswrites) > 0";
 585  
 586          if ($logspresent && !stats_run_query($sql, array('fpcontext'=>$fpcontext->id, 'siteid'=>SITEID, 'nextm'=>$nextmidnight))) {
 587              $failed = true;
 588              break;
 589          }
 590          $DB->update_temp_table_stats();
 591          stats_progress('15');
 592  
 593          // How many view actions for guests or not-logged-in on frontpage
 594          $sql = "INSERT INTO {temp_stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
 595  
 596                  SELECT stattype, timeend, courseid, $guestrole AS roleid,
 597                         SUM(statsreads) AS stat1, SUM(statswrites) AS stat2
 598                    FROM (
 599                      SELECT sud.stattype, sud.timeend, sud.courseid,
 600                             sud.statsreads, sud.statswrites
 601                        FROM {temp_stats_user_daily} sud
 602                       WHERE (sud.userid = $guest OR sud.userid = 0)
 603                         AND sud.timeend = $nextmidnight
 604                         AND sud.courseid = ".SITEID."
 605                         AND sud.stattype='activity'
 606                         ) inline_view
 607                   GROUP BY stattype, timeend, courseid
 608                   HAVING SUM(statsreads) > 0 OR SUM(statswrites) > 0";
 609  
 610          if ($logspresent && !stats_run_query($sql)) {
 611              $failed = true;
 612              break;
 613          }
 614          stats_progress('16');
 615  
 616          stats_temp_table_clean();
 617  
 618          stats_progress('out');
 619  
 620          // remember processed days
 621          set_config('statslastdaily', $nextmidnight);
 622          $elapsed = time()-$daystart;
 623          mtrace("  finished until $nextmidnight: ".userdate($nextmidnight)." (in $elapsed s)");
 624          $total += $elapsed;
 625  
 626          $timestart    = $nextmidnight;
 627          $nextmidnight = stats_get_next_day_start($nextmidnight);
 628      }
 629  
 630      stats_temp_table_drop();
 631  
 632      set_cron_lock('statsrunning', null);
 633  
 634      if ($failed) {
 635          $days--;
 636          mtrace("...error occurred, completed $days days of statistics in {$total} s.");
 637          return false;
 638  
 639      } else if ($timeout) {
 640          mtrace("...stopping early, reached maximum number of $maxdays days ({$total} s) - will continue next time.");
 641          return false;
 642  
 643      } else {
 644          mtrace("...completed $days days of statistics in {$total} s.");
 645          return true;
 646      }
 647  }
 648  
 649  
 650  /**
 651   * Execute weekly statistics gathering
 652   * @return boolean success
 653   */
 654  function stats_cron_weekly() {
 655      global $CFG, $DB;
 656      require_once($CFG->libdir.'/adminlib.php');
 657  
 658      $now = time();
 659  
 660      // read last execution date from db
 661      if (!$timestart = get_config(NULL, 'statslastweekly')) {
 662          $timestart = stats_get_base_daily(stats_get_start_from('weekly'));
 663          set_config('statslastweekly', $timestart);
 664      }
 665  
 666      $nextstartweek = stats_get_next_week_start($timestart);
 667  
 668      // are there any weeks that need to be processed?
 669      if ($now < $nextstartweek) {
 670          return true; // everything ok and up-to-date
 671      }
 672  
 673      $timeout = empty($CFG->statsmaxruntime) ? 60*60*24 : $CFG->statsmaxruntime;
 674  
 675      if (!set_cron_lock('statsrunning', $now + $timeout)) {
 676          return false;
 677      }
 678  
 679      // fisrt delete entries that should not be there yet
 680      $DB->delete_records_select('stats_weekly',      "timeend > $timestart");
 681      $DB->delete_records_select('stats_user_weekly', "timeend > $timestart");
 682  
 683      mtrace("Running weekly statistics gathering, starting at $timestart:");
 684      cron_trace_time_and_memory();
 685  
 686      $weeks = 0;
 687      while ($now > $nextstartweek) {
 688          core_php_time_limit::raise($timeout - 200);
 689          $weeks++;
 690  
 691          if ($weeks > 1) {
 692              // move the lock
 693              set_cron_lock('statsrunning', time() + $timeout, true);
 694          }
 695  
 696          $stattimesql = "timeend > $timestart AND timeend <= $nextstartweek";
 697  
 698          $weekstart = time();
 699          stats_progress('init');
 700  
 701      /// process login info first
 702          $sql = "INSERT INTO {stats_user_weekly} (stattype, timeend, courseid, userid, statsreads)
 703  
 704                  SELECT 'logins', timeend, courseid, userid, SUM(statsreads)
 705                    FROM (
 706                             SELECT $nextstartweek AS timeend, courseid, userid, statsreads
 707                               FROM {stats_user_daily} sd
 708                              WHERE stattype = 'logins' AND $stattimesql
 709                         ) inline_view
 710                GROUP BY timeend, courseid, userid
 711                  HAVING SUM(statsreads) > 0";
 712  
 713          $DB->execute($sql);
 714  
 715          stats_progress('1');
 716  
 717          $sql = "INSERT INTO {stats_weekly} (stattype, timeend, courseid, roleid, stat1, stat2)
 718  
 719                  SELECT 'logins' AS stattype, $nextstartweek AS timeend, ".SITEID." as courseid, 0,
 720                         COALESCE((SELECT SUM(statsreads)
 721                                     FROM {stats_user_weekly} s1
 722                                    WHERE s1.stattype = 'logins' AND timeend = $nextstartweek), 0) AS nstat1,
 723                         (SELECT COUNT('x')
 724                            FROM {stats_user_weekly} s2
 725                           WHERE s2.stattype = 'logins' AND timeend = $nextstartweek) AS nstat2" .
 726                  $DB->sql_null_from_clause();
 727  
 728          $DB->execute($sql);
 729  
 730          stats_progress('2');
 731  
 732      /// now enrolments averages
 733          $sql = "INSERT INTO {stats_weekly} (stattype, timeend, courseid, roleid, stat1, stat2)
 734  
 735                  SELECT 'enrolments', ntimeend, courseid, roleid, " . $DB->sql_ceil('AVG(stat1)') . ", " . $DB->sql_ceil('AVG(stat2)') . "
 736                    FROM (
 737                             SELECT $nextstartweek AS ntimeend, courseid, roleid, stat1, stat2
 738                               FROM {stats_daily} sd
 739                              WHERE stattype = 'enrolments' AND $stattimesql
 740                         ) inline_view
 741                GROUP BY ntimeend, courseid, roleid";
 742  
 743          $DB->execute($sql);
 744  
 745          stats_progress('3');
 746  
 747      /// activity read/write averages
 748          $sql = "INSERT INTO {stats_weekly} (stattype, timeend, courseid, roleid, stat1, stat2)
 749  
 750                  SELECT 'activity', ntimeend, courseid, roleid, SUM(stat1), SUM(stat2)
 751                    FROM (
 752                             SELECT $nextstartweek AS ntimeend, courseid, roleid, stat1, stat2
 753                               FROM {stats_daily}
 754                              WHERE stattype = 'activity' AND $stattimesql
 755                         ) inline_view
 756                GROUP BY ntimeend, courseid, roleid";
 757  
 758          $DB->execute($sql);
 759  
 760          stats_progress('4');
 761  
 762      /// user read/write averages
 763          $sql = "INSERT INTO {stats_user_weekly} (stattype, timeend, courseid, userid, statsreads, statswrites)
 764  
 765                  SELECT 'activity', ntimeend, courseid, userid, SUM(statsreads), SUM(statswrites)
 766                    FROM (
 767                             SELECT $nextstartweek AS ntimeend, courseid, userid, statsreads, statswrites
 768                               FROM {stats_user_daily}
 769                              WHERE stattype = 'activity' AND $stattimesql
 770                         ) inline_view
 771                GROUP BY ntimeend, courseid, userid";
 772  
 773          $DB->execute($sql);
 774  
 775          stats_progress('5');
 776  
 777          set_config('statslastweekly', $nextstartweek);
 778          $elapsed = time()-$weekstart;
 779          mtrace(" finished until $nextstartweek: ".userdate($nextstartweek) ." (in $elapsed s)");
 780  
 781          $timestart     = $nextstartweek;
 782          $nextstartweek = stats_get_next_week_start($nextstartweek);
 783      }
 784  
 785      set_cron_lock('statsrunning', null);
 786      mtrace("...completed $weeks weeks of statistics.");
 787      return true;
 788  }
 789  
 790  /**
 791   * Execute monthly statistics gathering
 792   * @return boolean success
 793   */
 794  function stats_cron_monthly() {
 795      global $CFG, $DB;
 796      require_once($CFG->libdir.'/adminlib.php');
 797  
 798      $now = time();
 799  
 800      // read last execution date from db
 801      if (!$timestart = get_config(NULL, 'statslastmonthly')) {
 802          $timestart = stats_get_base_monthly(stats_get_start_from('monthly'));
 803          set_config('statslastmonthly', $timestart);
 804      }
 805  
 806      $nextstartmonth = stats_get_next_month_start($timestart);
 807  
 808      // are there any months that need to be processed?
 809      if ($now < $nextstartmonth) {
 810          return true; // everything ok and up-to-date
 811      }
 812  
 813      $timeout = empty($CFG->statsmaxruntime) ? 60*60*24 : $CFG->statsmaxruntime;
 814  
 815      if (!set_cron_lock('statsrunning', $now + $timeout)) {
 816          return false;
 817      }
 818  
 819      // fisr delete entries that should not be there yet
 820      $DB->delete_records_select('stats_monthly', "timeend > $timestart");
 821      $DB->delete_records_select('stats_user_monthly', "timeend > $timestart");
 822  
 823      $startmonth = stats_get_base_monthly($now);
 824  
 825  
 826      mtrace("Running monthly statistics gathering, starting at $timestart:");
 827      cron_trace_time_and_memory();
 828  
 829      $months = 0;
 830      while ($now > $nextstartmonth) {
 831          core_php_time_limit::raise($timeout - 200);
 832          $months++;
 833  
 834          if ($months > 1) {
 835              // move the lock
 836              set_cron_lock('statsrunning', time() + $timeout, true);
 837          }
 838  
 839          $stattimesql = "timeend > $timestart AND timeend <= $nextstartmonth";
 840  
 841          $monthstart = time();
 842          stats_progress('init');
 843  
 844      /// process login info first
 845          $sql = "INSERT INTO {stats_user_monthly} (stattype, timeend, courseid, userid, statsreads)
 846  
 847                  SELECT 'logins', timeend, courseid, userid, SUM(statsreads)
 848                    FROM (
 849                             SELECT $nextstartmonth AS timeend, courseid, userid, statsreads
 850                               FROM {stats_user_daily} sd
 851                              WHERE stattype = 'logins' AND $stattimesql
 852                         ) inline_view
 853                GROUP BY timeend, courseid, userid
 854                  HAVING SUM(statsreads) > 0";
 855  
 856          $DB->execute($sql);
 857  
 858          stats_progress('1');
 859  
 860          $sql = "INSERT INTO {stats_monthly} (stattype, timeend, courseid, roleid, stat1, stat2)
 861  
 862                  SELECT 'logins' AS stattype, $nextstartmonth AS timeend, ".SITEID." as courseid, 0,
 863                         COALESCE((SELECT SUM(statsreads)
 864                                     FROM {stats_user_monthly} s1
 865                                    WHERE s1.stattype = 'logins' AND timeend = $nextstartmonth), 0) AS nstat1,
 866                         (SELECT COUNT('x')
 867                            FROM {stats_user_monthly} s2
 868                           WHERE s2.stattype = 'logins' AND timeend = $nextstartmonth) AS nstat2" .
 869                  $DB->sql_null_from_clause();
 870  
 871          $DB->execute($sql);
 872  
 873          stats_progress('2');
 874  
 875      /// now enrolments averages
 876          $sql = "INSERT INTO {stats_monthly} (stattype, timeend, courseid, roleid, stat1, stat2)
 877  
 878                  SELECT 'enrolments', ntimeend, courseid, roleid, " . $DB->sql_ceil('AVG(stat1)') . ", " . $DB->sql_ceil('AVG(stat2)') . "
 879                    FROM (
 880                             SELECT $nextstartmonth AS ntimeend, courseid, roleid, stat1, stat2
 881                               FROM {stats_daily} sd
 882                              WHERE stattype = 'enrolments' AND $stattimesql
 883                         ) inline_view
 884                GROUP BY ntimeend, courseid, roleid";
 885  
 886          $DB->execute($sql);
 887  
 888          stats_progress('3');
 889  
 890      /// activity read/write averages
 891          $sql = "INSERT INTO {stats_monthly} (stattype, timeend, courseid, roleid, stat1, stat2)
 892  
 893                  SELECT 'activity', ntimeend, courseid, roleid, SUM(stat1), SUM(stat2)
 894                    FROM (
 895                             SELECT $nextstartmonth AS ntimeend, courseid, roleid, stat1, stat2
 896                               FROM {stats_daily}
 897                              WHERE stattype = 'activity' AND $stattimesql
 898                         ) inline_view
 899                GROUP BY ntimeend, courseid, roleid";
 900  
 901          $DB->execute($sql);
 902  
 903          stats_progress('4');
 904  
 905      /// user read/write averages
 906          $sql = "INSERT INTO {stats_user_monthly} (stattype, timeend, courseid, userid, statsreads, statswrites)
 907  
 908                  SELECT 'activity', ntimeend, courseid, userid, SUM(statsreads), SUM(statswrites)
 909                    FROM (
 910                             SELECT $nextstartmonth AS ntimeend, courseid, userid, statsreads, statswrites
 911                               FROM {stats_user_daily}
 912                              WHERE stattype = 'activity' AND $stattimesql
 913                         ) inline_view
 914                GROUP BY ntimeend, courseid, userid";
 915  
 916          $DB->execute($sql);
 917  
 918          stats_progress('5');
 919  
 920          set_config('statslastmonthly', $nextstartmonth);
 921          $elapsed = time() - $monthstart;
 922          mtrace(" finished until $nextstartmonth: ".userdate($nextstartmonth) ." (in $elapsed s)");
 923  
 924          $timestart      = $nextstartmonth;
 925          $nextstartmonth = stats_get_next_month_start($nextstartmonth);
 926      }
 927  
 928      set_cron_lock('statsrunning', null);
 929      mtrace("...completed $months months of statistics.");
 930      return true;
 931  }
 932  
 933  /**
 934   * Return starting date of stats processing
 935   * @param string $str name of table - daily, weekly or monthly
 936   * @return int timestamp
 937   */
 938  function stats_get_start_from($str) {
 939      global $CFG, $DB;
 940  
 941      // are there any data in stats table? Should not be...
 942      if ($timeend = $DB->get_field_sql('SELECT MAX(timeend) FROM {stats_'.$str.'}')) {
 943          return $timeend;
 944      }
 945      // decide what to do based on our config setting (either all or none or a timestamp)
 946      switch ($CFG->statsfirstrun) {
 947          case 'all':
 948              $manager = get_log_manager();
 949              $stores = $manager->get_readers();
 950              $firstlog = false;
 951              foreach ($stores as $store) {
 952                  if ($store instanceof \core\log\sql_internal_table_reader) {
 953                      $logtable = $store->get_internal_log_table_name();
 954                      if (!$logtable) {
 955                          continue;
 956                      }
 957                      $first = $DB->get_field_sql("SELECT MIN(timecreated) FROM {{$logtable}}");
 958                      if ($first and (!$firstlog or $firstlog > $first)) {
 959                          $firstlog = $first;
 960                      }
 961                  }
 962              }
 963  
 964              $first = $DB->get_field_sql('SELECT MIN(time) FROM {log}');
 965              if ($first and (!$firstlog or $firstlog > $first)) {
 966                  $firstlog = $first;
 967              }
 968  
 969              if ($firstlog) {
 970                  return $firstlog;
 971              }
 972  
 973          default:
 974              if (is_numeric($CFG->statsfirstrun)) {
 975                  return time() - $CFG->statsfirstrun;
 976              }
 977              // not a number? use next instead
 978          case 'none':
 979              return strtotime('-3 day', time());
 980      }
 981  }
 982  
 983  /**
 984   * Start of day
 985   * @param int $time timestamp
 986   * @return int start of day
 987   */
 988  function stats_get_base_daily($time=0) {
 989      if (empty($time)) {
 990          $time = time();
 991      }
 992  
 993      core_date::set_default_server_timezone();
 994      $time = strtotime(date('d-M-Y', $time));
 995  
 996      return $time;
 997  }
 998  
 999  /**
1000   * Start of week
1001   * @param int $time timestamp
1002   * @return int start of week
1003   */
1004  function stats_get_base_weekly($time=0) {
1005      global $CFG;
1006  
1007      $time = stats_get_base_daily($time);
1008      $startday = $CFG->calendar_startwday;
1009  
1010      core_date::set_default_server_timezone();
1011      $thisday = date('w', $time);
1012  
1013      if ($thisday > $startday) {
1014          $time = $time - (($thisday - $startday) * 60*60*24);
1015      } else if ($thisday < $startday) {
1016          $time = $time - ((7 + $thisday - $startday) * 60*60*24);
1017      }
1018      return $time;
1019  }
1020  
1021  /**
1022   * Start of month
1023   * @param int $time timestamp
1024   * @return int start of month
1025   */
1026  function stats_get_base_monthly($time=0) {
1027      if (empty($time)) {
1028          $time = time();
1029      }
1030  
1031      core_date::set_default_server_timezone();
1032      $return = strtotime(date('1-M-Y', $time));
1033  
1034      return $return;
1035  }
1036  
1037  /**
1038   * Start of next day
1039   * @param int $time timestamp
1040   * @return start of next day
1041   */
1042  function stats_get_next_day_start($time) {
1043      $next = stats_get_base_daily($time);
1044      $next = $next + 60*60*26;
1045      $next = stats_get_base_daily($next);
1046      if ($next <= $time) {
1047          //DST trouble - prevent infinite loops
1048          $next = $next + 60*60*24;
1049      }
1050      return $next;
1051  }
1052  
1053  /**
1054   * Start of next week
1055   * @param int $time timestamp
1056   * @return start of next week
1057   */
1058  function stats_get_next_week_start($time) {
1059      $next = stats_get_base_weekly($time);
1060      $next = $next + 60*60*24*9;
1061      $next = stats_get_base_weekly($next);
1062      if ($next <= $time) {
1063          //DST trouble - prevent infinite loops
1064          $next = $next + 60*60*24*7;
1065      }
1066      return $next;
1067  }
1068  
1069  /**
1070   * Start of next month
1071   * @param int $time timestamp
1072   * @return start of next month
1073   */
1074  function stats_get_next_month_start($time) {
1075      $next = stats_get_base_monthly($time);
1076      $next = $next + 60*60*24*33;
1077      $next = stats_get_base_monthly($next);
1078      if ($next <= $time) {
1079          //DST trouble - prevent infinite loops
1080          $next = $next + 60*60*24*31;
1081      }
1082      return $next;
1083  }
1084  
1085  /**
1086   * Remove old stats data
1087   */
1088  function stats_clean_old() {
1089      global $DB;
1090      mtrace("Running stats cleanup tasks...");
1091      cron_trace_time_and_memory();
1092      $deletebefore =  stats_get_base_monthly();
1093  
1094      // delete dailies older than 3 months (to be safe)
1095      $deletebefore = strtotime('-3 months', $deletebefore);
1096      $DB->delete_records_select('stats_daily',      "timeend < $deletebefore");
1097      $DB->delete_records_select('stats_user_daily', "timeend < $deletebefore");
1098  
1099      // delete weeklies older than 9  months (to be safe)
1100      $deletebefore = strtotime('-6 months', $deletebefore);
1101      $DB->delete_records_select('stats_weekly',      "timeend < $deletebefore");
1102      $DB->delete_records_select('stats_user_weekly', "timeend < $deletebefore");
1103  
1104      // don't delete monthlies
1105  
1106      mtrace("...stats cleanup finished");
1107  }
1108  
1109  function stats_get_parameters($time,$report,$courseid,$mode,$roleid=0) {
1110      global $CFG, $DB;
1111  
1112      $param = new stdClass();
1113      $param->params = array();
1114  
1115      if ($time < 10) { // dailies
1116          // number of days to go back = 7* time
1117          $param->table = 'daily';
1118          $param->timeafter = strtotime("-".($time*7)." days",stats_get_base_daily());
1119      } elseif ($time < 20) { // weeklies
1120          // number of weeks to go back = time - 10 * 4 (weeks) + base week
1121          $param->table = 'weekly';
1122          $param->timeafter = strtotime("-".(($time - 10)*4)." weeks",stats_get_base_weekly());
1123      } else { // monthlies.
1124          // number of months to go back = time - 20 * months + base month
1125          $param->table = 'monthly';
1126          $param->timeafter = strtotime("-".($time - 20)." months",stats_get_base_monthly());
1127      }
1128  
1129      $param->extras = '';
1130  
1131      switch ($report) {
1132      // ******************** STATS_MODE_GENERAL ******************** //
1133      case STATS_REPORT_LOGINS:
1134          $param->fields = 'timeend,sum(stat1) as line1,sum(stat2) as line2';
1135          $param->fieldscomplete = true;
1136          $param->stattype = 'logins';
1137          $param->line1 = get_string('statslogins');
1138          $param->line2 = get_string('statsuniquelogins');
1139          if ($courseid == SITEID) {
1140              $param->extras = 'GROUP BY timeend';
1141          }
1142          break;
1143  
1144      case STATS_REPORT_READS:
1145          $param->fields = $DB->sql_concat('timeend','roleid').' AS uniqueid, timeend, roleid, stat1 as line1';
1146          $param->fieldscomplete = true; // set this to true to avoid anything adding stuff to the list and breaking complex queries.
1147          $param->aggregategroupby = 'roleid';
1148          $param->stattype = 'activity';
1149          $param->crosstab = true;
1150          $param->extras = 'GROUP BY timeend,roleid,stat1';
1151          if ($courseid == SITEID) {
1152              $param->fields = $DB->sql_concat('timeend','roleid').' AS uniqueid, timeend, roleid, sum(stat1) as line1';
1153              $param->extras = 'GROUP BY timeend,roleid';
1154          }
1155          break;
1156  
1157      case STATS_REPORT_WRITES:
1158          $param->fields = $DB->sql_concat('timeend','roleid').' AS uniqueid, timeend, roleid, stat2 as line1';
1159          $param->fieldscomplete = true; // set this to true to avoid anything adding stuff to the list and breaking complex queries.
1160          $param->aggregategroupby = 'roleid';
1161          $param->stattype = 'activity';
1162          $param->crosstab = true;
1163          $param->extras = 'GROUP BY timeend,roleid,stat2';
1164          if ($courseid == SITEID) {
1165              $param->fields = $DB->sql_concat('timeend','roleid').' AS uniqueid, timeend, roleid, sum(stat2) as line1';
1166              $param->extras = 'GROUP BY timeend,roleid';
1167          }
1168          break;
1169  
1170      case STATS_REPORT_ACTIVITY:
1171          $param->fields = $DB->sql_concat('timeend','roleid').' AS uniqueid, timeend, roleid, sum(stat1+stat2) as line1';
1172          $param->fieldscomplete = true; // set this to true to avoid anything adding stuff to the list and breaking complex queries.
1173          $param->aggregategroupby = 'roleid';
1174          $param->stattype = 'activity';
1175          $param->crosstab = true;
1176          $param->extras = 'GROUP BY timeend,roleid';
1177          if ($courseid == SITEID) {
1178              $param->extras = 'GROUP BY timeend,roleid';
1179          }
1180          break;
1181  
1182      case STATS_REPORT_ACTIVITYBYROLE;
1183          $param->fields = 'stat1 AS line1, stat2 AS line2';
1184          $param->stattype = 'activity';
1185          $rolename = $DB->get_field('role','name', array('id'=>$roleid));
1186          $param->line1 = $rolename . get_string('statsreads');
1187          $param->line2 = $rolename . get_string('statswrites');
1188          if ($courseid == SITEID) {
1189              $param->extras = 'GROUP BY timeend';
1190          }
1191          break;
1192  
1193      // ******************** STATS_MODE_DETAILED ******************** //
1194      case STATS_REPORT_USER_ACTIVITY:
1195          $param->fields = 'statsreads as line1, statswrites as line2';
1196          $param->line1 = get_string('statsuserreads');
1197          $param->line2 = get_string('statsuserwrites');
1198          $param->stattype = 'activity';
1199          break;
1200  
1201      case STATS_REPORT_USER_ALLACTIVITY:
1202          $param->fields = 'statsreads+statswrites as line1';
1203          $param->line1 = get_string('statsuseractivity');
1204          $param->stattype = 'activity';
1205          break;
1206  
1207      case STATS_REPORT_USER_LOGINS:
1208          $param->fields = 'statsreads as line1';
1209          $param->line1 = get_string('statsuserlogins');
1210          $param->stattype = 'logins';
1211          break;
1212  
1213      case STATS_REPORT_USER_VIEW:
1214          $param->fields = 'statsreads as line1, statswrites as line2, statsreads+statswrites as line3';
1215          $param->line1 = get_string('statsuserreads');
1216          $param->line2 = get_string('statsuserwrites');
1217          $param->line3 = get_string('statsuseractivity');
1218          $param->stattype = 'activity';
1219          break;
1220  
1221      // ******************** STATS_MODE_RANKED ******************** //
1222      case STATS_REPORT_ACTIVE_COURSES:
1223          $param->fields = 'sum(stat1+stat2) AS line1';
1224          $param->stattype = 'activity';
1225          $param->orderby = 'line1 DESC';
1226          $param->line1 = get_string('useractivity');
1227          $param->graphline = 'line1';
1228          break;
1229  
1230      case STATS_REPORT_ACTIVE_COURSES_WEIGHTED:
1231          $threshold = 0;
1232          if (!empty($CFG->statsuserthreshold) && is_numeric($CFG->statsuserthreshold)) {
1233              $threshold = $CFG->statsuserthreshold;
1234          }
1235          $param->fields = '';
1236          $param->sql = 'SELECT activity.courseid, activity.all_activity AS line1, enrolments.highest_enrolments AS line2,
1237                          activity.all_activity / enrolments.highest_enrolments as line3
1238                         FROM (
1239                              SELECT courseid, sum(stat1+stat2) AS all_activity
1240                                FROM {stats_'.$param->table.'}
1241                               WHERE stattype=\'activity\' AND timeend >= '.(int)$param->timeafter.' AND roleid = 0 GROUP BY courseid
1242                         ) activity
1243                         INNER JOIN
1244                              (
1245                              SELECT courseid, max(stat1) AS highest_enrolments
1246                                FROM {stats_'.$param->table.'}
1247                               WHERE stattype=\'enrolments\' AND timeend >= '.(int)$param->timeafter.' AND stat1 > '.(int)$threshold.'
1248                            GROUP BY courseid
1249                        ) enrolments
1250                        ON (activity.courseid = enrolments.courseid)
1251                        ORDER BY line3 DESC';
1252          $param->line1 = get_string('useractivity');
1253          $param->line2 = get_string('users');
1254          $param->line3 = get_string('activityweighted');
1255          $param->graphline = 'line3';
1256          break;
1257  
1258      case STATS_REPORT_PARTICIPATORY_COURSES:
1259          $threshold = 0;
1260          if (!empty($CFG->statsuserthreshold) && is_numeric($CFG->statsuserthreshold)) {
1261              $threshold = $CFG->statsuserthreshold;
1262          }
1263          $param->fields = '';
1264          $param->sql = 'SELECT courseid, ' . $DB->sql_ceil('avg(all_enrolments)') . ' as line1, ' .
1265                           $DB->sql_ceil('avg(active_enrolments)') . ' as line2, avg(proportion_active) AS line3
1266                         FROM (
1267                             SELECT courseid, timeend, stat2 as active_enrolments,
1268                                    stat1 as all_enrolments, '.$DB->sql_cast_char2real('stat2').'/'.$DB->sql_cast_char2real('stat1').' AS proportion_active
1269                               FROM {stats_'.$param->table.'}
1270                              WHERE stattype=\'enrolments\' AND roleid = 0 AND stat1 > '.(int)$threshold.'
1271                         ) aq
1272                         WHERE timeend >= '.(int)$param->timeafter.'
1273                         GROUP BY courseid
1274                         ORDER BY line3 DESC';
1275  
1276          $param->line1 = get_string('users');
1277          $param->line2 = get_string('activeusers');
1278          $param->line3 = get_string('participationratio');
1279          $param->graphline = 'line3';
1280          break;
1281  
1282      case STATS_REPORT_PARTICIPATORY_COURSES_RW:
1283          $param->fields = '';
1284          $param->sql =  'SELECT courseid, sum(views) AS line1, sum(posts) AS line2,
1285                             avg(proportion_active) AS line3
1286                           FROM (
1287                             SELECT courseid, timeend, stat1 as views, stat2 AS posts,
1288                                    '.$DB->sql_cast_char2real('stat2').'/'.$DB->sql_cast_char2real('stat1').' as proportion_active
1289                               FROM {stats_'.$param->table.'}
1290                              WHERE stattype=\'activity\' AND roleid = 0 AND stat1 > 0
1291                         ) aq
1292                         WHERE timeend >= '.(int)$param->timeafter.'
1293                         GROUP BY courseid
1294                         ORDER BY line3 DESC';
1295          $param->line1 = get_string('views');
1296          $param->line2 = get_string('posts');
1297          $param->line3 = get_string('participationratio');
1298          $param->graphline = 'line3';
1299          break;
1300      }
1301  
1302      /*
1303      if ($courseid == SITEID && $mode != STATS_MODE_RANKED) { // just aggregate all courses.
1304          $param->fields = preg_replace('/(?:sum)([a-zA-Z0-9+_]*)\W+as\W+([a-zA-Z0-9_]*)/i','sum($1) as $2',$param->fields);
1305          $param->extras = ' GROUP BY timeend'.((!empty($param->aggregategroupby)) ? ','.$param->aggregategroupby : '');
1306      }
1307      */
1308      //TODO must add the SITEID reports to the rest of the reports.
1309      return $param;
1310  }
1311  
1312  function stats_get_view_actions() {
1313      return array('view','view all','history');
1314  }
1315  
1316  function stats_get_post_actions() {
1317      return array('add','delete','edit','add mod','delete mod','edit section'.'enrol','loginas','new','unenrol','update','update mod');
1318  }
1319  
1320  function stats_get_action_names($str) {
1321      global $CFG, $DB;
1322  
1323      $mods = $DB->get_records('modules');
1324      $function = 'stats_get_'.$str.'_actions';
1325      $actions = $function();
1326      foreach ($mods as $mod) {
1327          $file = $CFG->dirroot.'/mod/'.$mod->name.'/lib.php';
1328          if (!is_readable($file)) {
1329              continue;
1330          }
1331          require_once($file);
1332          $function = $mod->name.'_get_'.$str.'_actions';
1333          if (function_exists($function)) {
1334              $mod_actions = $function();
1335              if (is_array($mod_actions)) {
1336                  $actions = array_merge($actions, $mod_actions);
1337              }
1338          }
1339      }
1340  
1341      // The array_values() forces a stack-like array
1342      // so we can later loop over safely...
1343      $actions =  array_values(array_unique($actions));
1344      $c = count($actions);
1345      for ($n=0;$n<$c;$n++) {
1346          $actions[$n] = $actions[$n];
1347      }
1348      return $actions;
1349  }
1350  
1351  function stats_get_time_options($now,$lastweekend,$lastmonthend,$earliestday,$earliestweek,$earliestmonth) {
1352  
1353      $now = stats_get_base_daily(time());
1354      // it's really important that it's TIMEEND in the table. ie, tuesday 00:00:00 is monday night.
1355      // so we need to take a day off here (essentially add a day to $now
1356      $now += 60*60*24;
1357  
1358      $timeoptions = array();
1359  
1360      if ($now - (60*60*24*7) >= $earliestday) {
1361          $timeoptions[STATS_TIME_LASTWEEK] = get_string('numweeks','moodle',1);
1362      }
1363      if ($now - (60*60*24*14) >= $earliestday) {
1364          $timeoptions[STATS_TIME_LAST2WEEKS] = get_string('numweeks','moodle',2);
1365      }
1366      if ($now - (60*60*24*21) >= $earliestday) {
1367          $timeoptions[STATS_TIME_LAST3WEEKS] = get_string('numweeks','moodle',3);
1368      }
1369      if ($now - (60*60*24*28) >= $earliestday) {
1370          $timeoptions[STATS_TIME_LAST4WEEKS] = get_string('numweeks','moodle',4);// show dailies up to (including) here.
1371      }
1372      if ($lastweekend - (60*60*24*56) >= $earliestweek) {
1373          $timeoptions[STATS_TIME_LAST2MONTHS] = get_string('nummonths','moodle',2);
1374      }
1375      if ($lastweekend - (60*60*24*84) >= $earliestweek) {
1376          $timeoptions[STATS_TIME_LAST3MONTHS] = get_string('nummonths','moodle',3);
1377      }
1378      if ($lastweekend - (60*60*24*112) >= $earliestweek) {
1379          $timeoptions[STATS_TIME_LAST4MONTHS] = get_string('nummonths','moodle',4);
1380      }
1381      if ($lastweekend - (60*60*24*140) >= $earliestweek) {
1382          $timeoptions[STATS_TIME_LAST5MONTHS] = get_string('nummonths','moodle',5);
1383      }
1384      if ($lastweekend - (60*60*24*168) >= $earliestweek) {
1385          $timeoptions[STATS_TIME_LAST6MONTHS] = get_string('nummonths','moodle',6); // show weeklies up to (including) here
1386      }
1387      if (strtotime('-7 months',$lastmonthend) >= $earliestmonth) {
1388          $timeoptions[STATS_TIME_LAST7MONTHS] = get_string('nummonths','moodle',7);
1389      }
1390      if (strtotime('-8 months',$lastmonthend) >= $earliestmonth) {
1391          $timeoptions[STATS_TIME_LAST8MONTHS] = get_string('nummonths','moodle',8);
1392      }
1393      if (strtotime('-9 months',$lastmonthend) >= $earliestmonth) {
1394          $timeoptions[STATS_TIME_LAST9MONTHS] = get_string('nummonths','moodle',9);
1395      }
1396      if (strtotime('-10 months',$lastmonthend) >= $earliestmonth) {
1397          $timeoptions[STATS_TIME_LAST10MONTHS] = get_string('nummonths','moodle',10);
1398      }
1399      if (strtotime('-11 months',$lastmonthend) >= $earliestmonth) {
1400          $timeoptions[STATS_TIME_LAST11MONTHS] = get_string('nummonths','moodle',11);
1401      }
1402      if (strtotime('-1 year',$lastmonthend) >= $earliestmonth) {
1403          $timeoptions[STATS_TIME_LASTYEAR] = get_string('lastyear');
1404      }
1405  
1406      $years = (int)date('y', $now) - (int)date('y', $earliestmonth);
1407      if ($years > 1) {
1408          for($i = 2; $i <= $years; $i++) {
1409              $timeoptions[$i*12+20] = get_string('numyears', 'moodle', $i);
1410          }
1411      }
1412  
1413      return $timeoptions;
1414  }
1415  
1416  function stats_get_report_options($courseid,$mode) {
1417      global $CFG, $DB;
1418  
1419      $reportoptions = array();
1420  
1421      switch ($mode) {
1422      case STATS_MODE_GENERAL:
1423          $reportoptions[STATS_REPORT_ACTIVITY] = get_string('statsreport'.STATS_REPORT_ACTIVITY);
1424          if ($courseid != SITEID && $context = context_course::instance($courseid)) {
1425              $sql = 'SELECT r.id, r.name FROM {role} r JOIN {stats_daily} s ON s.roleid = r.id WHERE s.courseid = :courseid GROUP BY r.id, r.name';
1426              if ($roles = $DB->get_records_sql($sql, array('courseid' => $courseid))) {
1427                  foreach ($roles as $role) {
1428                      $reportoptions[STATS_REPORT_ACTIVITYBYROLE.$role->id] = get_string('statsreport'.STATS_REPORT_ACTIVITYBYROLE). ' '.$role->name;
1429                  }
1430              }
1431          }
1432          $reportoptions[STATS_REPORT_READS] = get_string('statsreport'.STATS_REPORT_READS);
1433          $reportoptions[STATS_REPORT_WRITES] = get_string('statsreport'.STATS_REPORT_WRITES);
1434          if ($courseid == SITEID) {
1435              $reportoptions[STATS_REPORT_LOGINS] = get_string('statsreport'.STATS_REPORT_LOGINS);
1436          }
1437  
1438          break;
1439      case STATS_MODE_DETAILED:
1440          $reportoptions[STATS_REPORT_USER_ACTIVITY] = get_string('statsreport'.STATS_REPORT_USER_ACTIVITY);
1441          $reportoptions[STATS_REPORT_USER_ALLACTIVITY] = get_string('statsreport'.STATS_REPORT_USER_ALLACTIVITY);
1442          if (has_capability('report/stats:view', context_system::instance())) {
1443              $site = get_site();
1444              $reportoptions[STATS_REPORT_USER_LOGINS] = get_string('statsreport'.STATS_REPORT_USER_LOGINS);
1445          }
1446          break;
1447      case STATS_MODE_RANKED:
1448          if (has_capability('report/stats:view', context_system::instance())) {
1449              $reportoptions[STATS_REPORT_ACTIVE_COURSES] = get_string('statsreport'.STATS_REPORT_ACTIVE_COURSES);
1450              $reportoptions[STATS_REPORT_ACTIVE_COURSES_WEIGHTED] = get_string('statsreport'.STATS_REPORT_ACTIVE_COURSES_WEIGHTED);
1451              $reportoptions[STATS_REPORT_PARTICIPATORY_COURSES] = get_string('statsreport'.STATS_REPORT_PARTICIPATORY_COURSES);
1452              $reportoptions[STATS_REPORT_PARTICIPATORY_COURSES_RW] = get_string('statsreport'.STATS_REPORT_PARTICIPATORY_COURSES_RW);
1453          }
1454          break;
1455      }
1456  
1457      return $reportoptions;
1458  }
1459  
1460  /**
1461   * Fix missing entries in the statistics.
1462   *
1463   * This creates a dummy stat when nothing happened during a day/week/month.
1464   *
1465   * @param array $stats array of statistics.
1466   * @param int $timeafter unused.
1467   * @param string $timestr type of statistics to generate (dayly, weekly, monthly).
1468   * @param boolean $line2
1469   * @param boolean $line3
1470   * @return array of fixed statistics.
1471   */
1472  function stats_fix_zeros($stats,$timeafter,$timestr,$line2=true,$line3=false) {
1473  
1474      if (empty($stats)) {
1475          return;
1476      }
1477  
1478      $timestr = str_replace('user_','',$timestr); // just in case.
1479  
1480      // Gets the current user base time.
1481      $fun = 'stats_get_base_'.$timestr;
1482      $now = $fun();
1483  
1484      // Extract the ending time of the statistics.
1485      $actualtimes = array();
1486      $actualtimeshour = null;
1487      foreach ($stats as $statid => $s) {
1488          // Normalise the month date to the 1st if for any reason it's set to later. But we ignore
1489          // anything above or equal to 29 because sometimes we get the end of the month. Also, we will
1490          // set the hours of the result to all of them, that way we prevent DST differences.
1491          if ($timestr == 'monthly') {
1492              $day = date('d', $s->timeend);
1493              if (date('d', $s->timeend) > 1 && date('d', $s->timeend) < 29) {
1494                  $day = 1;
1495              }
1496              if (is_null($actualtimeshour)) {
1497                  $actualtimeshour = date('H', $s->timeend);
1498              }
1499              $s->timeend = mktime($actualtimeshour, 0, 0, date('m', $s->timeend), $day, date('Y', $s->timeend));
1500          }
1501          $stats[$statid] = $s;
1502          $actualtimes[] = $s->timeend;
1503      }
1504  
1505      $actualtimesvalues = array_values($actualtimes);
1506      $timeafter = array_pop($actualtimesvalues);
1507  
1508      // Generate a base timestamp for each possible month/week/day.
1509      $times = array();
1510      while ($timeafter < $now) {
1511          $times[] = $timeafter;
1512          if ($timestr == 'daily') {
1513              $timeafter = stats_get_next_day_start($timeafter);
1514          } else if ($timestr == 'weekly') {
1515              $timeafter = stats_get_next_week_start($timeafter);
1516          } else if ($timestr == 'monthly') {
1517              // We can't just simply +1 month because the 31st Jan + 1 month = 2nd of March.
1518              $year = date('Y', $timeafter);
1519              $month = date('m', $timeafter);
1520              $day = date('d', $timeafter);
1521              $dayofnextmonth = $day;
1522              if ($day >= 29) {
1523                  $daysinmonth = date('n', mktime(0, 0, 0, $month+1, 1, $year));
1524                  if ($day > $daysinmonth) {
1525                      $dayofnextmonth = $daysinmonth;
1526                  }
1527              }
1528              $timeafter = mktime($actualtimeshour, 0, 0, $month+1, $dayofnextmonth, $year);
1529          } else {
1530              // This will put us in a never ending loop.
1531              return $stats;
1532          }
1533      }
1534  
1535      // Add the base timestamp to the statistics if not present.
1536      foreach ($times as $count => $time) {
1537          if (!in_array($time,$actualtimes) && $count != count($times) -1) {
1538              $newobj = new StdClass;
1539              $newobj->timeend = $time;
1540              $newobj->id = 0;
1541              $newobj->roleid = 0;
1542              $newobj->line1 = 0;
1543              if (!empty($line2)) {
1544                  $newobj->line2 = 0;
1545              }
1546              if (!empty($line3)) {
1547                  $newobj->line3 = 0;
1548              }
1549              $newobj->zerofixed = true;
1550              $stats[] = $newobj;
1551          }
1552      }
1553  
1554      usort($stats,"stats_compare_times");
1555      return $stats;
1556  }
1557  
1558  // helper function to sort arrays by $obj->timeend
1559  function stats_compare_times($a,$b) {
1560     if ($a->timeend == $b->timeend) {
1561         return 0;
1562     }
1563     return ($a->timeend > $b->timeend) ? -1 : 1;
1564  }
1565  
1566  function stats_check_uptodate($courseid=0) {
1567      global $CFG, $DB;
1568  
1569      if (empty($courseid)) {
1570          $courseid = SITEID;
1571      }
1572  
1573      $latestday = stats_get_start_from('daily');
1574  
1575      if ((time() - 60*60*24*2) < $latestday) { // we're ok
1576          return NULL;
1577      }
1578  
1579      $a = new stdClass();
1580      $a->daysdone = $DB->get_field_sql("SELECT COUNT(DISTINCT(timeend)) FROM {stats_daily}");
1581  
1582      // how many days between the last day and now?
1583      $a->dayspending = ceil((stats_get_base_daily() - $latestday)/(60*60*24));
1584  
1585      if ($a->dayspending == 0 && $a->daysdone != 0) {
1586          return NULL; // we've only just started...
1587      }
1588  
1589      //return error as string
1590      return get_string('statscatchupmode','error',$a);
1591  }
1592  
1593  /**
1594   * Create temporary tables to speed up log generation
1595   */
1596  function stats_temp_table_create() {
1597      global $CFG, $DB;
1598  
1599      $dbman = $DB->get_manager(); // We are going to use database_manager services
1600  
1601      stats_temp_table_drop();
1602  
1603      $tables = array();
1604  
1605      /// Define tables user to be created
1606      $table = new xmldb_table('temp_stats_daily');
1607      $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1608      $table->add_field('courseid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, '0');
1609      $table->add_field('timeend', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, '0');
1610      $table->add_field('roleid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, '0');
1611      $table->add_field('stattype', XMLDB_TYPE_CHAR, 20, null, XMLDB_NOTNULL, null, 'activity');
1612      $table->add_field('stat1', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, '0');
1613      $table->add_field('stat2', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, '0');
1614      $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1615      $table->add_index('courseid', XMLDB_INDEX_NOTUNIQUE, array('courseid'));
1616      $table->add_index('timeend', XMLDB_INDEX_NOTUNIQUE, array('timeend'));
1617      $table->add_index('roleid', XMLDB_INDEX_NOTUNIQUE, array('roleid'));
1618      $tables['temp_stats_daily'] = $table;
1619  
1620      $table = new xmldb_table('temp_stats_user_daily');
1621      $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1622      $table->add_field('courseid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, '0');
1623      $table->add_field('userid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, '0');
1624      $table->add_field('roleid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, '0');
1625      $table->add_field('timeend', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, '0');
1626      $table->add_field('statsreads', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, '0');
1627      $table->add_field('statswrites', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, '0');
1628      $table->add_field('stattype', XMLDB_TYPE_CHAR, 30, null, XMLDB_NOTNULL, null, null);
1629      $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1630      $table->add_index('courseid', XMLDB_INDEX_NOTUNIQUE, array('courseid'));
1631      $table->add_index('userid', XMLDB_INDEX_NOTUNIQUE, array('userid'));
1632      $table->add_index('timeend', XMLDB_INDEX_NOTUNIQUE, array('timeend'));
1633      $table->add_index('roleid', XMLDB_INDEX_NOTUNIQUE, array('roleid'));
1634      $tables['temp_stats_user_daily'] = $table;
1635  
1636      $table = new xmldb_table('temp_enroled');
1637      $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1638      $table->add_field('userid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, '0');
1639      $table->add_field('courseid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, '0');
1640      $table->add_field('roleid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, null);
1641      $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1642      $table->add_index('userid', XMLDB_INDEX_NOTUNIQUE, array('userid'));
1643      $table->add_index('courseid', XMLDB_INDEX_NOTUNIQUE, array('courseid'));
1644      $table->add_index('roleid', XMLDB_INDEX_NOTUNIQUE, array('roleid'));
1645      $tables['temp_enroled'] = $table;
1646  
1647  
1648      $table = new xmldb_table('temp_log1');
1649      $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1650      $table->add_field('userid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, '0');
1651      $table->add_field('course', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, '0');
1652      $table->add_field('action', XMLDB_TYPE_CHAR, 40, null, XMLDB_NOTNULL, null, null);
1653      $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1654      $table->add_index('action', XMLDB_INDEX_NOTUNIQUE, array('action'));
1655      $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
1656      $table->add_index('user', XMLDB_INDEX_NOTUNIQUE, array('userid'));
1657      $table->add_index('usercourseaction', XMLDB_INDEX_NOTUNIQUE, array('userid','course','action'));
1658      $tables['temp_log1'] = $table;
1659  
1660      /// temp_log2 is exactly the same as temp_log1.
1661      $tables['temp_log2'] = clone $tables['temp_log1'];
1662      $tables['temp_log2']->setName('temp_log2');
1663  
1664      try {
1665  
1666          foreach ($tables as $table) {
1667              $dbman->create_temp_table($table);
1668          }
1669  
1670      } catch (Exception $e) {
1671          mtrace('Temporary table creation failed: '. $e->getMessage());
1672          return false;
1673      }
1674  
1675      return true;
1676  }
1677  
1678  /**
1679   * Deletes summary logs table for stats calculation
1680   */
1681  function stats_temp_table_drop() {
1682      global $DB;
1683  
1684      $dbman = $DB->get_manager();
1685  
1686      $tables = array('temp_log1', 'temp_log2', 'temp_stats_daily', 'temp_stats_user_daily', 'temp_enroled');
1687  
1688      foreach ($tables as $name) {
1689  
1690          if ($dbman->table_exists($name)) {
1691              $table = new xmldb_table($name);
1692  
1693              try {
1694                  $dbman->drop_table($table);
1695              } catch (Exception $e) {
1696                  mtrace("Error occured while dropping temporary tables!");
1697              }
1698          }
1699      }
1700  }
1701  
1702  /**
1703   * Fills the temporary stats tables with new data
1704   *
1705   * This function is meant to be called once at the start of stats generation
1706   *
1707   * @param int timestart timestamp of the start time of logs view
1708   * @param int timeend timestamp of the end time of logs view
1709   * @return bool success (true) or failure(false)
1710   */
1711  function stats_temp_table_setup() {
1712      global $DB;
1713  
1714      $sql = "INSERT INTO {temp_enroled} (userid, courseid, roleid)
1715  
1716                 SELECT ue.userid, e.courseid, ra.roleid
1717                  FROM {role_assignments} ra
1718                  JOIN {context} c ON (c.id = ra.contextid AND c.contextlevel = :courselevel)
1719                  JOIN {enrol} e ON e.courseid = c.instanceid
1720                  JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = ra.userid)";
1721  
1722      return stats_run_query($sql, array('courselevel' => CONTEXT_COURSE));
1723  }
1724  
1725  /**
1726   * Fills the temporary stats tables with new data
1727   *
1728   * This function is meant to be called to get a new day of data
1729   *
1730   * @param int timestamp of the start time of logs view
1731   * @param int timestamp of the end time of logs view
1732   * @return bool success (true) or failure(false)
1733   */
1734  function stats_temp_table_fill($timestart, $timeend) {
1735      global $DB;
1736  
1737      // First decide from where we want the data.
1738  
1739      $params = array('timestart' => $timestart,
1740                      'timeend' => $timeend,
1741                      'participating' => \core\event\base::LEVEL_PARTICIPATING,
1742                      'teaching' => \core\event\base::LEVEL_TEACHING,
1743                      'loginevent1' => '\core\event\user_loggedin',
1744                      'loginevent2' => '\core\event\user_loggedin',
1745      );
1746  
1747      $filled = false;
1748      $manager = get_log_manager();
1749      $stores = $manager->get_readers();
1750      foreach ($stores as $store) {
1751          if ($store instanceof \core\log\sql_internal_table_reader) {
1752              $logtable = $store->get_internal_log_table_name();
1753              if (!$logtable) {
1754                  continue;
1755              }
1756  
1757              $sql = "SELECT COUNT('x')
1758                        FROM {{$logtable}}
1759                       WHERE timecreated >= :timestart AND timecreated < :timeend";
1760  
1761              if (!$DB->get_field_sql($sql, $params)) {
1762                  continue;
1763              }
1764  
1765              // Let's fake the old records using new log data.
1766              // We want only data relevant to educational process
1767              // done by real users.
1768  
1769              $sql = "INSERT INTO {temp_log1} (userid, course, action)
1770  
1771              SELECT userid,
1772                     CASE
1773                        WHEN courseid IS NULL THEN ".SITEID."
1774                        WHEN courseid = 0 THEN ".SITEID."
1775                        ELSE courseid
1776                     END,
1777                     CASE
1778                         WHEN eventname = :loginevent1 THEN 'login'
1779                         WHEN crud = 'r' THEN 'view'
1780                         ELSE 'update'
1781                     END
1782                FROM {{$logtable}}
1783               WHERE timecreated >= :timestart AND timecreated < :timeend
1784                     AND (origin = 'web' OR origin = 'ws')
1785                     AND (edulevel = :participating OR edulevel = :teaching OR eventname = :loginevent2)";
1786  
1787              $DB->execute($sql, $params);
1788              $filled = true;
1789          }
1790      }
1791  
1792      if (!$filled) {
1793          // Fallback to legacy data.
1794          $sql = "INSERT INTO {temp_log1} (userid, course, action)
1795  
1796              SELECT userid, course, action
1797                FROM {log}
1798               WHERE time >= :timestart AND time < :timeend";
1799  
1800          $DB->execute($sql, $params);
1801      }
1802  
1803      $sql = 'INSERT INTO {temp_log2} (userid, course, action)
1804  
1805              SELECT userid, course, action FROM {temp_log1}';
1806  
1807      $DB->execute($sql);
1808  
1809      // We have just loaded all the temp tables, collect statistics for that.
1810      $DB->update_temp_table_stats();
1811  
1812      return true;
1813  }
1814  
1815  
1816  /**
1817   * Deletes summary logs table for stats calculation
1818   *
1819   * @return bool success (true) or failure(false)
1820   */
1821  function stats_temp_table_clean() {
1822      global $DB;
1823  
1824      $sql = array();
1825  
1826      $sql['up1'] = 'INSERT INTO {stats_daily} (courseid, roleid, stattype, timeend, stat1, stat2)
1827  
1828                     SELECT courseid, roleid, stattype, timeend, stat1, stat2 FROM {temp_stats_daily}';
1829  
1830      $sql['up2'] = 'INSERT INTO {stats_user_daily}
1831                                 (courseid, userid, roleid, timeend, statsreads, statswrites, stattype)
1832  
1833                     SELECT courseid, userid, roleid, timeend, statsreads, statswrites, stattype
1834                       FROM {temp_stats_user_daily}';
1835  
1836      foreach ($sql as $id => $query) {
1837          if (! stats_run_query($query)) {
1838              mtrace("Error during table cleanup!");
1839              return false;
1840          }
1841      }
1842  
1843      $tables = array('temp_log1', 'temp_log2', 'temp_stats_daily', 'temp_stats_user_daily');
1844  
1845      foreach ($tables as $name) {
1846          $DB->delete_records($name);
1847      }
1848  
1849      return true;
1850  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1