Commit 19af23b2 authored by Robert Lyon's avatar Robert Lyon

Bug 1483963 - Better reporting on login activity

Added a tab 'Logins' to site statistics page that contains
how many total logins for an institution and also how many unique
users have logged in during a certain time period - defaults to
previous calendar month

One can get different results by adjusting the url like so
admin/statistics.php?type=logins&start=2014-01-01&end=2015-01-01
if needing to get a different time period

On upgrade it populates the usr_login_data table with the current
lastlogin time for non-deleted users

Currently usr_login_data only records user id and ctime (for login
time) but one could also add more columns tothe table if needing to
record something that happens once per successful login.

Change-Id: If59b207356894eaced7b9977b80d539a28cb7e56
Signed-off-by: Robert Lyon's avatarRobert Lyon <robertl@catalyst.net.nz>
parent efcf6b8a
......@@ -21,7 +21,7 @@ $offset = param_integer('offset', 0);
$extradata = json_decode(param_variable('extradata'));
$type = param_alpha('type', 'users');
$subpages = array('users', 'groups', 'views', 'content', 'historical', 'institutions');
$subpages = array('users', 'groups', 'views', 'content', 'historical', 'institutions', 'logins');
if (!in_array($type, $subpages)) {
$type = 'users';
}
......@@ -30,12 +30,18 @@ if ($type == 'historical') {
$field = (isset($extradata->field) ? $extradata->field : 'count_usr');
}
if ($type == 'institutions') {
if ($type == 'institutions' || $type == 'logins') {
$sort = (isset($extradata->sort) ? $extradata->sort : 'displayname');
$sortdesc = (isset($extradata->sortdesc) ? $extradata->sortdesc : false);
$start = param_alphanumext('start', null);
$end = param_alphanumext('end', null);
}
switch ($type) {
case 'logins':
$data = institution_logins_statistics($limit, $offset, $sort, $sortdesc, $start, $end);
$data = $data['table'];
break;
case 'institutions':
$data = institution_comparison_stats_table($limit, $offset, $sort, $sortdesc);
break;
......
......@@ -19,7 +19,7 @@ require(get_config('libroot') . 'registration.php');
define('TITLE', get_string('sitestatistics', 'admin'));
$type = param_alpha('type', 'users');
$subpages = array('users', 'groups', 'views', 'content', 'historical', 'institutions');
$subpages = array('users', 'groups', 'views', 'content', 'historical', 'institutions', 'logins');
$offset = param_integer('offset', 0);
$limit = param_integer('limit', 10);
......@@ -31,14 +31,19 @@ if ($type == 'historical') {
$field = param_alphanumext('field', 'count_usr');
}
if ($type == 'institutions') {
if ($type == 'institutions' || $type == 'logins') {
$sort = param_alphanumext('sort', 'displayname');
$sortdesc = param_boolean('sortdesc');
$start = param_alphanumext('start', null);
$end = param_alphanumext('end', null);
}
$sitedata = site_statistics(true);
switch ($type) {
case 'logins':
$data = institution_logins_statistics($limit, $offset, $sort, $sortdesc, $start, $end);
break;
case 'institutions':
$data = institution_comparison_statistics($limit, $offset, $sort, $sortdesc);
break;
......
......@@ -1650,11 +1650,12 @@ class LiveUser extends User {
$this->populate($user);
session_regenerate_id(true);
$time = time();
$this->lastlastlogin = $this->lastlogin;
$this->lastlogin = time();
$this->lastaccess = time();
$this->lastlogin = $time;
$this->lastaccess = $time;
$this->sessionid = session_id();
$this->logout_time = time() + get_config('session_timeout');
$this->logout_time = $time + get_config('session_timeout');
$this->sesskey = get_random_key();
// We need a user->id before we load_c*_preferences
......@@ -1662,6 +1663,9 @@ class LiveUser extends User {
$this->activityprefs = load_activity_preferences($user->id);
$this->accountprefs = load_account_preferences($user->id);
// Record the successful login in the usr_login_data table
insert_record('usr_login_data', (object) array('usr' => $user->id, 'ctime' => db_format_timestamp($time)));
// If user has chosen a language while logged out, save it as their lang pref.
$sessionlang = $this->SESSION->get('lang');
if (!empty($sessionlang) && $sessionlang != 'default'
......
......@@ -293,6 +293,8 @@ $string['viewsbytype'] = 'Pages by type';
$string['userstatstabletitle'] = 'Daily user statistics';
$string['groupstatstabletitle'] = 'Biggest groups';
$string['viewstatstabletitle'] = 'Most popular pages';
$string['institutionloginstabletitle'] = 'Active institutions';
$string['institutionloginstablesubtitle'] = 'For %s - %s';
$string['visitedtimesrank'] = 'visited %s times, ranked number %s';
$string['pageownedby'] = 'Owned by';
$string['contentstats'] = 'modified %s times for the current week and %s times in total';
......
......@@ -1163,6 +1163,7 @@ $string['content'] = 'Content';
$string['modified'] = 'Modified';
$string['historical'] = 'Historical data';
$string['institutions'] = 'Institutions';
$string['logins'] = 'Logins';
$string['members'] = 'Members';
$string['blocks'] = 'Blocks';
$string['artefacts'] = 'Artefacts';
......
......@@ -196,4 +196,7 @@ $string['user-count'] = 'User';
$string['institutiondataweekly'] = 'Institution weekly data';
$string['usersbytype'] = 'Users by type';
$string['staff'] = 'Staff';
$string['admins'] = 'Administrators';
\ No newline at end of file
$string['admins'] = 'Administrators';
$string['activeusers'] = "Active users";
$string['logins'] = "Logins";
\ No newline at end of file
......@@ -210,6 +210,17 @@
<KEY NAME="institution" TYPE="foreign" FIELDS="institution" REFTABLE="institution" REFFIELDS="name"/>
</KEYS>
</TABLE>
<TABLE NAME="usr_login_data">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" />
<FIELD NAME="usr" TYPE="int" LENGTH="10" NOTNULL="true"/>
<FIELD NAME="ctime" TYPE="datetime" NOTNULL="true"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" />
<KEY NAME="usr" TYPE="foreign" FIELDS="usr" REFTABLE="usr" REFFIELDS="id"/>
</KEYS>
</TABLE>
<TABLE NAME="usr_session">
<FIELDS>
<FIELD NAME="usr" TYPE="int" LENGTH="10" NOTNULL="true" />
......
......@@ -4156,5 +4156,30 @@ function xmldb_core_upgrade($oldversion=0) {
}
}
if ($oldversion < 2015081000) {
log_debug('Add user_login_data table to record when a user logs in');
$table = new XMLDBTable('usr_login_data');
$table->addFieldInfo('id', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, XMLDB_SEQUENCE);
$table->addFieldInfo('usr', XMLDB_TYPE_INTEGER, 10, false, XMLDB_NOTNULL);
$table->addFieldInfo('ctime', XMLDB_TYPE_DATETIME, null, null, XMLDB_NOTNULL);
$table->addKeyInfo('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->addKeyInfo('usrloginfk', XMLDB_KEY_FOREIGN, array('usr'), 'usr', array('id'));
create_table($table);
// Insert info about current users's logins
$results = get_records_sql_array("SELECT id,lastlogin FROM usr WHERE deleted = 0 AND lastlogin IS NOT NULL");
$count = 0;
$limit = 1000;
$total = count($results);
foreach ($results as $result) {
insert_record('usr_login_data', (object) array('usr' => $result->id, 'ctime' => $result->lastlogin));
$count++;
if (($count % $limit) == 0 || $count == $total) {
log_debug("$count/$total");
set_time_limit(30);
}
}
}
return $status;
}
......@@ -2111,3 +2111,136 @@ function graph_institution_data_weekly($type = null, $institutiondata) {
function graph_institution_data_daily(&$institutiondata) {
institution_view_type_graph(null, $institutiondata);
}
/**
* Create logins by institution layout for the site statistics page
*
* @param int $limit Limit results
* @param int $offset Starting offset
* @param string $sort DB Column to sort by
* @param string/int $sortdesc The direction to sort the $sort column by
* @param string $start The start date to filter results by - format 'YYYY-MM-DD HH:MM:SS'
* @param string $end The end date to filter results by - format 'YYYY-MM-DD HH:MM:SS'
*
* @results array Results containing the html / pagination data
*/
function institution_logins_statistics($limit, $offset, $sort, $sortdesc, $start=null, $end=null) {
// If no start/end dates provided then default to the previous full month
$start = ($start) ? $start : date('Y-m-d H:i:s', mktime(0,0,0,date('n')-1,1,date('Y'))); // first day of previous month
$end = ($end) ? $end : date('Y-m-d H:i:s', mktime(23,59,59,date('n'),0,date('Y'))); // last day of previous month
$data = array();
$data['tableheadings'] = array(
array(
'name' => get_string('institution'),
'class' => 'search-results-sort-column' . ($sort == 'displayname' ? ' ' . ($sortdesc ? 'desc' : 'asc') : ''),
'link' => get_config('wwwroot') . 'admin/statistics.php?type=logins&sort=displayname&sortdesc=' . ($sort == 'displayname' ? !$sortdesc : false) . '&limit=' . $limit . '&offset=' . $offset . '&start=' . $start . '&end=' . $end
),
array(
'name' => get_string('logins', 'statistics'),
'class' => 'search-results-sort-column' . ($sort == 'count_logins' ? ' ' . ($sortdesc ? 'desc' : 'asc') : ''),
'link' => get_config('wwwroot') . 'admin/statistics.php?type=logins&sort=count_logins&sortdesc=' . ($sort == 'count_logins' ? !$sortdesc : true) . '&limit=' . $limit . '&offset=' . $offset . '&start=' . $start . '&end=' . $end
),
array(
'name' => get_string('activeusers', 'statistics'),
'class' => 'search-results-sort-column' . ($sort == 'count_active' ? ' ' . ($sortdesc ? 'desc' : 'asc') : ''),
'link' => get_config('wwwroot') . 'admin/statistics.php?type=logins&sort=count_active&sortdesc=' . ($sort == 'count_active' ? !$sortdesc : true) . '&limit=' . $limit . '&offset=' . $offset . '&start=' . $start . '&end=' . $end
),
);
$data['table'] = institution_logins_stats_table($limit, $offset, $sort, $sortdesc, $start, $end);
$data['tabletitle'] = get_string('institutionloginstabletitle', 'admin');
$data['tablesubtitle'] = get_string('institutionloginstablesubtitle', 'admin', format_date(strtotime($start), 'strftimedate'), format_date(strtotime($end), 'strftimedate'));
$data['summary'] = $data['table']['count'] == 0 ? get_string('nostats', 'admin') : null;
return $data;
}
/**
* Create logins by institution table for the site statistics page
*
* @param int $limit Limit results
* @param int $offset Starting offset
* @param string $sort DB Column to sort by
* @param string/int $sortdesc The direction to sort the $sort column by
* @param string $start The start date to filter results by - format 'YYYY-MM-DD HH:MM:SS'
* @param string $end The end date to filter results by - format 'YYYY-MM-DD HH:MM:SS'
*
* @results array Results containing the html / pagination data
*/
function institution_logins_stats_table($limit, $offset, $sort, $sortdesc, $start, $end) {
global $USER;
$rawdata = users_active_data(null, null, $sort, $sortdesc, $start, $end);
$count = ($rawdata) ? count($rawdata) : 0;
$pagination = build_pagination(array(
'id' => 'stats_pagination',
'url' => get_config('wwwroot') . 'admin/statistics.php?type=logins&start=' . date('Y-m-d', strtotime($start)) . '&end=' . date('Y-m-d', strtotime($end)),
'jsonscript' => 'admin/statistics.json.php',
'datatable' => 'statistics_table',
'count' => $count,
'limit' => $limit,
'offset' => $offset,
'setlimit' => true,
));
$result = array(
'count' => $count,
'tablerows' => '',
'pagination' => $pagination['html'],
'pagination_js' => $pagination['javascript'],
);
if ($count < 1) {
return $result;
}
$csvfields = array('name', 'displayname', 'count_logins', 'count_active');
$USER->set_download_file(generate_csv($rawdata, $csvfields), 'userloginstatistics.csv', 'text/csv');
$result['csv'] = true;
$data = array_slice($rawdata, $offset, $limit);
$smarty = smarty_core();
$smarty->assign('data', $data);
$result['tablerows'] = $smarty->fetch('admin/userloginsummary.tpl');
return $result;
}
/**
* Get records of how many users have their last login fall within a certain time period.
* Group the results by institution.
*
* @param string $start The start of the time period - format 'YYYY-MM-DD HH:II:SS'
* @param string $end The end of the time period - format 'YYYY-MM-DD HH:II:SS'
* @param string $institution Restrict the results to a particular institution.
*
* @result int $count The total count of 'users per institution' rows
* @result array $results The count of users per institution
*/
function users_active_data($limit=0, $offset=0, $sort='displayname', $sortdesc='DESC', $start = null, $end = null, $institution = null) {
if (!$start) {
$start = db_format_timestamp(strtotime("-1 months"));
}
if (!$end) {
$end = db_format_timestamp(time());
}
$sql = "SELECT CASE WHEN i.name IS NOT NULL THEN i.name ELSE 'mahara' END AS name,
CASE WHEN i.displayname IS NOT NULL THEN i.displayname ELSE 'No institution' END AS displayname,
COUNT(u.ctime) AS count_logins, COUNT(DISTINCT u.usr) AS count_active
FROM {usr_login_data} u
LEFT JOIN {usr_institution} ui ON ui.usr = u.usr
LEFT JOIN {institution} i ON i.name = ui.institution
WHERE (u.ctime >= ? AND u.ctime <= ?)";
$where = array($start, $end);
if ($institution) {
$sql .= " AND i.name = ?";
$where[] = $institution;
}
$sql .= " GROUP BY i.name, i.displayname ORDER BY " . $sort . " " . ($sortdesc ? 'DESC' : 'ASC');
$results = get_records_sql_array($sql, $where, $offset, $limit);
return $results;
}
......@@ -1520,6 +1520,7 @@ function delete_user($userid) {
delete_records('usr_password_request', 'usr', $userid);
delete_records('usr_watchlist_view', 'usr', $userid);
delete_records('view_access', 'usr', $userid);
delete_records('usr_login_data', 'usr', $userid);
// Remove the user's views & artefacts
$viewids = get_column('view', 'id', 'owner', $userid);
......
......@@ -16,7 +16,7 @@ $config = new stdClass();
// See https://wiki.mahara.org/index.php/Developer_Area/Version_Numbering_Policy
// For upgrades on stable branches, increment the version by one. On master, use the date.
$config->version = 2015072000;
$config->version = 2015081000;
$config->series = '15.10';
$config->release = '15.10dev';
$config->minupgradefrom = 2009022600;
......
......@@ -20,9 +20,10 @@
</div>
<div class="subpage panel-body row" id="site-stats-wrap2">
<div id="statistics_table_container" class="col-md-12">
<h3>{$subpagedata.tabletitle}</h3>
{if $subpagedata.tablesubtitle}<div class="small">{$subpagedata.tablesubtitle}</div>{/if}
{if $subpagedata.table.count == 0}{else}
<div id="statistics_table_container" class="col-md-12">
<h3>{$subpagedata.tabletitle}</h3>
<table id="statistics_table" class="table table-striped fullwidth">
<thead>
<tr>
......@@ -36,8 +37,8 @@
</tbody>
</table>
{$subpagedata.table.pagination|safe}
</div>
{/if}
</div>
{if $subpagedata.summary}
<div class="col-md-12 image-right">
{$subpagedata.summary|safe}
......
{if $data}
{foreach from=$data item=institution}
<tr>
<td><a href="{$WWWROOT}institution/index.php?institution={$institution->name}">{$institution->displayname}</a></td>
<td>{$institution->count_logins}</td>
<td>{$institution->count_active}</td>
</tr>
{/foreach}
{/if}
......@@ -57,6 +57,7 @@ Scenario: Clicking randomly around Mahara (Bug: 1426983)
# Checking Admin home Menu and submenu
And I choose "Register" in "Admin home"
And I choose "Site statistics" in "Admin home"
And I follow "Logins"
And I choose "Overview" in "Admin home"
# Checking Configure site Menu and submenu
And I choose "Site options" in "Configure site"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment