diff --git a/admin/stats.php b/admin/stats.php index c008e35c2..b992d7532 100644 --- a/admin/stats.php +++ b/admin/stats.php @@ -93,6 +93,97 @@ SELECT return $output; } +function get_month_of_last_years ($last = 'all') +{ + + $query = ' +SELECT + year, + month, + day, + hour, + nb_pages +FROM '.HISTORY_SUMMARY_TABLE.' +WHERE month IS NOT NULL + AND day IS NULL +ORDER BY + year DESC, + month DESC'; + + if ($last !== 'all') + { + $date = new DateTime(); + $limit = ($last - 1)*12+$date->format('n') - 1; + $query .= +' LIMIT '.$limit; + } + + return query2array($query.';'); +} + +function get_month_stats() +{ + $result = array(); + $date = new DateTime(); + $date_last_month = clone $date; + $date_last_year = clone $date; + $months = array(); + + $date_last_month->sub(new DateInterval('P1M')); + $date_last_year->sub(new DateInterval('P1Y')); + $query = ' +SELECT + year, + month, + day, + hour, + nb_pages +FROM '.HISTORY_SUMMARY_TABLE.' +WHERE + ( + (year = '.$date->format('Y').' AND month = '.$date->format('n').') + OR (year = '.$date_last_month->format('Y').' AND month = '.$date_last_month->format('n').') + OR (year = '.$date_last_year->format('Y').' AND month = '.$date_last_year->format('n').') + ) + AND day IS NOT NULL + AND hour IS NULL +ORDER BY + year DESC, + month DESC +;'; + + foreach (query2array($query) as $value) + { + $date = get_date_object($value); + @$months[$date->format('Y-m')][] = $value; + } + + foreach ($months as $key => $val) + { + $result['month'][] = set_missing_values('day',$val, false); + } + + $query = ' +SELECT + AVG(nb_pages) +FROM '.HISTORY_SUMMARY_TABLE.' +WHERE + ( + year = '.$date->format('Y').' OR + (year = '.($date->format('Y')-1).' and month > '.$date->format('n').') + ) + AND day IS NOT NULL + AND hour IS NULL +ORDER BY + year DESC, + month DESC +;'; + + $result['avg'] = query2array($query)[0]['AVG(nb_pages)']; + + return $result; +} + // +-----------------------------------------------------------------------+ // | Check Access and exit when user status is not ok | // +-----------------------------------------------------------------------+ @@ -100,7 +191,7 @@ SELECT check_status(ACCESS_ADMINISTRATOR); // +-----------------------------------------------------------------------+ -// | Display statistics header | +// | Display statistics header | // +-----------------------------------------------------------------------+ $template->set_filename('stats', 'stats.tpl'); @@ -118,10 +209,10 @@ $template->assign( ); // +-----------------------------------------------------------------------+ -// | Set missing rows to 0 | +// | Set missing rows to 0 | // +-----------------------------------------------------------------------+ -function set_missing_values($unit, $data) +function set_missing_values($unit, $data, $keep_size=true) { $limit = count($data); $result = array(); @@ -150,10 +241,19 @@ function set_missing_values($unit, $data) } //Fill an empty array with all the dates - for ($i=0; $i < $limit; $i++) - { - $result[$date->format($date_format)] = 0; - $date->add(new DateInterval($date_add)); + if ($keep_size) + { + for ($i=0; $i < $limit; $i++) + { + $result[$date->format($date_format)] = 0; + $date->add(new DateInterval($date_add)); + } + } else { + $date_end = get_date_object($data[0]); + while ($date <= $date_end) { + $result[$date->format($date_format)] = 0; + $date->add(new DateInterval($date_add)); + } } //Overload with database rows @@ -197,6 +297,8 @@ function get_date_object($row) // | Send data to template | // +-----------------------------------------------------------------------+ +$template->append('compareYears', set_missing_values('month', get_month_of_last_years($conf['stat_compare_year_displayed']), false)); +$template->append('monthStats', get_month_stats()); $template->append('lastHours', set_missing_values('hour',get_last(72, 'hour'))); $template->append('lastDays', set_missing_values('day',get_last(90, 'day'))); $template->append('lastMonths', set_missing_values('month',get_last(24, 'month'))); diff --git a/admin/themes/default/js/stats.js b/admin/themes/default/js/stats.js index 956927d95..0b0a4d3e5 100644 --- a/admin/themes/default/js/stats.js +++ b/admin/themes/default/js/stats.js @@ -6,6 +6,8 @@ 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["compare-years"] = $("#data").data("compare-years")[0]; +data["month-stats"] = $("#data").data("month-stats")[0]; data_unit = { "hours":"day", @@ -14,17 +16,21 @@ data_unit = { "years": "year" } +compareMode = false; + /*------- Creating graph -------*/ var ctx = document.getElementById('stat-graph').getContext('2d'); //Create the gradient under the curve -var gradient = ctx.createLinearGradient(0,400, 0,0); -gradient.addColorStop(0, 'rgba(255,119,0,0)'); -gradient.addColorStop(1, 'rgba(255,119,0,1)'); +function gradient(r, g, b) { + let gradient = ctx.createLinearGradient(0,400, 0,0); + gradient.addColorStop(0, 'rgba('+r+','+g+','+b+',0)'); + gradient.addColorStop(1, 'rgba('+r+','+g+','+b+',1)'); + return gradient; +} //Setup the graph -Chart.defaults.global.legend.display = false; Chart.defaults.global.elements.point.radius = 0.1; Chart.defaults.global.elements.point.hitRadius = 10 Chart.defaults.global.defaultFontSize = 14; @@ -32,45 +38,79 @@ Chart.defaults.global.defaultFontColor = '#888'; Chart.defaults.global.tooltips.mode = 'index'; Chart.defaults.global.tooltips.intersect = false; Chart.defaults.global.legend.onClick = null; + var statGraph = new Chart(ctx, { type: 'line', maintainAspectRatio: false, - options: { - scales: { - xAxes: [{ - type: 'time', - time: { - tooltipFormat: 'll' - }, - gridLines: { - display: false - } - }], - } - } }); //Line options var displayOptions = { - backgroundColor: gradient, + backgroundColor: gradient(255, 119, 0), borderColor: 'rgba(255,119,0,1)', lineTension : 0.2 } -function changeData(dataType, label, options = displayOptions) { - statGraph.data = { - datasets: [{ - label: label, - data: getValues(data[dataType]), - ...options - }] +function changeData(dataType, options = displayOptions) { + if (!compareMode) { + statGraph.data = { + datasets: [{ + label: str_number_page_visited, + data: getValues(data[dataType]), + ...options + }] + } + statGraph.options = { + scales: { + xAxes: [{ + type: 'time', + time: { + tooltipFormat: 'll' + }, + gridLines: { + display: false + } + }], + }, + legend: { + display:false + } + } + 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(); + } else { + statGraph.options.legend.display = true; + if (dataType == "years") { + statGraph.data = { + datasets: getComparedYearDataset() + } + statGraph.options.scales = { + xAxes: [{ + type: 'category', + labels: str_months + }] + } + } else if (dataType == "months") { + days = []; + for (let i = 1; i<=31; i++) { + days.push(i); + } + statGraph.data = { + datasets: getMonthStatsDataset() + } + statGraph.options.scales = { + xAxes: [{ + type: 'category', + labels : days + }] + } + } + statGraph.update(); } - 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(); } //Make Data readable by Chart.js @@ -78,24 +118,105 @@ function getValues(data) { values = []; Object.keys(data).forEach(function(key) { var newPoint = { - x:new Date(key), - y:data[key] + x:new Date(key), + y:data[key] } values.push(newPoint) }); return values; } +function getComparedYearDataset() { + colors = ["#ffa744", "#ff5252", "#896af3", "#2883c3", "#6ece5e"] + values = {}; + dataset = []; + + Object.keys(data["compare-years"]).forEach(function(key) { + date = new Date(key) + if (values[date.getFullYear()] == undefined) { + values[date.getFullYear()] = []; + } + values[date.getFullYear()][parseInt(date.getMonth())] = data["compare-years"][key]; + }); + + Object.keys(values).forEach(function(key) { + dataset.push({ + label : str_number_page_visited_with_year.replace('%s', key), + data : values[key], + lineTension : 0.2, + borderColor : colors[parseInt(key) % colors.length], + backgroundColor: "rgba(0,0,0,0)" + }) + }); + + return dataset; +} + +function getMonthStatsDataset() { + colors = ["#ffa744", "#ff5252", "#896af3", "#2883c3", "#6ece5e"] + dataset = []; + colorIndice = 0; + let date; + + data["month-stats"]["month"].forEach(values => { + let days_data = []; + Object.keys(values).forEach(function(key) { + date = new Date(key) + days_data[parseInt(date.getUTCDate()) - 1] = values[key]; + }); + console.log(days_data); + dataset.push({ + label : str_number_page_visited_with_year.replace('%s', str_months[date.getMonth()]+" "+date.getFullYear()), + data : days_data, + lineTension : 0.2, + borderColor : colors[colorIndice % colors.length], + backgroundColor: "rgba(0,0,0,0)" + }) + colorIndice++ + }); + + averageTab = []; + for (let i = 0; i < 31; i++) { + averageTab[i] = data["month-stats"]["avg"]; + } + dataset.push({ + label : str_avg, + data : averageTab, + lineTension : 0.2, + borderColor : colors[4], + backgroundColor: "rgba(0,0,0,0)" + }) + + return dataset; +} + //Event listener $(".stat-data-selector label").on("click", function(){ - let dataType = $(this).data("value"); - changeData(dataType, str_number_page_visited) + dataType = $(this).data("value"); + changeData(dataType); +}) + +$(".stat-compare-mode input").on("change", function(){ + compareMode = $(this)[0].checked; + + if (compareMode) { + $("#hours-selector + label, #days-selector + label").addClass('unavailable'); + if ($("#hours-selector").prop('checked')||$("#days-selector").prop('checked')) { + $("#years-selector").prop('checked', true); + $("#hours-selector, #days-selector").prop('checked', false); + changeData("years"); + } else { + changeData($(".stat-data-selector input:checked + label").data("value")) + } + } else { + $("#hours-selector + label, #days-selector + label").removeClass('unavailable'); + changeData($(".stat-data-selector input:checked + label").data("value")); + } }) /*------- Initialize the page -------*/ $(function() { - let dataType = $(".stat-data-selector input:checked + label").data("value"); - changeData(dataType, str_number_page_visited) + changeData($(".stat-data-selector input:checked + label").data("value")); }) diff --git a/admin/themes/default/template/stats.tpl b/admin/themes/default/template/stats.tpl index d891272c3..97c65eeec 100644 --- a/admin/themes/default/template/stats.tpl +++ b/admin/themes/default/template/stats.tpl @@ -1,5 +1,6 @@ {footer_script} var str_number_page_visited = "{'Page Visited'|@translate}"; +var str_number_page_visited_with_year = "{'Page Visited in %s'|@translate}"; var str_tooltip_format = { "years":"YYYY", "months":"MMMM YYYY", @@ -10,6 +11,8 @@ var str_unit_format = { "day":"dddd", "month":"MMM YYYY" } +var str_avg = "{'Average last 12 months'|@translate}" +var str_months = ["{'January'|@translate}", "{'February'|@translate}", "{'March'|@translate}", "{'April'|@translate}", "{'May'|@translate}", "{'June'|@translate}", "{'July'|@translate}", "{'August'|@translate}", "{'September'|@translate}", "{'Octobember'|@translate}", "{'November'|@translate}", "{'December'|@translate}"]; moment.locale("{$langCode}"); {/footer_script} @@ -24,7 +27,22 @@ moment.locale("{$langCode}");

