Issue #1189 : Statistic page redesign

* Statistic page totally redesign, replace by a single graphic which display for last hours, days, months and years
 * Add Chart.js plugin for graphic representation
 * Add Moment.js plugin for time operations
This commit is contained in:
Zacharie
2020-05-26 13:08:26 +02:00
committed by plegall
parent 7055ee1e67
commit b32daddafe
7 changed files with 285 additions and 244 deletions
+102 -221
View File
@@ -6,9 +6,9 @@
// | file that was distributed with this source code. |
// +-----------------------------------------------------------------------+
if (!defined("PHPWG_ROOT_PATH"))
if (!defined('PHPWG_ROOT_PATH'))
{
die ("Hacking attempt!");
die ('Hacking attempt!');
}
include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
@@ -18,7 +18,7 @@ include_once(PHPWG_ROOT_PATH.'admin/include/functions_history.inc.php');
// | Functions |
// +-----------------------------------------------------------------------+
function get_summary($year = null, $month = null, $day = null)
function get_last($last_number=60, $type='year')
{
$query = '
SELECT
@@ -29,42 +29,45 @@ SELECT
nb_pages
FROM '.HISTORY_SUMMARY_TABLE;
if (isset($day))
if ($type === 'hour')
{
$query.= '
WHERE year = '.$year.'
AND month = '.$month.'
AND day = '.$day.'
WHERE year IS NOT NULL
AND month IS NOT NULL
AND day IS NOT NULL
AND hour IS NOT NULL
ORDER BY
year ASC,
month ASC,
day ASC,
hour ASC
year DESC,
month DESC,
day DESC,
hour DESC
LIMIT '.$last_number.'
;';
}
elseif (isset($month))
elseif ($type === 'day')
{
$query.= '
WHERE year = '.$year.'
AND month = '.$month.'
WHERE year IS NOT NULL
AND month IS NOT NULL
AND day IS NOT NULL
AND hour IS NULL
ORDER BY
year ASC,
month ASC,
day ASC
year DESC,
month DESC,
day DESC
LIMIT '.$last_number.'
;';
}
elseif (isset($year))
elseif ($type === 'month')
{
$query.= '
WHERE year = '.$year.'
WHERE year IS NOT NULL
AND month IS NOT NULL
AND day IS NULL
ORDER BY
year ASC,
month ASC
year DESC,
month DESC
LIMIT '.$last_number.'
;';
}
else
@@ -73,7 +76,8 @@ SELECT
WHERE year IS NOT NULL
AND month IS NULL
ORDER BY
year ASC
year DESC
LIMIT '.$last_number.'
;';
}
@@ -94,94 +98,10 @@ SELECT
check_status(ACCESS_ADMINISTRATOR);
// +-----------------------------------------------------------------------+
// | Refresh summary from details |
// +-----------------------------------------------------------------------+
history_summarize();
// +-----------------------------------------------------------------------+
// | Page parameters check |
// +-----------------------------------------------------------------------+
foreach (array('day', 'month', 'year') as $key)
{
if (isset($_GET[$key]))
{
$page[$key] = (int)$_GET[$key];
}
}
if (isset($page['day']))
{
if (!isset($page['month']))
{
die('month is missing in URL');
}
}
if (isset($page['month']))
{
if (!isset($page['year']))
{
die('year is missing in URL');
}
}
$summary_lines = get_summary(
@$page['year'],
@$page['month'],
@$page['day']
);
// +-----------------------------------------------------------------------+
// | Display statistics header |
// +-----------------------------------------------------------------------+
// page title creation
$title_parts = array();
$url = PHPWG_ROOT_PATH.'admin.php?page=stats';
$title_parts[] = '<a href="'.$url.'">'.l10n('Overall').'</a>';
$period_label = l10n('Year');
if (isset($page['year']))
{
$url.= '&amp;year='.$page['year'];
$title_parts[] = '<a href="'.$url.'">'.$page['year'].'</a>';
$period_label = l10n('Month');
}
if (isset($page['month']))
{
$url.= '&amp;month='.$page['month'];
$title_parts[] = '<a href="'.$url.'">'.$lang['month'][$page['month']].'</a>';
$period_label = l10n('Day');
}
if (isset($page['day']))
{
$url.= '&amp;day='.$page['day'];
$time = mktime(12, 0, 0, $page['month'], $page['day'], $page['year']);
$day_title = sprintf(
'%u (%s)',
$page['day'],
$lang['day'][date('w', $time)]
);
$title_parts[] = '<a href="'.$url.'">'.$day_title.'</a>';
$period_label = l10n('Hour');
}
$template->set_filename('stats', 'stats.tpl');
// TabSheet initialization
@@ -191,131 +111,92 @@ $base_url = get_root_url().'admin.php?page=history';
$template->assign(
array(
'L_STAT_TITLE' => implode($conf['level_separator'], $title_parts),
'PERIOD_LABEL' => $period_label,
'U_HELP' => get_root_url().'admin/popuphelp.php?page=history',
'F_ACTION' => $base_url,
)
);
// +-----------------------------------------------------------------------+
// | Set missing unit to 0 |
// +-----------------------------------------------------------------------+
function set_missing_value($unit, $data)
{
$limit = count($data);
$result = array();
$date = get_date_object($data[count($data) - 1]);
if ($unit == 'year')
{
$date_format = 'Y';
$date_add = 'P1Y';
}
else if ($unit == 'month')
{
$date_format = 'Y-m';
$date_add = 'P1M';
}
else if ($unit == 'day')
{
$date_format = 'Y-m-d';
$date_add = 'P1D';
}
else if ($unit == 'hour')
{
$date_format = 'Y-m-d\TH:00';
$date_add = 'PT1H';
}
for ($i=0; $i < $limit; $i++)
{
$result[$date->format($date_format)] = 0;
$date->add(new DateInterval($date_add));
}
foreach ($data as $value)
{
$str = get_date_object($value)->format($date_format);
if (isset($result[$str]))
{
$result[$str] += $value['nb_pages'];
}
}
return $result;
}
function get_date_object($row) {
$date_string = $row['year'];
if ($row['month'] != null)
{
$date_string = $date_string.'-'.$row['month'] ;
if ($row['day'] != null)
{
$date_string = $date_string.'-'.$row['day'];
if ($row['hour'] != null)
{
$date_string = $date_string.' '.$row['hour'].':00';
}
}
}
else
{
$date_string .= '-1';
}
return new DateTime($date_string);
}
// +-----------------------------------------------------------------------+
// | Display statistic rows |
// +-----------------------------------------------------------------------+
$max_width = 400;
$template->append('lastHours', set_missing_value('hour',get_last(72, 'hour')));
$template->append('lastDays', set_missing_value('day',get_last(90, 'day')));
$template->append('lastMonths', set_missing_value('month',get_last(24, 'month')));
$template->append('lastYears', set_missing_value('year',get_last(60, 'year')));
$datas = array();
if (isset($page['day']))
{
$key = 'hour';
$min_x = 0;
$max_x = 23;
}
elseif (isset($page['month']))
{
$key = 'day';
$min_x = 1;
$max_x = date(
't',
mktime(12, 0, 0, $page['month'], 1, $page['year'])
);
}
elseif (isset($page['year']))
{
$key = 'month';
$min_x = 1;
$max_x = 12;
}
else
{
$key = 'year';
}
$max_pages = 1;
foreach ($summary_lines as $line)
{
if ($line['nb_pages'] > $max_pages)
{
$max_pages = $line['nb_pages'];
}
$datas[ $line[$key] ] = $line['nb_pages'];
}
if (!isset($min_x) and !isset($max_x) and count($datas) > 0)
{
$min_x = min(array_keys($datas));
$max_x = max(array_keys($datas));
}
if (count($datas) > 0)
{
for ($i = $min_x; $i <= $max_x; $i++)
{
if (!isset($datas[$i]))
{
$datas[$i] = 0;
}
$url = null;
if (isset($page['day']))
{
$value = sprintf('%02u', $i);
}
else if (isset($page['month']))
{
$url =
get_root_url().'admin.php'
.'?page=stats'
.'&amp;year='.$page['year']
.'&amp;month='.$page['month']
.'&amp;day='.$i
;
$time = mktime(12, 0, 0, $page['month'], $i, $page['year']);
$value = $i.' ('.$lang['day'][date('w', $time)].')';
}
else if (isset($page['year']))
{
$url =
get_root_url().'admin.php'
.'?page=stats'
.'&amp;year='.$page['year']
.'&amp;month='.$i
;
$value = $lang['month'][$i];
}
else
{
// at least the year is defined
$url =
get_root_url().'admin.php'
.'?page=stats'
.'&amp;year='.$i
;
$value = $i;
}
if ($datas[$i] != 0 and isset($url))
{
$value = '<a href="'.$url.'">'.$value.'</a>';
}
$template->append(
'statrows',
array(
'VALUE' => $value,
'PAGES' => $datas[$i],
'WIDTH' => ceil(($datas[$i] * $max_width) / $max_pages ),
)
);
}
}
// +-----------------------------------------------------------------------+
// | Sending html code |
+97
View File
@@ -0,0 +1,97 @@
/*-------
Data Get
-------*/
data = {};
data["hours"] = $("#data").data("hours")[0];
data["days"] = $("#data").data("days")[0];
data["months"] = $("#data").data("months")[0];
data["years"] = $("#data").data("years")[0];
data_unit = {
"hours":"day",
"days":"month",
"months": "year",
"years": "year"
}
/*-------
Creating graph
-------*/
var ctx = document.getElementById('stat-graph').getContext('2d');
var gradient = ctx.createLinearGradient(0,400, 0,0);
gradient.addColorStop(0, 'rgba(255,166,70,0)');
gradient.addColorStop(1, '#FFA646');
Chart.defaults.global.elements.point.radius = 0.1;
Chart.defaults.global.elements.point.hitRadius = 10
var statGraph = new Chart(ctx, {
type: 'line',
maintainAspectRatio: false,
options: {
scales: {
xAxes: [{
type: 'time',
time: {
tooltipFormat: 'll'
},
gridLines: {
display: false
}
}],
}
}
});
var displayOptions = {
backgroundColor: gradient,
borderColor: '#FFA646',
lineTension : 0.2
}
function changeData(dataType, label, options = displayOptions) {
statGraph.data = {
datasets: [{
label: label,
data: getValues(data[dataType]),
...options
}]
}
statGraph.options.scales.xAxes.forEach(axe => {
axe.time.tooltipFormat = str_tooltip_format[dataType];
axe.time.unit = data_unit[dataType];
axe.time.displayFormats = str_unit_format;
})
statGraph.update();
}
function getValues(data) {
values = [];
Object.keys(data).forEach(function(key) {
var newPoint = {
x:new Date(key),
y:data[key]
}
values.push(newPoint)
});
return values;
}
function addAZero(number) {
if (number < 10)
return "0"+number;
return number;
}
$(".stat-data-selector label").on("click", function(){
let dataType = $(this).data("value");
changeData(dataType, str_number_page_visited)
})
/*-------
Initialize the page
-------*/
$(function() {
let dataType = $(".stat-data-selector input:checked + label").data("value");
changeData(dataType, str_number_page_visited)
})
+39 -19
View File
@@ -1,25 +1,45 @@
{footer_script}
var str_months_names = ["{'January'|@translate}","{'February'|@translate}","{'March'|@translate}","{'April'|@translate}","{'May'|@translate}","{'June'|@translate}","{'July'|@translate}","{'August'|@translate}","{'September'|@translate}","{'October'|@translate}","{'November'|@translate}","{'December'|@translate}"];
var str_number_page_visited = "{'Number of visited pages'|@translate}";
var str_tooltip_format = {
"years":"YYYY",
"months":"MMMM YYYY",
"days":"DD MMM",
"hours":"H[h] - dddd"
};
var str_unit_format = {
"day":"dddd",
"month":"MMM YYYY"
}
moment.locale({'en'|@translate});
{/footer_script}
{combine_script id='chart.js' load='footer' path='themes/default/js/plugins/Chart.min.js'}
{combine_css path="themes/default/js/plugins/Chart.min.css"}
{combine_script id='moment-with-locales.js' path='themes/default/js/plugins/moment-with-locales.min.js'}
{combine_script id='stats' load='footer' path='admin/themes/default/js/stats.js'}
<div class="titrePage">
<h2>{'History'|@translate} {$TABSHEET_TITLE}</h2>
<h2>{'History'|@translate}</h2>
</div>
<h3>{$L_STAT_TITLE}</h3>
<div id="data" data-hours='{json_encode($lastHours)}' data-days='{json_encode($lastDays)}' data-months='{json_encode($lastMonths)}' data-years='{json_encode($lastYears)}'></div>
<table class="table2" id="dailyStats">
<tr class="throw">
<th>{$PERIOD_LABEL}</th>
<th>{'Pages seen'|@translate}</th>
<th></th>
</tr>
<div class="stat-legend-container">
<div class="stat-data-selector">
<input type="radio" id="hours-selector" name="stat-data-type" checked>
<label for="hours-selector" data-value="hours">{"Hours"|@translate}</label>
<input type="radio" id="days-selector" name="stat-data-type">
<label for="days-selector" data-value="days">{"Day"|@translate}</label>
<input type="radio" id="months-selector" name="stat-data-type">
<label for="months-selector" data-value="months">{"Month"|@translate}</label>
<input type="radio" id="years-selector" name="stat-data-type">
<label for="years-selector" data-value="years">{"Year"|@translate}</label>
</div>
</div>
{if not empty($statrows)}
{foreach from=$statrows item=row}
<tr>
<td style="white-space: nowrap">{$row.VALUE}</td>
<td class="number">{$row.PAGES}</td>
<td><div class="statBar" style="width:{$row.WIDTH}px"></div></td>
</tr>
{/foreach}
{/if}
</table>
<div class="stat-graph-container">
<canvas id="stat-graph" width="400" height="200" role="img"><p>Your browser does not support the canvas element.</p></canvas>
</div>
+37 -4
View File
@@ -248,10 +248,43 @@ TABLE.doubleSelect SELECT.categoryList {
width: 100%; max-width: 100%; overflow-x: auto;
}
.statBar {
height: 10px;
background-color: #ff7700;
border: 1px solid #666;
.stat-legend-container {
display: inline-block;
}
.stat-data-selector {
border-radius: 10px;
display: flex;
overflow: hidden;
font-weight: bold;
}
.stat-data-selector label {
color: black;
padding: 10px;
background-color: #eee;
transition: 0.2s ease;
}
.stat-data-selector label:hover {
background-color: #ccc
}
.stat-data-selector input:checked + label {
background-color: #FFA646;
color: white;
}
.stat-data-selector input{
display: none;
}
.stat-graph-container {
margin: auto;
position: relative;
width: 80%;
margin-top: 20px;
margin-bottom: 20px;
}
.over{
+1
View File
@@ -0,0 +1 @@
@keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long