Commit 9b6349c0 authored by Hugh Davenport's avatar Hugh Davenport
Browse files

Add institution statistics page



- Shows statistics per institution
- Similar to site statistics page
- Adds links to both statistics pages in menu
Signed-off-by: default avatarHugh Davenport <hugh@catalyst.net.nz>

Change-Id: Ie82b12ac994c64ac63615c2857aeb6b910dc1527
parent 33e184ff
......@@ -29,19 +29,16 @@ define('INTERNAL', 1);
define('STAFF', 1);
require(dirname(dirname(__FILE__)).'/init.php');
$type = param_alpha('type');
$type = param_alphanumext('type');
switch ($type) {
case 'weekly':
case 'institutions':
case 'viewtypes':
case 'grouptypes':
$maxage = 3600;
header('Content-type: ' . 'image/png');
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $maxage) .' GMT');
header('Cache-Control: max-age=' . $maxage);
header('Pragma: public');
if (preg_match('/^([a-z]*_)?(viewtypes|weekly)$/', $type) ||
$type == 'institutions' || $type == 'grouptypes') {
$maxage = 3600;
header('Content-type: ' . 'image/png');
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $maxage) .' GMT');
header('Cache-Control: max-age=' . $maxage);
header('Pragma: public');
readfile(get_config('dataroot') . 'images/' . $type . '.png');
exit;
readfile(get_config('dataroot') . 'images/' . $type . '.png');
exit;
}
<?php
/**
* Mahara: Electronic portfolio, weblog, resume builder and social networking
* Copyright (C) 2012 Catalyst IT Ltd and others; see:
* http://wiki.mahara.org/Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package mahara
* @subpackage core
* @author Hugh Davenport <hugh@catalyst.net.nz
*
*/
define('INTERNAL', 1);
define('ADMIN', 1);
define('JSON', 1);
require(dirname(dirname(dirname(__FILE__))) . '/init.php');
require(get_config('libroot') . 'registration.php');
$limit = param_integer('limit', 10);
$offset = param_integer('offset');
$extradata = json_decode(param_variable('extradata'));
$institution = (isset($extradata->institution) ? $extradata->institution : 'mahara');
$type = param_alpha('type', 'users');
$subpages = array('users', 'views');
if (!in_array($type, $subpages)) {
$type = 'users';
}
$institutiondata = institution_statistics($institution, true);
switch ($type) {
case 'views':
$data = institution_view_stats_table($limit, $offset, $institutiondata);
break;
case 'users':
default:
$data = institution_user_stats_table($limit, $offset, $institutiondata);
}
json_reply(false, (object) array('message' => false, 'data' => $data));
<?php
/**
* Mahara: Electronic portfolio, weblog, resume builder and social networking
* Copyright (C) 2012 Catalyst IT Ltd and others; see:
* http://wiki.mahara.org/Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package mahara
* @subpackage admin
* @author Hugh Davenport <hugh@catalyst.net.nz>
*
*/
define('INTERNAL', 1);
define('INSTITUTIONALSTAFF', 1);
define('MENUITEM', 'manageinstitutions/statistics');
require(dirname(dirname(dirname(__FILE__))).'/init.php');
require(get_config('libroot') . 'registration.php');
require_once('institution.php');
if (!is_logged_in()) {
throw new AccessDeniedException();
}
$institutionelement = get_institution_selector();
if (empty($institutionelement)) {
$smarty = smarty();
$smarty->display('admin/users/noinstitutions.tpl');
exit;
}
$institution = param_alphanum('institution', null);
if (!$institution || !$USER->can_edit_institution($institution)) {
$institution = empty($institutionelement['value']) ? $institutionelement['defaultvalue'] : $institutionelement['value'];
}
else if (!empty($institution)) {
$institutionelement['defaultvalue'] = $institution;
}
$institutionselector = pieform(array(
'name' => 'usertypeselect',
'elements' => array(
'institution' => $institutionelement,
)
));
define('TITLE', get_string('institutionstatisticsfor', 'admin', get_field('institution', 'displayname', 'name', $institution)));
$type = param_alpha('type', 'users');
$subpages = array('users', 'views');
$offset = param_integer('offset', 0);
$limit = param_integer('limit', 10);
if (!in_array($type, $subpages)) {
$type = 'users';
}
$institutiondata = institution_statistics($institution, true);
switch ($type) {
case 'views':
$data = institution_view_statistics($limit, $offset, $institutiondata);
break;
case 'users':
default:
$data = institution_user_statistics($limit, $offset, $institutiondata);
}
$wwwroot = get_config('wwwroot');
$js = <<< EOF
addLoadEvent(function () {
{$data['table']['pagination_js']}
});
function reloadStats() {
window.location.href = '{$wwwroot}admin/users/statistics.php?institution='+$('usertypeselect_institution').value+'&type={$type}';
}
addLoadEvent(function() {
connect($('usertypeselect_institution'), 'onchange', reloadStats);
});
EOF;
$smarty = smarty(array('paginator'));
$smarty->assign('PAGEHEADING', TITLE);
$smarty->assign('INLINEJAVASCRIPT', $js);
$smarty->assign('institutiondata', $institutiondata);
$smarty->assign('type', $type);
$smarty->assign('subpages', $subpages);
$smarty->assign('subpagedata', $data);
$smarty->assign('institutionselector', $institutionselector);
$smarty->display('admin/users/statistics.tpl');
......@@ -256,6 +256,7 @@ $string['statsmaxviews'] = 'Has made %s pages (most is <a href="%s">%s</a> with
$string['statsnoviews'] = 'Has made 0 pages :(';
$string['statsmaxgroups'] = 'Is in %s groups (most is <a href="%s">%s</a> with %d)';
$string['statsnogroups'] = 'Is in 0 groups :(';
$string['statsnoquota'] = 'Is using no storage :(';
$string['statsmaxquotaused'] = 'Has used about %s of disk quota (most is <a href="%s">%s</a> with %s)';
$string['groupcountsbytype'] = 'Number of groups by group type';
$string['groupcountsbyjointype'] = 'Number of groups by access type';
......@@ -267,6 +268,11 @@ $string['userstatstabletitle'] = 'Daily user statistics';
$string['groupstatstabletitle'] = 'Biggest groups';
$string['viewstatstabletitle'] = 'Most popular pages';
// Institutional statistics
$string['statistics'] = 'Statistics';
$string['institutionstatisticsfor'] = 'Institution statistics for \'%s\'';
$string['institutioncreated'] = 'Institution created';
// Site options
$string['adminsonly'] = 'Administrators only';
$string['adminsandstaffonly'] = 'Administrators and staff only';
......
......@@ -425,6 +425,82 @@ function institution_data_current($institution) {
return $data;
}
function institution_statistics($institution, $full=false) {
$data = array();
if ($full) {
$data = institution_data_current($institution);
$data['weekly'] = stats_graph_url($institution . '_weekly');
if (is_postgres()) {
$weekago = "CURRENT_DATE - INTERVAL '1 week'";
$thisweeksql = "COALESCE((lastaccess > $weekago)::int, 0)::int";
$todaysql = 'COALESCE((lastaccess > CURRENT_DATE)::int, 0)::int';
$eversql = "(NOT lastaccess IS NULL)::int";
}
else {
$weekago = 'CURRENT_DATE - INTERVAL 1 week';
$thisweeksql = "COALESCE(lastaccess > $weekago, 0)";
$todaysql = 'COALESCE(lastaccess > CURRENT_DATE, 0)';
$eversql = "NOT lastaccess IS NULL";
}
if (!$data['users']) {
$active = get_record_sql("SELECT 0 AS today, 0 AS thisweek, $weekago AS weekago, 0 AS ever");
}
else {
$sql = "SELECT SUM($todaysql) AS today, SUM($thisweeksql) AS thisweek, $weekago AS weekago, SUM($eversql) AS ever FROM {usr}
WHERE id IN (" . join(',', array_fill(0, $data['users'], '?')) . ")";
$active = get_record_sql($sql, $data['members']);
}
$data['usersloggedin'] = get_string('loggedinsince', 'admin', $active->today, $active->thisweek, format_date(strtotime($active->weekago), 'strftimedateshort'), $active->ever);
if (!$data['users']) {
$data['groupmemberaverage'] = 0;
}
else {
$memberships = count_records_sql("
SELECT COUNT(*)
FROM {group_member} m JOIN {group} g ON g.id = m.group
WHERE g.deleted = 0 AND m.member IN (" . join(',', array_fill(0, $data['users'], '?')) . ")
", $data['members']);
$data['groupmemberaverage'] = round($memberships/$data['users'], 1);
}
$data['strgroupmemberaverage'] = get_string('groupmemberaverage', 'admin', $data['groupmemberaverage']);
if (!$data['views']) {
$data['viewsperuser'] = 0;
}
else {
$data['viewsperuser'] = get_field_sql("
SELECT (0.0 + COUNT(id)) / NULLIF(COUNT(DISTINCT \"owner\"), 0)
FROM {view}
WHERE id IN (" . join(',', array_fill(0, $data['views'], '?')) . ")
", $data['viewids']);
$data['viewsperuser'] = round($data['viewsperuser'], 1);
}
$data['strviewsperuser'] = get_string('viewsperuser', 'admin', $data['viewsperuser']);
}
$data['name'] = $institution;
$data['release'] = get_config('release');
$data['version'] = get_config('version');
if ($institution == 'mahara') {
$data['installdate'] = format_date(strtotime(get_config('installation_time')), 'strftimedate');
}
else {
// *** FIXME: See if better way to get this
$data['installdate'] = format_date(strtotime(get_field_sql('SELECT MIN(ui.ctime) FROM {usr_institution} ui WHERE ui.institution = ?', array($institution))), 'strftimedate');
}
if ($data['users']) {
$data['diskusage'] = get_field_sql("
SELECT SUM(quotaused)
FROM {usr}
WHERE deleted = 0 AND id IN (" . join(',', array_fill(0, $data['users'], '?')) . ")
", $data['members']);
}
return($data);
}
function user_statistics($limit, $offset, &$sitedata) {
$data = array();
$data['tableheadings'] = array(
......@@ -622,6 +698,227 @@ function user_stats_table($limit, $offset) {
return $result;
}
function institution_user_statistics($limit, $offset, &$institutiondata) {
$data = array();
$data['tableheadings'] = array(
array('name' => get_string('date')),
array('name' => get_string('Loggedin', 'admin'), 'class' => 'center'),
array('name' => get_string('Joined', 'group'), 'class' => 'center'),
array('name' => get_string('Total'), 'class' => 'center'),
);
$data['table'] = institution_user_stats_table($limit, $offset, $institutiondata);
$data['tabletitle'] = get_string('userstatstabletitle', 'admin');
if (!$institutiondata['users']) {
$data['strmaxfriends'] = get_string('statsnofriends', 'admin');
$data['strmaxviews'] = get_string('statsnoviews', 'admin');
$data['strmaxgroups'] = get_string('statsnogroups', 'admin');
$data['strmaxquotaused'] = get_string('statsnoquota', 'admin');
$smarty = smarty_core();
$smarty->assign('data', $data);
$data['summary'] = $smarty->fetch('admin/userstatssummary.tpl');
return $data;
}
$maxfriends = get_records_sql_array("
SELECT u.id, u.firstname, u.lastname, u.preferredname, u.urlid, SUM(f.friends) AS friends
FROM {usr} u INNER JOIN (
SELECT DISTINCT(usr1) AS id, COUNT(usr1) AS friends
FROM {usr_friend}
GROUP BY usr1
UNION SELECT DISTINCT(usr2) AS id, COUNT(usr2) AS friends
FROM {usr_friend}
GROUP BY usr2
) f ON u.id = f.id
WHERE u.id IN (" . join(',', array_fill(0, $institutiondata['users'], '?')) . ")
GROUP BY u.id, u.firstname, u.lastname, u.preferredname, u.urlid
ORDER BY friends DESC
LIMIT 1", $institutiondata['members']);
$maxfriends = $maxfriends[0];
$meanfriends = count_records_sql('SELECT COUNT(*) FROM
(SELECT * FROM {usr_friend}
WHERE usr1 IN (' . join(',', array_fill(0, $institutiondata['users'], '?')) . ')
UNION ALL SELECT * FROM {usr_friend}
WHERE usr2 IN (' . join(',', array_fill(0, $institutiondata['users'], '?')) . ')
) tmp', array_merge($institutiondata['members'], $institutiondata['members'])) / $institutiondata['users'];
if ($maxfriends) {
$data['strmaxfriends'] = get_string(
'statsmaxfriends',
'admin',
round($meanfriends, 1),
profile_url($maxfriends),
hsc(display_name($maxfriends, null, true)),
$maxfriends->friends
);
}
else {
$data['strmaxfriends'] = get_string('statsnofriends', 'admin');
}
$maxviews = get_records_sql_array("
SELECT u.id, u.firstname, u.lastname, u.preferredname, u.urlid, COUNT(v.id) AS views
FROM {usr} u JOIN {view} v ON u.id = v.owner
WHERE \"owner\" IN (" . join(',', array_fill(0, $institutiondata['users'], '?')) . ")
GROUP BY u.id, u.firstname, u.lastname, u.preferredname, u.urlid
ORDER BY views DESC
LIMIT 1", $institutiondata['members']);
$maxviews = $maxviews[0];
if ($maxviews) {
$data['strmaxviews'] = get_string(
'statsmaxviews',
'admin',
$institutiondata['viewsperuser'],
profile_url($maxviews),
hsc(display_name($maxviews, null, true)),
$maxviews->views
);
}
else {
$data['strmaxviews'] = get_string('statsnoviews', 'admin');
}
$maxgroups = get_records_sql_array("
SELECT u.id, u.firstname, u.lastname, u.preferredname, u.urlid, COUNT(m.group) AS groups
FROM {usr} u JOIN {group_member} m ON u.id = m.member JOIN {group} g ON m.group = g.id
WHERE g.deleted = 0 AND u.id IN (" . join(',', array_fill(0, $institutiondata['users'], '?')) . ")
GROUP BY u.id, u.firstname, u.lastname, u.preferredname, u.urlid
ORDER BY groups DESC
LIMIT 1", $institutiondata['members']);
$maxgroups = $maxgroups[0];
if ($maxgroups) {
$data['strmaxgroups'] = get_string(
'statsmaxgroups',
'admin',
$institutiondata['groupmemberaverage'],
profile_url($maxgroups),
hsc(display_name($maxgroups, null, true)),
$maxgroups->groups
);
}
else {
$data['strmaxgroups'] = get_string('statsnogroups', 'admin');
}
$maxquotaused = get_records_sql_array("
SELECT id, firstname, lastname, preferredname, urlid, quotaused
FROM {usr}
WHERE id IN (" . join(',', array_fill(0, $institutiondata['users'], '?')) . ")
ORDER BY quotaused DESC
LIMIT 1", $institutiondata['members']);
$maxquotaused = $maxquotaused[0];
$avgquota = get_field_sql("
SELECT AVG(quotaused)
FROM {usr}
WHERE id IN (" . join(',', array_fill(0, $institutiondata['users'], '?')) . ")
", $institutiondata['members']);
$data['strmaxquotaused'] = get_string(
'statsmaxquotaused',
'admin',
display_size($avgquota),
profile_url($maxquotaused),
hsc(display_name($maxquotaused, null, true)),
display_size($maxquotaused->quotaused)
);
$smarty = smarty_core();
$smarty->assign('data', $data);
$data['summary'] = $smarty->fetch('admin/userstatssummary.tpl');
return $data;
}
function institution_user_stats_table($limit, $offset, &$institutiondata) {
$count = count_records('institution_data', 'type', 'user-count-daily', 'institution', $institutiondata['name']);
$pagination = build_pagination(array(
'id' => 'stats_pagination',
'url' => get_config('wwwroot') . 'admin/users/statistics.php?institution=' . $institutiondata['name'] . '&type=users',
'jsonscript' => 'admin/users/statistics.json.php',
'datatable' => 'statistics_table',
'count' => $count,
'limit' => $limit,
'offset' => $offset,
'extradata' => array('institution' => $institutiondata['name']),
));
$result = array(
'count' => $count,
'tablerows' => '',
'pagination' => $pagination['html'],
'pagination_js' => $pagination['javascript'],
);
if ($count < 1) {
return $result;
}
$day = is_postgres() ? "to_date(t.ctime::text, 'YYYY-MM-DD')" : 'DATE(t.ctime)'; // TODO: make work on other databases?
$daterange = get_record_sql(
"SELECT
MIN($day) AS mindate,
MAX($day) AS maxdate
FROM (
SELECT ctime
FROM {institution_data}
WHERE type = ? AND institution = ?
ORDER BY ctime DESC
LIMIT $limit
OFFSET $offset
) t",
array('user-count-daily', $institutiondata['name'])
);
$dayinterval = is_postgres() ? "'1 day'" : '1 day';
$day = is_postgres() ? "to_date(ctime::text, 'YYYY-MM-DD')" : 'DATE(ctime)';
$userdata = get_records_sql_array(
"SELECT ctime, type, \"value\", $day AS date
FROM {institution_data}
WHERE type IN (?,?) AND institution = ? AND ctime >= ? AND ctime < (date(?) + INTERVAL $dayinterval)
ORDER BY type = ? DESC, ctime DESC",
array('user-count-daily', 'loggedin-users-daily', $institutiondata['name'], $daterange->mindate, $daterange->maxdate, 'user-count-daily')
);
$userscreated = get_records_sql_array(
"SELECT $day as cdate, COUNT(usr) AS users
FROM {usr_institution}
WHERE institution = ?
AND NOT ctime IS NULL AND ctime >= ? AND ctime < (date(?) + INTERVAL $dayinterval)
GROUP BY cdate",
array($institutiondata['name'], $daterange->mindate, $daterange->maxdate)
);
$data = array();
if ($userdata) {
foreach ($userdata as &$r) {
if ($r->type == 'user-count-daily') {
$data[$r->date] = array(
'date' => $r->date,
'total' => $r->value,
);
}
else if ($r->type == 'loggedin-users-daily' && isset($data[$r->date])) {
$data[$r->date]['loggedin'] = $r->value;
}
}
if ($userscreated) {
foreach ($userscreated as &$r) {
if (isset($data[$r->cdate])) {
$data[$r->cdate]['created'] = $r->users;
}
}
}
}
$smarty = smarty_core();
$smarty->assign('data', $data);
$result['tablerows'] = $smarty->fetch('admin/userstats.tpl');
return $result;
}
function user_institution_graph() {
// Draw a bar graph showing the number of users in each institution
......@@ -1030,6 +1327,118 @@ function view_type_graph() {
}
}
function institution_view_statistics($limit, $offset, &$institutiondata) {
$data = array();
$data['tableheadings'] = array(
array('name' => '#'),
array('name' => get_string('view')),
array('name' => get_string('Owner', 'view')),
array('name' => get_string('Visits'), 'class' => 'center'),
array('name' => get_string('Comments', 'artefact.comment'), 'class' => 'center'),
);
$data['table'] = institution_view_stats_table($limit, $offset, $institutiondata);
$data['tabletitle'] = get_string('viewstatstabletitle', 'admin');
$smarty = smarty_core();
$maxblocktypes = 5;
if ($institutiondata['views']) {
$smarty->assign('blocktypecounts', get_records_sql_array("
SELECT
b.blocktype,
CASE WHEN bi.artefactplugin IS NULL THEN b.blocktype
ELSE bi.artefactplugin || '/' || b.blocktype END AS langsection,
COUNT(b.id) AS blocks
FROM {block_instance} b
JOIN {blocktype_installed} bi ON (b.blocktype = bi.name)
JOIN {view} v ON (b.view = v.id AND v.type = 'portfolio')
WHERE v.id IN (" . join(',', array_fill(0, $institutiondata['views'], '?')) . ")
GROUP BY b.blocktype, langsection
ORDER BY blocks DESC",
$institutiondata['viewids'], 0, $maxblocktypes
));
}
$smarty->assign('viewtypes', stats_graph_url($institutiondata['name'] . '_viewtypes'));
$smarty->assign('viewcount', $data['table']['count']);
$data['summary'] = $smarty->fetch('admin/viewstatssummary.tpl');
return $data;
}
function institution_view_stats_table($limit, $offset, &$institutiondata) {
if ($institutiondata['views'] != 0) {
$count = count_records_select('view', 'id IN (' . join(',', array_fill(0, $institutiondata['views'], '?')) . ') AND type != ?',
array_merge($institutiondata['viewids'], array('dashboard')));
}
else {
$count = 0;
}
$pagination = build_pagination(array(
'id' => 'stats_pagination',
'url' => get_config('wwwroot') . 'admin/users/statistics.php?institution=' . $institutiondata['name'] . '&type=views',
'jsonscript' => 'admin/users/statistics.json.php',
'datatable' => 'statistics_table',
'count' => $count,
'limit' => $limit,
'offset' => $offset,
'extradata' => array('institution' => $institutiondata['name']),
));
$result = array(
'count' => $count,
'tablerows' => '',
'pagination' => $pagination['html'],
'pagination_js' => $pagination['javascript'],
);
if ($count < 1) {
return $result;
}
$viewdata = get_records_sql_assoc(
"SELECT