{'History'|@translate}

-
+
+ + {'Compare mode'|@translate} +
+ +
diff --git a/admin/themes/default/theme.css b/admin/themes/default/theme.css index e92b45498..2647afbf9 100644 --- a/admin/themes/default/theme.css +++ b/admin/themes/default/theme.css @@ -266,6 +266,13 @@ TABLE.doubleSelect SELECT.categoryList { transition: 0.2s ease; } +.stat-data-selector label.unavailable { + color: gray; + background-color: #aaa; + cursor: default; + pointer-events: none; +} + .stat-data-selector label:hover { background-color: #ccc } @@ -273,6 +280,7 @@ TABLE.doubleSelect SELECT.categoryList { .stat-data-selector input:checked + label { background-color: #FFA646; color: white; + pointer-events: none; } .stat-data-selector input{ @@ -287,6 +295,12 @@ TABLE.doubleSelect SELECT.categoryList { margin-bottom: 20px; } +.stat-compare-mode { + position: absolute; + right: 10px; + transform: translate(0, 10px); +} + .over{ position: relative; z-index: 0; diff --git a/include/config_default.inc.php b/include/config_default.inc.php index 67269846b..455ea5020 100644 --- a/include/config_default.inc.php +++ b/include/config_default.inc.php @@ -271,6 +271,9 @@ $conf['update_notify_reminder_period'] = 7*24*60*60; // only the first page (value=false) $conf['album_description_on_all_pages'] = false; +// Number of years displayed in the history compare mode (for the years chart) +$conf['stat_compare_year_displayed'] = 5; + // +-----------------------------------------------------------------------+ // | email | // +-----------------------------------------------------------------------+