3 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
21 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 defined('MOODLE_INTERNAL') ||
die();
27 /** THESE CONSTANTS ARE USED FOR THE REPORTING PAGE. */
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.
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.
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);
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);
53 // start after 10 = show weeklies
54 define('STATS_TIME_LAST2MONTHS',12);
56 define('STATS_TIME_LAST3MONTHS',13);
57 define('STATS_TIME_LAST4MONTHS',14);
58 define('STATS_TIME_LAST5MONTHS',15);
59 define('STATS_TIME_LAST6MONTHS',16);
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);
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
75 * Print daily cron progress
76 * @param string $ident
78 function stats_daily_progress($ident) {
82 if ($ident == 'init') {
83 $init = $start = time();
87 $elapsed = time() - $start;
90 if (debugging('', DEBUG_ALL
)) {
91 mtrace("$ident:$elapsed ", '');
98 * Execute daily statistics gathering
99 * @param int $maxdays maximum number of days to be processed
100 * @return boolean success
102 function stats_cron_daily($maxdays=1) {
107 $fpcontext = get_context_instance(CONTEXT_COURSE
, SITEID
, MUST_EXIST
);
109 // read last execution date from db
110 if (!$timestart = get_config(NULL, 'statslastdaily')) {
111 $timestart = stats_get_base_daily(stats_get_start_from('daily'));
112 set_config('statslastdaily', $timestart);
115 // calculate scheduled time
116 $scheduledtime = stats_get_base_daily() +
$CFG->statsruntimestarthour
*60*60 +
$CFG->statsruntimestartminute
*60;
118 // Note: This will work fine for sites running cron each 4 hours or less (hopefully, 99.99% of sites). MDL-16709
119 // check to make sure we're due to run, at least 20 hours after last run
120 if (isset($CFG->statslastexecution
) and ((time() - 20*60*60) < $CFG->statslastexecution
)) {
121 mtrace("...preventing stats to run, last execution was less than 20 hours ago.");
123 // also check that we are a max of 4 hours after scheduled time, stats won't run after that
124 } else if (time() > $scheduledtime +
4*60*60) {
125 mtrace("...preventing stats to run, more than 4 hours since scheduled time.");
128 set_config('statslastexecution', time()); /// Grab this execution as last one
131 $nextmidnight = stats_get_next_day_start($timestart);
133 // are there any days that need to be processed?
134 if ($now < $nextmidnight) {
135 return true; // everything ok and up-to-date
139 $timeout = empty($CFG->statsmaxruntime
) ?
60*60*24 : $CFG->statsmaxruntime
;
141 if (!set_cron_lock('statsrunning', $now +
$timeout)) {
145 // first delete entries that should not be there yet
146 $DB->delete_records_select('stats_daily', "timeend > $timestart");
147 $DB->delete_records_select('stats_user_daily', "timeend > $timestart");
149 // Read in a few things we'll use later
150 $viewactions = stats_get_action_names('view');
151 $postactions = stats_get_action_names('post');
153 $guest = (int)$CFG->siteguest
;
154 $guestrole = (int)$CFG->guestroleid
;
155 $defaultfproleid = (int)$CFG->defaultfrontpageroleid
;
157 mtrace("Running daily statistics gathering, starting at $timestart:");
160 $failed = false; // failed stats flag
162 while ($now > $nextmidnight) {
163 if ($days >= $maxdays) {
164 mtrace("...stopping early, reached maximum number of $maxdays days - will continue next time.");
165 set_cron_lock('statsrunning', null);
170 @set_time_limit
($timeout - 200);
174 set_cron_lock('statsrunning', time() +
$timeout, true);
179 $timesql = "l.time >= $timestart AND l.time < $nextmidnight";
180 $timesql1 = "l1.time >= $timestart AND l1.time < $nextmidnight";
181 $timesql2 = "l2.time >= $timestart AND l2.time < $nextmidnight";
183 stats_daily_progress('init');
186 /// find out if any logs available for this day
190 $logspresent = $DB->get_records_sql($sql, null, 0, 1);
192 /// process login info first
193 $sql = "INSERT INTO {stats_user_daily} (stattype, timeend, courseid, userid, statsreads)
195 SELECT 'logins', timeend, courseid, userid, count(statsreads)
197 SELECT $nextmidnight AS timeend, ".SITEID
." AS courseid, l.userid, l.id AS statsreads
199 WHERE action = 'login' AND $timesql
201 GROUP BY timeend, courseid, userid
202 HAVING count(statsreads) > 0";
204 if ($logspresent and !$DB->execute($sql)) {
208 stats_daily_progress('1');
210 $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
212 SELECT 'logins' AS stattype, $nextmidnight AS timeend, ".SITEID
." as courseid, 0,
213 COALESCE((SELECT SUM(statsreads)
214 FROM {stats_user_daily} s1
215 WHERE s1.stattype = 'logins' AND timeend = $nextmidnight), 0) AS stat1,
217 FROM {stats_user_daily} s2
218 WHERE s2.stattype = 'logins' AND timeend = $nextmidnight) AS stat2" .
219 $DB->sql_null_from_clause();
221 if ($logspresent and !$DB->execute($sql)) {
225 stats_daily_progress('2');
228 // Enrolments and active enrolled users
230 // Unfortunately, we do not know how many users were registered
231 // at given times in history :-(
232 // - stat1: enrolled users
233 // - stat2: enrolled users active in this period
234 // - SITEID is special case here, because it's all about default enrolment
235 // in that case, we'll count non-deleted users.
238 $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
240 SELECT 'enrolments', timeend, courseid, roleid, COUNT(DISTINCT userid), 0
242 SELECT $nextmidnight AS timeend, e.courseid, ra.roleid, ue.userid
243 FROM {role_assignments} ra
244 JOIN {context} c ON (c.id = ra.contextid AND c.contextlevel = :courselevel)
245 JOIN {enrol} e ON e.courseid = c.instanceid
246 JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = ra.userid)
248 GROUP BY timeend, courseid, roleid";
250 if (!$DB->execute($sql, array('courselevel'=>CONTEXT_COURSE
))) {
254 stats_daily_progress('3');
256 // using table alias in UPDATE does not work in pg < 8.2
257 $sql = "UPDATE {stats_daily}
258 SET stat2 = (SELECT COUNT(DISTINCT ra.userid)
259 FROM {role_assignments} ra
260 JOIN {context} c ON (c.id = ra.contextid AND c.contextlevel = :courselevel)
261 JOIN {enrol} e ON e.courseid = c.instanceid
262 JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = ra.userid)
263 WHERE ra.roleid = {stats_daily}.roleid AND
264 e.courseid = {stats_daily}.courseid AND
267 WHERE l.course = {stats_daily}.courseid AND
268 l.userid = ra.userid AND $timesql))
269 WHERE {stats_daily}.stattype = 'enrolments' AND
270 {stats_daily}.timeend = $nextmidnight AND
271 {stats_daily}.courseid IN
272 (SELECT DISTINCT l.course
276 if (!$DB->execute($sql, array('courselevel'=>CONTEXT_COURSE
))) {
280 stats_daily_progress('4');
282 /// now get course total enrolments (roleid==0) - except frontpage
283 $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
285 SELECT 'enrolments', timeend, id, nroleid, COUNT(DISTINCT userid), 0
287 SELECT $nextmidnight AS timeend, e.courseid AS id, 0 AS nroleid, ue.userid
289 JOIN {user_enrolments} ue ON ue.enrolid = e.id
291 GROUP BY timeend, id, nroleid
292 HAVING COUNT(DISTINCT userid) > 0";
294 if ($logspresent and !$DB->execute($sql)) {
298 stats_daily_progress('5');
300 $sql = "UPDATE {stats_daily}
301 SET stat2 = (SELECT COUNT(DISTINCT ue.userid)
303 JOIN {user_enrolments} ue ON ue.enrolid = e.id
304 WHERE e.courseid = {stats_daily}.courseid AND
307 WHERE l.course = {stats_daily}.courseid AND
308 l.userid = ue.userid AND $timesql))
309 WHERE {stats_daily}.stattype = 'enrolments' AND
310 {stats_daily}.timeend = $nextmidnight AND
311 {stats_daily}.roleid = 0 AND
312 {stats_daily}.courseid IN
315 WHERE $timesql AND l.course <> ".SITEID
.")";
317 if ($logspresent and !$DB->execute($sql, array())) {
321 stats_daily_progress('6');
323 /// frontapge(==site) enrolments total
324 $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
326 SELECT 'enrolments', $nextmidnight, ".SITEID
.", 0,
329 WHERE u.deleted = 0) AS stat1,
330 (SELECT COUNT(DISTINCT u.id)
332 JOIN {log} l ON l.userid = u.id
333 WHERE u.deleted = 0 AND $timesql) AS stat2" .
334 $DB->sql_null_from_clause();
336 if ($logspresent and !$DB->execute($sql)) {
340 stats_daily_progress('7');
342 /// Default frontpage role enrolments are all site users (not deleted)
343 if ($defaultfproleid) {
344 // first remove default frontpage role counts if created by previous query
347 WHERE stattype = 'enrolments' AND courseid = ".SITEID
." AND
348 roleid = $defaultfproleid AND timeend = $nextmidnight";
349 if ($logspresent and !$DB->execute($sql)) {
353 stats_daily_progress('8');
355 $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
357 SELECT 'enrolments', $nextmidnight, ".SITEID
.", $defaultfproleid,
360 WHERE u.deleted = 0) AS stat1,
361 (SELECT COUNT(DISTINCT u.id)
363 JOIN {log} l ON l.userid = u.id
364 WHERE u.deleted = 0 AND $timesql) AS stat2" .
365 $DB->sql_null_from_clause();;
367 if ($logspresent and !$DB->execute($sql)) {
371 stats_daily_progress('9');
374 stats_daily_progress('x');
375 stats_daily_progress('x');
380 /// individual user stats (including not-logged-in) in each course, this is slow - reuse this data if possible
381 list($viewactionssql, $params1) = $DB->get_in_or_equal($viewactions, SQL_PARAMS_NAMED
, 'view');
382 list($postactionssql, $params2) = $DB->get_in_or_equal($postactions, SQL_PARAMS_NAMED
, 'post');
383 $sql = "INSERT INTO {stats_user_daily} (stattype, timeend, courseid, userid, statsreads, statswrites)
385 SELECT 'activity' AS stattype, $nextmidnight AS timeend, d.courseid, d.userid,
388 WHERE l.userid = d.userid AND
389 l.course = d.courseid AND $timesql AND
390 l.action $viewactionssql) AS statsreads,
393 WHERE l.userid = d.userid AND
394 l.course = d.courseid AND $timesql AND
395 l.action $postactionssql) AS statswrites
396 FROM (SELECT DISTINCT u.id AS userid, l.course AS courseid
397 FROM {user} u, {log} l
398 WHERE u.id = l.userid AND $timesql
400 SELECT 0 AS userid, ".SITEID
." AS courseid" . $DB->sql_null_from_clause() . ") d";
401 // can not use group by here because pg can not handle it :-(
403 if ($logspresent and !$DB->execute($sql, array_merge($params1, $params2))) {
407 stats_daily_progress('10');
410 /// how many view/post actions in each course total
411 $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
413 SELECT 'activity' AS stattype, $nextmidnight AS timeend, c.id AS courseid, 0,
416 WHERE l1.course = c.id AND l1.action $viewactionssql AND
420 WHERE l2.course = c.id AND l2.action $postactionssql AND
423 WHERE EXISTS (SELECT 'x'
425 WHERE l.course = c.id and $timesql)";
427 if ($logspresent and !$DB->execute($sql, array_merge($params1, $params2))) {
431 stats_daily_progress('11');
434 /// how many view actions for each course+role - excluding guests and frontpage
436 $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
438 SELECT 'activity', timeend, courseid, roleid, SUM(statsreads), SUM(statswrites)
440 SELECT $nextmidnight AS timeend, pl.courseid, pl.roleid, sud.statsreads, sud.statswrites
441 FROM {stats_user_daily} sud,
442 (SELECT DISTINCT ra.userid, ra.roleid, e.courseid
443 FROM {role_assignments} ra
444 JOIN {context} c ON (c.id = ra.contextid AND c.contextlevel = :courselevel)
445 JOIN {enrol} e ON e.courseid = c.instanceid
446 JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = ra.userid)
447 WHERE ra.roleid <> $guestrole AND
450 WHERE sud.userid = pl.userid AND
451 sud.courseid = pl.courseid AND
452 sud.timeend = $nextmidnight AND
453 sud.stattype='activity'
455 GROUP BY timeend, courseid, roleid
456 HAVING SUM(statsreads) > 0 OR SUM(statswrites) > 0";
458 if ($logspresent and !$DB->execute($sql, array('courselevel'=>CONTEXT_COURSE
))) {
462 stats_daily_progress('12');
464 /// how many view actions from guests only in each course - excluding frontpage
465 /// normal users may enter course with temporary guest access too
467 $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
469 SELECT 'activity', timeend, courseid, nroleid, SUM(statsreads), SUM(statswrites)
471 SELECT $nextmidnight AS timeend, sud.courseid, $guestrole AS nroleid, sud.statsreads, sud.statswrites
472 FROM {stats_user_daily} sud
473 WHERE sud.timeend = $nextmidnight AND sud.courseid <> ".SITEID
." AND
474 sud.stattype='activity' AND
475 (sud.userid = $guest OR sud.userid
476 NOT IN (SELECT ue.userid
477 FROM {user_enrolments} ue
478 JOIN {enrol} e ON ue.enrolid = e.id
479 WHERE e.courseid = sud.courseid))
481 GROUP BY timeend, courseid, nroleid
482 HAVING SUM(statsreads) > 0 OR SUM(statswrites) > 0";
484 if ($logspresent and !$DB->execute($sql, array())) {
488 stats_daily_progress('13');
491 /// how many view actions for each role on frontpage - excluding guests, not-logged-in and default frontpage role
492 $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
494 SELECT 'activity', timeend, courseid, roleid, SUM(statsreads), SUM(statswrites)
496 SELECT $nextmidnight AS timeend, pl.courseid, pl.roleid, sud.statsreads, sud.statswrites
497 FROM {stats_user_daily} sud,
498 (SELECT DISTINCT ra.userid, ra.roleid, c.instanceid AS courseid
499 FROM {role_assignments} ra
500 JOIN {context} c ON c.id = ra.contextid
501 WHERE ra.contextid = :fpcontext AND
502 ra.roleid <> $defaultfproleid AND
503 ra.roleid <> $guestrole AND
506 WHERE sud.userid = pl.userid AND
507 sud.courseid = pl.courseid AND
508 sud.timeend = $nextmidnight AND
509 sud.stattype='activity'
511 GROUP BY timeend, courseid, roleid
512 HAVING SUM(statsreads) > 0 OR SUM(statswrites) > 0";
514 if ($logspresent and !$DB->execute($sql, array('fpcontext'=>$fpcontext->id
))) {
518 stats_daily_progress('14');
521 /// how many view actions for default frontpage role on frontpage only
522 $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
524 SELECT 'activity', timeend, courseid, nroleid, SUM(statsreads), SUM(statswrites)
526 SELECT sud.timeend AS timeend, sud.courseid, $defaultfproleid AS nroleid, sud.statsreads, sud.statswrites
527 FROM {stats_user_daily} sud
528 WHERE sud.timeend = :nextm AND sud.courseid = :siteid AND
529 sud.stattype='activity' AND
530 sud.userid <> $guest AND sud.userid <> 0 AND sud.userid
531 NOT IN (SELECT ra.userid
532 FROM {role_assignments} ra
533 WHERE ra.roleid <> $guestrole AND
534 ra.roleid <> $defaultfproleid AND ra.contextid = :fpcontext)
536 GROUP BY timeend, courseid, nroleid
537 HAVING SUM(statsreads) > 0 OR SUM(statswrites) > 0";
539 if ($logspresent and !$DB->execute($sql, array('fpcontext'=>$fpcontext->id
, 'siteid'=>SITEID
, 'nextm'=>$nextmidnight))) {
543 stats_daily_progress('15');
545 /// how many view actions for guests or not-logged-in on frontpage
546 $sql = "INSERT INTO {stats_daily} (stattype, timeend, courseid, roleid, stat1, stat2)
548 SELECT 'activity', timeend, courseid, nroleid, SUM(statsreads), SUM(statswrites)
550 SELECT $nextmidnight AS timeend, ".SITEID
." AS courseid, $guestrole AS nroleid, pl.statsreads, pl.statswrites
552 SELECT sud.statsreads, sud.statswrites
553 FROM {stats_user_daily} sud
554 WHERE (sud.userid = $guest OR sud.userid = 0) AND
555 sud.timeend = $nextmidnight AND sud.courseid = ".SITEID
." AND
556 sud.stattype='activity'
559 GROUP BY timeend, courseid, nroleid
560 HAVING SUM(statsreads) > 0 OR SUM(statswrites) > 0";
562 if ($logspresent and !$DB->execute($sql)) {
566 stats_daily_progress('16');
568 // remember processed days
569 set_config('statslastdaily', $nextmidnight);
570 mtrace(" finished until $nextmidnight: ".userdate($nextmidnight)." (in ".(time()-$daystart)." s)");
572 $timestart = $nextmidnight;
573 $nextmidnight = stats_get_next_day_start($nextmidnight);
576 set_cron_lock('statsrunning', null);
580 mtrace("...error occurred, completed $days days of statistics.");
584 mtrace("...completed $days days of statistics.");
591 * Execute weekly statistics gathering
592 * @return boolean success
594 function stats_cron_weekly() {
599 // read last execution date from db
600 if (!$timestart = get_config(NULL, 'statslastweekly')) {
601 $timestart = stats_get_base_daily(stats_get_start_from('weekly'));
602 set_config('statslastweekly', $timestart);
605 $nextstartweek = stats_get_next_week_start($timestart);
607 // are there any weeks that need to be processed?
608 if ($now < $nextstartweek) {
609 return true; // everything ok and up-to-date
612 $timeout = empty($CFG->statsmaxruntime
) ?
60*60*24 : $CFG->statsmaxruntime
;
614 if (!set_cron_lock('statsrunning', $now +
$timeout)) {
618 // fisrt delete entries that should not be there yet
619 $DB->delete_records_select('stats_weekly', "timeend > $timestart");
620 $DB->delete_records_select('stats_user_weekly', "timeend > $timestart");
622 mtrace("Running weekly statistics gathering, starting at $timestart:");
625 while ($now > $nextstartweek) {
626 @set_time_limit
($timeout - 200);
631 set_cron_lock('statsrunning', time() +
$timeout, true);
634 $logtimesql = "l.time >= $timestart AND l.time < $nextstartweek";
635 $stattimesql = "timeend > $timestart AND timeend <= $nextstartweek";
637 /// process login info first
638 $sql = "INSERT INTO {stats_user_weekly} (stattype, timeend, courseid, userid, statsreads)
640 SELECT 'logins', timeend, courseid, userid, COUNT(statsreads)
642 SELECT $nextstartweek AS timeend, ".SITEID
." as courseid, l.userid, l.id AS statsreads
644 WHERE action = 'login' AND $logtimesql
646 GROUP BY timeend, courseid, userid
647 HAVING count(statsreads) > 0";
651 $sql = "INSERT INTO {stats_weekly} (stattype, timeend, courseid, roleid, stat1, stat2)
653 SELECT 'logins' AS stattype, $nextstartweek AS timeend, ".SITEID
." as courseid, 0,
654 COALESCE((SELECT SUM(statsreads)
655 FROM {stats_user_weekly} s1
656 WHERE s1.stattype = 'logins' AND timeend = $nextstartweek), 0) AS nstat1,
658 FROM {stats_user_weekly} s2
659 WHERE s2.stattype = 'logins' AND timeend = $nextstartweek) AS nstat2" .
660 $DB->sql_null_from_clause();
665 /// now enrolments averages
666 $sql = "INSERT INTO {stats_weekly} (stattype, timeend, courseid, roleid, stat1, stat2)
668 SELECT 'enrolments', ntimeend, courseid, roleid, " . $DB->sql_ceil('AVG(stat1)') . ", " . $DB->sql_ceil('AVG(stat2)') . "
670 SELECT $nextstartweek AS ntimeend, courseid, roleid, stat1, stat2
671 FROM {stats_daily} sd
672 WHERE stattype = 'enrolments' AND $stattimesql
674 GROUP BY ntimeend, courseid, roleid";
679 /// activity read/write averages
680 $sql = "INSERT INTO {stats_weekly} (stattype, timeend, courseid, roleid, stat1, stat2)
682 SELECT 'activity', ntimeend, courseid, roleid, SUM(stat1), SUM(stat2)
684 SELECT $nextstartweek AS ntimeend, courseid, roleid, stat1, stat2
686 WHERE stattype = 'activity' AND $stattimesql
688 GROUP BY ntimeend, courseid, roleid";
693 /// user read/write averages
694 $sql = "INSERT INTO {stats_user_weekly} (stattype, timeend, courseid, userid, statsreads, statswrites)
696 SELECT 'activity', ntimeend, courseid, userid, SUM(statsreads), SUM(statswrites)
698 SELECT $nextstartweek AS ntimeend, courseid, userid, statsreads, statswrites
699 FROM {stats_user_daily}
700 WHERE stattype = 'activity' AND $stattimesql
702 GROUP BY ntimeend, courseid, userid";
706 set_config('statslastweekly', $nextstartweek);
707 mtrace(" finished until $nextstartweek: ".userdate($nextstartweek));
709 $timestart = $nextstartweek;
710 $nextstartweek = stats_get_next_week_start($nextstartweek);
713 set_cron_lock('statsrunning', null);
714 mtrace("...completed $weeks weeks of statistics.");
719 * Execute monthly statistics gathering
720 * @return boolean success
722 function stats_cron_monthly() {
727 // read last execution date from db
728 if (!$timestart = get_config(NULL, 'statslastmonthly')) {
729 $timestart = stats_get_base_monthly(stats_get_start_from('monthly'));
730 set_config('statslastmonthly', $timestart);
733 $nextstartmonth = stats_get_next_month_start($timestart);
735 // are there any months that need to be processed?
736 if ($now < $nextstartmonth) {
737 return true; // everything ok and up-to-date
740 $timeout = empty($CFG->statsmaxruntime
) ?
60*60*24 : $CFG->statsmaxruntime
;
742 if (!set_cron_lock('statsrunning', $now +
$timeout)) {
746 // fisr delete entries that should not be there yet
747 $DB->delete_records_select('stats_monthly', "timeend > $timestart");
748 $DB->delete_records_select('stats_user_monthly', "timeend > $timestart");
750 $startmonth = stats_get_base_monthly($now);
753 mtrace("Running monthly statistics gathering, starting at $timestart:");
756 while ($now > $nextstartmonth) {
757 @set_time_limit
($timeout - 200);
762 set_cron_lock('statsrunning', time() +
$timeout, true);
765 $logtimesql = "l.time >= $timestart AND l.time < $nextstartmonth";
766 $stattimesql = "timeend > $timestart AND timeend <= $nextstartmonth";
768 /// process login info first
769 $sql = "INSERT INTO {stats_user_monthly} (stattype, timeend, courseid, userid, statsreads)
771 SELECT 'logins', timeend, courseid, userid, COUNT(statsreads)
773 SELECT $nextstartmonth AS timeend, ".SITEID
." as courseid, l.userid, l.id AS statsreads
775 WHERE action = 'login' AND $logtimesql
777 GROUP BY timeend, courseid, userid";
781 $sql = "INSERT INTO {stats_monthly} (stattype, timeend, courseid, roleid, stat1, stat2)
783 SELECT 'logins' AS stattype, $nextstartmonth AS timeend, ".SITEID
." as courseid, 0,
784 COALESCE((SELECT SUM(statsreads)
785 FROM {stats_user_monthly} s1
786 WHERE s1.stattype = 'logins' AND timeend = $nextstartmonth), 0) AS nstat1,
788 FROM {stats_user_monthly} s2
789 WHERE s2.stattype = 'logins' AND timeend = $nextstartmonth) AS nstat2" .
790 $DB->sql_null_from_clause();
795 /// now enrolments averages
796 $sql = "INSERT INTO {stats_monthly} (stattype, timeend, courseid, roleid, stat1, stat2)
798 SELECT 'enrolments', ntimeend, courseid, roleid, " . $DB->sql_ceil('AVG(stat1)') . ", " . $DB->sql_ceil('AVG(stat2)') . "
800 SELECT $nextstartmonth AS ntimeend, courseid, roleid, stat1, stat2
801 FROM {stats_daily} sd
802 WHERE stattype = 'enrolments' AND $stattimesql
804 GROUP BY ntimeend, courseid, roleid";
809 /// activity read/write averages
810 $sql = "INSERT INTO {stats_monthly} (stattype, timeend, courseid, roleid, stat1, stat2)
812 SELECT 'activity', ntimeend, courseid, roleid, SUM(stat1), SUM(stat2)
814 SELECT $nextstartmonth AS ntimeend, courseid, roleid, stat1, stat2
816 WHERE stattype = 'activity' AND $stattimesql
818 GROUP BY ntimeend, courseid, roleid";
823 /// user read/write averages
824 $sql = "INSERT INTO {stats_user_monthly} (stattype, timeend, courseid, userid, statsreads, statswrites)
826 SELECT 'activity', ntimeend, courseid, userid, SUM(statsreads), SUM(statswrites)
828 SELECT $nextstartmonth AS ntimeend, courseid, userid, statsreads, statswrites
829 FROM {stats_user_daily}
830 WHERE stattype = 'activity' AND $stattimesql
832 GROUP BY ntimeend, courseid, userid";
836 set_config('statslastmonthly', $nextstartmonth);
837 mtrace(" finished until $nextstartmonth: ".userdate($nextstartmonth));
839 $timestart = $nextstartmonth;
840 $nextstartmonth = stats_get_next_month_start($nextstartmonth);
843 set_cron_lock('statsrunning', null);
844 mtrace("...completed $months months of statistics.");
849 * Return starting date of stats processing
850 * @param string $str name of table - daily, weekly or monthly
851 * @return int timestamp
853 function stats_get_start_from($str) {
856 // are there any data in stats table? Should not be...
857 if ($timeend = $DB->get_field_sql('SELECT MAX(timeend) FROM {stats_'.$str.'}')) {
860 // decide what to do based on our config setting (either all or none or a timestamp)
861 switch ($CFG->statsfirstrun
) {
863 if ($firstlog = $DB->get_field_sql('SELECT MIN(time) FROM {log}')) {
867 if (is_numeric($CFG->statsfirstrun
)) {
868 return time() - $CFG->statsfirstrun
;
870 // not a number? use next instead
872 return strtotime('-3 day', time());
878 * @param int $time timestamp
879 * @return start of day
881 function stats_get_base_daily($time=0) {
887 if ($CFG->timezone
== 99) {
888 $time = strtotime(date('d-M-Y', $time));
891 $offset = get_timezone_offset($CFG->timezone
);
892 $gtime = $time +
$offset;
893 $gtime = intval($gtime / (60*60*24)) * 60*60*24;
894 return $gtime - $offset;
900 * @param int $time timestamp
901 * @return start of week
903 function stats_get_base_weekly($time=0) {
906 $time = stats_get_base_daily($time);
907 $startday = $CFG->calendar_startwday
;
908 if ($CFG->timezone
== 99) {
909 $thisday = date('w', $time);
911 $offset = get_timezone_offset($CFG->timezone
);
912 $gtime = $time +
$offset;
913 $thisday = gmdate('w', $gtime);
915 if ($thisday > $startday) {
916 $time = $time - (($thisday - $startday) * 60*60*24);
917 } else if ($thisday < $startday) {
918 $time = $time - ((7 +
$thisday - $startday) * 60*60*24);
925 * @param int $time timestamp
926 * @return start of month
928 function stats_get_base_monthly($time=0) {
934 if ($CFG->timezone
== 99) {
935 return strtotime(date('1-M-Y', $time));
938 $time = stats_get_base_daily($time);
939 $offset = get_timezone_offset($CFG->timezone
);
940 $gtime = $time +
$offset;
941 $day = gmdate('d', $gtime);
945 return $gtime - (($day-1) * 60*60*24);
951 * @param int $time timestamp
952 * @return start of next day
954 function stats_get_next_day_start($time) {
955 $next = stats_get_base_daily($time);
956 $next = $next +
60*60*26;
957 $next = stats_get_base_daily($next);
958 if ($next <= $time) {
959 //DST trouble - prevent infinite loops
960 $next = $next +
60*60*24;
967 * @param int $time timestamp
968 * @return start of next week
970 function stats_get_next_week_start($time) {
971 $next = stats_get_base_weekly($time);
972 $next = $next +
60*60*24*9;
973 $next = stats_get_base_weekly($next);
974 if ($next <= $time) {
975 //DST trouble - prevent infinite loops
976 $next = $next +
60*60*24*7;
982 * Start of next month
983 * @param int $time timestamp
984 * @return start of next month
986 function stats_get_next_month_start($time) {
987 $next = stats_get_base_monthly($time);
988 $next = $next +
60*60*24*33;
989 $next = stats_get_base_monthly($next);
990 if ($next <= $time) {
991 //DST trouble - prevent infinite loops
992 $next = $next +
60*60*24*31;
998 * Remove old stats data
1000 function stats_clean_old() {
1002 mtrace("Running stats cleanup tasks...");
1003 $deletebefore = stats_get_base_monthly();
1005 // delete dailies older than 3 months (to be safe)
1006 $deletebefore = strtotime('-3 months', $deletebefore);
1007 $DB->delete_records_select('stats_daily', "timeend < $deletebefore");
1008 $DB->delete_records_select('stats_user_daily', "timeend < $deletebefore");
1010 // delete weeklies older than 9 months (to be safe)
1011 $deletebefore = strtotime('-6 months', $deletebefore);
1012 $DB->delete_records_select('stats_weekly', "timeend < $deletebefore");
1013 $DB->delete_records_select('stats_user_weekly', "timeend < $deletebefore");
1015 // don't delete monthlies
1017 mtrace("...stats cleanup finished");
1020 function stats_get_parameters($time,$report,$courseid,$mode,$roleid=0) {
1023 $param = new stdClass();
1024 $param->params
= array();
1026 if ($time < 10) { // dailies
1027 // number of days to go back = 7* time
1028 $param->table
= 'daily';
1029 $param->timeafter
= strtotime("-".($time*7)." days",stats_get_base_daily());
1030 } elseif ($time < 20) { // weeklies
1031 // number of weeks to go back = time - 10 * 4 (weeks) + base week
1032 $param->table
= 'weekly';
1033 $param->timeafter
= strtotime("-".(($time - 10)*4)." weeks",stats_get_base_weekly());
1034 } else { // monthlies.
1035 // number of months to go back = time - 20 * months + base month
1036 $param->table
= 'monthly';
1037 $param->timeafter
= strtotime("-".($time - 20)." months",stats_get_base_monthly());
1040 $param->extras
= '';
1043 // ******************** STATS_MODE_GENERAL ******************** //
1044 case STATS_REPORT_LOGINS
:
1045 $param->fields
= 'timeend,sum(stat1) as line1,sum(stat2) as line2';
1046 $param->fieldscomplete
= true;
1047 $param->stattype
= 'logins';
1048 $param->line1
= get_string('statslogins');
1049 $param->line2
= get_string('statsuniquelogins');
1050 if ($courseid == SITEID
) {
1051 $param->extras
= 'GROUP BY timeend';
1055 case STATS_REPORT_READS
:
1056 $param->fields
= $DB->sql_concat('timeend','roleid').' AS uniqueid, timeend, roleid, stat1 as line1';
1057 $param->fieldscomplete
= true; // set this to true to avoid anything adding stuff to the list and breaking complex queries.
1058 $param->aggregategroupby
= 'roleid';
1059 $param->stattype
= 'activity';
1060 $param->crosstab
= true;
1061 $param->extras
= 'GROUP BY timeend,roleid,stat1';
1062 if ($courseid == SITEID
) {
1063 $param->fields
= $DB->sql_concat('timeend','roleid').' AS uniqueid, timeend, roleid, sum(stat1) as line1';
1064 $param->extras
= 'GROUP BY timeend,roleid';
1068 case STATS_REPORT_WRITES
:
1069 $param->fields
= $DB->sql_concat('timeend','roleid').' AS uniqueid, timeend, roleid, stat2 as line1';
1070 $param->fieldscomplete
= true; // set this to true to avoid anything adding stuff to the list and breaking complex queries.
1071 $param->aggregategroupby
= 'roleid';
1072 $param->stattype
= 'activity';
1073 $param->crosstab
= true;
1074 $param->extras
= 'GROUP BY timeend,roleid,stat2';
1075 if ($courseid == SITEID
) {
1076 $param->fields
= $DB->sql_concat('timeend','roleid').' AS uniqueid, timeend, roleid, sum(stat2) as line1';
1077 $param->extras
= 'GROUP BY timeend,roleid';
1081 case STATS_REPORT_ACTIVITY
:
1082 $param->fields
= $DB->sql_concat('timeend','roleid').' AS uniqueid, timeend, roleid, sum(stat1+stat2) as line1';
1083 $param->fieldscomplete
= true; // set this to true to avoid anything adding stuff to the list and breaking complex queries.
1084 $param->aggregategroupby
= 'roleid';
1085 $param->stattype
= 'activity';
1086 $param->crosstab
= true;
1087 $param->extras
= 'GROUP BY timeend,roleid';
1088 if ($courseid == SITEID
) {
1089 $param->extras
= 'GROUP BY timeend,roleid';
1093 case STATS_REPORT_ACTIVITYBYROLE
;
1094 $param->fields
= 'stat1 AS line1, stat2 AS line2';
1095 $param->stattype
= 'activity';
1096 $rolename = $DB->get_field('role','name', array('id'=>$roleid));
1097 $param->line1
= $rolename . get_string('statsreads');
1098 $param->line2
= $rolename . get_string('statswrites');
1099 if ($courseid == SITEID
) {
1100 $param->extras
= 'GROUP BY timeend';
1104 // ******************** STATS_MODE_DETAILED ******************** //
1105 case STATS_REPORT_USER_ACTIVITY
:
1106 $param->fields
= 'statsreads as line1, statswrites as line2';
1107 $param->line1
= get_string('statsuserreads');
1108 $param->line2
= get_string('statsuserwrites');
1109 $param->stattype
= 'activity';
1112 case STATS_REPORT_USER_ALLACTIVITY
:
1113 $param->fields
= 'statsreads+statswrites as line1';
1114 $param->line1
= get_string('statsuseractivity');
1115 $param->stattype
= 'activity';
1118 case STATS_REPORT_USER_LOGINS
:
1119 $param->fields
= 'statsreads as line1';
1120 $param->line1
= get_string('statsuserlogins');
1121 $param->stattype
= 'logins';
1124 case STATS_REPORT_USER_VIEW
:
1125 $param->fields
= 'statsreads as line1, statswrites as line2, statsreads+statswrites as line3';
1126 $param->line1
= get_string('statsuserreads');
1127 $param->line2
= get_string('statsuserwrites');
1128 $param->line3
= get_string('statsuseractivity');
1129 $param->stattype
= 'activity';
1132 // ******************** STATS_MODE_RANKED ******************** //
1133 case STATS_REPORT_ACTIVE_COURSES
:
1134 $param->fields
= 'sum(stat1+stat2) AS line1';
1135 $param->stattype
= 'activity';
1136 $param->orderby
= 'line1 DESC';
1137 $param->line1
= get_string('activity');
1138 $param->graphline
= 'line1';
1141 case STATS_REPORT_ACTIVE_COURSES_WEIGHTED
:
1143 if (!empty($CFG->statsuserthreshold
) && is_numeric($CFG->statsuserthreshold
)) {
1144 $threshold = $CFG->statsuserthreshold
;
1146 $param->fields
= '';
1147 $param->sql
= 'SELECT activity.courseid, activity.all_activity AS line1, enrolments.highest_enrolments AS line2,
1148 activity.all_activity / enrolments.highest_enrolments as line3
1150 SELECT courseid, sum(stat1+stat2) AS all_activity
1151 FROM {stats_'.$param->table
.'}
1152 WHERE stattype=\'activity\' AND timeend >= '.(int)$param->timeafter
.' AND roleid = 0 GROUP BY courseid
1156 SELECT courseid, max(stat1) AS highest_enrolments
1157 FROM {stats_'.$param->table
.'}
1158 WHERE stattype=\'enrolments\' AND timeend >= '.(int)$param->timeafter
.' AND stat1 > '.(int)$threshold.'
1161 ON (activity.courseid = enrolments.courseid)
1162 ORDER BY line3 DESC';
1163 $param->line1
= get_string('activity');
1164 $param->line2
= get_string('users');
1165 $param->line3
= get_string('activityweighted');
1166 $param->graphline
= 'line3';
1169 case STATS_REPORT_PARTICIPATORY_COURSES
:
1171 if (!empty($CFG->statsuserthreshold
) && is_numeric($CFG->statsuserthreshold
)) {
1172 $threshold = $CFG->statsuserthreshold
;
1174 $param->fields
= '';
1175 $param->sql
= 'SELECT courseid, ' . $DB->sql_ceil('avg(all_enrolments)') . ' as line1, ' .
1176 $DB->sql_ceil('avg(active_enrolments)') . ' as line2, avg(proportion_active) AS line3
1178 SELECT courseid, timeend, stat2 as active_enrolments,
1179 stat1 as all_enrolments, '.$DB->sql_cast_char2real('stat2').'/'.$DB->sql_cast_char2real('stat1').' AS proportion_active
1180 FROM {stats_'.$param->table
.'}
1181 WHERE stattype=\'enrolments\' AND roleid = 0 AND stat1 > '.(int)$threshold.'
1183 WHERE timeend >= '.(int)$param->timeafter
.'
1185 ORDER BY line3 DESC';
1187 $param->line1
= get_string('users');
1188 $param->line2
= get_string('activeusers');
1189 $param->line3
= get_string('participationratio');
1190 $param->graphline
= 'line3';
1193 case STATS_REPORT_PARTICIPATORY_COURSES_RW
:
1194 $param->fields
= '';
1195 $param->sql
= 'SELECT courseid, sum(views) AS line1, sum(posts) AS line2,
1196 avg(proportion_active) AS line3
1198 SELECT courseid, timeend, stat1 as views, stat2 AS posts,
1199 '.$DB->sql_cast_char2real('stat2').'/'.$DB->sql_cast_char2real('stat1').' as proportion_active
1200 FROM {stats_'.$param->table
.'}
1201 WHERE stattype=\'activity\' AND roleid = 0 AND stat1 > 0
1203 WHERE timeend >= '.(int)$param->timeafter
.'
1205 ORDER BY line3 DESC';
1206 $param->line1
= get_string('views');
1207 $param->line2
= get_string('posts');
1208 $param->line3
= get_string('participationratio');
1209 $param->graphline
= 'line3';
1214 if ($courseid == SITEID && $mode != STATS_MODE_RANKED) { // just aggregate all courses.
1215 $param->fields = preg_replace('/(?:sum)([a-zA-Z0-9+_]*)\W+as\W+([a-zA-Z0-9_]*)/i','sum($1) as $2',$param->fields);
1216 $param->extras = ' GROUP BY timeend'.((!empty($param->aggregategroupby)) ? ','.$param->aggregategroupby : '');
1219 //TODO must add the SITEID reports to the rest of the reports.
1223 function stats_get_view_actions() {
1224 return array('view','view all','history');
1227 function stats_get_post_actions() {
1228 return array('add','delete','edit','add mod','delete mod','edit section'.'enrol','loginas','new','unenrol','update','update mod');
1231 function stats_get_action_names($str) {
1234 $mods = $DB->get_records('modules');
1235 $function = 'stats_get_'.$str.'_actions';
1236 $actions = $function();
1237 foreach ($mods as $mod) {
1238 $file = $CFG->dirroot
.'/mod/'.$mod->name
.'/lib.php';
1239 if (!is_readable($file)) {
1242 require_once($file);
1243 $function = $mod->name
.'_get_'.$str.'_actions';
1244 if (function_exists($function)) {
1245 $mod_actions = $function();
1246 if (is_array($mod_actions)) {
1247 $actions = array_merge($actions, $mod_actions);
1252 // The array_values() forces a stack-like array
1253 // so we can later loop over safely...
1254 $actions = array_values(array_unique($actions));
1255 $c = count($actions);
1256 for ($n=0;$n<$c;$n++
) {
1257 $actions[$n] = $actions[$n];
1262 function stats_get_time_options($now,$lastweekend,$lastmonthend,$earliestday,$earliestweek,$earliestmonth) {
1264 $now = stats_get_base_daily(time());
1265 // it's really important that it's TIMEEND in the table. ie, tuesday 00:00:00 is monday night.
1266 // so we need to take a day off here (essentially add a day to $now
1269 $timeoptions = array();
1271 if ($now - (60*60*24*7) >= $earliestday) {
1272 $timeoptions[STATS_TIME_LASTWEEK
] = get_string('numweeks','moodle',1);
1274 if ($now - (60*60*24*14) >= $earliestday) {
1275 $timeoptions[STATS_TIME_LAST2WEEKS
] = get_string('numweeks','moodle',2);
1277 if ($now - (60*60*24*21) >= $earliestday) {
1278 $timeoptions[STATS_TIME_LAST3WEEKS
] = get_string('numweeks','moodle',3);
1280 if ($now - (60*60*24*28) >= $earliestday) {
1281 $timeoptions[STATS_TIME_LAST4WEEKS
] = get_string('numweeks','moodle',4);// show dailies up to (including) here.
1283 if ($lastweekend - (60*60*24*56) >= $earliestweek) {
1284 $timeoptions[STATS_TIME_LAST2MONTHS
] = get_string('nummonths','moodle',2);
1286 if ($lastweekend - (60*60*24*84) >= $earliestweek) {
1287 $timeoptions[STATS_TIME_LAST3MONTHS
] = get_string('nummonths','moodle',3);
1289 if ($lastweekend - (60*60*24*112) >= $earliestweek) {
1290 $timeoptions[STATS_TIME_LAST4MONTHS
] = get_string('nummonths','moodle',4);
1292 if ($lastweekend - (60*60*24*140) >= $earliestweek) {
1293 $timeoptions[STATS_TIME_LAST5MONTHS
] = get_string('nummonths','moodle',5);
1295 if ($lastweekend - (60*60*24*168) >= $earliestweek) {
1296 $timeoptions[STATS_TIME_LAST6MONTHS
] = get_string('nummonths','moodle',6); // show weeklies up to (including) here
1298 if (strtotime('-7 months',$lastmonthend) >= $earliestmonth) {
1299 $timeoptions[STATS_TIME_LAST7MONTHS
] = get_string('nummonths','moodle',7);
1301 if (strtotime('-8 months',$lastmonthend) >= $earliestmonth) {
1302 $timeoptions[STATS_TIME_LAST8MONTHS
] = get_string('nummonths','moodle',8);
1304 if (strtotime('-9 months',$lastmonthend) >= $earliestmonth) {
1305 $timeoptions[STATS_TIME_LAST9MONTHS
] = get_string('nummonths','moodle',9);
1307 if (strtotime('-10 months',$lastmonthend) >= $earliestmonth) {
1308 $timeoptions[STATS_TIME_LAST10MONTHS
] = get_string('nummonths','moodle',10);
1310 if (strtotime('-11 months',$lastmonthend) >= $earliestmonth) {
1311 $timeoptions[STATS_TIME_LAST11MONTHS
] = get_string('nummonths','moodle',11);
1313 if (strtotime('-1 year',$lastmonthend) >= $earliestmonth) {
1314 $timeoptions[STATS_TIME_LASTYEAR
] = get_string('lastyear');
1317 $years = (int)date('y', $now) - (int)date('y', $earliestmonth);
1319 for($i = 2; $i <= $years; $i++
) {
1320 $timeoptions[$i*12+
20] = get_string('numyears', 'moodle', $i);
1324 return $timeoptions;
1327 function stats_get_report_options($courseid,$mode) {
1330 $reportoptions = array();
1333 case STATS_MODE_GENERAL
:
1334 $reportoptions[STATS_REPORT_ACTIVITY
] = get_string('statsreport'.STATS_REPORT_ACTIVITY
);
1335 if ($courseid != SITEID
&& $context = get_context_instance(CONTEXT_COURSE
, $courseid)) {
1336 $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';
1337 if ($roles = $DB->get_records_sql($sql, array('courseid' => $courseid))) {
1338 foreach ($roles as $role) {
1339 $reportoptions[STATS_REPORT_ACTIVITYBYROLE
.$role->id
] = get_string('statsreport'.STATS_REPORT_ACTIVITYBYROLE
). ' '.$role->name
;
1343 $reportoptions[STATS_REPORT_READS
] = get_string('statsreport'.STATS_REPORT_READS
);
1344 $reportoptions[STATS_REPORT_WRITES
] = get_string('statsreport'.STATS_REPORT_WRITES
);
1345 if ($courseid == SITEID
) {
1346 $reportoptions[STATS_REPORT_LOGINS
] = get_string('statsreport'.STATS_REPORT_LOGINS
);
1350 case STATS_MODE_DETAILED
:
1351 $reportoptions[STATS_REPORT_USER_ACTIVITY
] = get_string('statsreport'.STATS_REPORT_USER_ACTIVITY
);
1352 $reportoptions[STATS_REPORT_USER_ALLACTIVITY
] = get_string('statsreport'.STATS_REPORT_USER_ALLACTIVITY
);
1353 if (has_capability('report/stats:view', get_context_instance(CONTEXT_SYSTEM
))) {
1355 $reportoptions[STATS_REPORT_USER_LOGINS
] = get_string('statsreport'.STATS_REPORT_USER_LOGINS
);
1358 case STATS_MODE_RANKED
:
1359 if (has_capability('report/stats:view', get_context_instance(CONTEXT_SYSTEM
))) {
1360 $reportoptions[STATS_REPORT_ACTIVE_COURSES
] = get_string('statsreport'.STATS_REPORT_ACTIVE_COURSES
);
1361 $reportoptions[STATS_REPORT_ACTIVE_COURSES_WEIGHTED
] = get_string('statsreport'.STATS_REPORT_ACTIVE_COURSES_WEIGHTED
);
1362 $reportoptions[STATS_REPORT_PARTICIPATORY_COURSES
] = get_string('statsreport'.STATS_REPORT_PARTICIPATORY_COURSES
);
1363 $reportoptions[STATS_REPORT_PARTICIPATORY_COURSES_RW
] = get_string('statsreport'.STATS_REPORT_PARTICIPATORY_COURSES_RW
);
1368 return $reportoptions;
1371 function stats_fix_zeros($stats,$timeafter,$timestr,$line2=true,$line3=false) {
1373 if (empty($stats)) {
1377 $timestr = str_replace('user_','',$timestr); // just in case.
1378 $fun = 'stats_get_base_'.$timestr;
1383 // add something to timeafter since it is our absolute base
1384 $actualtimes = array();
1385 foreach ($stats as $statid=>$s) {
1386 //normalize the times in stats - those might have been created in different timezone, DST etc.
1387 $s->timeend
= $fun($s->timeend +
60*60*5);
1388 $stats[$statid] = $s;
1390 $actualtimes[] = $s->timeend
;
1393 $timeafter = array_pop(array_values($actualtimes));
1395 while ($timeafter < $now) {
1396 $times[] = $timeafter;
1397 if ($timestr == 'daily') {
1398 $timeafter = stats_get_next_day_start($timeafter);
1399 } else if ($timestr == 'weekly') {
1400 $timeafter = stats_get_next_week_start($timeafter);
1401 } else if ($timestr == 'monthly') {
1402 $timeafter = stats_get_next_month_start($timeafter);
1404 return $stats; // this will put us in a never ending loop.
1408 foreach ($times as $count => $time) {
1409 if (!in_array($time,$actualtimes) && $count != count($times) -1) {
1410 $newobj = new StdClass
;
1411 $newobj->timeend
= $time;
1413 $newobj->roleid
= 0;
1415 if (!empty($line2)) {
1418 if (!empty($line3)) {
1421 $newobj->zerofixed
= true;
1426 usort($stats,"stats_compare_times");
1431 // helper function to sort arrays by $obj->timeend
1432 function stats_compare_times($a,$b) {
1433 if ($a->timeend
== $b->timeend
) {
1436 return ($a->timeend
> $b->timeend
) ?
-1 : 1;
1439 function stats_check_uptodate($courseid=0) {
1442 if (empty($courseid)) {
1446 $latestday = stats_get_start_from('daily');
1448 if ((time() - 60*60*24*2) < $latestday) { // we're ok
1452 $a = new stdClass();
1453 $a->daysdone
= $DB->get_field_sql("SELECT COUNT(DISTINCT(timeend)) FROM {stats_daily}");
1455 // how many days between the last day and now?
1456 $a->dayspending
= ceil((stats_get_base_daily() - $latestday)/(60*60*24));
1458 if ($a->dayspending
== 0 && $a->daysdone
!= 0) {
1459 return NULL; // we've only just started...
1462 //return error as string
1463 return get_string('statscatchupmode','error',$a);