Merge pull request #4040 from Roardom/livewire-stats

(Update) Lazy load stats index
This commit is contained in:
HDVinnie
2024-08-11 17:42:09 -04:00
committed by GitHub
11 changed files with 386 additions and 187 deletions
+1 -68
View File
@@ -16,9 +16,7 @@ declare(strict_types=1);
namespace App\Http\Controllers;
use App\Models\Category;
use App\Models\Group;
use App\Models\History;
use App\Models\Language;
use App\Models\Peer;
use App\Models\Torrent;
@@ -46,75 +44,10 @@ class StatsController extends Controller
/**
* Show Extra-Stats Index.
*
* @throws Exception
*/
public function index(): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
{
// Total Torrents Count
$numTorrent = cache()->remember('num_torrent', $this->carbon, fn () => Torrent::count());
// Total SD Count
$numSd = cache()->remember('num_sd', $this->carbon, fn () => Torrent::where('sd', '=', 1)->count());
// Generally sites have more seeders than leechers, so it ends up being faster (by approximately 50%) to compute these stats instead of computing them individually
$leecherCount = cache()->remember('peer_seeder_count', $this->carbon, fn () => Peer::where('seeder', '=', false)->where('active', '=', true)->count());
$peerCount = cache()->remember('peer_count', $this->carbon, fn () => Peer::where('active', '=', true)->count());
$historyStats = cache()->remember(
'history_stats',
$this->carbon,
fn () => History::query()
->selectRaw('SUM(actual_uploaded) as actual_upload')
->selectRaw('SUM(uploaded) as credited_upload')
->selectRaw('SUM(actual_downloaded) as actual_download')
->selectRaw('SUM(downloaded) as credited_download')
->first()
);
$bannedGroup = cache()->rememberForever('banned_group', fn () => Group::where('slug', '=', 'banned')->pluck('id'));
return view('stats.index', [
'all_user' => cache()->remember(
'all_user',
$this->carbon,
fn () => User::withTrashed()->count()
),
'active_user' => cache()->remember(
'active_user',
$this->carbon,
fn () => User::whereNotIn('group_id', Group::select('id')->whereIn('slug', ['banned', 'validating', 'disabled', 'pruned']))->count()
),
'disabled_user' => cache()->remember(
'disabled_user',
$this->carbon,
fn () => User::whereRelation('group', 'slug', '=', 'disabled')->count()
),
'pruned_user' => cache()->remember(
'pruned_user',
$this->carbon,
fn () => User::onlyTrashed()->whereRelation('group', 'slug', '=', 'pruned')->count()
),
'banned_user' => cache()->remember(
'banned_user',
$this->carbon,
fn () => User::whereRelation('group', 'slug', '=', 'banned')->count()
),
'num_torrent' => $numTorrent,
'categories' => Category::withCount('torrents')->orderBy('position')->get(),
'num_hd' => $numTorrent - $numSd,
'num_sd' => $numSd,
'torrent_size' => cache()->remember('torrent_size', $this->carbon, fn () => Torrent::sum('size')),
'num_seeders' => $peerCount - $leecherCount,
'num_leechers' => $leecherCount,
'num_peers' => $peerCount,
'actual_upload' => $historyStats->actual_upload,
'actual_download' => $historyStats->actual_download,
'actual_up_down' => $historyStats->actual_upload + $historyStats->actual_download,
'credited_upload' => $historyStats->credited_upload,
'credited_download' => $historyStats->credited_download,
'credited_up_down' => $historyStats->credited_upload + $historyStats->credited_download,
]);
return view('stats.index');
}
/**
+58
View File
@@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author Roardom <roardom@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Http\Livewire\Stats;
use App\Models\Peer;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Lazy;
use Livewire\Component;
#[Lazy(isolate: true)]
class PeerStats extends Component
{
#[Computed(cache: true, seconds: 10 * 60)]
final public function leecherCount(): int
{
// Generally sites have more seeders than leechers, so it ends up being faster (by approximately 50%) to compute leechers and total instead of seeders and leechers.
return Peer::query()->where('seeder', '=', false)->where('active', '=', true)->count();
}
#[Computed(cache: true, seconds: 10 * 60)]
final public function peerCount(): int
{
return Peer::query()->where('active', '=', true)->count();
}
final public function placeholder(): string
{
return <<<'HTML'
<section class="panelV2">
<h2 class="panel__heading">{{ __('torrent.peers') }}</h2>
<div class="panel__body">Loading...</div>
</section>
HTML;
}
final public function render(): \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Contracts\Foundation\Application
{
return view('livewire.stats.peer-stats', [
'num_seeders' => $this->peerCount - $this->leecherCount,
'num_leechers' => $this->leecherCount,
'num_peers' => $this->peerCount,
]);
}
}
+75
View File
@@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author Roardom <roardom@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Http\Livewire\Stats;
use App\Models\Category;
use App\Models\Torrent;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Lazy;
use Livewire\Component;
#[Lazy(isolate: true)]
class TorrentStats extends Component
{
#[Computed(cache: true, seconds: 10 * 60)]
final public function totalCount(): int
{
return Torrent::query()->count();
}
#[Computed(cache: true, seconds: 10 * 60)]
final public function sdCount(): int
{
return Torrent::query()->where('sd', '=', 1)->count();
}
/**
* @return \Illuminate\Database\Eloquent\Collection<int, Category>
*/
#[Computed(cache: true, seconds: 10 * 60)]
final public function categories(): \Illuminate\Database\Eloquent\Collection
{
return Category::query()->withCount('torrents')->orderBy('position')->get();
}
#[Computed(cache: true, seconds: 10 * 60)]
final public function sizeSum(): int
{
return (int) Torrent::query()->sum('size');
}
final public function placeholder(): string
{
return <<<'HTML'
<section class="panelV2">
<h2 class="panel__heading">{{ __('torrent.torrents') }}</h2>
<div class="panel__body">Loading...</div>
</section>
HTML;
}
final public function render(): \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Contracts\Foundation\Application
{
return view('livewire.stats.torrent-stats', [
'num_torrent' => $this->totalCount,
'categories' => $this->categories,
'num_hd' => $this->totalCount - $this->sdCount,
'num_sd' => $this->sdCount,
'torrent_size' => $this->sizeSum,
]);
}
}
+72
View File
@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author Roardom <roardom@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Http\Livewire\Stats;
use App\Models\History;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Lazy;
use Livewire\Component;
#[Lazy(isolate: true)]
class TrafficStats extends Component
{
#[Computed(cache: true, seconds: 10 * 60)]
final public function actualUpload(): int
{
return (int) History::query()->sum('actual_uploaded');
}
#[Computed(cache: true, seconds: 10 * 60)]
final public function creditedUpload(): int
{
return (int) History::query()->sum('uploaded');
}
#[Computed(cache: true, seconds: 10 * 60)]
final public function actualDownload(): int
{
return (int) History::query()->sum('actual_downloaded');
}
#[Computed(cache: true, seconds: 10 * 60)]
final public function creditedDownload(): int
{
return (int) History::query()->sum('downloaded');
}
final public function placeholder(): string
{
return <<<'HTML'
<section class="panelV2">
<h2 class="panel__heading">{{ __('stat.total-traffic') }}</h2>
<div class="panel__body">Loading...</div>
</section>
HTML;
}
final public function render(): \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Contracts\Foundation\Application
{
return view('livewire.stats.traffic-stats', [
'actual_upload' => $this->actualUpload,
'actual_download' => $this->actualDownload,
'actual_up_down' => $this->actualUpload + $this->actualDownload,
'credited_upload' => $this->creditedUpload,
'credited_download' => $this->creditedDownload,
'credited_up_down' => $this->creditedUpload + $this->creditedDownload,
]);
}
}
+77
View File
@@ -0,0 +1,77 @@
<?php
declare(strict_types=1);
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author Roardom <roardom@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Http\Livewire\Stats;
use App\Models\User;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Lazy;
use Livewire\Component;
#[Lazy(isolate: true)]
class UserStats extends Component
{
#[Computed(cache: true, seconds: 10 * 60)]
final public function allUsers(): int
{
return User::query()->withTrashed()->count();
}
#[Computed(cache: true, seconds: 10 * 60)]
final public function activeUsers(): int
{
return User::query()->whereHas('group', fn ($query) => $query->whereNotIn('slug', ['banned', 'validating', 'disabled', 'pruned']))->count();
}
#[Computed(cache: true, seconds: 10 * 60)]
final public function disableUsers(): int
{
return User::query()->whereRelation('group', 'slug', '=', 'disabled')->count();
}
#[Computed(cache: true, seconds: 10 * 60)]
final public function prunedUsers(): int
{
return User::query()->onlyTrashed()->whereRelation('group', 'slug', '=', 'pruned')->count();
}
#[Computed(cache: true, seconds: 10 * 60)]
final public function bannedUsers(): int
{
return User::query()->whereRelation('group', 'slug', '=', 'banned')->count();
}
final public function placeholder(): string
{
return <<<'HTML'
<section class="panelV2">
<h2 class="panel__heading">{{ __('common.users') }}</h2>
<div class="panel__body">Loading...</div>
</section>
HTML;
}
final public function render(): \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Contracts\Foundation\Application
{
return view('livewire.stats.user-stats', [
'all_user' => $this->allUsers,
'active_user' => $this->activeUsers,
'disabled_user' => $this->disableUsers,
'pruned_user' => $this->prunedUsers,
'banned_user' => $this->bannedUsers,
]);
}
}
-20
View File
@@ -405,26 +405,6 @@ parameters:
count: 1
path: app/Http/Controllers/Staff/VersionController.php
-
message: "#^Access to an undefined property App\\\\Models\\\\History\\:\\:\\$actual_download\\.$#"
count: 2
path: app/Http/Controllers/StatsController.php
-
message: "#^Access to an undefined property App\\\\Models\\\\History\\:\\:\\$actual_upload\\.$#"
count: 2
path: app/Http/Controllers/StatsController.php
-
message: "#^Access to an undefined property App\\\\Models\\\\History\\:\\:\\$credited_download\\.$#"
count: 2
path: app/Http/Controllers/StatsController.php
-
message: "#^Access to an undefined property App\\\\Models\\\\History\\:\\:\\$credited_upload\\.$#"
count: 2
path: app/Http/Controllers/StatsController.php
-
message: "#^Access to an undefined property App\\\\Models\\\\Post\\:\\:\\$post_count\\.$#"
count: 1
@@ -0,0 +1,17 @@
<section class="panelV2 panel--grid-item">
<h2 class="panel__heading">{{ __('torrent.peers') }}</h2>
<dl class="key-value">
<div class="key-value__group">
<dt>{{ __('torrent.seeders') }}</dt>
<dd>{{ $num_seeders }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('torrent.leechers') }}</dt>
<dd>{{ $num_leechers }}</dd>
</div>
<div class="key-value__group">
<dt>Total</dt>
<dd>{{ $num_peers }}</dd>
</div>
</dl>
</section>
@@ -0,0 +1,28 @@
<section class="panelV2 panel--grid-item">
<h2 class="panel__heading">{{ __('torrent.torrents') }}</h2>
<dl class="key-value">
@foreach ($categories as $category)
<div class="key-value__group">
<dt>{{ $category->name }} {{ __('common.category') }}</dt>
<dd>{{ $category->torrents_count }}</dd>
</div>
@endforeach
<div class="key-value__group">
<dt>HD</dt>
<dd>{{ $num_hd }}</dd>
</div>
<div class="key-value__group">
<dt>SD</dt>
<dd>{{ $num_sd }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.total-torrents') }}</dt>
<dd>{{ $num_torrent }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.total-torrents') }} {{ __('torrent.size') }}</dt>
<dd>{{ App\Helpers\StringHelper::formatBytes($torrent_size, 2) }}</dd>
</div>
</dl>
</section>
@@ -0,0 +1,29 @@
<section class="panelV2 panel--grid-item">
<h2 class="panel__heading">{{ __('stat.total-traffic') }}</h2>
<dl class="key-value">
<div class="key-value__group">
<dt>{{ __('stat.real') }} {{ __('stat.total-upload') }}</dt>
<dd>{{ \App\Helpers\StringHelper::formatBytes($actual_upload, 2) }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.real') }} {{ __('stat.total-download') }}</dt>
<dd>{{ \App\Helpers\StringHelper::formatBytes($actual_download, 2) }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.real') }} {{ __('stat.total-traffic') }}</dt>
<dd>{{ \App\Helpers\StringHelper::formatBytes($actual_up_down, 2) }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.credited') }} {{ __('stat.total-upload') }}</dt>
<dd>{{ \App\Helpers\StringHelper::formatBytes($credited_upload, 2) }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.credited') }} {{ __('stat.total-download') }}</dt>
<dd>{{ \App\Helpers\StringHelper::formatBytes($credited_download, 2) }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.credited') }} {{ __('stat.total-traffic') }}</dt>
<dd>{{ \App\Helpers\StringHelper::formatBytes($credited_up_down, 2) }}</dd>
</div>
</dl>
</section>
@@ -0,0 +1,25 @@
<section class="panelV2 panel--grid-item">
<h2 class="panel__heading">{{ __('common.users') }}</h2>
<dl class="key-value">
<div class="key-value__group">
<dt>{{ __('stat.all') }} {{ __('common.users') }}</dt>
<dd>{{ $all_user }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.active') }} {{ __('common.users') }}</dt>
<dd>{{ $active_user }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.disabled') }} {{ __('common.users') }}</dt>
<dd>{{ $disabled_user }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.pruned') }} {{ __('common.users') }}</dt>
<dd>{{ $pruned_user }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.banned') }} {{ __('common.users') }}</dt>
<dd>{{ $banned_user }}</dd>
</div>
</dl>
</section>
+4 -99
View File
@@ -55,104 +55,9 @@
<div class="panel__body">{{ __('stat.nerd-stats-desc') }}. {{ __('stat.updated') }}</div>
</section>
<div class="stats__panels">
<section class="panelV2 panel--grid-item">
<h2 class="panel__heading">{{ __('torrent.torrents') }}</h2>
<dl class="key-value">
@foreach ($categories as $category)
<div class="key-value__group">
<dt>{{ $category->name }} {{ __('common.category') }}</dt>
<dd>{{ $category->torrents_count }}</dd>
</div>
@endforeach
<div class="key-value__group">
<dt>HD</dt>
<dd>{{ $num_hd }}</dd>
</div>
<div class="key-value__group">
<dt>SD</dt>
<dd>{{ $num_sd }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.total-torrents') }}</dt>
<dd>{{ $num_torrent }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.total-torrents') }} {{ __('torrent.size') }}</dt>
<dd>{{ App\Helpers\StringHelper::formatBytes($torrent_size, 2) }}</dd>
</div>
</dl>
</section>
<section class="panelV2 panel--grid-item">
<h2 class="panel__heading">{{ __('common.users') }}</h2>
<dl class="key-value">
<div class="key-value__group">
<dt>{{ __('stat.all') }} {{ __('common.users') }}</dt>
<dd>{{ $all_user }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.active') }} {{ __('common.users') }}</dt>
<dd>{{ $active_user }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.disabled') }} {{ __('common.users') }}</dt>
<dd>{{ $disabled_user }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.pruned') }} {{ __('common.users') }}</dt>
<dd>{{ $pruned_user }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.banned') }} {{ __('common.users') }}</dt>
<dd>{{ $banned_user }}</dd>
</div>
</dl>
</section>
<section class="panelV2 panel--grid-item">
<h2 class="panel__heading">{{ __('torrent.peers') }}</h2>
<dl class="key-value">
<div class="key-value__group">
<dt>{{ __('torrent.seeders') }}</dt>
<dd>{{ $num_seeders }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('torrent.leechers') }}</dt>
<dd>{{ $num_leechers }}</dd>
</div>
<div class="key-value__group">
<dt>Total</dt>
<dd>{{ $num_peers }}</dd>
</div>
</dl>
</section>
<section class="panelV2 panel--grid-item">
<h2 class="panel__heading">{{ __('stat.total-traffic') }}</h2>
<dl class="key-value">
<div class="key-value__group">
<dt>{{ __('stat.real') }} {{ __('stat.total-upload') }}</dt>
<dd>{{ \App\Helpers\StringHelper::formatBytes($actual_upload, 2) }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.real') }} {{ __('stat.total-download') }}</dt>
<dd>{{ \App\Helpers\StringHelper::formatBytes($actual_download, 2) }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.real') }} {{ __('stat.total-traffic') }}</dt>
<dd>{{ \App\Helpers\StringHelper::formatBytes($actual_up_down, 2) }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.credited') }} {{ __('stat.total-upload') }}</dt>
<dd>{{ \App\Helpers\StringHelper::formatBytes($credited_upload, 2) }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.credited') }} {{ __('stat.total-download') }}</dt>
<dd>{{ \App\Helpers\StringHelper::formatBytes($credited_download, 2) }}</dd>
</div>
<div class="key-value__group">
<dt>{{ __('stat.credited') }} {{ __('stat.total-traffic') }}</dt>
<dd>{{ \App\Helpers\StringHelper::formatBytes($credited_up_down, 2) }}</dd>
</div>
</dl>
</section>
<livewire:stats.torrent-stats />
<livewire:stats.user-stats />
<livewire:stats.peer-stats />
<livewire:stats.traffic-stats />
</div>
@endsection