Skip to content

Commit bfe9117

Browse files
Add upload and download latency information (alexjustesen#1353)
Co-authored-by: Alex Justesen <[email protected]>
1 parent d22db76 commit bfe9117

File tree

6 files changed

+348
-0
lines changed

6 files changed

+348
-0
lines changed

app/Filament/Exports/ResultExporter.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,30 @@ public static function getColumns(): array
6464
->state(function (Result $record): ?string {
6565
return $record->ping_jitter;
6666
}),
67+
ExportColumn::make('upload_latency_high')
68+
->state(function (Result $record): ?string {
69+
return $record->upload_latency_high;
70+
}),
71+
ExportColumn::make('upload_latency_low')
72+
->state(function (Result $record): ?string {
73+
return $record->upload_latency_low;
74+
}),
75+
ExportColumn::make('upload_latency_avg')
76+
->state(function (Result $record): ?string {
77+
return $record->upload_latency_iqm;
78+
}),
79+
ExportColumn::make('download_latency_high')
80+
->state(function (Result $record): ?string {
81+
return $record->download_latency_high;
82+
}),
83+
ExportColumn::make('download_latency_low')
84+
->state(function (Result $record): ?string {
85+
return $record->download_latency_low;
86+
}),
87+
ExportColumn::make('download_latency_avg')
88+
->state(function (Result $record): ?string {
89+
return $record->download_latency_iqm;
90+
}),
6791
ExportColumn::make('comments')
6892
->enabledByDefault(false),
6993
// ExportColumn::make('status'), // TODO: enable status when upgrading to PHP v8.3: https://php.watch/versions/8.3/dynamic-class-const-enum-member-syntax-support

app/Filament/Pages/Dashboard.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55
use App\Actions\Speedtests\RunOoklaSpeedtest;
66
use App\Filament\Widgets\RecentDownloadChartWidget;
7+
use App\Filament\Widgets\RecentDownloadLatencyChartWidget;
78
use App\Filament\Widgets\RecentJitterChartWidget;
89
use App\Filament\Widgets\RecentPingChartWidget;
910
use App\Filament\Widgets\RecentUploadChartWidget;
11+
use App\Filament\Widgets\RecentUploadLatencyChartWidget;
1012
use App\Filament\Widgets\StatsOverviewWidget;
1113
use App\Settings\GeneralSettings;
1214
use Filament\Actions\Action;
@@ -67,6 +69,8 @@ protected function getHeaderWidgets(): array
6769
RecentUploadChartWidget::make(),
6870
RecentPingChartWidget::make(),
6971
RecentJitterChartWidget::make(),
72+
RecentUploadLatencyChartWidget::make(),
73+
RecentDownloadLatencyChartWidget::make(),
7074
];
7175
}
7276
}

