#3: Implemented the majority of the public facing charts I'd envisaged
This commit is contained in:
parent
51bd8ca32f
commit
7f27921cf7
@ -4,11 +4,64 @@ namespace App\Http\Controllers\Gallery;
|
|||||||
|
|
||||||
use App\Facade\Theme;
|
use App\Facade\Theme;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Photo;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class StatisticsController extends Controller
|
class StatisticsController extends Controller
|
||||||
{
|
{
|
||||||
|
public function albumSizeByPhotosChart(Request $request)
|
||||||
|
{
|
||||||
|
$stats = DB::table('photos')
|
||||||
|
->join('albums', 'albums.id', '=', 'photos.album_id')
|
||||||
|
->groupBy('albums.name')
|
||||||
|
->select('albums.name', DB::raw('count(photos.id) as photo_count'))
|
||||||
|
->orderBy('photo_count', 'desc')
|
||||||
|
->limit(10)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$labels = [];
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
foreach ($stats as $stat)
|
||||||
|
{
|
||||||
|
$labels[] = $stat->name;
|
||||||
|
$data[] = $stat->photo_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'labels' => $labels,
|
||||||
|
'backgrounds' => $this->rotateColoursForData($data),
|
||||||
|
'data' => $data
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function albumSizeByPhotoSizeChart(Request $request)
|
||||||
|
{
|
||||||
|
$stats = DB::table('photos')
|
||||||
|
->join('albums', 'albums.id', '=', 'photos.album_id')
|
||||||
|
->groupBy('albums.name')
|
||||||
|
->select('albums.name', DB::raw('sum(photos.file_size) as photo_size'))
|
||||||
|
->orderBy('photo_size', 'desc')
|
||||||
|
->limit(10)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$labels = [];
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
foreach ($stats as $stat)
|
||||||
|
{
|
||||||
|
$labels[] = $stat->name;
|
||||||
|
$data[] = ceil($stat->photo_size / 1024 / 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'labels' => $labels,
|
||||||
|
'backgrounds' => $this->rotateColoursForData($data),
|
||||||
|
'data' => $data
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public function camerasChart(Request $request)
|
public function camerasChart(Request $request)
|
||||||
{
|
{
|
||||||
$stats = DB::table('photos')
|
$stats = DB::table('photos')
|
||||||
@ -30,7 +83,7 @@ class StatisticsController extends Controller
|
|||||||
// E.g. CANON - CANON EOS 1200D becomes just CANON EOS 1200D
|
// E.g. CANON - CANON EOS 1200D becomes just CANON EOS 1200D
|
||||||
if (substr($stat->camera_model, 0, strlen($stat->camera_make)) == $stat->camera_make)
|
if (substr($stat->camera_model, 0, strlen($stat->camera_make)) == $stat->camera_make)
|
||||||
{
|
{
|
||||||
$stat->camera_make = substr($stat->camera_make, strlen($stat->camera_make));
|
$stat->camera_make = trim(substr($stat->camera_make, strlen($stat->camera_make)));
|
||||||
}
|
}
|
||||||
|
|
||||||
$labels[] = sprintf('%s %s', $stat->camera_make, $stat->camera_model);
|
$labels[] = sprintf('%s %s', $stat->camera_make, $stat->camera_model);
|
||||||
@ -44,11 +97,118 @@ class StatisticsController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function fileSizeChart(Request $request)
|
||||||
|
{
|
||||||
|
$labels = [
|
||||||
|
trans('gallery.statistics.file_sizes_legend.small'),
|
||||||
|
trans('gallery.statistics.file_sizes_legend.medium'),
|
||||||
|
trans('gallery.statistics.file_sizes_legend.large'),
|
||||||
|
trans('gallery.statistics.file_sizes_legend.huge')
|
||||||
|
];
|
||||||
|
$data = [0, 0, 0, 0];
|
||||||
|
|
||||||
|
$stats = DB::table('photos');
|
||||||
|
$stats->chunk(100, function($photos) use (&$data)
|
||||||
|
{
|
||||||
|
foreach ($photos as $photo)
|
||||||
|
{
|
||||||
|
if ($photo->file_size < (1 * 1024 * 1024))
|
||||||
|
{
|
||||||
|
$data[0]++;
|
||||||
|
}
|
||||||
|
else if ($photo->file_size < (3 * 1024 * 1024))
|
||||||
|
{
|
||||||
|
$data[1]++;
|
||||||
|
}
|
||||||
|
else if ($photo->file_size < (5 * 1024 * 1024))
|
||||||
|
{
|
||||||
|
$data[2]++;
|
||||||
|
}
|
||||||
|
else if ($photo->file_size >= (5 * 1024 * 1024))
|
||||||
|
{
|
||||||
|
$data[3]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'labels' => $labels,
|
||||||
|
'backgrounds' => $this->rotateColoursForData($data),
|
||||||
|
'data' => $data
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public function index(Request $request)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
return Theme::render('gallery.statistics');
|
return Theme::render('gallery.statistics');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function photosTaken12Months(Request $request)
|
||||||
|
{
|
||||||
|
$labels = [];
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
foreach ($this->last12MonthsDates() as $date)
|
||||||
|
{
|
||||||
|
$fromDate = sprintf('%04d-%02d-01 00:00:00', $date[0], $date[1]);
|
||||||
|
$toDate = sprintf('%04d-%02d-%02d 23:59:59', $date[0], $date[1], cal_days_in_month(CAL_GREGORIAN, $date[1], $date[0]));
|
||||||
|
|
||||||
|
$photoCount = Photo::whereBetween('taken_at', array($fromDate, $toDate))->count();
|
||||||
|
|
||||||
|
$labels[] = date('M Y', strtotime($fromDate));
|
||||||
|
$data[] = $photoCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'labels' => array_reverse($labels),
|
||||||
|
'data' => array_reverse($data)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function photosUploaded12Months(Request $request)
|
||||||
|
{
|
||||||
|
$labels = [];
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
foreach ($this->last12MonthsDates() as $date)
|
||||||
|
{
|
||||||
|
$fromDate = sprintf('%04d-%02d-01 00:00:00', $date[0], $date[1]);
|
||||||
|
$toDate = sprintf('%04d-%02d-%02d 23:59:59', $date[0], $date[1], cal_days_in_month(CAL_GREGORIAN, $date[1], $date[0]));
|
||||||
|
|
||||||
|
$photoCount = Photo::whereBetween('created_at', array($fromDate, $toDate))->count();
|
||||||
|
|
||||||
|
$labels[] = date('M Y', strtotime($fromDate));
|
||||||
|
$data[] = $photoCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'labels' => array_reverse($labels),
|
||||||
|
'data' => array_reverse($data)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function last12MonthsDates()
|
||||||
|
{
|
||||||
|
$year = intval(date('Y'));
|
||||||
|
$month = intval(date('m'));
|
||||||
|
|
||||||
|
$datesNeeded = [];
|
||||||
|
|
||||||
|
while (count($datesNeeded) < 12)
|
||||||
|
{
|
||||||
|
$datesNeeded[] = [$year, $month];
|
||||||
|
|
||||||
|
$month--;
|
||||||
|
if ($month == 0)
|
||||||
|
{
|
||||||
|
$month = 12;
|
||||||
|
$year--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $datesNeeded;
|
||||||
|
}
|
||||||
|
|
||||||
private function rotateColoursForData(array $data = [])
|
private function rotateColoursForData(array $data = [])
|
||||||
{
|
{
|
||||||
$colours = ['#0F2240', '#174E79', '#287598', '#46BBB5', '#35DCAD'];
|
$colours = ['#0F2240', '#174E79', '#287598', '#46BBB5', '#35DCAD'];
|
||||||
|
@ -16,8 +16,19 @@ return [
|
|||||||
'other_albums_description' => 'You may also be interested in the following albums.',
|
'other_albums_description' => 'You may also be interested in the following albums.',
|
||||||
'other_albums_heading' => 'More Albums in :album_name',
|
'other_albums_heading' => 'More Albums in :album_name',
|
||||||
'statistics' => [
|
'statistics' => [
|
||||||
|
'album_by_photos' => 'Top 10 largest albums - number of photos',
|
||||||
|
'album_by_size' => 'Top 10 largest albums - photo size (MB)',
|
||||||
'cameras' => 'Cameras Used',
|
'cameras' => 'Cameras Used',
|
||||||
|
'file_sizes' => 'Image file sizes',
|
||||||
|
'file_sizes_legend' => [
|
||||||
|
'small' => 'Small (<1MB)',
|
||||||
|
'medium' => 'Medium (<3MB)',
|
||||||
|
'large' => 'Large (<5MB)',
|
||||||
|
'huge' => 'Huge (>5MB)'
|
||||||
|
],
|
||||||
'intro' => 'This page displays some interesting graphs and statistics about :gallery_name.',
|
'intro' => 'This page displays some interesting graphs and statistics about :gallery_name.',
|
||||||
'title' => 'Statistics'
|
'taken_12_months' => 'Photos taken in the last 12 months',
|
||||||
|
'title' => 'Statistics',
|
||||||
|
'uploaded_12_months' => 'Photos uploaded in the last 12 months',
|
||||||
]
|
]
|
||||||
];
|
];
|
@ -18,7 +18,29 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6 mb-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">@lang('gallery.statistics.taken_12_months')</div>
|
||||||
|
<div class="card-body text-center" id="taken-12m-graph">
|
||||||
|
<canvas id="taken-12m-chart" style="display: none;"></canvas>
|
||||||
|
<img class="loading" src="{{ asset('ripple.svg') }}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6 mb-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">@lang('gallery.statistics.uploaded_12_months')</div>
|
||||||
|
<div class="card-body text-center" id="uploaded-12m-graph">
|
||||||
|
<canvas id="uploaded-12m-chart" style="display: none;"></canvas>
|
||||||
|
<img class="loading" src="{{ asset('ripple.svg') }}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-4">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">@lang('gallery.statistics.cameras')</div>
|
<div class="card-header">@lang('gallery.statistics.cameras')</div>
|
||||||
<div class="card-body text-center" id="cameras-graph">
|
<div class="card-body text-center" id="cameras-graph">
|
||||||
@ -27,6 +49,38 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6 mb-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">@lang('gallery.statistics.file_sizes')</div>
|
||||||
|
<div class="card-body text-center" id="filesizes-graph">
|
||||||
|
<canvas id="filesizes-chart" style="display: none;"></canvas>
|
||||||
|
<img class="loading" src="{{ asset('ripple.svg') }}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">@lang('gallery.statistics.album_by_photos')</div>
|
||||||
|
<div class="card-body text-center" id="album-photos-graph">
|
||||||
|
<canvas id="album-photos-chart" style="display: none;"></canvas>
|
||||||
|
<img class="loading" src="{{ asset('ripple.svg') }}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6 mb-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">@lang('gallery.statistics.album_by_size')</div>
|
||||||
|
<div class="card-body text-center" id="album-size-graph">
|
||||||
|
<canvas id="album-size-chart" style="display: none;"></canvas>
|
||||||
|
<img class="loading" src="{{ asset('ripple.svg') }}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
@ -36,13 +90,66 @@
|
|||||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.bundle.min.js"></script>
|
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.bundle.min.js"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
/* Photos taken in the last 12 months */
|
||||||
|
$.get('{{ route('statistics.taken12Months') }}', function(result)
|
||||||
|
{
|
||||||
|
$('.loading', '#taken-12m-graph').hide();
|
||||||
|
$('canvas', '#taken-12m-graph').show();
|
||||||
|
var myLineChart = new Chart($('#taken-12m-chart'),
|
||||||
|
{
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
borderColor: '#0F2240',
|
||||||
|
data: result.data,
|
||||||
|
fill: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
labels: result.labels
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Photos uploaded in the last 12 months */
|
||||||
|
$.get('{{ route('statistics.uploaded12Months') }}', function(result)
|
||||||
|
{
|
||||||
|
$('.loading', '#uploaded-12m-graph').hide();
|
||||||
|
$('canvas', '#uploaded-12m-graph').show();
|
||||||
|
var myLineChart = new Chart($('#uploaded-12m-chart'),
|
||||||
|
{
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
borderColor: '#0F2240',
|
||||||
|
data: result.data,
|
||||||
|
fill: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
labels: result.labels
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Cameras */
|
||||||
$.get('{{ route('statistics.cameras') }}', function(result)
|
$.get('{{ route('statistics.cameras') }}', function(result)
|
||||||
{
|
{
|
||||||
$('.loading', '#cameras-graph').hide();
|
$('.loading', '#cameras-graph').hide();
|
||||||
$('canvas', '#cameras-graph').show();
|
$('canvas', '#cameras-graph').show();
|
||||||
var myPieChart = new Chart($('#cameras-chart'),
|
var myPieChart = new Chart($('#cameras-chart'),
|
||||||
{
|
{
|
||||||
type: 'pie',
|
type: 'doughnut',
|
||||||
data: {
|
data: {
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
@ -51,9 +158,93 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
labels: result.labels
|
labels: result.labels
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
legend: {
|
||||||
|
display: true,
|
||||||
|
position: 'bottom'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* File Sizes */
|
||||||
|
$.get('{{ route('statistics.fileSizes') }}', function(result)
|
||||||
|
{
|
||||||
|
$('.loading', '#filesizes-graph').hide();
|
||||||
|
$('canvas', '#filesizes-graph').show();
|
||||||
|
var myPieChart = new Chart($('#filesizes-chart'),
|
||||||
|
{
|
||||||
|
type: 'doughnut',
|
||||||
|
data: {
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
backgroundColor: result.backgrounds,
|
||||||
|
data: result.data
|
||||||
|
}
|
||||||
|
],
|
||||||
|
labels: result.labels
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
legend: {
|
||||||
|
display: true,
|
||||||
|
position: 'bottom'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Album size by photo count */
|
||||||
|
$.get('{{ route('statistics.albumSizePhotos') }}', function(result)
|
||||||
|
{
|
||||||
|
$('.loading', '#album-photos-graph').hide();
|
||||||
|
$('canvas', '#album-photos-graph').show();
|
||||||
|
var myPieChart = new Chart($('#album-photos-chart'),
|
||||||
|
{
|
||||||
|
type: 'doughnut',
|
||||||
|
data: {
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
backgroundColor: result.backgrounds,
|
||||||
|
data: result.data
|
||||||
|
}
|
||||||
|
],
|
||||||
|
labels: result.labels
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
legend: {
|
||||||
|
display: true,
|
||||||
|
position: 'bottom'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Album size by photo size */
|
||||||
|
$.get('{{ route('statistics.albumSizePhotoSize') }}', function(result)
|
||||||
|
{
|
||||||
|
$('.loading', '#album-size-graph').hide();
|
||||||
|
$('canvas', '#album-size-graph').show();
|
||||||
|
var myPieChart = new Chart($('#album-size-chart'),
|
||||||
|
{
|
||||||
|
type: 'doughnut',
|
||||||
|
data: {
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
backgroundColor: result.backgrounds,
|
||||||
|
data: result.data
|
||||||
|
}
|
||||||
|
],
|
||||||
|
labels: result.labels
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
legend: {
|
||||||
|
display: true,
|
||||||
|
position: 'bottom'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@endpush
|
@endpush
|
@ -70,7 +70,12 @@ Route::get('/password/change', 'Auth\ChangePasswordController@showChangePassword
|
|||||||
Route::post('/password/change', 'Auth\ChangePasswordController@processChangePassword')->name('auth.processChangePassword');
|
Route::post('/password/change', 'Auth\ChangePasswordController@processChangePassword')->name('auth.processChangePassword');
|
||||||
Route::get('/sitemap.xml', 'Gallery\DefaultController@sitemapXml');
|
Route::get('/sitemap.xml', 'Gallery\DefaultController@sitemapXml');
|
||||||
Route::get('/statistics', 'Gallery\StatisticsController@index')->name('statistics.index');
|
Route::get('/statistics', 'Gallery\StatisticsController@index')->name('statistics.index');
|
||||||
|
Route::get('/statistics/album-size-photo-count', 'Gallery\StatisticsController@albumSizeByPhotosChart')->name('statistics.albumSizePhotos');
|
||||||
|
Route::get('/statistics/album-size-photo-size', 'Gallery\StatisticsController@albumSizeByPhotoSizeChart')->name('statistics.albumSizePhotoSize');
|
||||||
Route::get('/statistics/cameras', 'Gallery\StatisticsController@camerasChart')->name('statistics.cameras');
|
Route::get('/statistics/cameras', 'Gallery\StatisticsController@camerasChart')->name('statistics.cameras');
|
||||||
|
Route::get('/statistics/file-sizes', 'Gallery\StatisticsController@fileSizeChart')->name('statistics.fileSizes');
|
||||||
|
Route::get('/statistics/taken-12m', 'Gallery\StatisticsController@photosTaken12Months')->name('statistics.taken12Months');
|
||||||
|
Route::get('/statistics/uploaded-12m', 'Gallery\StatisticsController@photosUploaded12Months')->name('statistics.uploaded12Months');
|
||||||
Route::get('a/{albumUrlAlias}', 'Gallery\AlbumController@index')
|
Route::get('a/{albumUrlAlias}', 'Gallery\AlbumController@index')
|
||||||
->name('viewAlbum')
|
->name('viewAlbum')
|
||||||
->where('albumUrlAlias', '.*');
|
->where('albumUrlAlias', '.*');
|
||||||
|
Loading…
Reference in New Issue
Block a user