From 22721643be6b62c7b53c47f302e4b1ef3f07d73b Mon Sep 17 00:00:00 2001 From: Stephen Cameron Date: Thu, 25 Aug 2022 19:25:58 +0200 Subject: [PATCH] WIP #5316 @27 --- .../FluidbookPublication/StatsOperation.php | 56 ++-- resources/views/fluidbook_stats/API.blade.php | 40 ++- .../views/fluidbook_stats/summary.blade.php | 239 ++++++++++++------ .../fluidbook_publication/stats.blade.php | 2 +- 4 files changed, 227 insertions(+), 110 deletions(-) diff --git a/app/Http/Controllers/Admin/Operations/FluidbookPublication/StatsOperation.php b/app/Http/Controllers/Admin/Operations/FluidbookPublication/StatsOperation.php index ff4e3de39..7c68a0aaf 100644 --- a/app/Http/Controllers/Admin/Operations/FluidbookPublication/StatsOperation.php +++ b/app/Http/Controllers/Admin/Operations/FluidbookPublication/StatsOperation.php @@ -14,7 +14,9 @@ trait StatsOperation { Route::get($segment . '/stats/API', $controller . '@statsAPI'); // Route is only secured by hash - Route::get($segment . '/stats/{id}_{hash}/{date?}', $controller . '@statsSummary')->withoutMiddleware([CheckIfAdmin::class]); + Route::get($segment . '/stats/{fluidbook_id}_{hash}/{date?}', $controller . '@statsSummary') + ->withoutMiddleware([CheckIfAdmin::class]) + ->name('stats'); // Named route is used to generate URLs more consistently using route helper } protected function setupStatsDefaults() @@ -124,6 +126,14 @@ trait StatsOperation $mode = 'overview'; $start_date = $fluidbook->created_at->isoFormat('YYYY-MM-DD'); $date_range = $start_date . ',' . date('Y-m-d'); + + // In some cases, the range will be very short (eg. just a few days or a month), which makes the bar chart + // look strange due to lack of entries. There's no easy way to limit the width of the bar chart while + // retaining the responsiveness, so instead we fetch a longer period of stats, even if they'll be mostly empty. + if (Carbon::now()->diffInMonths($fluidbook->created_at) < 12) { // For the overview, we want at least 12 months + $date_range = 'last12'; // Special range format that Matomo understands + } + $period = 'month'; // Segregate stats by month } @@ -131,7 +141,7 @@ trait StatsOperation $report = $this->_getReporting($fluidbook_id); - echo "Getting stats for date range $date_range, segregated by $period"; + // echo "Getting stats for date range $date_range, segregated by $period"; //dump(collect($report->getVisits($fluidbook_id, $date_range, $period))->sum('nb_visits')); //dump($report->getVisits($fluidbook_id, $date_range, 'range')); // dd($report->getVisits($fluidbook_id, $date_range, $period)); @@ -142,35 +152,37 @@ trait StatsOperation $searches = collect($report->getSearchKeywords($fluidbook_id, $date_range, 'range')); // Format dates for display as labels on the x-axis - $labels = $visits->keys()->map(function ($label, $index) use ($period) { + $labels = $visits->keys()->map(function ($label, $index) use ($period, $mode) { return match ($period) { 'day' => Carbon::parse($label)->isoFormat('DD'), // Convert YYYY-MM-DD string from API into zero-padded day alone - 'month' => Carbon::parse($label)->isoFormat('MMM'), // Convert to abbreviated month name + 'month' => $mode === 'overview' ? Carbon::parse($label)->isoFormat('MMM YYYY') : Carbon::parse($label)->isoFormat('MMM'), // Convert to abbreviated month name (including year when showing all months) default => $label, }; })->toArray(); // Format dates for display in the tooltip title $formatted_dates = $visits->keys()->map(function ($label, $index) use ($period) { - return match ($period) { - 'day' => Carbon::parse($label)->isoFormat('dddd Do MMMM YYYY'), - 'month' => Carbon::parse($label)->isoFormat('MMMM YYYY'), - default => $label, - }; + return $this->formatDateForPeriod($label, $period); })->toArray(); -// $chart = Chartisan::build() -// ->labels($labels) -// ->extra(['tooltip_labels' => array_combine($labels, $formatted_dates)]) -// ->dataset('Visits', $visits->pluck('nb_visits')->toArray()) -// ->dataset('Page Views', $pageviews->pluck('nb_pageviews')->toArray()) -// ->toJSON(); + $tooltip_labels = array_combine($labels, $formatted_dates); + + // Generate a list of available periods, based on the visits API data + // The goal is to find the non-empty results and create a list of Carbon date classes from those + // This list is used to generate the links / formatted dates list + $available_periods = $visits->filter(fn ($value, $key) => !empty($value)) // First, remove empty values + ->map(function ($item, $key) use ($period, $fluidbook_id, $hash) { // Add new key to data for formatted date + $date = $key; + $item['formatted_date'] = $this->formatDateForPeriod($key, $period); // Formatting depends on the period - // header('Content-Type: application/json; charset=utf-8'); - // return $chart; + if ($period === 'month') { + $item['URL'] = route('stats', compact('fluidbook_id', 'hash', 'date')); // Generate URL using named route + } - return view('fluidbook_stats.summary', compact('fluidbook', 'visits', 'pageviews', 'searches', /*'chart',*/ 'mode', 'period', 'dates')); + return $item; + }); + return view('fluidbook_stats.summary', compact('fluidbook', 'labels', 'tooltip_labels', 'visits', 'pageviews', 'searches', 'mode', 'period', 'dates', 'available_periods')); } @@ -187,4 +199,12 @@ trait StatsOperation return view('fluidbook_stats.API', compact('matomo_tokens')); } + protected function formatDateForPeriod($date, $period) { + return match ($period) { + 'day' => Carbon::parse($date)->isoFormat('dddd Do MMMM YYYY'), + 'month' => Carbon::parse($date)->isoFormat('MMMM YYYY'), + default => $date, + }; + } + } diff --git a/resources/views/fluidbook_stats/API.blade.php b/resources/views/fluidbook_stats/API.blade.php index 895b71ca5..c296de818 100644 --- a/resources/views/fluidbook_stats/API.blade.php +++ b/resources/views/fluidbook_stats/API.blade.php @@ -24,6 +24,13 @@ color: #fff; } + /* Spacing + dividers between results */ + .API-result + .API-result { + margin-top: 1.5rem; + padding-top: 1.5rem; + border-top: 1px solid #ccc; + } + @@ -88,7 +95,7 @@ -
+
@@ -141,16 +148,25 @@ } let requestDetails = `

- Results for ${$this.method} - (date: ${$this.date}, - period: ${$this.period}) + + ▼ + ${Object.values(filteredData).length} results for ${$this.method} + (ID: ${$this.siteID}, + date: ${$this.date}, + period: ${$this.period}) + RAW ↗️

`; let result = ''; - // Check first item of filteredData object to see if it contains a nested array of data - if (Array.isArray(Object.values(filteredData)[0])) { + // See if the API has returned an array instead of an object + // This test is done on the unfiltered data because the filtering process turns it into an object + if (Array.isArray(data)) { + result += $this.tableFromData(data, true); + + // Also check first item of filteredData object to see if it contains a nested array of data + } else if (Array.isArray(Object.values(filteredData)[0])) { for (const [date, nestedData] of Object.entries(filteredData)) { console.log(date, nestedData) result += `

