1
0
mirror of https://github.com/flarum/core.git synced 2025-08-07 08:56:38 +02:00
Files
php-flarum/extensions/statistics/src/Api/Controller/ShowStatisticsData.php
David Wheatley 9964ddd731 [statistics] fix: add missing last period to custom date ranges (#3661)
* fix: last node in previous data matches first node of current data

* fix: add previous period support for custom periods

* test: update to show previous period for custom range
2022-11-06 18:21:28 +00:00

168 lines
5.4 KiB
PHP

<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Statistics\Api\Controller;
use Carbon\Carbon;
use DateTime;
use Flarum\Discussion\Discussion;
use Flarum\Http\RequestUtil;
use Flarum\Post\Post;
use Flarum\Post\RegisteredTypesScope;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\User\User;
use Illuminate\Contracts\Cache\Repository as CacheRepository;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Laminas\Diactoros\Response\JsonResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Tobscure\JsonApi\Exception\InvalidParameterException;
class ShowStatisticsData implements RequestHandlerInterface
{
/**
* The amount of time to cache lifetime statistics data for in seconds.
*/
public static $lifetimeStatsCacheTtl = 300;
/**
* The amount of time to cache timed statistics data for in seconds.
*/
public static $timedStatsCacheTtl = 900;
protected $entities = [];
/**
* @var SettingsRepositoryInterface
*/
protected $settings;
/**
* @var CacheRepository
*/
protected $cache;
public function __construct(SettingsRepositoryInterface $settings, CacheRepository $cache)
{
$this->settings = $settings;
$this->cache = $cache;
$this->entities = [
'users' => [User::query(), 'joined_at'],
'discussions' => [Discussion::query(), 'created_at'],
'posts' => [Post::where('type', 'comment')->withoutGlobalScope(RegisteredTypesScope::class), 'created_at']
];
}
public function handle(ServerRequestInterface $request): ResponseInterface
{
$actor = RequestUtil::getActor($request);
// Must be an admin to get statistics data -- this is only visible on the admin
// control panel.
$actor->assertAdmin();
$query = $request->getQueryParams();
$reportingPeriod = Arr::get($query, 'period');
$model = Arr::get($query, 'model');
$customDateRange = Arr::get($query, 'dateRange');
return new JsonResponse($this->getResponse($model, $reportingPeriod, $customDateRange));
}
private function getResponse(?string $model, ?string $period, ?array $customDateRange): array
{
if ($period === 'lifetime') {
return $this->getLifetimeStatistics();
}
if (! Arr::exists($this->entities, $model)) {
throw new InvalidParameterException('A model must be specified');
}
if ($period === 'custom') {
$start = (int) $customDateRange['start'];
$end = (int) $customDateRange['end'];
if (! $customDateRange || ! $start || ! $end) {
throw new InvalidParameterException('A custom date range must be specified');
}
// Seconds-based timestamps
$startRange = Carbon::createFromTimestampUTC($start)->toDateTime();
$endRange = Carbon::createFromTimestampUTC($end)->toDateTime();
// We can't really cache this
return $this->getTimedCounts($this->entities[$model][0], $this->entities[$model][1], $startRange, $endRange);
}
return $this->getTimedStatistics($model);
}
private function getLifetimeStatistics()
{
return $this->cache->remember('flarum-subscriptions.lifetime_stats', self::$lifetimeStatsCacheTtl, function () {
return array_map(function ($entity) {
return $entity[0]->count();
}, $this->entities);
});
}
private function getTimedStatistics(string $model)
{
return $this->cache->remember("flarum-subscriptions.timed_stats.$model", self::$lifetimeStatsCacheTtl, function () use ($model) {
return $this->getTimedCounts($this->entities[$model][0], $this->entities[$model][1]);
});
}
private function getTimedCounts(Builder $query, string $column, ?DateTime $startDate = null, ?DateTime $endDate = null)
{
$diff = $startDate && $endDate ? $startDate->diff($endDate) : null;
if (! isset($startDate)) {
// need -12 months and period before that
$startDate = new DateTime('-2 years');
} else {
// If the start date is custom, we need to include an equal amount beforehand
// to show the data for the previous period.
$startDate = (new Carbon($startDate))->subtract($diff)->toDateTime();
}
if (! isset($endDate)) {
$endDate = new DateTime();
}
$results = $query
->selectRaw(
'DATE_FORMAT(
@date := '.$column.',
IF(@date > ?, \'%Y-%m-%d %H:00:00\', \'%Y-%m-%d\') -- if within the last 24 hours, group by hour
) as time_group',
[new DateTime('-25 hours')]
)
->selectRaw('COUNT(id) as count')
->where($column, '>', $startDate)
->where($column, '<=', $endDate)
->groupBy('time_group')
->pluck('count', 'time_group');
$timed = [];
$results->each(function ($count, $time) use (&$timed) {
$time = new DateTime($time);
$timed[$time->getTimestamp()] = (int) $count;
});
return $timed;
}
}