From: Vincent Vanwaelscappel Date: Fri, 9 Jun 2023 10:22:27 +0000 (+0200) Subject: wip #5877 @4 X-Git-Url: http://git.cubedesigners.com/?a=commitdiff_plain;h=e0707dacd6907be4c32c79fd9685d0a161a13c39;p=fluidbook-toolbox.git wip #5877 @4 --- diff --git a/app/Fluidbook/Stats.php b/app/Fluidbook/Stats.php index c1aca3331..aea014eeb 100644 --- a/app/Fluidbook/Stats.php +++ b/app/Fluidbook/Stats.php @@ -79,13 +79,13 @@ class Stats extends Reporting $this->setLanguage(app()->getLocale()); $this->viewData = new Data(['fluidbook_id' => $fluidbook->id, 'hash' => $this->hash, 'title' => $fluidbook->title, 'page_count' => $fluidbook->getPagesNumber(), 'base_URL' => route('stats', ['fluidbook_id' => $this->fluidbook_id, 'hash' => $this->hash])]); - parent::__construct('https://' . static::getMatomoServer($this->fluidbook->id) . '/', null, $this->fluidbook_id); + parent::__construct('https://' . static::getMatomoServer($this->fluidbook->id, $this->fluidbook->region) . '/', null, $this->fluidbook_id); } public function getToken(): string { - return $this->getMatomoToken(static::getMatomoServer($this->fluidbook->id)); + return $this->getMatomoToken(static::getMatomoServer($this->fluidbook->id, $this->fluidbook->region)); } @@ -116,13 +116,16 @@ class Stats extends Reporting * @param $id int * @return string */ - public static function getMatomoServer($id): string + public static function getMatomoServer($id, $region = 'EU'): string { // Get the appropriate server based on the Fluidbook ID // Stats are split across different servers depending on the ID: // ID < 21210 = stats3.fluidbook.com // ID >= 21210 (even numbers) = stats4.fluidbook.com // ID >= 21211 (odd numbers) = stats5.fluidbook.com + if ($region === 'US') { + return 'stats6.fluidbook.com'; + } $id = intval($id); if ($id < 21210) { return 'stats3.fluidbook.com'; @@ -265,6 +268,7 @@ class Stats extends Reporting 'data' => $this->_pagesByPeriod->pluck('nb_visits')->toArray(), 'order' => 1, 'bar_offset' => -5, // Negative offset shifts bar to left + 'yAxisID' => 'y2', ], [ 'label' => __('Pages vues'), @@ -272,6 +276,7 @@ class Stats extends Reporting 'data' => $this->_pagesByPeriod->pluck('nb_hits')->toArray(), 'order' => 2, 'bar_offset' => 5, // Positive offset shifts bar to right + 'yAxisID' => 'y', ], ]; @@ -284,6 +289,7 @@ class Stats extends Reporting 'data' => $this->_pagesByPeriod->pluck('nb_uniq_visitors')->toArray(), 'order' => 1, 'bar_offset' => -8, // Negative offset shifts bar to left + 'yAxisID' => 'y2', ] ], $res); @@ -319,7 +325,6 @@ class Stats extends Reporting } - protected function _getTableMap() { $res = [ @@ -347,7 +352,6 @@ class Stats extends Reporting // Older Fluidbooks can't show unique visitors (see notes above) if (!$this->_supportUniqueVisitors()) { unset($res['summary']['nb_uniq_visitors']); - unset($res['per-page']['nb_uniq_visitors']); } return $res; @@ -516,85 +520,59 @@ class Stats extends Reporting // Fetch stats for pages, separated by their URLs. // Since we're interested in per-page not per-period stats, we fetch the report flattened for the full date range $pageUrls = collect($this->getPageUrls(['period' => 'range', 'flat' => 1])) - ->keyBy('label') - ->reject(function ($value, $key) { - // Remove the '/' URL because it's not a real page (it's created by the "Open Fluidbook" - // event and a separate, "real" page view is recorded at the same time) - return $key === '/'; - }); + ->keyBy('label'); + + $this->viewData->openings = $pageUrls['/']; + unset($pageUrls['/']); //=== Group and combine page stats with related data $res = []; - for ($page_number = 1; $page_number <= $this->fluidbook->getPagesNumber(); $page_number++) { - // For first and last pages or independent pages, don't group pages into spreads - // There's also a special case where a Fluidbook has spreads but has uneven pages - // (ie. no back cover, so the last page shouldn't be excluded from grouping) - if ($this->fluidbook->isOnePage() || $page_number === 1 || ($page_number === $this->fluidbook->getPagesNumber() && $this->fluidbook->getPagesNumber() % 2 === 0)) { + for ($page_number = 0; $page_number <= $this->fluidbook->getPagesNumber(); $page_number++) { + if ($this->fluidbook->isOnePage()) { $page_group = $page_number; - - $res[$page_group] = [ - 'page_group' => $page_group, - 'page_number' => $page_number, // Used by table column sorter - 'nb_pageviews' => $pageUrls["/page/$page_number"]['nb_hits'] ?? 0, - 'nb_zooms' => data_get($this->_eventsByPage, "zoom.subtable.$page_number.nb_events", 0), - 'nb_bookmarks' => data_get($this->_eventsByPage, "bookmark.subtable.$page_number.nb_events", 0), - 'nb_shares' => data_get($this->_eventsByPage, "share.subtable.$page_number.nb_events", 0), - ]; - - // Special case: the first page of double-page Fluidbooks is tracked as "page 0", meaning that - // for our stats labelled as "page 1", we must get certain stats from the "page 0" data... - if (!$this->fluidbook->isOnePage() && $page_number === 1) { - $res[$page_group]['nb_visits'] = $pageUrls["/page/0"]['nb_visits'] ?? 0; - $res[$page_group]['nb_uniq_visitors'] = $pageUrls["/page/0"]['sum_daily_nb_uniq_visitors'] ?? 0; - $res[$page_group]['nb_pageviews'] = $pageUrls["/page/0"]['nb_hits'] ?? 0; - $res[$page_group]['nb_zooms'] = data_get($this->_eventsByPage, "zoom.subtable.0.nb_events", 0); - - // Bookmarks and shares are already set above, using the page 1 data. - // In this case, page 0 doesn't exist as a bookmarking or sharing option, so nothing else to add. - } - } else { - - $start_page = $page_number; - if ($page_number % 2 == 1) { - $start_page--; - } - - // Only stats for even pages are considered when grouped (except for bookmarks / shares) - $following_page = $start_page + 1; // By logic, there should always be a following page - $page_group = $start_page . '—' . $following_page; - - $single_page_data = [ - 'page_group' => $page_group, - 'page_number' => $page_number, // Used by table column sorter - 'nb_pageviews' => $pageUrls["/page/$page_number"]['nb_hits'] ?? 0, - 'nb_zooms' => data_get($this->_eventsByPage, "zoom.subtable.$page_number.nb_events", 0), - // Bookmarks and shares are counted and summed for both pages in the spread - 'nb_bookmarks' => data_get($this->_eventsByPage, "bookmark.subtable.$page_number.nb_events", 0) - + data_get($this->_eventsByPage, "bookmark.subtable.$following_page.nb_events", 0), - 'nb_shares' => data_get($this->_eventsByPage, "share.subtable.$page_number.nb_events", 0) - + data_get($this->_eventsByPage, "share.subtable.$following_page.nb_events", 0), - ]; - - if (isset($res[$page_group])) { - $res[$page_group] = $this->_sumArrays($res[$page_group], $single_page_data); + if ($page_number <= 1) { + $page_group = 1; + } elseif ($page_number == $this->fluidbook->getPagesNumber() && $this->fluidbook->getPagesNumber() % 2 == 0) { + $page_group = $page_number; } else { - $res[$page_group] = $single_page_data; + $start_page = $page_number; + if ($page_number % 2 == 1) { + $start_page--; + } + $following_page = $start_page + 1; + $page_group = $start_page . '—' . $following_page; } } + + $single_page_data = [ + 'page_group' => $page_group, + 'page_number' => $page_number, // Used by table column sorter + 'nb_pageviews' => $pageUrls["/page/$page_number"]['nb_hits'] ?? 0, + 'nb_zooms' => data_get($this->_eventsByPage, "zoom.subtable.$page_number.nb_events", 0), + 'nb_bookmarks' => data_get($this->_eventsByPage, "bookmark.subtable.$page_number.nb_events", 0), + 'nb_shares' => data_get($this->_eventsByPage, "share.subtable.$page_number.nb_events", 0) + ]; + + if (isset($res[$page_group])) { + $res[$page_group] = $this->_sumArrays($res[$page_group], $single_page_data); + } else { + $res[$page_group] = $single_page_data; + } } return $res; } protected function _sumArrays() { + $exclude = ['page_number', 'page_group']; $res = []; foreach (func_get_args() as $array) { foreach ($array as $k => $v) { if (!isset($res[$k])) { $res[$k] = 0; } - if (is_numeric($v)) { + if (is_numeric($v) && !in_array($k, $exclude)) { $res[$k] += $v; } else { $res[$k] = $v; diff --git a/app/Http/Controllers/Admin/Operations/FluidbookPublication/StatsOperation.php b/app/Http/Controllers/Admin/Operations/FluidbookPublication/StatsOperation.php index 83d9119ef..55056d2e8 100644 --- a/app/Http/Controllers/Admin/Operations/FluidbookPublication/StatsOperation.php +++ b/app/Http/Controllers/Admin/Operations/FluidbookPublication/StatsOperation.php @@ -16,7 +16,7 @@ trait StatsOperation { protected function setupStatsRoutes($segment, $routeName, $controller) { - Route::get($segment . '/stats/{fluidbook_id}_{hash}/matomo', $controller . '@redirectMatomo') + Route::get($segment . '/stats/{fluidbook_id}_{hash}/matomo/{period}/{date}', $controller . '@redirectMatomo') ->withoutMiddleware([CheckIfAdmin::class])->name('statsmatomo'); // Named route is used to generate URLs more consistently using route helper // Main route is only secured by hash (security by obscurity) @@ -58,9 +58,10 @@ trait StatsOperation } - protected function redirectMatomo($fluidbook_id) + protected function redirectMatomo($fluidbook_id, $hash, $period, $date) { - $url = 'https://' . Stats::getMatomoServer($fluidbook_id) . '/index.php?module=CoreHome&action=index&idSite=' . $fluidbook_id . '&period=day&date=yesterday'; + $fluidbook = FluidbookPublication::withoutGlobalScopes()->where('id', $fluidbook_id)->where('hash', $hash)->firstOrFail(); + $url = 'https://' . Stats::getMatomoServer($fluidbook_id, $fluidbook->region) . '/index.php?module=CoreHome&action=index&idSite=' . $fluidbook_id . '&period=' . $period . '&date=' . $date; return redirect($url); } diff --git a/resources/views/fluidbook_stats/summary.blade.php b/resources/views/fluidbook_stats/summary.blade.php index c45586755..7330629eb 100644 --- a/resources/views/fluidbook_stats/summary.blade.php +++ b/resources/views/fluidbook_stats/summary.blade.php @@ -38,17 +38,6 @@
{{ __('Nombre de pages') }}
{{ $page_count }}
- @if(isset($visits_summary['nb_uniq_visitors'])) -
{{ __('Visiteurs uniques') }}
-
{{ $visits_summary['nb_uniq_visitors'] }}
- @endif - - @if(isset($visits_summary['nb_visits'])) -
{{ __('Visites') }}
-
{{ $visits_summary['nb_visits'] }}
- @endif - - {{-- Summary of totals --}} @if($period_details->isNotEmpty()) @foreach ($table_map['summary'] as $summary_key => $summary_heading) @@ -88,7 +77,9 @@ {{-- Chart --}} - +
+ +
{{-- Stats for each period entry (year, month, week or day) --}} @@ -244,7 +235,7 @@ @can('fluidbook-publication:admin')
{{__('Voir les données brutes sur Matomo')}} + href="{{route('statsmatomo',['fluidbook_id'=>$fluidbook->id,'hash'=>$fluidbook->hash,'period'=>'range','date'=>$start_date.','.$end_date])}}">{{__('Voir les données brutes sur Matomo')}} @can('superadmin') | {{__('API Matomo')}} @endcan @@ -307,18 +298,6 @@ let arrow = document.createElement('span'); arrow.className = 'arrow-right'; rangeInputs.insertBefore(arrow, rangeInputs.children[1]); // Insert in 2nd position - - @can('fluidbook-publication:admin') -
- {{__('Voir les données brutes sur Matomo')}} - @can('superadmin') - | {{__('API Matomo')}} - @endcan -
- @endcan - - {{--============================================================================================================--}} @@ -333,6 +312,14 @@ datasets: {!! $chart_datasets !!} }; + let maxvisits = 0; + $.each(data.datasets[1].data, function (k, v) { + if (v === null) { + return; + } + maxvisits = Math.max(maxvisits, v); + }); + //=== Plugins //== Offset Bars Plugin: creates a small offset between stacked bars const offsetBars = { @@ -367,20 +354,29 @@ type: 'bar', data: data, options: { - // The best way to make the chart responsive is to set the aspect ratio without setting any sizes on - // the canvas element itself. This way, Chart JS will manage the size, and it will behave much like a - // responsive image - it will take the available width, scaling the height proportionally. - // The aspectRatio value is a measure of the width compared to the height. In other words: - // aspectRatio: 3 = 3x the width compared to height - // aspectRatio: 1 = square - // and an aspectRatio less than 1 will result in a canvas that is taller than it is wide. - aspectRatio: 3, - responsive: true, // Default for Chart JS but defining it explicitly here for clarity - maintainAspectRatio: true, // As above, also a library default - maxBarThickness: 100, // Prevent bars being ridiculously wide when there isn't much data + responsive: true, + maintainAspectRatio: false, + maxBarThickness: 50, // Prevent bars being ridiculously wide when there isn't much data scales: { x: {stacked: true}, - y: {stacked: false}, // Don't stack y-axis: prevents datasets from adding + y: { + type: 'linear', // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance + position: 'left', + stacked: false, + }, + y2: { + type: 'linear', // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance + position: 'right', + reverse: false, + ticks: { + color: '#9bc200', + }, + grid: { + drawOnChartArea: false // only want the grid lines for one axis to show up + }, + max: maxvisits * 3, + stacked: false, + }, }, plugins: { tooltip: {