app/Filament/Resources/ResultResource.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,20 @@ public static function form(Form $form): Form
6868
->label('Ping (ms)'),
6969
Forms\Components\TextInput::make('data.download.latency.jitter')
7070
->label('Download Jitter (ms)'),
71+
Forms\Components\TextInput::make('data.download.latency.high')
72+
->label('Download Latency High'),
73+
Forms\Components\TextInput::make('data.download.latency.low')
74+
->label('Download Latency low'),
75+
Forms\Components\TextInput::make('data.download.latency.iqm')
76+
->label('Download Latency iqm'),
7177
Forms\Components\TextInput::make('data.upload.latency.jitter')
7278
->label('Upload Jitter (ms)'),
79+
Forms\Components\TextInput::make('data.upload.latency.high')
80+
->label('Upload Latency High'),
81+
Forms\Components\TextInput::make('data.upload.latency.low')
82+
->label('Upload Latency low'),
83+
Forms\Components\TextInput::make('data.upload.latency.iqm')
84+
->label('Upload Latency iqm'),
7385
Forms\Components\TextInput::make('data.ping.jitter')
7486
->label('Ping Jitter (ms)'),
7587
Forms\Components\TextInput::make('data.packetLoss')
@@ -151,12 +163,48 @@ public static function table(Table $table): Table
151163
->sortable(query: function (Builder $query, string $direction): Builder {
152164
return $query->orderBy('data->download->latency->jitter', $direction);
153165
}),
166+
Tables\Columns\TextColumn::make('download_latency_high')
167+
->toggleable()
168+
->toggledHiddenByDefault()
169+
->sortable(query: function (Builder $query, string $direction): Builder {
170+
return $query->orderBy('data->download->latency->high', $direction);
171+
}),
172+
Tables\Columns\TextColumn::make('download_latency_low')
173+
->toggleable()
174+
->toggledHiddenByDefault()
175+
->sortable(query: function (Builder $query, string $direction): Builder {
176+
return $query->orderBy('data->download->latency->low', $direction);
177+
}),
178+
Tables\Columns\TextColumn::make('download_latency_iqm')
179+
->toggleable()
180+
->toggledHiddenByDefault()
181+
->sortable(query: function (Builder $query, string $direction): Builder {
182+
return $query->orderBy('data->download->latency->iqm', $direction);
183+
}),
154184
Tables\Columns\TextColumn::make('upload_jitter')
155185
->toggleable()
156186
->toggledHiddenByDefault()
157187
->sortable(query: function (Builder $query, string $direction): Builder {
158188
return $query->orderBy('data->upload->latency->jitter', $direction);
159189
}),
190+
Tables\Columns\TextColumn::make('upload_latency_high')
191+
->toggleable()
192+
->toggledHiddenByDefault()
193+
->sortable(query: function (Builder $query, string $direction): Builder {
194+
return $query->orderBy('data->upload->latency->high', $direction);
195+
}),
196+
Tables\Columns\TextColumn::make('upload_latency_low')
197+
->toggleable()
198+
->toggledHiddenByDefault()
199+
->sortable(query: function (Builder $query, string $direction): Builder {
200+
return $query->orderBy('data->upload->latency->low', $direction);
201+
}),
202+
Tables\Columns\TextColumn::make('upload_latency_iqm')
203+
->toggleable()
204+
->toggledHiddenByDefault()
205+
->sortable(query: function (Builder $query, string $direction): Builder {
206+
return $query->orderBy('data->upload->latency->iqm', $direction);
207+
}),
160208
Tables\Columns\TextColumn::make('ping_jitter')
161209
->toggleable()
162210
->toggledHiddenByDefault()
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<?php
2+
3+
namespace App\Filament\Widgets;
4+
5+
use App\Enums\ResultStatus;
6+
use App\Helpers\TimeZoneHelper;
7+
use App\Models\Result;
8+
use App\Settings\GeneralSettings;
9+
use Filament\Widgets\ChartWidget;
10+
11+
class RecentDownloadLatencyChartWidget extends ChartWidget
12+
{
13+
protected static ?string $heading = 'Download Latency';
14+
15+
protected int|string|array $columnSpan = 'full';
16+
17+
protected static ?string $maxHeight = '250px';
18+
19+
public ?string $filter = '24h';
20+
21+
protected function getPollingInterval(): ?string
22+
{
23+
return config('speedtest.dashboard_polling');
24+
}
25+
26+
protected function getFilters(): ?array
27+
{
28+
return [
29+
'24h' => 'Last 24h',
30+
'week' => 'Last week',
31+
'month' => 'Last month',
32+
];
33+
}
34+
35+
protected function getData(): array
36+
{
37+
$settings = new GeneralSettings();
38+
39+
$results = Result::query()
40+
->select(['id', 'data', 'created_at'])
41+
->where('status', '=', ResultStatus::Completed)
42+
->when($this->filter == '24h', function ($query) {
43+
$query->where('created_at', '>=', now()->subDay());
44+
})
45+
->when($this->filter == 'week', function ($query) {
46+
$query->where('created_at', '>=', now()->subWeek());
47+
})
48+
->when($this->filter == 'month', function ($query) {
49+
$query->where('created_at', '>=', now()->subMonth());
50+
})
51+
->orderBy('created_at')
52+
->get();
53+
54+
return [
55+
'datasets' => [
56+
[
57+
'label' => 'Average (ms)',
58+
'data' => $results->map(fn ($item) => $item->download_latency_iqm ? number_format($item->pdownload_latency_iqm, 2) : 0),
59+
'borderColor' => '#10b981',
60+
'backgroundColor' => '#10b981',
61+
'fill' => false,
62+
'cubicInterpolationMode' => 'monotone',
63+
'tension' => 0.4,
64+
],
65+
[
66+
'label' => 'High (ms)',
67+
'data' => $results->map(fn ($item) => $item->download_latency_high ? number_format($item->download_latency_high, 2) : 0),
68+
'borderColor' => '#0ea5e9',
69+
'backgroundColor' => '#0ea5e9',
70+
'fill' => false,
71+
'cubicInterpolationMode' => 'monotone',
72+
'tension' => 0.4,
73+
],
74+
[
75+
'label' => 'Low (ms)',
76+
'data' => $results->map(fn ($item) => $item->download_latency_low ? number_format($item->download_latency_low, 2) : 0),
77+
'borderColor' => '#8b5cf6',
78+
'backgroundColor' => '#8b5cf6',
79+
'fill' => false,
80+
'cubicInterpolationMode' => 'monotone',
81+
'tension' => 0.4,
82+
],
83+
],
84+
'labels' => $results->map(fn ($item) => $item->created_at->timezone(TimeZoneHelper::displayTimeZone($settings))->format('M d - G:i')),
85+
];
86+
}
87+
88+
protected function getOptions(): array
89+
{
90+
return [
91+
'scales' => [
92+
'y' => [
93+
'beginAtZero' => true,
94+
],
95+
],
96+
];
97+
}
98+
99+
protected function getType(): string
100+
{
101+
return 'line';
102+
}
103+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<?php
2+
3+
namespace App\Filament\Widgets;
4+
5+
use App\Enums\ResultStatus;
6+
use App\Helpers\TimeZoneHelper;
7+
use App\Models\Result;
8+
use App\Settings\GeneralSettings;
9+
use Filament\Widgets\ChartWidget;
10+
11+
class RecentUploadLatencyChartWidget extends ChartWidget
12+
{
13+
protected static ?string $heading = 'Upload Latency';
14+
15+
protected int|string|array $columnSpan = 'full';
16+
17+
protected static ?string $maxHeight = '250px';
18+
19+
public ?string $filter = '24h';
20+
21+
protected function getPollingInterval(): ?string
22+
{
23+
return config('speedtest.dashboard_polling');
24+
}
25+
26+
protected function getFilters(): ?array
27+
{
28+
return [
29+
'24h' => 'Last 24h',
30+
'week' => 'Last week',
31+
'month' => 'Last month',
32+
];
33+
}
34+
35+
protected function getData(): array
36+
{
37+
$settings = new GeneralSettings();
38+
39+
$results = Result::query()
40+
->select(['id', 'data', 'created_at'])
41+
->where('status', '=', ResultStatus::Completed)
42+
->when($this->filter == '24h', function ($query) {
43+
$query->where('created_at', '>=', now()->subDay());
44+
})
45+
->when($this->filter == 'week', function ($query) {
46+
$query->where('created_at', '>=', now()->subWeek());
47+
})
48+
->when($this->filter == 'month', function ($query) {
49+
$query->where('created_at', '>=', now()->subMonth());
50+
})
51+
->orderBy('created_at')
52+
->get();
53+
54+
return [
55+
'datasets' => [
56+
[
57+
'label' => 'Average (ms)',
58+
'data' => $results->map(fn ($item) => $item->upload_latency_iqm ? number_format($item->upload_latency_iqm, 2) : 0),
59+
'borderColor' => '#10b981',
60+
'backgroundColor' => '#10b981',
61+
'fill' => false,
62+
'cubicInterpolationMode' => 'monotone',
63+
'tension' => 0.4,
64+
],
65+
[
66+
'label' => 'High (ms)',
67+
'data' => $results->map(fn ($item) => $item->upload_latency_high ? number_format($item->upload_latency_high, 2) : 0),
68+
'borderColor' => '#0ea5e9',
69+
'backgroundColor' => '#0ea5e9',
70+
'fill' => false,
71+
'cubicInterpolationMode' => 'monotone',
72+
'tension' => 0.4,
73+
],
74+
[
75+
'label' => 'Low (ms)',
76+
'data' => $results->map(fn ($item) => $item->upload_latency_low ? number_format($item->upload_latency_low, 2) : 0),
77+
'borderColor' => '#8b5cf6',
78+
'backgroundColor' => '#8b5cf6',
79+
'fill' => false,
80+
'cubicInterpolationMode' => 'monotone',
81+
'tension' => 0.4,
82+
],
83+
],
84+
'labels' => $results->map(fn ($item) => $item->created_at->timezone(TimeZoneHelper::displayTimeZone($settings))->format('M d - G:i')),
85+
];
86+
}
87+
88+
protected function getOptions(): array
89+
{
90+
return [
91+
'scales' => [
92+
'y' => [
93+
'beginAtZero' => true,
94+
],
95+
],
96+
];
97+
}
98+
99+
protected function getType(): string
100+
{
101+
return 'line';
102+
}
103+
}

0 commit comments

Comments
 (0)