From 6857d32d223f08810c32288d6c743b36693427e2 Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Mon, 11 Dec 2017 19:01:38 +1030 Subject: [PATCH] Add "today" period with hourly breakdown, and fix timezone issues This fix ensures that before aggregating daily/hourly statistics, dates are converted into the local timezone ("flarum-statistics.timezone" in the settings table). --- .../statistics/js/admin/dist/extension.js | 61 +++++++++++-------- .../admin/src/components/StatisticsWidget.js | 37 ++++++----- .../src/Listener/AddStatisticsData.php | 49 ++++++++++++++- 3 files changed, 106 insertions(+), 41 deletions(-) diff --git a/extensions/statistics/js/admin/dist/extension.js b/extensions/statistics/js/admin/dist/extension.js index b6fdfe435..8208b88fe 100644 --- a/extensions/statistics/js/admin/dist/extension.js +++ b/extensions/statistics/js/admin/dist/extension.js @@ -36,14 +36,14 @@ System.register('flarum/statistics/components/StatisticsWidget', ['flarum/compon value: function init() { babelHelpers.get(StatisticsWidget.prototype.__proto__ || Object.getPrototypeOf(StatisticsWidget.prototype), 'init', this).call(this); - var today = new Date(); - today.setHours(0, 0, 0, 0); + var today = new Date().setHours(0, 0, 0, 0) / 1000; this.entities = ['users', 'discussions', 'posts']; this.periods = { - last_7_days: { start: today - 86400000 * 6, end: today, step: 86400000 }, - last_28_days: { start: today - 86400000 * 27, end: today, step: 86400000 }, - last_12_months: { start: today - 86400000 * 364, end: today, step: 86400000 * 7 } + today: { start: today, end: today + 86400, step: 3600 }, + last_7_days: { start: today - 86400 * 7, end: today, step: 86400 }, + last_28_days: { start: today - 86400 * 28, end: today, step: 86400 }, + last_12_months: { start: today - 86400 * 364, end: today, step: 86400 * 7 } }; this.selectedEntity = 'users'; @@ -81,7 +81,10 @@ System.register('flarum/statistics/components/StatisticsWidget', ['flarum/compon Object.keys(this.periods).map(function (period) { return m( Button, - { active: period === _this2.selectedPeriod, onclick: _this2.changePeriod.bind(_this2, period) }, + { + active: period === _this2.selectedPeriod, + onclick: _this2.changePeriod.bind(_this2, period), + icon: period === _this2.selectedPeriod ? 'check' : true }, app.translator.trans('flarum-statistics.admin.statistics.' + period + '_label') ); }) @@ -128,22 +131,37 @@ System.register('flarum/statistics/components/StatisticsWidget', ['flarum/compon }, { key: 'drawChart', value: function drawChart(elm, isInitialized, context) { - var entity = this.selectedEntity; + if (context.chart && context.entity === this.selectedEntity && context.period === this.selectedPeriod) { + return; + } + var period = this.periods[this.selectedPeriod]; + var periodLength = period.end - period.start; var labels = []; var thisPeriod = []; var lastPeriod = []; - for (var i = period.start; i <= period.end; i += period.step) { - labels.push(moment(i).format('D MMM')); + for (var i = period.start; i < period.end; i += period.step) { + var label = void 0; - thisPeriod.push(this.getPeriodCount(entity, { start: i, end: i + period.step })); + if (period.step < 86400) { + label = moment.unix(i).format('h A'); + } else { + label = moment.unix(i).format('D MMM'); - var periodLength = period.end - period.start; - lastPeriod.push(this.getPeriodCount(entity, { start: i - periodLength, end: i - periodLength + period.step })); + if (period.step > 86400) { + label += ' - ' + moment.unix(i + period.step - 1).format('D MMM'); + } + } + + labels.push(label); + + thisPeriod.push(this.getPeriodCount(this.selectedEntity, { start: i, end: i + period.step })); + + lastPeriod.push(this.getPeriodCount(this.selectedEntity, { start: i - periodLength, end: i - periodLength + period.step })); } - var datasets = [{ values: lastPeriod, title: 'Last period ➡' }, { values: thisPeriod, title: 'This period' }]; + var datasets = [{ values: lastPeriod }, { values: thisPeriod }]; if (!context.chart) { context.chart = new Chart({ @@ -155,17 +173,14 @@ System.register('flarum/statistics/components/StatisticsWidget', ['flarum/compon y_axis_mode: 'span', is_series: 1, show_dots: 0, - colors: ['rgba(0,0,0,0.1)', app.forum.attribute('themePrimaryColor')], - format_tooltip_x: function format_tooltip_x(d) { - return d; - }, - format_tooltip_y: function format_tooltip_y(d) { - return d; - } + colors: ['rgba(0, 0, 0, 0.1)', app.forum.attribute('themePrimaryColor')] }); + } else { + context.chart.update_values(datasets, labels); } - context.chart.update_values(datasets, labels); + context.entity = this.selectedEntity; + context.period = this.selectedPeriod; } }, { key: 'changeEntity', @@ -189,9 +204,7 @@ System.register('flarum/statistics/components/StatisticsWidget', ['flarum/compon var count = 0; for (var day in daily) { - var date = new Date(day); - - if (date > period.start && date < period.end) { + if (day >= period.start && day < period.end) { count += daily[day]; } } diff --git a/extensions/statistics/js/admin/src/components/StatisticsWidget.js b/extensions/statistics/js/admin/src/components/StatisticsWidget.js index 417129255..8a1856e85 100644 --- a/extensions/statistics/js/admin/src/components/StatisticsWidget.js +++ b/extensions/statistics/js/admin/src/components/StatisticsWidget.js @@ -19,14 +19,14 @@ export default class StatisticsWidget extends DashboardWidget { init() { super.init(); - const today = new Date(); - today.setHours(0, 0, 0, 0); + const today = new Date().setHours(0, 0, 0, 0) / 1000; this.entities = ['users', 'discussions', 'posts']; this.periods = { - last_7_days: {start: today - 86400000 * 6, end: today, step: 86400000}, - last_28_days: {start: today - 86400000 * 27, end: today, step: 86400000}, - last_12_months: {start: today - 86400000 * 364, end: today, step: 86400000 * 7} + today: {start: today, end: today + 86400, step: 3600}, + last_7_days: {start: today - 86400 * 7, end: today, step: 86400}, + last_28_days: {start: today - 86400 * 28, end: today, step: 86400}, + last_12_months: {start: today - 86400 * 364, end: today, step: 86400 * 7} }; this.selectedEntity = 'users'; @@ -91,19 +91,30 @@ export default class StatisticsWidget extends DashboardWidget { return; } - const entity = this.selectedEntity; const period = this.periods[this.selectedPeriod]; + const periodLength = period.end - period.start; const labels = []; const thisPeriod = []; const lastPeriod = []; - for (let i = period.start; i <= period.end; i += period.step) { - labels.push(moment(i).format('D MMM')); + for (let i = period.start; i < period.end; i += period.step) { + let label; - thisPeriod.push(this.getPeriodCount(entity, {start: i, end: i + period.step})); + if (period.step < 86400) { + label = moment.unix(i).format('h A'); + } else { + label = moment.unix(i).format('D MMM'); - const periodLength = period.end - period.start; - lastPeriod.push(this.getPeriodCount(entity, {start: i - periodLength, end: i - periodLength + period.step})); + if (period.step > 86400) { + label += ' - ' + moment.unix(i + period.step - 1).format('D MMM'); + } + } + + labels.push(label); + + thisPeriod.push(this.getPeriodCount(this.selectedEntity, {start: i, end: i + period.step})); + + lastPeriod.push(this.getPeriodCount(this.selectedEntity, {start: i - periodLength, end: i - periodLength + period.step})); } const datasets = [ @@ -148,9 +159,7 @@ export default class StatisticsWidget extends DashboardWidget { let count = 0; for (const day in daily) { - const date = new Date(day); - - if (date > period.start && date < period.end) { + if (day >= period.start && day < period.end) { count += daily[day]; } } diff --git a/extensions/statistics/src/Listener/AddStatisticsData.php b/extensions/statistics/src/Listener/AddStatisticsData.php index f67c34267..dbc36adfe 100644 --- a/extensions/statistics/src/Listener/AddStatisticsData.php +++ b/extensions/statistics/src/Listener/AddStatisticsData.php @@ -12,20 +12,41 @@ namespace Flarum\Statistics\Listener; use DateTime; +use DateTimeZone; use Flarum\Core\Discussion; use Flarum\Core\Post; use Flarum\Core\User; use Flarum\Event\ConfigureWebApp; +use Flarum\Settings\SettingsRepositoryInterface; use Illuminate\Database\Eloquent\Builder; use Illuminate\Contracts\Events\Dispatcher; class AddStatisticsData { + /** + * @var SettingsRepositoryInterface + */ + protected $settings; + + /** + * @param SettingsRepositoryInterface $settings + */ + public function __construct(SettingsRepositoryInterface $settings) + { + $this->settings = $settings; + } + + /** + * @param Dispatcher $events + */ public function subscribe(Dispatcher $events) { $events->listen(ConfigureWebApp::class, [$this, 'addStatisticsData']); } + /** + * @param ConfigureWebApp $event + */ public function addStatisticsData(ConfigureWebApp $event) { $event->view->setVariable('statistics', $this->getStatistics()); @@ -49,11 +70,33 @@ class AddStatisticsData private function getDailyCounts(Builder $query, $column) { + // Calculate the offset between the server timezone (which is used for + // dates stored in the database) and the user's timezone (set via the + // settings table). We will use this to adjust dates before aggregating + // daily/hourly statistics. + $offset = $this->getTimezoneOffset(); + return $query - ->selectRaw('DATE('.$column.') as date') + ->selectRaw( + 'UNIX_TIMESTAMP( + DATE_FORMAT( + @date := DATE_ADD('.$column.', INTERVAL ? SECOND), -- correct for timezone + IF(@date > ?, \'%Y-%m-%d %H:00:00\', \'%Y-%m-%d\') -- if within the last 48 hours, group by hour + ) + ) as period', + [$offset, new DateTime('-48 hours')] + ) ->selectRaw('COUNT(id) as count') ->where($column, '>', new DateTime('-24 months')) - ->groupBy('date') - ->lists('count', 'date'); + ->groupBy('period') + ->lists('count', 'period'); + } + + private function getTimezoneOffset() + { + $dataTimezone = new DateTimeZone(date_default_timezone_get()); + $displayTimezone = new DateTimeZone($this->settings->get('flarum-statistics.timezone', date_default_timezone_get())); + + return $displayTimezone->getOffset(new DateTime('now', $dataTimezone)); } }