${date}

`; @@ -160,7 +176,12 @@ result += $this.tableFromData(filteredData); } - $this.$refs.result.insertAdjacentHTML('afterbegin', requestDetails + result + '
') + let html = `
+ ${requestDetails} +
${result}
+
`; + + $this.$refs.result.insertAdjacentHTML('afterbegin', html) }); }, @@ -175,7 +196,7 @@ : ` `; // Only a singular value returned by API - table += ``; + table += ``; if (!hideDateColumn) { table += `Date` @@ -223,6 +244,9 @@ } + + + {{----}} diff --git a/resources/views/fluidbook_stats/summary.blade.php b/resources/views/fluidbook_stats/summary.blade.php index 971778e48..e3db6b933 100644 --- a/resources/views/fluidbook_stats/summary.blade.php +++ b/resources/views/fluidbook_stats/summary.blade.php @@ -1,91 +1,164 @@ @extends(backpack_view('blank')) +@section('after_styles') + +@endsection + +@section('header') +

{{ sprintf(__('Statistiques de la publication « %s »'), $fluidbook->name) }}

+@endsection + @section('content') -

{{ __('Statistiques') }}

-{{--
--}} -{{--
--}} -{{--
--}} +
+
{{ __('Creation Date') }}
+
{{ $fluidbook->created_at->isoFormat('dddd Do MMMM YYYY') }}
+ +
{{ __('Total Visits') }}
+
{{ $visits->sum('nb_visits') }}
+ +
{{ __('Total Page Views') }}
+
{{ $pageviews->sum('nb_pageviews') }}
+ + {{-- TODO: get extended stats for links, sharing, etc --}} + +
+ + @if ($period === 'month') +

{{ __('Monthly Details') }}

+ @elseif ($period === 'day') +

{{ __('Daily Details') }}

+ @endif + + + +


+ @foreach($available_periods as $date_key => $period_data) + @if (isset($period_data['URL'])) + {{ $period_data['formatted_date'] }} + @else + {{ $period_data['formatted_date'] }} + @endif +
+ @endforeach - @dump($fluidbook, $visits, $pageviews, $searches) + {{--@dump($fluidbook, $visits, $pageviews, $searches)--}} @endsection -{{--@push('after_scripts')--}} -{{-- --}}{{-- Charting library --}} -{{-- --}} -{{-- --}}{{-- Chartisan --}} -{{-- --}} - -{{-- --}} - -{{--@endpush--}} +@push('after_scripts') + {{-- Charting library --}} + + + + + +@endpush diff --git a/resources/views/vendor/backpack/crud/buttons/fluidbook_publication/stats.blade.php b/resources/views/vendor/backpack/crud/buttons/fluidbook_publication/stats.blade.php index dae0f57a4..a3b29170b 100644 --- a/resources/views/vendor/backpack/crud/buttons/fluidbook_publication/stats.blade.php +++ b/resources/views/vendor/backpack/crud/buttons/fluidbook_publication/stats.blade.php @@ -1,5 +1,5 @@ @if($entry->stats) - {{__('Stats')}} -- 2.39.5