]> _ Git - fluidbook-toolbox.git/commitdiff
wip #5877 @4
authorVincent Vanwaelscappel <vincent@cubedesigners.com>
Fri, 9 Jun 2023 10:22:27 +0000 (12:22 +0200)
committerVincent Vanwaelscappel <vincent@cubedesigners.com>
Fri, 9 Jun 2023 10:22:27 +0000 (12:22 +0200)
app/Fluidbook/Stats.php
app/Http/Controllers/Admin/Operations/FluidbookPublication/StatsOperation.php
resources/views/fluidbook_stats/summary.blade.php

index c1aca3331b8535fb7a557ce65733e38a4692e944..aea014eebd0a9de801a2dac08c566a0dce1cc05d 100644 (file)
@@ -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;
index 83d9119ef8aaed0a96dca1796e967b21dccfe93b..55056d2e816ca770158089e94f6042aa18cce045 100644 (file)
@@ -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);
     }
 
index c45586755156abf075f913d7d8641b067fb61e65..7330629ebde5765ff0e5ecb0abd6acf2d7b279aa 100644 (file)
     <dt>{{ __('Nombre de pages') }}</dt>
     <dd>{{ $page_count }}</dd>
 
-    @if(isset($visits_summary['nb_uniq_visitors']))
-        <dt>{{ __('Visiteurs uniques') }}</dt>
-        <dd>{{ $visits_summary['nb_uniq_visitors'] }}</dd>
-    @endif
-
-    @if(isset($visits_summary['nb_visits']))
-        <dt>{{ __('Visites') }}</dt>
-        <dd>{{ $visits_summary['nb_visits'] }}</dd>
-    @endif
-
-
     {{-- Summary of totals --}}
     @if($period_details->isNotEmpty())
         @foreach ($table_map['summary'] as $summary_key => $summary_heading)
@@ -88,7 +77,9 @@
     </div>
 
     {{-- Chart --}}
-    <canvas id="stats_chart"></canvas>
+    <div style="height: 500px">
+        <canvas id="stats_chart"></canvas>
+    </div>
 
     {{-- Stats for each period entry (year, month, week or day) --}}
     <table class="sortable stats-details mt-5">
 @can('fluidbook-publication:admin')
     <div style="text-align: right;margin-top: 20px;">
         <a target="_blank"
-           href="{{route('statsmatomo',['fluidbook_id'=>$fluidbook->id,'hash'=>$fluidbook->hash])}}">{{__('Voir les données brutes sur Matomo')}}</a>
+           href="{{route('statsmatomo',['fluidbook_id'=>$fluidbook->id,'hash'=>$fluidbook->hash,'period'=>'range','date'=>$start_date.','.$end_date])}}">{{__('Voir les données brutes sur Matomo')}}</a>
         @can('superadmin')
             | <a target="_blank" href="/fluidbook-publication/stats/API/{{$fluidbook->id}}">{{__('API Matomo')}}</a>
         @endcan
     let arrow = document.createElement('span');
     arrow.className = 'arrow-right';
     rangeInputs.insertBefore(arrow, rangeInputs.children[1]); // Insert in 2nd position
-
-    @can('fluidbook-publication:admin')
-    <div style="text-align: right;margin-top: 20px;">
-    <a target="_blank"
-       href="{{route('statsmatomo',['fluidbook_id'=>$fluidbook->id,'hash'=>$fluidbook->hash])}}">{{__('Voir les données brutes sur Matomo')}}</a>
-    @can('superadmin')
-    | <a target="_blank" href="/fluidbook-publication/stats/API/{{$fluidbook->id}}">{{__('API Matomo')}}</a>
-    @endcan
-    </div>
-    @endcan
-
-
 </script>
 
 {{--============================================================================================================--}}
         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 = {
         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: {