diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php index 421735948..d99f53e1f 100644 --- a/.phpstorm.meta.php +++ b/.phpstorm.meta.php @@ -13,6 +13,7 @@ */ override(new \Illuminate\Contracts\Container\Container, map([ '' => '@', + 'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class, 'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class, 'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class, 'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class, @@ -171,11 +172,6 @@ 'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class, 'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class, 'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class, - 'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class, - 'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class, - 'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class, - 'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class, - 'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class, 'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class, 'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class, 'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class, @@ -214,7 +210,6 @@ 'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class, 'encrypter' => \Illuminate\Encryption\Encrypter::class, 'events' => \Illuminate\Events\Dispatcher::class, - 'excel' => \Maatwebsite\Excel\Excel::class, 'filament' => \Filament\FilamentManager::class, 'files' => \Illuminate\Filesystem\Filesystem::class, 'filesystem' => \Illuminate\Filesystem\FilesystemManager::class, @@ -251,6 +246,7 @@ ])); override(\Illuminate\Container\Container::makeWith(0), map([ '' => '@', + 'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class, 'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class, 'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class, 'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class, @@ -409,11 +405,6 @@ 'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class, 'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class, 'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class, - 'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class, - 'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class, - 'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class, - 'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class, - 'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class, 'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class, 'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class, 'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class, @@ -452,7 +443,6 @@ 'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class, 'encrypter' => \Illuminate\Encryption\Encrypter::class, 'events' => \Illuminate\Events\Dispatcher::class, - 'excel' => \Maatwebsite\Excel\Excel::class, 'filament' => \Filament\FilamentManager::class, 'files' => \Illuminate\Filesystem\Filesystem::class, 'filesystem' => \Illuminate\Filesystem\FilesystemManager::class, @@ -489,6 +479,7 @@ ])); override(\Illuminate\Contracts\Container\Container::get(0), map([ '' => '@', + 'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class, 'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class, 'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class, 'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class, @@ -647,11 +638,6 @@ 'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class, 'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class, 'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class, - 'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class, - 'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class, - 'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class, - 'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class, - 'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class, 'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class, 'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class, 'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class, @@ -690,7 +676,6 @@ 'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class, 'encrypter' => \Illuminate\Encryption\Encrypter::class, 'events' => \Illuminate\Events\Dispatcher::class, - 'excel' => \Maatwebsite\Excel\Excel::class, 'filament' => \Filament\FilamentManager::class, 'files' => \Illuminate\Filesystem\Filesystem::class, 'filesystem' => \Illuminate\Filesystem\FilesystemManager::class, @@ -727,6 +712,7 @@ ])); override(\Illuminate\Contracts\Container\Container::make(0), map([ '' => '@', + 'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class, 'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class, 'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class, 'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class, @@ -885,11 +871,6 @@ 'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class, 'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class, 'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class, - 'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class, - 'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class, - 'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class, - 'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class, - 'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class, 'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class, 'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class, 'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class, @@ -928,7 +909,6 @@ 'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class, 'encrypter' => \Illuminate\Encryption\Encrypter::class, 'events' => \Illuminate\Events\Dispatcher::class, - 'excel' => \Maatwebsite\Excel\Excel::class, 'filament' => \Filament\FilamentManager::class, 'files' => \Illuminate\Filesystem\Filesystem::class, 'filesystem' => \Illuminate\Filesystem\FilesystemManager::class, @@ -965,6 +945,7 @@ ])); override(\Illuminate\Contracts\Container\Container::makeWith(0), map([ '' => '@', + 'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class, 'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class, 'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class, 'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class, @@ -1123,11 +1104,6 @@ 'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class, 'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class, 'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class, - 'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class, - 'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class, - 'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class, - 'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class, - 'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class, 'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class, 'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class, 'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class, @@ -1166,7 +1142,6 @@ 'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class, 'encrypter' => \Illuminate\Encryption\Encrypter::class, 'events' => \Illuminate\Events\Dispatcher::class, - 'excel' => \Maatwebsite\Excel\Excel::class, 'filament' => \Filament\FilamentManager::class, 'files' => \Illuminate\Filesystem\Filesystem::class, 'filesystem' => \Illuminate\Filesystem\FilesystemManager::class, @@ -1203,6 +1178,7 @@ ])); override(\App::get(0), map([ '' => '@', + 'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class, 'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class, 'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class, 'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class, @@ -1361,11 +1337,6 @@ 'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class, 'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class, 'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class, - 'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class, - 'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class, - 'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class, - 'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class, - 'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class, 'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class, 'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class, 'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class, @@ -1404,7 +1375,6 @@ 'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class, 'encrypter' => \Illuminate\Encryption\Encrypter::class, 'events' => \Illuminate\Events\Dispatcher::class, - 'excel' => \Maatwebsite\Excel\Excel::class, 'filament' => \Filament\FilamentManager::class, 'files' => \Illuminate\Filesystem\Filesystem::class, 'filesystem' => \Illuminate\Filesystem\FilesystemManager::class, @@ -1441,6 +1411,7 @@ ])); override(\App::make(0), map([ '' => '@', + 'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class, 'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class, 'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class, 'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class, @@ -1599,11 +1570,6 @@ 'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class, 'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class, 'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class, - 'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class, - 'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class, - 'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class, - 'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class, - 'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class, 'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class, 'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class, 'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class, @@ -1642,7 +1608,6 @@ 'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class, 'encrypter' => \Illuminate\Encryption\Encrypter::class, 'events' => \Illuminate\Events\Dispatcher::class, - 'excel' => \Maatwebsite\Excel\Excel::class, 'filament' => \Filament\FilamentManager::class, 'files' => \Illuminate\Filesystem\Filesystem::class, 'filesystem' => \Illuminate\Filesystem\FilesystemManager::class, @@ -1679,6 +1644,7 @@ ])); override(\App::makeWith(0), map([ '' => '@', + 'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class, 'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class, 'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class, 'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class, @@ -1837,11 +1803,6 @@ 'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class, 'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class, 'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class, - 'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class, - 'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class, - 'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class, - 'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class, - 'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class, 'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class, 'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class, 'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class, @@ -1880,7 +1841,6 @@ 'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class, 'encrypter' => \Illuminate\Encryption\Encrypter::class, 'events' => \Illuminate\Events\Dispatcher::class, - 'excel' => \Maatwebsite\Excel\Excel::class, 'filament' => \Filament\FilamentManager::class, 'files' => \Illuminate\Filesystem\Filesystem::class, 'filesystem' => \Illuminate\Filesystem\FilesystemManager::class, @@ -1917,6 +1877,7 @@ ])); override(\app(0), map([ '' => '@', + 'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class, 'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class, 'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class, 'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class, @@ -2075,11 +2036,6 @@ 'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class, 'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class, 'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class, - 'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class, - 'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class, - 'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class, - 'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class, - 'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class, 'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class, 'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class, 'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class, @@ -2118,7 +2074,6 @@ 'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class, 'encrypter' => \Illuminate\Encryption\Encrypter::class, 'events' => \Illuminate\Events\Dispatcher::class, - 'excel' => \Maatwebsite\Excel\Excel::class, 'filament' => \Filament\FilamentManager::class, 'files' => \Illuminate\Filesystem\Filesystem::class, 'filesystem' => \Illuminate\Filesystem\FilesystemManager::class, @@ -2155,6 +2110,7 @@ ])); override(\resolve(0), map([ '' => '@', + 'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class, 'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class, 'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class, 'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class, @@ -2313,11 +2269,6 @@ 'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class, 'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class, 'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class, - 'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class, - 'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class, - 'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class, - 'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class, - 'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class, 'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class, 'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class, 'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class, @@ -2356,7 +2307,6 @@ 'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class, 'encrypter' => \Illuminate\Encryption\Encrypter::class, 'events' => \Illuminate\Events\Dispatcher::class, - 'excel' => \Maatwebsite\Excel\Excel::class, 'filament' => \Filament\FilamentManager::class, 'files' => \Illuminate\Filesystem\Filesystem::class, 'filesystem' => \Illuminate\Filesystem\FilesystemManager::class, @@ -2393,6 +2343,7 @@ ])); override(\Psr\Container\ContainerInterface::get(0), map([ '' => '@', + 'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class, 'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class, 'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class, 'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class, @@ -2551,11 +2502,6 @@ 'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class, 'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class, 'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class, - 'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class, - 'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class, - 'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class, - 'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class, - 'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class, 'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class, 'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class, 'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class, @@ -2594,7 +2540,6 @@ 'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class, 'encrypter' => \Illuminate\Encryption\Encrypter::class, 'events' => \Illuminate\Events\Dispatcher::class, - 'excel' => \Maatwebsite\Excel\Excel::class, 'filament' => \Filament\FilamentManager::class, 'files' => \Illuminate\Filesystem\Filesystem::class, 'filesystem' => \Illuminate\Filesystem\FilesystemManager::class, diff --git a/README.md b/README.md index 30c6af482..6d7fc6acc 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # Speedtest Tracker +> [!IMPORTANT] +> `v0.16.0` includes a breaking change that requires user action for existing installs. Read the [release](https://github.com/alexjustesen/speedtest-tracker/releases/tag/v0.16.0) notes regarding the data migration. + [![Star History Chart](https://api.star-history.com/svg?repos=alexjustesen/speedtest-tracker&type=Date)](https://star-history.com/#alexjustesen/speedtest-tracker&Date) ## Introduction diff --git a/_ide_helper.php b/_ide_helper.php index d61cb094a..783bb99bf 100644 --- a/_ide_helper.php +++ b/_ide_helper.php @@ -17898,231 +17898,6 @@ } } -namespace Maatwebsite\Excel\Facades { - /** - * - * - */ class Excel { - /** - * - * - * @param object $export - * @param string|null $fileName - * @param string $writerType - * @param array $headers - * @return \Symfony\Component\HttpFoundation\BinaryFileResponse - * @throws \PhpOffice\PhpSpreadsheet\Exception - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception - * @static - */ public static function download($export, $fileName, $writerType = null, $headers = []) - { - /** @var \Maatwebsite\Excel\Excel $instance */ - return $instance->download($export, $fileName, $writerType, $headers); - } - /** - * - * - * @param string|null $disk Fallback for usage with named properties - * @param object $export - * @param string $filePath - * @param string|null $diskName - * @param string $writerType - * @param mixed $diskOptions - * @return bool - * @throws \PhpOffice\PhpSpreadsheet\Exception - * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception - * @static - */ public static function store($export, $filePath, $diskName = null, $writerType = null, $diskOptions = [], $disk = null) - { - /** @var \Maatwebsite\Excel\Excel $instance */ - return $instance->store($export, $filePath, $diskName, $writerType, $diskOptions, $disk); - } - /** - * - * - * @param object $export - * @param string $filePath - * @param string|null $disk - * @param string $writerType - * @param mixed $diskOptions - * @return \Illuminate\Foundation\Bus\PendingDispatch - * @static - */ public static function queue($export, $filePath, $disk = null, $writerType = null, $diskOptions = []) - { - /** @var \Maatwebsite\Excel\Excel $instance */ - return $instance->queue($export, $filePath, $disk, $writerType, $diskOptions); - } - /** - * - * - * @param object $export - * @param string $writerType - * @return string - * @static - */ public static function raw($export, $writerType) - { - /** @var \Maatwebsite\Excel\Excel $instance */ - return $instance->raw($export, $writerType); - } - /** - * - * - * @param object $import - * @param string|\Symfony\Component\HttpFoundation\File\UploadedFile $filePath - * @param string|null $disk - * @param string|null $readerType - * @return \Maatwebsite\Excel\Reader|\Illuminate\Foundation\Bus\PendingDispatch - * @static - */ public static function import($import, $filePath, $disk = null, $readerType = null) - { - /** @var \Maatwebsite\Excel\Excel $instance */ - return $instance->import($import, $filePath, $disk, $readerType); - } - /** - * - * - * @param object $import - * @param string|\Symfony\Component\HttpFoundation\File\UploadedFile $filePath - * @param string|null $disk - * @param string|null $readerType - * @return array - * @static - */ public static function toArray($import, $filePath, $disk = null, $readerType = null) - { - /** @var \Maatwebsite\Excel\Excel $instance */ - return $instance->toArray($import, $filePath, $disk, $readerType); - } - /** - * - * - * @param object $import - * @param string|\Symfony\Component\HttpFoundation\File\UploadedFile $filePath - * @param string|null $disk - * @param string|null $readerType - * @return \Illuminate\Support\Collection - * @static - */ public static function toCollection($import, $filePath, $disk = null, $readerType = null) - { - /** @var \Maatwebsite\Excel\Excel $instance */ - return $instance->toCollection($import, $filePath, $disk, $readerType); - } - /** - * - * - * @param \Illuminate\Contracts\Queue\ShouldQueue $import - * @param string|\Symfony\Component\HttpFoundation\File\UploadedFile $filePath - * @param string|null $disk - * @param string $readerType - * @return \Illuminate\Foundation\Bus\PendingDispatch - * @static - */ public static function queueImport($import, $filePath, $disk = null, $readerType = null) - { - /** @var \Maatwebsite\Excel\Excel $instance */ - return $instance->queueImport($import, $filePath, $disk, $readerType); - } - /** - * - * - * @param string $concern - * @param callable $handler - * @param string $event - * @static - */ public static function extend($concern, $handler, $event = 'Maatwebsite\\Excel\\Events\\BeforeWriting') - { - return \Maatwebsite\Excel\Excel::extend($concern, $handler, $event); - } - /** - * When asserting downloaded, stored, queued or imported, use regular expression - * to look for a matching file path. - * - * @return void - * @static - */ public static function matchByRegex() - { - /** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */ - $instance->matchByRegex(); - } - /** - * When asserting downloaded, stored, queued or imported, use regular string - * comparison for matching file path. - * - * @return void - * @static - */ public static function doNotMatchByRegex() - { - /** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */ - $instance->doNotMatchByRegex(); - } - /** - * - * - * @param string $fileName - * @param callable|null $callback - * @static - */ public static function assertDownloaded($fileName, $callback = null) - { - /** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */ - return $instance->assertDownloaded($fileName, $callback); - } - /** - * - * - * @param string $filePath - * @param string|callable|null $disk - * @param callable|null $callback - * @static - */ public static function assertStored($filePath, $disk = null, $callback = null) - { - /** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */ - return $instance->assertStored($filePath, $disk, $callback); - } - /** - * - * - * @param string $filePath - * @param string|callable|null $disk - * @param callable|null $callback - * @static - */ public static function assertQueued($filePath, $disk = null, $callback = null) - { - /** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */ - return $instance->assertQueued($filePath, $disk, $callback); - } - /** - * - * - * @static - */ public static function assertQueuedWithChain($chain) - { - /** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */ - return $instance->assertQueuedWithChain($chain); - } - /** - * - * - * @param string $classname - * @param callable|null $callback - * @static - */ public static function assertExportedInRaw($classname, $callback = null) - { - /** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */ - return $instance->assertExportedInRaw($classname, $callback); - } - /** - * - * - * @param string $filePath - * @param string|callable|null $disk - * @param callable|null $callback - * @static - */ public static function assertImported($filePath, $disk = null, $callback = null) - { - /** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */ - return $instance->assertImported($filePath, $disk, $callback); - } - } - } - namespace Spatie\LaravelIgnition\Facades { /** * @@ -18495,41 +18270,6 @@ /** * * - * @template TKey of array-key - * @template-covariant TValue - * @implements \ArrayAccess - * @implements \Illuminate\Support\Enumerable - */ class Collection { - /** - * - * - * @see \Maatwebsite\Excel\Mixins\DownloadCollectionMixin::downloadExcel() - * @param string $fileName - * @param string|null $writerType - * @param mixed $withHeadings - * @param array $responseHeaders - * @static - */ public static function downloadExcel($fileName, $writerType = null, $withHeadings = false, $responseHeaders = []) - { - return \Illuminate\Support\Collection::downloadExcel($fileName, $writerType, $withHeadings, $responseHeaders); - } - /** - * - * - * @see \Maatwebsite\Excel\Mixins\StoreCollectionMixin::storeExcel() - * @param string $filePath - * @param string|null $disk - * @param string|null $writerType - * @param mixed $withHeadings - * @static - */ public static function storeExcel($filePath, $disk = null, $writerType = null, $withHeadings = false) - { - return \Illuminate\Support\Collection::storeExcel($filePath, $disk, $writerType, $withHeadings); - } - } - /** - * - * */ class Str { /** * @@ -23138,56 +22878,6 @@ class Eloquent extends \Illuminate\Database\Eloquent\Model { */ public static function getRelationWithoutConstraintsProxy($relation) { return \Illuminate\Database\Eloquent\Builder::getRelationWithoutConstraintsProxy($relation); - } - /** - * - * - * @see \Maatwebsite\Excel\Mixins\DownloadQueryMacro::__invoke() - * @param string $fileName - * @param string|null $writerType - * @param mixed $withHeadings - * @static - */ public static function downloadExcel($fileName, $writerType = null, $withHeadings = false) - { - return \Illuminate\Database\Eloquent\Builder::downloadExcel($fileName, $writerType, $withHeadings); - } - /** - * - * - * @see \Maatwebsite\Excel\Mixins\StoreQueryMacro::__invoke() - * @param string $filePath - * @param string|null $disk - * @param string|null $writerType - * @param mixed $withHeadings - * @static - */ public static function storeExcel($filePath, $disk = null, $writerType = null, $withHeadings = false) - { - return \Illuminate\Database\Eloquent\Builder::storeExcel($filePath, $disk, $writerType, $withHeadings); - } - /** - * - * - * @see \Maatwebsite\Excel\Mixins\ImportMacro::__invoke() - * @param string $filename - * @param string|null $disk - * @param string|null $readerType - * @static - */ public static function import($filename, $disk = null, $readerType = null) - { - return \Illuminate\Database\Eloquent\Builder::import($filename, $disk, $readerType); - } - /** - * - * - * @see \Maatwebsite\Excel\Mixins\ImportAsMacro::__invoke() - * @param string $filename - * @param callable $mapping - * @param string|null $disk - * @param string|null $readerType - * @static - */ public static function importAs($filename, $mapping, $disk = null, $readerType = null) - { - return \Illuminate\Database\Eloquent\Builder::importAs($filename, $mapping, $disk, $readerType); } /** * Set the columns to be selected. @@ -25186,7 +24876,6 @@ class EloquentSerialize extends \AnourValar\EloquentSerialize\Facades\EloquentSe class Livewire extends \Livewire\Livewire {} class Action extends \Lorisleiva\Actions\Facades\Actions {} class Lody extends \Lorisleiva\Lody\Lody {} - class Excel extends \Maatwebsite\Excel\Facades\Excel {} class Flare extends \Spatie\LaravelIgnition\Facades\Flare {} } diff --git a/app/Actions/MigrateBadJsonResults.php b/app/Actions/MigrateBadJsonResults.php new file mode 100644 index 000000000..9bb9f08df --- /dev/null +++ b/app/Actions/MigrateBadJsonResults.php @@ -0,0 +1,105 @@ +bad_json_migrated) { + Notification::make() + ->title('❌ Hmmm it seems someone has already migrated the data!') + ->body('Check your results table and make sure you\'re not triggering a duplicate data migration.') + ->danger() + ->sendToDatabase($user); + + return; + } + + if (! Schema::hasTable('results')) { + Notification::make() + ->title('❌ Could not migrate bad json results!') + ->body('The "results" table is missing.') + ->danger() + ->sendToDatabase($user); + + return; + } + + if (! Schema::hasTable($tableName)) { + Notification::make() + ->title('❌ Could not migrate bad json results!') + ->body('The "results_bad_json" table is missing.') + ->danger() + ->sendToDatabase($user); + + return; + } + + /** + * Copy backup data to the new results table and reformat it. + */ + try { + DB::table($tableName)->chunkById(100, function ($results) { + foreach ($results as $result) { + $record = [ + 'service' => 'ookla', + 'ping' => $result->ping, + 'download' => $result->download, + 'upload' => $result->upload, + 'comments' => $result->comments, + 'data' => json_decode($result->data), + 'status' => match ($result->successful) { + 1 => ResultStatus::Completed, + default => ResultStatus::Failed, + }, + 'scheduled' => $result->scheduled, + 'created_at' => $result->created_at, + 'updated_at' => now(), + ]; + + DB::table('results')->insert($record); + } + }); + } catch (\Throwable $e) { + Log::error($e); + + Notification::make() + ->title('There was an issue migrating the data!') + ->body('Check the logs for an output of the issue.') + ->danger() + ->sendToDatabase($user); + + return; + } + + $dataSettings->bad_json_migrated = true; + + $dataSettings->save(); + + Notification::make() + ->title('Data migration completed!') + ->body('Your history has been successfully migrated.') + ->success() + ->sendToDatabase($user); + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index c69e12152..e0b0e3aa2 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -5,6 +5,7 @@ use App\Console\Commands\RunOoklaSpeedtest; use App\Console\Commands\SystemMaintenance; use App\Console\Commands\VersionChecker; +use App\Models\Result; use App\Settings\GeneralSettings; use Cron\CronExpression; use Illuminate\Console\Scheduling\Schedule; @@ -19,6 +20,15 @@ protected function schedule(Schedule $schedule): void { $settings = new GeneralSettings(); + /** + * Checks if Result model records should be pruned. + */ + if ($settings->prune_results_older_than > 0) { + $schedule->command('model:prune', [ + '--model' => [Result::class], + ])->daily(); + } + /** * Perform system maintenance weekly on Sunday morning, * start off the week nice and fresh. diff --git a/app/Enums/ResultStatus.php b/app/Enums/ResultStatus.php new file mode 100644 index 000000000..448dd7237 --- /dev/null +++ b/app/Enums/ResultStatus.php @@ -0,0 +1,17 @@ +name; + } +} diff --git a/app/Exports/ResultsSelectedBulkExport.php b/app/Exports/ResultsSelectedBulkExport.php deleted file mode 100644 index 68b655322..000000000 --- a/app/Exports/ResultsSelectedBulkExport.php +++ /dev/null @@ -1,39 +0,0 @@ -results = $results; - } - - public function array(): array - { - return $this->results; - } - - public function headings(): array - { - return [ - 'id', - 'ping', - 'download', - 'upload', - 'server id', - 'server url', - 'server name', - 'result url', - 'scheduled', - 'successful', - 'data', - 'created at', - ]; - } -} diff --git a/app/Filament/Exports/ResultExporter.php b/app/Filament/Exports/ResultExporter.php new file mode 100644 index 000000000..d6116ce44 --- /dev/null +++ b/app/Filament/Exports/ResultExporter.php @@ -0,0 +1,90 @@ +label('ID'), + ExportColumn::make('ip_address') + ->label('IP address') + ->state(function (Result $record): ?string { + return $record->ip_address; + }) + ->enabledByDefault(false), + ExportColumn::make('service'), + ExportColumn::make('server_id') + ->label('Server ID') + ->state(function (Result $record): ?string { + return $record->server_id; + }) + ->enabledByDefault(false), + ExportColumn::make('server_name') + ->state(function (Result $record): ?string { + return $record->server_name; + }) + ->enabledByDefault(false), + + ExportColumn::make('download') + ->state(function (Result $record): ?string { + return $record->download_bits; + }), + ExportColumn::make('upload') + ->state(function (Result $record): ?string { + return $record->download_bits; + }), + ExportColumn::make('ping'), + ExportColumn::make('download_jitter') + ->state(function (Result $record): ?string { + return $record->download_jitter; + }), + ExportColumn::make('upload_jitter') + ->state(function (Result $record): ?string { + return $record->upload_jitter; + }), + ExportColumn::make('ping_jitter') + ->state(function (Result $record): ?string { + return $record->ping_jitter; + }), + ExportColumn::make('comments') + ->enabledByDefault(false), + // 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 + ExportColumn::make('scheduled') + ->state(function (Result $record): string { + return $record->scheduled ? 'Yes' : 'No'; + }), + ExportColumn::make('created_at'), + ExportColumn::make('updated_at') + ->enabledByDefault(false), + ]; + } + + public static function getCompletedNotificationBody(Export $export): string + { + $body = 'Your result export has completed and '.number_format($export->successful_rows).' '.str('row')->plural($export->successful_rows).' exported.'; + + if ($failedRowsCount = $export->getFailedRowsCount()) { + $body .= ' '.number_format($failedRowsCount).' '.str('row')->plural($failedRowsCount).' failed to export.'; + } + + return $body; + } +} diff --git a/app/Filament/Pages/Settings/GeneralPage.php b/app/Filament/Pages/Settings/GeneralPage.php index 5874b6a48..3ca1bae59 100644 --- a/app/Filament/Pages/Settings/GeneralPage.php +++ b/app/Filament/Pages/Settings/GeneralPage.php @@ -92,6 +92,14 @@ public function form(Form $form): Form ->hint(new HtmlString('🔗Cron Generator')) ->nullable() ->columnSpan(1), + Forms\Components\TextInput::make('prune_results_older_than') + ->helperText('Set to zero to disable pruning.') + ->suffix('days') + ->numeric() + ->required() + ->minValue(0) + ->maxValue(9999) + ->columnSpan(1), Forms\Components\Select::make('speedtest_server') ->label('Speedtest servers') ->helperText('Leave empty to let the system pick the best server.') @@ -102,7 +110,7 @@ public function form(Form $form): Form ->options(GetOoklaSpeedtestServers::run()) ->getSearchResultsUsing(fn (string $search): array => $this->getServerSearchOptions($search)) ->getOptionLabelsUsing(fn (array $values): array => $this->getServerLabels($values)) - ->columnSpan('full'), + ->columnSpanFull(), ]) ->compact() ->columns([ diff --git a/app/Filament/Resources/ResultResource.php b/app/Filament/Resources/ResultResource.php index e41059c9e..41e373e01 100644 --- a/app/Filament/Resources/ResultResource.php +++ b/app/Filament/Resources/ResultResource.php @@ -2,24 +2,28 @@ namespace App\Filament\Resources; -use App\Exports\ResultsSelectedBulkExport; +use App\Actions\MigrateBadJsonResults; +use App\Enums\ResultStatus; +use App\Filament\Exports\ResultExporter; use App\Filament\Resources\ResultResource\Pages; +use App\Helpers\Number; use App\Helpers\TimeZoneHelper; use App\Models\Result; +use App\Settings\DataMigrationSettings; use App\Settings\GeneralSettings; use Carbon\Carbon; use Filament\Forms; use Filament\Forms\Components\TextInput; use Filament\Forms\Form; +use Filament\Notifications\Notification; use Filament\Resources\Resource; +use Filament\Support\Enums\Alignment; use Filament\Tables; use Filament\Tables\Actions\Action; -use Filament\Tables\Columns\IconColumn; -use Filament\Tables\Columns\TextColumn; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; -use Illuminate\Database\Eloquent\Collection; -use Maatwebsite\Excel\Facades\Excel; +use Illuminate\Support\Facades\Auth; +use Illuminate\Support\HtmlString; class ResultResource extends Resource { @@ -52,17 +56,6 @@ public static function form(Form $form): Form $component->state(Carbon::parse($state)->format($settings->time_format ?? 'M j, Y G:i:s')); }) ->columnSpan(2), - Forms\Components\TextInput::make('server_id') - ->label('Server ID'), - Forms\Components\TextInput::make('server_name') - ->label('Server name') - ->columnSpan(2), - Forms\Components\TextInput::make('server_host') - ->label('Server host') - ->columnSpan([ - 'default' => 2, - 'md' => 3, - ]), Forms\Components\TextInput::make('download') ->label('Download (Mbps)') ->afterStateHydrated(function (TextInput $component, $state) { @@ -75,11 +68,31 @@ public static function form(Form $form): Form }), Forms\Components\TextInput::make('ping') ->label('Ping (Ms)'), + Forms\Components\TextInput::make('data.download.latency.jitter') + ->label('Download Jitter (Ms)'), + Forms\Components\TextInput::make('data.upload.latency.jitter') + ->label('Upload Jitter (Ms)'), + Forms\Components\TextInput::make('data.ping.jitter') + ->label('Ping Jitter (Ms)'), + Forms\Components\Textarea::make('data.message') + ->label('Error Message') + ->hint(new HtmlString('🔗Error Messages')) + ->hidden(fn (Result $record): bool => $record->status !== ResultStatus::Failed) + ->columnSpanFull(), ]) ->columnSpan(2), Forms\Components\Section::make() ->schema([ - Forms\Components\Checkbox::make('successful'), + Forms\Components\Placeholder::make('service') + ->content(fn (Result $result): string => $result->service), + Forms\Components\Placeholder::make('server_name') + ->content(fn (Result $result): ?string => $result->server_name), + Forms\Components\Placeholder::make('server_id') + ->label('Server ID') + ->content(fn (Result $result): ?string => $result->server_id), + Forms\Components\Placeholder::make('server_host') + ->label('Server ID') + ->content(fn (Result $result): ?string => $result->server_id), Forms\Components\Checkbox::make('scheduled'), ]) ->columns(1) @@ -88,65 +101,95 @@ public static function form(Form $form): Form 'md' => 1, ]), ]), - Forms\Components\Textarea::make('data') - ->rows(10) - ->columnSpan(2), ]); } public static function table(Table $table): Table { + $dataSettings = new DataMigrationSettings(); + $settings = new GeneralSettings(); return $table ->columns([ - TextColumn::make('id') + Tables\Columns\TextColumn::make('id') ->label('ID') ->sortable(), - TextColumn::make('server') - ->getStateUsing(fn (Result $record): ?string => ! blank($record->server_id) ? $record->server_id.' ('.$record->server_name.')' : null) + Tables\Columns\TextColumn::make('ip_address') + ->label('IP address') ->toggleable() + ->toggledHiddenByDefault() ->sortable(), - IconColumn::make('successful') - ->boolean() - ->toggleable(), - IconColumn::make('scheduled') - ->boolean() - ->toggleable(), - TextColumn::make('download') - ->label('Download (Mbps)') - ->getStateUsing(fn (Result $record): ?string => ! blank($record->download) ? toBits(convertSize($record->download), 2) : null) + Tables\Columns\TextColumn::make('service') + ->toggleable() + ->toggledHiddenByDefault() ->sortable(), - TextColumn::make('upload') - ->label('Upload (Mbps)') - ->getStateUsing(fn (Result $record): ?string => ! blank($record->upload) ? toBits(convertSize($record->upload), 2) : null) + Tables\Columns\TextColumn::make('server_id') + ->label('Server ID') + ->toggleable() ->sortable(), - TextColumn::make('ping') - ->label('Ping (Ms)') + Tables\Columns\TextColumn::make('server_name') ->toggleable() ->sortable(), - TextColumn::make('download_jitter') - ->getStateUsing(fn (Result $record): ?string => json_decode($record->data, true)['download']['latency']['jitter'] ?? null) + Tables\Columns\TextColumn::make('download') + ->getStateUsing(fn (Result $record): ?string => ! blank($record->download) ? Number::fileSizeBits(bits: $record->download, precision: 2, perSecond: true) : null) + ->sortable(), + Tables\Columns\TextColumn::make('upload') + ->getStateUsing(fn (Result $record): ?string => ! blank($record->upload) ? Number::fileSizeBits(bits: $record->upload, precision: 2, perSecond: true) : null) + ->sortable(), + Tables\Columns\TextColumn::make('ping') + ->toggleable() + ->sortable(), + Tables\Columns\TextColumn::make('download_jitter') ->toggleable() ->toggledHiddenByDefault() ->sortable(), - TextColumn::make('upload_jitter') - ->getStateUsing(fn (Result $record): ?string => json_decode($record->data, true)['upload']['latency']['jitter'] ?? null) + Tables\Columns\TextColumn::make('upload_jitter') ->toggleable() ->toggledHiddenByDefault() ->sortable(), - TextColumn::make('ping_jitter') - ->getStateUsing(fn (Result $record): ?string => json_decode($record->data, true)['ping']['jitter'] ?? null) + Tables\Columns\TextColumn::make('ping_jitter') ->toggleable() ->toggledHiddenByDefault() ->sortable(), - TextColumn::make('created_at') - ->label('Created') + Tables\Columns\TextColumn::make('status') + ->toggleable() + ->sortable(), + Tables\Columns\IconColumn::make('scheduled') + ->boolean() + ->toggleable() + ->toggledHiddenByDefault() + ->alignment(Alignment::Center), + Tables\Columns\TextColumn::make('created_at') ->dateTime($settings->time_format ?? 'M j, Y G:i:s') ->timezone(TimeZoneHelper::displayTimeZone($settings)) - ->sortable(), + ->toggleable() + ->sortable() + ->alignment(Alignment::End), + Tables\Columns\TextColumn::make('updated_at') + ->dateTime($settings->time_format ?? 'M j, Y G:i:s') + ->timezone(TimeZoneHelper::displayTimeZone($settings)) + ->toggleable() + ->toggledHiddenByDefault() + ->sortable() + ->alignment(Alignment::End), ]) ->filters([ + Tables\Filters\SelectFilter::make('ip_address') + ->label('IP address') + ->multiple() + ->options(function (): array { + return Result::query() + ->select('data->interface->externalIp AS public_ip_address') + ->where('status', '=', ResultStatus::Completed) + ->distinct() + ->get() + ->mapWithKeys(function (Result $item, int $key) { + return [$item['public_ip_address'] => $item['public_ip_address']]; + }) + ->toArray(); + }) + ->attribute('data->interface->externalIp'), Tables\Filters\TernaryFilter::make('scheduled') ->placeholder('-') ->trueLabel('Only scheduled speedtests') @@ -156,23 +199,17 @@ public static function table(Table $table): Table false: fn (Builder $query) => $query->where('scheduled', false), blank: fn (Builder $query) => $query, ), - Tables\Filters\TernaryFilter::make('successful') - ->placeholder('-') - ->trueLabel('Only successful speedtests') - ->falseLabel('Only failed speedtests') - ->queries( - true: fn (Builder $query) => $query->where('successful', true), - false: fn (Builder $query) => $query->where('successful', false), - blank: fn (Builder $query) => $query, - ), + Tables\Filters\SelectFilter::make('status') + ->multiple() + ->options(ResultStatus::class), ]) ->actions([ Tables\Actions\ActionGroup::make([ Action::make('view result') ->label('View on Speedtest.net') ->icon('heroicon-o-link') - ->url(fn (Result $record): ?string => $record?->url) - ->hidden(fn (Result $record): bool => ! $record->is_successful) + ->url(fn (Result $record): ?string => $record->result_url) + ->hidden(fn (Result $record): bool => $record->status !== ResultStatus::Completed) ->openUrlInNewTab(), Tables\Actions\ViewAction::make(), Tables\Actions\Action::make('updateComments') @@ -195,25 +232,31 @@ public static function table(Table $table): Table ]), ]) ->bulkActions([ - Tables\Actions\BulkAction::make('export') - ->label('Export selected') - ->icon('heroicon-o-arrow-down-tray') - ->hidden(fn (): bool => ! auth()->user()->is_admin) - ->action(function (Collection $records) { - $export = new ResultsSelectedBulkExport($records->toArray()); - - return Excel::download($export, 'results_'.now()->timestamp.'.csv', \Maatwebsite\Excel\Excel::CSV); - }), Tables\Actions\DeleteBulkAction::make(), ]) - ->defaultSort('created_at', 'desc'); - } + ->headerActions([ + Tables\Actions\ExportAction::make() + ->exporter(ResultExporter::class) + ->fileName(fn (): string => 'results-'.now()->timestamp), + Tables\Actions\Action::make('migrate') + ->action(function (): void { + Notification::make() + ->title('Starting data migration...') + ->body('This can take a little bit depending how much data you have.') + ->warning() + ->sendToDatabase(Auth::user()); - public static function getRelations(): array - { - return [ - // - ]; + MigrateBadJsonResults::dispatch(Auth::user()); + }) + ->hidden($dataSettings->bad_json_migrated) + ->requiresConfirmation() + ->modalHeading('Migrate History') + ->modalDescription(new HtmlString('

v0.16.0 archived the old "results" table, to migrate your history click the button below.

For more information read the docs.

')) + ->modalSubmitActionLabel('Yes, migrate it'), + ]) + ->defaultSort('created_at', 'desc') + ->paginated([5, 15, 25, 50, 100]) + ->defaultPaginationPageOption(15); } public static function getPages(): array diff --git a/app/Filament/Widgets/RecentJitterChartWidget.php b/app/Filament/Widgets/RecentJitterChartWidget.php index f7d9d2bad..b52fce9b3 100644 --- a/app/Filament/Widgets/RecentJitterChartWidget.php +++ b/app/Filament/Widgets/RecentJitterChartWidget.php @@ -2,6 +2,7 @@ namespace App\Filament\Widgets; +use App\Enums\ResultStatus; use App\Helpers\TimeZoneHelper; use App\Models\Result; use App\Settings\GeneralSettings; @@ -37,6 +38,7 @@ protected function getData(): array $results = Result::query() ->select(['data', 'created_at']) + ->where('status', '=', ResultStatus::Completed) ->when($this->filter == '24h', function ($query) { $query->where('created_at', '>=', now()->subDay()); }) @@ -52,7 +54,7 @@ protected function getData(): array 'datasets' => [ [ 'label' => 'Download (ms)', - 'data' => $results->map(fn ($item) => $item->getJitterData()['download'] ? number_format($item->getJitterData()['download'], 2) : 0), + 'data' => $results->map(fn ($item) => $item->download_jitter ? number_format($item->download_jitter, 2) : 0), 'borderColor' => '#0ea5e9', 'backgroundColor' => '#0ea5e9', 'fill' => false, @@ -61,7 +63,7 @@ protected function getData(): array ], [ 'label' => 'Upload (ms)', - 'data' => $results->map(fn ($item) => $item->getJitterData()['upload'] ? number_format($item->getJitterData()['upload'], 2) : 0), + 'data' => $results->map(fn ($item) => $item->upload_jitter ? number_format($item->upload_jitter, 2) : 0), 'borderColor' => '#8b5cf6', 'backgroundColor' => '#8b5cf6', 'fill' => false, @@ -70,7 +72,7 @@ protected function getData(): array ], [ 'label' => 'Ping (ms)', - 'data' => $results->map(fn ($item) => $item->getJitterData()['ping'] ? number_format($item->getJitterData()['ping'], 2) : 0), + 'data' => $results->map(fn ($item) => $item->ping_jitter ? number_format($item->ping_jitter, 2) : 0), 'borderColor' => '#10b981', 'backgroundColor' => '#10b981', 'fill' => false, diff --git a/app/Filament/Widgets/RecentPingChartWidget.php b/app/Filament/Widgets/RecentPingChartWidget.php index 873f1f9f8..a50199cff 100644 --- a/app/Filament/Widgets/RecentPingChartWidget.php +++ b/app/Filament/Widgets/RecentPingChartWidget.php @@ -2,6 +2,7 @@ namespace App\Filament\Widgets; +use App\Enums\ResultStatus; use App\Helpers\TimeZoneHelper; use App\Models\Result; use App\Settings\GeneralSettings; @@ -37,6 +38,7 @@ protected function getData(): array $results = Result::query() ->select(['ping', 'created_at']) + ->where('status', '=', ResultStatus::Completed) ->when($this->filter == '24h', function ($query) { $query->where('created_at', '>=', now()->subDay()); }) diff --git a/app/Filament/Widgets/RecentSpeedChartWidget.php b/app/Filament/Widgets/RecentSpeedChartWidget.php index 9be0032e5..b02156c94 100644 --- a/app/Filament/Widgets/RecentSpeedChartWidget.php +++ b/app/Filament/Widgets/RecentSpeedChartWidget.php @@ -2,6 +2,7 @@ namespace App\Filament\Widgets; +use App\Enums\ResultStatus; use App\Helpers\TimeZoneHelper; use App\Models\Result; use App\Settings\GeneralSettings; @@ -37,6 +38,7 @@ protected function getData(): array $results = Result::query() ->select(['id', 'download', 'upload', 'created_at']) + ->where('status', '=', ResultStatus::Completed) ->when($this->filter == '24h', function ($query) { $query->where('created_at', '>=', now()->subDay()); }) diff --git a/app/Filament/Widgets/StatsOverviewWidget.php b/app/Filament/Widgets/StatsOverviewWidget.php index 1237d6c82..0d0f43174 100644 --- a/app/Filament/Widgets/StatsOverviewWidget.php +++ b/app/Filament/Widgets/StatsOverviewWidget.php @@ -2,6 +2,8 @@ namespace App\Filament\Widgets; +use App\Enums\ResultStatus; +use App\Helpers\Number; use App\Models\Result; use Filament\Widgets\StatsOverviewWidget as BaseWidget; use Filament\Widgets\StatsOverviewWidget\Stat; @@ -18,10 +20,12 @@ protected function getPollingInterval(): ?string protected function getCards(): array { $this->result = Result::query() + ->select(['id', 'ping', 'download', 'upload', 'status', 'created_at']) + ->where('status', '=', ResultStatus::Completed) ->latest() ->first(); - if (blank($this->result) || ! $this->result->successful) { + if (blank($this->result)) { return [ Stat::make('Latest download', '-') ->icon('heroicon-o-arrow-down-tray'), @@ -33,17 +37,19 @@ protected function getCards(): array } $previous = Result::query() + ->select(['id', 'ping', 'download', 'upload', 'status', 'created_at']) ->where('id', '<', $this->result->id) + ->where('status', '=', ResultStatus::Completed) ->latest() ->first(); - if (! $previous || ! $previous->successful) { + if (! $previous) { return [ - Stat::make('Latest download', fn (): string => ! blank($this->result) ? toBits(convertSize($this->result->download), 2).' (Mbps)' : 'n/a') + Stat::make('Latest download', fn (): string => ! blank($this->result) ? Number::fileSizeBits(bits: $this->result->download_bits, precision: 2, perSecond: true) : 'n/a') ->icon('heroicon-o-arrow-down-tray'), - Stat::make('Latest upload', fn (): string => ! blank($this->result) ? toBits(convertSize($this->result->upload), 2).' (Mbps)' : 'n/a') + Stat::make('Latest upload', fn (): string => ! blank($this->result) ? Number::fileSizeBits(bits: $this->result->upload_bits, precision: 2, perSecond: true) : 'n/a') ->icon('heroicon-o-arrow-up-tray'), - Stat::make('Latest ping', fn (): string => ! blank($this->result) ? number_format($this->result->ping, 2).' (ms)' : 'n/a') + Stat::make('Latest ping', fn (): string => ! blank($this->result) ? number_format($this->result->ping, 2).' Ms' : 'n/a') ->icon('heroicon-o-clock'), ]; } @@ -53,17 +59,17 @@ protected function getCards(): array $pingChange = percentChange($this->result->ping, $previous->ping, 2); return [ - Stat::make('Latest download', fn (): string => ! blank($this->result) ? toBits(convertSize($this->result->download), 2).' (Mbps)' : 'n/a') + Stat::make('Latest download', fn (): string => ! blank($this->result) ? Number::fileSizeBits(bits: $this->result->download_bits, precision: 2, perSecond: true) : 'n/a') ->icon('heroicon-o-arrow-down-tray') ->description($downloadChange > 0 ? $downloadChange.'% faster' : abs($downloadChange).'% slower') ->descriptionIcon($downloadChange > 0 ? 'heroicon-m-arrow-trending-up' : 'heroicon-m-arrow-trending-down') ->color($downloadChange > 0 ? 'success' : 'danger'), - Stat::make('Latest upload', fn (): string => ! blank($this->result) ? toBits(convertSize($this->result->upload), 2).' (Mbps)' : 'n/a') + Stat::make('Latest upload', fn (): string => ! blank($this->result) ? Number::fileSizeBits(bits: $this->result->upload_bits, precision: 2, perSecond: true) : 'n/a') ->icon('heroicon-o-arrow-up-tray') ->description($uploadChange > 0 ? $uploadChange.'% faster' : abs($uploadChange).'% slower') ->descriptionIcon($uploadChange > 0 ? 'heroicon-m-arrow-trending-up' : 'heroicon-m-arrow-trending-down') ->color($uploadChange > 0 ? 'success' : 'danger'), - Stat::make('Latest ping', fn (): string => ! blank($this->result) ? number_format($this->result->ping, 2).' (ms)' : 'n/a') + Stat::make('Latest ping', fn (): string => ! blank($this->result) ? number_format($this->result->ping, 2).' Ms' : 'n/a') ->icon('heroicon-o-clock') ->description($pingChange > 0 ? $pingChange.'% slower' : abs($pingChange).'% faster') ->descriptionIcon($pingChange > 0 ? 'heroicon-m-arrow-trending-up' : 'heroicon-m-arrow-trending-down') diff --git a/app/Http/Controllers/API/Speedtest/GetLatestController.php b/app/Http/Controllers/API/Speedtest/GetLatestController.php index 9ff4bc454..7cd5d6be3 100644 --- a/app/Http/Controllers/API/Speedtest/GetLatestController.php +++ b/app/Http/Controllers/API/Speedtest/GetLatestController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\API\Speedtest; +use App\Enums\ResultStatus; use App\Http\Controllers\Controller; use App\Models\Result; use Illuminate\Http\JsonResponse; @@ -14,6 +15,7 @@ class GetLatestController extends Controller public function __invoke(): JsonResponse { $latest = Result::query() + ->whereIn('status', [ResultStatus::Completed, ResultStatus::Failed]) ->latest() ->first(); @@ -28,16 +30,16 @@ public function __invoke(): JsonResponse 'data' => [ 'id' => $latest->id, 'ping' => $latest->ping, - 'download' => ! blank($latest->download) ? toBits(convertSize($latest->download)) : null, - 'upload' => ! blank($latest->upload) ? toBits(convertSize($latest->upload)) : null, + 'download' => $latest->download_bits, + 'upload' => $latest->upload_bits, 'server_id' => $latest->server_id, 'server_host' => $latest->server_host, 'server_name' => $latest->server_name, - 'url' => $latest->url, + 'url' => $latest->result_url, 'scheduled' => $latest->scheduled, - 'failed' => ! $latest->successful, + 'failed' => $latest->status === ResultStatus::Failed, 'created_at' => $latest->created_at->toISOString(true), - 'updated_at' => $latest->created_at->toISOString(true), // faking updated at to match legacy api payload + 'updated_at' => $latest->updated_at->toISOString(true), ], ]); } diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index c00b2efb1..7ea5aa13f 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers; +use App\Enums\ResultStatus; use App\Models\Result; use App\Settings\GeneralSettings; use Illuminate\Http\Request; @@ -20,7 +21,8 @@ public function __invoke(Request $request) } $latestResult = Result::query() - ->select(['id', 'ping', 'download', 'upload', 'successful', 'created_at']) + ->select(['id', 'ping', 'download', 'upload', 'status', 'created_at']) + ->where('status', '=', ResultStatus::Completed) ->latest() ->first(); diff --git a/app/Jobs/ExecSpeedtest.php b/app/Jobs/ExecSpeedtest.php index dd76f2032..75ddca52b 100644 --- a/app/Jobs/ExecSpeedtest.php +++ b/app/Jobs/ExecSpeedtest.php @@ -2,12 +2,14 @@ namespace App\Jobs; +use App\Enums\ResultStatus; use App\Models\Result; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Arr; use Illuminate\Support\Facades\Log; use Symfony\Component\Process\Exception\ProcessFailedException; use Symfony\Component\Process\Process; @@ -50,28 +52,26 @@ public function handle(): void $message = collect(array_filter($messages, 'json_validate'))->last(); Result::create([ + 'service' => 'ookla', + 'data' => json_decode($message, true), + 'status' => ResultStatus::Failed, 'scheduled' => $this->scheduled, - 'successful' => false, - 'data' => $message, ]); return; } try { - $output = $process->getOutput(); - $results = json_decode($output, true); + $results = json_decode($process->getOutput(), true); Result::create([ - 'ping' => $results['ping']['latency'], - 'download' => $results['download']['bandwidth'], - 'upload' => $results['upload']['bandwidth'], - 'server_id' => $results['server']['id'], - 'server_name' => $results['server']['name'], - 'server_host' => $results['server']['host'].':'.$results['server']['port'], - 'url' => $results['result']['url'], + 'service' => 'ookla', + 'ping' => Arr::get($results, 'ping.latency'), + 'download' => Arr::get($results, 'download.bandwidth'), + 'upload' => Arr::get($results, 'upload.bandwidth'), + 'data' => $results, + 'status' => ResultStatus::Completed, 'scheduled' => $this->scheduled, - 'data' => $output, ]); } catch (\Exception $e) { Log::error($e->getMessage()); diff --git a/app/Models/Result.php b/app/Models/Result.php index 684c7d815..f84a72839 100644 --- a/app/Models/Result.php +++ b/app/Models/Result.php @@ -2,41 +2,26 @@ namespace App\Models; +use App\Enums\ResultStatus; use App\Events\ResultCreated; +use App\Settings\GeneralSettings; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Prunable; use Illuminate\Support\Arr; class Result extends Model { - use HasFactory; + use HasFactory, Prunable; /** - * Indicates if the model should be timestamped. - * - * @var bool - */ - public $timestamps = false; - - /** - * The attributes that are mass assignable. + * The attributes that aren't mass assignable. * * @var array */ - protected $fillable = [ - 'ping', - 'download', - 'upload', - 'server_id', - 'server_host', - 'server_name', - 'url', - 'comments', - 'scheduled', - 'successful', - 'data', - ]; + protected $guarded = []; /** * The attributes that should be cast. @@ -44,10 +29,9 @@ class Result extends Model * @var array */ protected $casts = [ - 'scheduled' => 'boolean', - 'successful' => 'boolean', 'data' => 'array', - 'created_at' => 'datetime', + 'status' => ResultStatus::class, + 'scheduled' => 'boolean', ]; /** @@ -76,36 +60,33 @@ public function formatTagsForInfluxDB2(): array */ public function formatForInfluxDB2() { - $data = json_decode($this->data, true); - return [ 'id' => $this->id, 'ping' => $this?->ping, 'download' => $this?->download, 'upload' => $this?->upload, - 'download_bits' => $this->download ? $this->download * 8 : null, - 'upload_bits' => $this->upload ? $this->upload * 8 : null, - 'ping_jitter' => Arr::get($data, 'ping.jitter'), - 'download_jitter' => Arr::get($data, 'download.latency.jitter'), - 'upload_jitter' => Arr::get($data, 'upload.latency.jitter'), + 'download_bits' => $this->download_bits, + 'upload_bits' => $this->upload_bits, + 'ping_jitter' => $this->ping_jitter, + 'download_jitter' => $this->download_jitter, + 'upload_jitter' => $this->upload_jitter, 'server_id' => $this?->server_id, 'server_host' => $this?->server_host, 'server_name' => $this?->server_name, 'scheduled' => $this->scheduled, - 'successful' => $this->successful, - 'packet_loss' => (float) Arr::get($data, 'packetLoss', 0), + 'successful' => $this->status === ResultStatus::Completed, + 'packet_loss' => (float) $this->packet_loss, ]; } - public function getJitterData(): array + /** + * Get the prunable model query. + */ + public function prunable(): Builder { - $data = json_decode($this->data, true); + $settings = new GeneralSettings(); - return [ - 'download' => Arr::get($data, 'download.latency.jitter'), - 'upload' => Arr::get($data, 'upload.latency.jitter'), - 'ping' => Arr::get($data, 'ping.jitter'), - ]; + return static::where('created_at', '<=', now()->subDays($settings->prune_results_older_than)); } /** @@ -114,21 +95,131 @@ public function getJitterData(): array protected function downloadBits(): Attribute { return Attribute::make( - get: fn (mixed $value): ?string => ! blank($this->download) && is_numeric($this->download) + get: fn (): ?string => ! blank($this->download) && is_numeric($this->download) ? number_format(num: $this->download * 8, decimals: 0, thousands_separator: '') : null, ); } + /** + * Get the result's download jitter in milliseconds. + */ + protected function downloadJitter(): Attribute + { + return Attribute::make( + get: fn () => Arr::get($this->data, 'download.latency.jitter'), + ); + } + + /** + * Get the result's download jitter in milliseconds. + */ + protected function errorMessage(): Attribute + { + return Attribute::make( + get: fn () => Arr::get($this->data, 'message', ''), + ); + } + + /** + * Get the result's external ip address (yours). + */ + protected function ipAddress(): Attribute + { + return Attribute::make( + get: fn () => Arr::get($this->data, 'interface.externalIp'), + ); + } + + /** + * Get the result's isp tied to the external (yours) ip address. + */ + protected function isp(): Attribute + { + return Attribute::make( + get: fn () => Arr::get($this->data, 'isp'), + ); + } + + /** + * Get the result's packet loss as a percentage. + */ + protected function packetLoss(): Attribute + { + return Attribute::make( + get: fn () => Arr::get($this->data, 'packetLoss'), + ); + } + + /** + * Get the result's ping jitter in milliseconds. + */ + protected function pingJitter(): Attribute + { + return Attribute::make( + get: fn () => Arr::get($this->data, 'ping.jitter'), + ); + } + + /** + * Get the result's server ID. + */ + protected function resultUrl(): Attribute + { + return Attribute::make( + get: fn () => Arr::get($this->data, 'result.url'), + ); + } + + /** + * Get the result's server host. + */ + protected function serverHost(): Attribute + { + return Attribute::make( + get: fn () => Arr::get($this->data, 'server.host'), + ); + } + + /** + * Get the result's server ID. + */ + protected function serverId(): Attribute + { + return Attribute::make( + get: fn () => Arr::get($this->data, 'server.id'), + ); + } + + /** + * Get the result's server name. + */ + protected function serverName(): Attribute + { + return Attribute::make( + get: fn () => Arr::get($this->data, 'server.name'), + ); + } + /** * Get the result's upload in bits. */ protected function uploadBits(): Attribute { return Attribute::make( - get: fn (mixed $value): ?string => ! blank($this->upload) && is_numeric($this->upload) + get: fn (): ?string => ! blank($this->upload) && is_numeric($this->upload) ? number_format(num: $this->upload * 8, decimals: 0, thousands_separator: '') : null, ); } + + /** + * Get the result's upload jitter in milliseconds. + */ + protected function uploadJitter(): Attribute + { + return Attribute::make( + get: fn () => Arr::get($this->data, 'upload.latency.jitter'), + ); + } } diff --git a/app/Settings/DataMigrationSettings.php b/app/Settings/DataMigrationSettings.php new file mode 100644 index 000000000..8823f7cbd --- /dev/null +++ b/app/Settings/DataMigrationSettings.php @@ -0,0 +1,15 @@ +id(); + $table->string('service')->default('ookla'); + $table->float('ping', 8, 3)->nullable(); + $table->unsignedBigInteger('download')->nullable(); + $table->unsignedBigInteger('upload')->nullable(); + $table->text('comments')->nullable(); + $table->json('data')->nullable(); + $table->string('status'); + $table->boolean('scheduled')->default(false); + $table->timestamps(); + }); + } + + /** + * Don't disable the schedule or send a notification if there are no records. + */ + if (! DB::table('results_bad_json')->count()) { + $dataSettings = new DataMigrationSettings(); + + $dataSettings->bad_json_migrated = true; + + $dataSettings->save(); + + return; + } + + $settings = new GeneralSettings(); + + $settings->speedtest_schedule = ''; + + $settings->save(); + + $admins = User::select(['id', 'name', 'email', 'role']) + ->where('role', 'admin') + ->get(); + + foreach ($admins as $user) { + Notification::make() + ->title('Breaking change, user action required!') + ->body('v0.16.0 includes a breaking change to resolve a data quality issue. Read the release notes regarding the data migration.') + ->danger() + ->actions([ + Action::make('Release notes') + ->button() + ->url('https://github.com/alexjustesen/speedtest-tracker/releases/tag/v0.16.0') + ->openUrlInNewTab(), + ]) + ->sendToDatabase($user); + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::drop('results'); + + if (! Schema::hasTable('results')) { + Schema::rename('results_bad_json', 'results'); + } + } +}; diff --git a/database/migrations/2024_02_19_134641_create_job_batches_table.php b/database/migrations/2024_02_19_134641_create_job_batches_table.php new file mode 100644 index 000000000..50e38c20f --- /dev/null +++ b/database/migrations/2024_02_19_134641_create_job_batches_table.php @@ -0,0 +1,35 @@ +string('id')->primary(); + $table->string('name'); + $table->integer('total_jobs'); + $table->integer('pending_jobs'); + $table->integer('failed_jobs'); + $table->longText('failed_job_ids'); + $table->mediumText('options')->nullable(); + $table->integer('cancelled_at')->nullable(); + $table->integer('created_at'); + $table->integer('finished_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('job_batches'); + } +}; diff --git a/database/migrations/2024_02_19_134706_create_imports_table.php b/database/migrations/2024_02_19_134706_create_imports_table.php new file mode 100644 index 000000000..34c5fc5b5 --- /dev/null +++ b/database/migrations/2024_02_19_134706_create_imports_table.php @@ -0,0 +1,35 @@ +id(); + $table->timestamp('completed_at')->nullable(); + $table->string('file_name'); + $table->string('file_path'); + $table->string('importer'); + $table->unsignedInteger('processed_rows')->default(0); + $table->unsignedInteger('total_rows'); + $table->unsignedInteger('successful_rows')->default(0); + $table->foreignId('user_id')->constrained()->cascadeOnDelete(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('imports'); + } +}; diff --git a/database/migrations/2024_02_19_134707_create_exports_table.php b/database/migrations/2024_02_19_134707_create_exports_table.php new file mode 100644 index 000000000..91624d270 --- /dev/null +++ b/database/migrations/2024_02_19_134707_create_exports_table.php @@ -0,0 +1,35 @@ +id(); + $table->timestamp('completed_at')->nullable(); + $table->string('file_disk'); + $table->string('file_name')->nullable(); + $table->string('exporter'); + $table->unsignedInteger('processed_rows')->default(0); + $table->unsignedInteger('total_rows'); + $table->unsignedInteger('successful_rows')->default(0); + $table->foreignId('user_id')->constrained()->cascadeOnDelete(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('exports'); + } +}; diff --git a/database/migrations/2024_02_19_134708_create_failed_import_rows_table.php b/database/migrations/2024_02_19_134708_create_failed_import_rows_table.php new file mode 100644 index 000000000..eae8cb2e2 --- /dev/null +++ b/database/migrations/2024_02_19_134708_create_failed_import_rows_table.php @@ -0,0 +1,30 @@ +id(); + $table->json('data'); + $table->foreignId('import_id')->constrained()->cascadeOnDelete(); + $table->text('validation_error')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('failed_import_rows'); + } +}; diff --git a/database/settings/2024_02_18_000000_create_data_migration_settings.php b/database/settings/2024_02_18_000000_create_data_migration_settings.php new file mode 100644 index 000000000..0b0b06e1c --- /dev/null +++ b/database/settings/2024_02_18_000000_create_data_migration_settings.php @@ -0,0 +1,11 @@ +migrator->add('data_migration.bad_json_migrated', false); + } +}; diff --git a/database/settings/2024_02_19_022251_add_prune_results_to_general_settings.php b/database/settings/2024_02_19_022251_add_prune_results_to_general_settings.php new file mode 100644 index 000000000..e7bf5e819 --- /dev/null +++ b/database/settings/2024_02_19_022251_add_prune_results_to_general_settings.php @@ -0,0 +1,11 @@ +migrator->add('general.prune_results_older_than', 0); + } +};