statistic on separate page wip

This commit is contained in:
salam committed 2026-04-28 16:50:23 +02:00
1 parent ad36ba555f
commit 422681df01
34 files changed
+660 -734

No files matched your search

@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Clusters\StaticData\Resources\StatisticFixtures\Pages;
use App\Filament\Clusters\StaticData\Resources\StatisticFixtures\StatisticFixturesResource;
use Filament\Resources\Pages\CreateRecord;
class CreateStatisticFixtures extends CreateRecord
{
protected static string $resource = StatisticFixturesResource::class;
}
@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Clusters\StaticData\Resources\StatisticFixtures\Pages;
use App\Filament\Clusters\StaticData\Resources\StatisticFixtures\StatisticFixturesResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditStatisticFixtures extends EditRecord
{
protected static string $resource = StatisticFixturesResource::class;
protected function getHeaderActions(): array
{
return [
DeleteAction::make(),
];
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Filament\Clusters\StaticData\Resources\StatisticFixtures\Pages;
use App\Filament\Clusters\StaticData\Resources\StatisticFixtures\StatisticFixturesResource;
use App\Models\StatistikoEvent;
use Filament\Actions\Action;
use Filament\Resources\Pages\ListRecords;
class ListStatisticFixtures extends ListRecords
{
protected static string $resource = StatisticFixturesResource::class;
protected function getHeaderActions(): array
{
return [
Action::make('delete_played')
->icon('phosphor-trash')
->requiresConfirmation()
->color('danger')
->action(function () {
StatistikoEvent::where('starttime', '<', now()->subHour())->delete();
}),
];
}
}
@@ -0,0 +1,18 @@
<?php
namespace App\Filament\Clusters\StaticData\Resources\StatisticFixtures\Schemas;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema;
class StatisticFixturesForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('exp_home')->required(),
TextInput::make('exp_away')->required(),
]);
}
}
@@ -0,0 +1,50 @@
<?php
namespace App\Filament\Clusters\StaticData\Resources\StatisticFixtures;
use App\Filament\Clusters\StaticData\Resources\StatisticFixtures\Pages\ListStatisticFixtures;
use App\Filament\Clusters\StaticData\Resources\StatisticFixtures\Schemas\StatisticFixturesForm;
use App\Filament\Clusters\StaticData\Resources\StatisticFixtures\Tables\StatisticFixturesTable;
use App\Filament\Clusters\StaticData\StaticDataCluster;
use App\Models\StatisticFixtures;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables\Table;
use UnitEnum;
class StatisticFixturesResource extends Resource
{
protected static ?string $model = StatisticFixtures::class;
protected static ?string $navigationLabel = 'Fixtures';
protected static ?string $modelLabel = 'Fixtures';
protected static string|UnitEnum|null $navigationGroup = 'Statistiko';
protected static ?int $navigationSort = 2;
protected static ?string $cluster = StaticDataCluster::class;
public static function form(Schema $schema): Schema
{
return StatisticFixturesForm::configure($schema);
}
public static function table(Table $table): Table
{
return StatisticFixturesTable::configure($table);
}
public static function getPages(): array
{
return [
'index' => ListStatisticFixtures::route('/'),
];
}
public static function getNavigationBadge(): ?string
{
return parent::getEloquentQuery()->count();
}
}
@@ -0,0 +1,69 @@
<?php
namespace App\Filament\Clusters\StaticData\Resources\StatisticFixtures\Tables;
use App\Models\CompetitionGroup;
use App\Models\StatisticFixtures;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteAction;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Enums\FiltersLayout;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;
class StatisticFixturesTable
{
public static function configure(Table $table): Table
{
return $table
->columns([
IconColumn::make('sport_id')
->label('')
->icon(fn (string $state): string => match ($state) {
'1' => 'icon-soccerball',
'2' => 'icon-icehockey',
'3' => 'icon-basketball',
'5' => 'icon-tennisball',
}),
TextColumn::make('league.name')
->label('League')
->searchable()
->wrap()
->hidden(fn () => auth()->user()->sport_id == 5)
->description(fn ($record) => ucwords($record->league?->country->name)),
TextColumn::make('starttime')->dateTime('M j, Y')->label('Date'),
TextColumn::make('home.name')->color('primary')->searchable(),
TextColumn::make('away.name')->color('success')->searchable(),
// TextColumn::make('exp_home')->alignCenter()->numeric(),
// TextColumn::make('exp_away')->alignCenter()->numeric(),
TextColumn::make('updated_at')->alignCenter()->date(),
])
->filters([
SelectFilter::make('league_id')
->options(function () {
$res = StatisticFixtures::where('sport_id', 1)
->groupBy('league_id')
->pluck('league_id')->toArray();
return CompetitionGroup::query()->whereIn('id', $res)->orderBy('name')->pluck('name', 'id');
})
->preload()
->searchable()
->columnSpan(2)
->label('League'),
], layout: FiltersLayout::AboveContent)
->deferFilters(false)
->recordActions([
EditAction::make()->iconButton(),
DeleteAction::make()->iconButton(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
]);
}
}
@@ -3,7 +3,6 @@
namespace App\Filament\Clusters\StaticData\Resources\StatistikoEvents\Pages;
use App\Filament\Clusters\StaticData\Resources\StatistikoEvents\StatistikoEventsResource;
// use App\Filament\Imports\StatistikoEventImporter;
use App\Models\StatistikoEvent;
use Filament\Actions\Action;
use Filament\Actions\CreateAction;
@@ -27,14 +26,6 @@ class ListStatistikoEvents extends ListRecords
CreateAction::make()
->label('New')
->icon('phosphor-plus'),
// ImportAction::make('importEvents')
// ->maxRows(200000)
// ->color('success')
// ->chunkSize(250)
// ->icon('phosphor-upload')
// ->label('Import')
// ->modalDescription('Max 20k rows per import, column separator: ";"')
// ->importer(StatistikoEventImporter::class),
];
}
}
@@ -47,9 +47,4 @@ class StatistikoEventsResource extends Resource
{
return parent::getEloquentQuery()->count();
}
public static function getNavigationBadgeColor(): ?string
{
return 'success';
}
}
@@ -2,7 +2,6 @@
namespace App\Filament\Clusters\StaticData\Resources\StatistikoEvents\Tables;
use App\Models\Sport;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
@@ -20,10 +20,12 @@ class StatistikoImportForm
->options(Sport::where('is_active', 1)->pluck('name', 'id')),
Select::make('data_type')
->required()
->allowHtml()
->options([
'player' => 'Player',
'events' => 'Events',
'events_player' => 'Events & Player',
'player' => '<span class="text-blue-500">Player</span>',
'events' => '<span class="text-green-500">Events</span>',
'fixtures' => '<span class="text-orange-500">Fixtures</span>',
'events_player' => '<span class="text-pink-600">Events & Player</span>',
]),
FileUpload::make('file_name')
@@ -34,11 +34,11 @@ class StatistikoImportsTable
->options([
'player' => '<span class="text-blue-500">Player</span>',
'events' => '<span class="text-green-500">Events</span>',
'fixtures' => '<span class="text-orange-500">Fixtures</span>',
'events_player' => '<span class="text-amber-500">Events & Player</span>',
]),
TextColumn::make('file_name_original')
->searchable()
->color('primary')
->label('Name'),
IconColumn::make('has_run')
->label('Import')
@@ -3,11 +3,9 @@
namespace App\Filament\Clusters\StaticData\Resources\StatistikoPlayers\Pages;
use App\Filament\Clusters\StaticData\Resources\StatistikoPlayers\StatistikoPlayerResource;
// use App\Filament\Imports\StatistikoPlayerImporter;
use App\Models\StatistikoPlayer;
use Filament\Actions\Action;
use Filament\Actions\CreateAction;
// use Filament\Actions\ImportAction;
use Filament\Resources\Pages\ListRecords;
class ListStatistikoPlayers extends ListRecords
@@ -27,14 +25,6 @@ class ListStatistikoPlayers extends ListRecords
CreateAction::make()
->label('New')
->icon('phosphor-plus'),
// ImportAction::make('importPlayer')
// ->maxRows(200000)
// ->color('success')
// ->chunkSize(250)
// ->icon('phosphor-upload')
// ->label('Import')
// ->modalDescription('Max 20k rows per import, column separator ";"')
// ->importer(StatistikoPlayerImporter::class),
];
}
}
@@ -24,7 +24,7 @@ class StatistikoPlayerResource extends Resource
protected static ?string $cluster = StaticDataCluster::class;
protected static ?int $navigationSort = 2;
protected static ?int $navigationSort = 4;
protected static ?string $recordTitleAttribute = 'giocatore';
@@ -49,9 +49,4 @@ class StatistikoPlayerResource extends Resource
{
return parent::getEloquentQuery()->count();
}
public static function getNavigationBadgeColor(): ?string
{
return 'success';
}
}
@@ -1,11 +0,0 @@
<?php
namespace App\Filament\Clusters\Statistic\Resources\StatisticEvents\Pages;
use App\Filament\Clusters\Statistic\Resources\StatisticEvents\StatisticEventsResource;
use Filament\Resources\Pages\CreateRecord;
class CreateStatisticEvents extends CreateRecord
{
protected static string $resource = StatisticEventsResource::class;
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Clusters\Statistic\Resources\StatisticEvents\Pages;
use App\Filament\Clusters\Statistic\Resources\StatisticEvents\StatisticEventsResource;
// use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditStatisticEvents extends EditRecord
{
protected static string $resource = StatisticEventsResource::class;
// protected function getHeaderActions(): array
// {
// return [
// DeleteAction::make(),
// ];
// }
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Clusters\Statistic\Resources\StatisticEvents\Pages;
use App\Filament\Clusters\Statistic\Resources\StatisticEvents\StatisticEventsResource;
// use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListStatisticEvents extends ListRecords
{
protected static string $resource = StatisticEventsResource::class;
// protected function getHeaderActions(): array
// {
// return [
// CreateAction::make(),
// ];
// }
}
@@ -1,16 +0,0 @@
<?php
namespace App\Filament\Clusters\Statistic\Resources\StatisticEvents\Schemas;
use Filament\Schemas\Schema;
class StatisticEventsForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
//
]);
}
}
@@ -1,47 +0,0 @@
<?php
namespace App\Filament\Clusters\Statistic\Resources\StatisticEvents;
use App\Filament\Clusters\Statistic\Resources\StatisticEvents\Pages\ListStatisticEvents;
use App\Filament\Clusters\Statistic\Resources\StatisticEvents\Schemas\StatisticEventsForm;
use App\Filament\Clusters\Statistic\Resources\StatisticEvents\Tables\StatisticEventsTable;
use App\Filament\Clusters\Statistic\StatisticCluster;
use App\Models\StatistikoEvent;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Tables\Table;
class StatisticEventsResource extends Resource
{
protected static ?string $model = StatistikoEvent::class;
protected static string|BackedEnum|null $navigationIcon = 'phosphor-chart-line-up';
protected static ?string $navigationLabel = 'Statistic Events';
protected static ?string $modelLabel = 'Statistic Events';
protected static ?int $navigationSort = 1;
protected static string|\UnitEnum|null $navigationGroup = 'Events';
protected static ?string $cluster = StatisticCluster::class;
public static function form(Schema $schema): Schema
{
return StatisticEventsForm::configure($schema);
}
public static function table(Table $table): Table
{
return StatisticEventsTable::configure($table);
}
public static function getPages(): array
{
return [
'index' => ListStatisticEvents::route('/'),
];
}
}
@@ -1,189 +0,0 @@
<?php
namespace App\Filament\Clusters\Statistic\Resources\StatisticEvents\Tables;
use App\Models\Country;
use App\Models\Event;
use App\Models\Sport;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\Select;
use Filament\Schemas\Components\Utilities\Get;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Enums\FiltersLayout;
use Filament\Tables\Filters\Filter;
use Filament\Tables\Filters\Indicator;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
use Illuminate\Support\HtmlString;
class StatisticEventsTable
{
public static function configure(Table $table): Table
{
return $table
->striped()
->columns([
IconColumn::make('sport_id')
->label('')
->icon(fn (string $state): string => match ($state) {
'1' => 'icon-soccerball',
'2' => 'icon-icehockey',
'3' => 'icon-basketball',
'5' => 'icon-tennisball',
}),
TextColumn::make('league.name')->searchable(),
TextColumn::make('starttime')->dateTime('M j, Y')->label('Date'),
TextColumn::make('home.name')->color('primary')->searchable(),
TextColumn::make('away.name')->color('success')->searchable(),
])
->filters([
Filter::make('events')
->columns(5)->columnSpan(5)
->schema([
Select::make('sport_id')
->default(1)
->hiddenLabel()
->options(Sport::whereIn('id', [1, 2, 3, 5])->pluck('name', 'id')),
Select::make(name: 'country_id')
->hidden(fn (Get $get) => $get('sport_id') == 5)
->placeholder('Country')
->prefixIcon('phosphor-flag')
->hiddenLabel()
->searchable()
->options(function (Get $get) {
$res = Event::where('has_markets', 1)
->where('sport_id', $get('sport_id'))
->groupBy('country_id')
->pluck('country_id')->toArray();
return Country::query()->whereIn('id', $res)->orderBy('name')->pluck('name', 'id');
})
->live()
->reactive()
->afterStateUpdated(function (callable $set) {
$set('competition_group_id', null);
}),
Select::make('competition_group_id')
->placeholder(fn (Get $get) => $get('sport_id') == 5 ? 'Tournament' : 'League')
->prefixIcon('phosphor-trophy')
->hiddenLabel()
// ->multiple()
->searchable()
->options(function (Get $get) {
$country_id = $get('country_id') ?? 0;
$res = Event::with('country')
->with('competition_group')
->where('sport_id', $get('sport_id'))
->when($country_id, function ($query, $country_id) {
$query->where('country_id', $country_id);
})->groupBy('competition_group_id')->get();
$arr = [];
foreach ($res as $val) {
$arr[(int) $val->competition_group_id] = $val->country?->name.' '.$val->competition_group?->name;
}
asort($arr);
return $arr;
})
->live(),
DatePicker::make('starttime_from')
->prefix(fn () => new HtmlString('<string class="text-info-500">From</string>'))
->displayFormat('d M Y')
->hiddenLabel()
->label(''),
DatePicker::make('starttime_to')
->prefix(fn () => new HtmlString('<string class="text-success-500">To:</string>'))
->displayFormat('d M Y')
->hiddenLabel()
->label(''),
])
->query(function (Builder $query, array $data): Builder {
return $query->when(
$data['sport_id'],
function (Builder $query, $sport_id) {
$query->where('sport_id', (int) $sport_id);
}
)->when(
$data['competition_group_id'],
function (Builder $query, $competition_group_id) {
// dd($competition_group_id);
$query->where('league_id', (int) $competition_group_id);
}
)->when(
$data['starttime_from'],
function (Builder $query, $starttime_from) {
if (isset($starttime_from)) {
$from = Carbon::createFromDate($starttime_from);
return $query->where('starttime', '>=', $starttime_from);
}
}
)->when(
$data['starttime_to'],
function (Builder $query, $starttime_to) {
if (isset($starttime_to)) {
$to = Carbon::createFromDate($starttime_to);
return $query->where('starttime', '<=', $starttime_to);
}
}
);
})->indicateUsing(function (array $data): array {
$indicators = [];
if ($data['starttime_from'] ?? null) {
$starttime_from = $data['starttime_from'];
if ($starttime_from ?? null) {
$text = Carbon::parse($starttime_from)->format('d. M. Y');
$indicators[] = Indicator::make('From: '.$text)->removeField('starttime_from')->color('info');
}
}
if ($data['starttime_to'] ?? null) {
$starttime_to = $data['starttime_to'];
if ($starttime_to ?? null) {
$text = Carbon::parse($starttime_to)->format('d. M. Y');
$indicators[] = Indicator::make('To: '.$text)->removeField('starttime_to')->color('success');
}
}
if ($data['country_id'] ?? null) {
$country_id = $data['country_id'];
if ($country_id ?? null) {
$text = Country::where('id', $country_id)->value('name');
$indicators[] = Indicator::make('Country: '.$text)->removeField('country_id');
}
}
// if ($data['competition_group_id'] ?? null) {
// $competition_groups = CompetitionGroup::whereIn('id', $data['competition_group_id'])->pluck('name')->toArray();
// if (count($competition_groups) > 1) {
// $indicators[] = Indicator::make(implode(', ', $competition_groups))->removeField('competition_group_id')->color('info');
// } else {
// $indicators[] = Indicator::make($competition_groups[0])->removeField('competition_group_id')->color('info')->color('info');
// }
// }
return $indicators;
}),
], layout: FiltersLayout::AboveContent)
->deferFilters(false)
->hiddenFilterIndicators(false)
->persistFiltersInSession()
->recordActions([
EditAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
]);
}
}
@@ -1,16 +0,0 @@
<?php
namespace App\Filament\Clusters\Statistic;
use BackedEnum;
use Filament\Clusters\Cluster;
use Filament\Pages\Enums\SubNavigationPosition;
class StatisticCluster extends Cluster
{
protected static string|BackedEnum|null $navigationIcon = 'phosphor-chart-line-up';
protected static ?SubNavigationPosition $subNavigationPosition = SubNavigationPosition::Top;
protected static ?int $navigationSort = 2;
}
@@ -0,0 +1,57 @@
<?php
namespace App\Filament\Imports\Helper;
use Filament\Notifications\Notification;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\HtmlString;
class ImportBenchmark
{
public static float $benchmarkStartTime;
public static int $benchmarkStartMemory;
public static int $startRowCount;
public static int $startQueries;
public static function start(string $table): void
{
self::$startRowCount = DB::connection('mongodb')->table($table)->count();
self::$benchmarkStartTime = microtime(true);
self::$benchmarkStartMemory = memory_get_usage();
DB::connection('mongodb')->enableQueryLog();
}
public static function end(string $table): void
{
$executionTime = microtime(true) - self::$benchmarkStartTime;
$memoryUsage = round((memory_get_usage() - self::$benchmarkStartMemory) / 1024 / 1024, 2);
$logs = DB::connection('mongodb')->getQueryLog();
$queriesCount = \count($logs);
// Get row count after we've stopped tracking queries
$rowDiff = DB::connection('mongodb')->table($table)->count() - self::$startRowCount;
$formattedTime = match (true) {
$executionTime >= 60 => sprintf('%dm %ds', floor($executionTime / 60), $executionTime % 60),
$executionTime >= 1 => round($executionTime, 2).'s',
default => round($executionTime * 1000).'ms',
};
$str = '⚡ TIME: '.$formattedTime;
$str .= '<br/>MEM: '.$memoryUsage.'MB';
$str .= '<br/>SQL: '.number_format($queriesCount);
$str .= '<br/>ROWS: '.number_format($rowDiff);
$recipient = auth()->user();
Notification::make()
->success()
->title('Import compleated')
->body(new HtmlString($str))
->sendToDatabase($recipient);
}
}
@@ -0,0 +1,59 @@
<?php
namespace App\Filament\Imports\Helper;
use App\Models\StatistikoEvent;
use Illuminate\Support\Carbon;
use Illuminate\Support\LazyCollection;
use Spatie\SimpleExcel\SimpleExcelReader;
class ImportPlayer
{
public static function importEvents(string $filePath, int $sport_id): void
{
$now = now()->format('Y-m-d H:i:s');
SimpleExcelReader::create(storage_path('app/public/'.$filePath))
->getRows()
->chunk(1000)
->each(function (LazyCollection $chunk) use ($now, $sport_id) {
foreach ($chunk as $row) {
$row = array_values($row);
StatistikoEvent::where('sport_id', $sport_id)
->where('league_id', $row[0])
->where('home_id', $row[1])
->where('away_id', $row[2])
->where('starttime', Carbon::parse($row[3])->toIso8601String())
->update(
[
'season' => $row[4],
'gls_h' => $row[5],
'gls_a' => $row[6],
'crn_h' => $row[7],
'crn_a' => $row[8],
'fls_h' => $row[9],
'fls_a' => $row[10],
'ofs_h' => $row[11],
'ofs_a' => $row[12],
'snt_h' => $row[13],
'snt_a' => $row[14],
'tsh_h' => $row[15],
'tsh_a' => $row[16],
'crd_h' => $row[17],
'crd_a' => $row[18],
'expgoalshome_value' => $row[19],
'expgoalsaway_value' => $row[20],
'exp_handicap' => $row[21],
'exp_total' => $row[22],
'created_at' => $now,
'updated_at' => $now,
],
['upsert' => true],
);
}
});
}
}
@@ -0,0 +1,45 @@
<?php
namespace App\Filament\Imports\Helper;
use App\Models\StatisticFixtures;
use Illuminate\Support\Carbon;
use Illuminate\Support\LazyCollection;
use Spatie\SimpleExcel\SimpleExcelReader;
class ImportFixtures
{
public static function run(string $filePath, $sport_id): void
{
SimpleExcelReader::create(storage_path('app/public/'.$filePath))
->getRows()
->chunk(200)
->each(function (LazyCollection $chunk) use ($sport_id) {
foreach ($chunk as $row) {
$row = array_values($row);
/*
0 => 87
1 => 3614
2 => 1491
3 => DateTimeImmutable @1777334400 {#2722 ▶}
4 => 0.90433699503783
5 => 1.035168989838
*/
StatisticFixtures::updateOrCreate(
[
'sport_id' => (int) $sport_id,
'league_id' => (int) $row[0],
'home_id' => (int) $row[1],
'away_id' => (int) $row[2],
'starttime' => Carbon::parse($row[3])->toDateString(),
],
['exp_home' => (float) $row[4], 'exp_away' => (float) $row[5]]
);
}
});
}
}
@@ -0,0 +1,52 @@
<?php
namespace App\Filament\Imports\Helper;
use App\Models\StatistikoPlayer;
use Illuminate\Support\Carbon;
use Illuminate\Support\LazyCollection;
use Spatie\SimpleExcel\SimpleExcelReader;
class ImportPlayer
{
public static function run(string $filePath): void
{
$now = now()->format('Y-m-d H:i:s');
SimpleExcelReader::create(storage_path('app/public/'.$filePath))
->getRows()
->chunk(1000)
->each(function (LazyCollection $chunk) use ($now) {
foreach ($chunk as $row) {
$row = array_values($row);
StatistikoPlayer::where('player_id', $row[0])
->update(
[
'name' => $row[1],
'birthdate' => Carbon::parse($row[2])->toDateString(),
'height' => $row[3],
'nationality' => $row[4],
'position' => $row[5],
'sport_radar_id' => $row[6],
'id_squadra_tm' => $row[7],
'full_name' => $row[8],
'no_specialities' => $row[9],
'rigorist' => $row[10],
'national_team_id' => $row[11],
'last_update' => Carbon::parse($row[12])->toDateString(),
'league_code' => $row[13],
'team_code' => $row[14],
'team_name' => $row[15],
'created_at' => $now,
'updated_at' => $now,
],
['upsert' => true],
);
}
});
}
}
@@ -1,104 +0,0 @@
<?php
namespace App\Filament\Imports;
use App\Models\StatistikoEvent;
use Filament\Actions\Imports\ImportColumn;
use Filament\Actions\Imports\Importer;
use Filament\Actions\Imports\Models\Import;
use Illuminate\Support\Carbon;
use Illuminate\Support\Number;
class StatistikoEventImporter extends Importer
{
protected static ?string $model = StatistikoEvent::class;
public function resolveRecord(): StatistikoEvent
{
$res = StatistikoEvent::query()
->where('sport_id', $this->data['sport_id'])
->where('league_id', $this->data['league_id'])
->where('home_id', $this->data['home_id'])
->where('away_id', $this->data['away_id'])
->where('starttime', $this->data['starttime'])
->first();
if ($res) {
return StatistikoEvent::find($res->id);
} else {
return new StatistikoEvent;
}
}
public static function getColumns(): array
{
return [
ImportColumn::make('sport_id')->numeric()->guess(['SPORT']),
ImportColumn::make('league_id')->numeric()->guess(['LEAGUE']),
ImportColumn::make('home_id')->numeric()->guess(['HOME']),
ImportColumn::make('away_id')->numeric()->guess(['AWAY']),
ImportColumn::make('starttime')
->guess(['STARTTIME'])
->castStateUsing(function ($state) {
return Carbon::parse($state);
}),
ImportColumn::make('season')->guess(['SEASON']),
ImportColumn::make('gls_h')->numeric(),
ImportColumn::make('gls_a')->numeric(),
ImportColumn::make('crn_h')->numeric(),
ImportColumn::make('fls_h')->numeric(),
ImportColumn::make('ofs_h')->numeric(),
ImportColumn::make('snt_h')->numeric(),
ImportColumn::make('tsh_h')->numeric(),
ImportColumn::make('crd_h')->numeric(),
ImportColumn::make('crn_a')->numeric(),
ImportColumn::make('fls_a')->numeric(),
ImportColumn::make('ofs_a')->numeric(),
ImportColumn::make('snt_a')->numeric(),
ImportColumn::make('tsh_a')->numeric(),
ImportColumn::make('crd_a')->numeric(),
ImportColumn::make('expgoalshome_value')
->castStateUsing(function (string $state) {
if (blank($state)) {
return null;
}
return (float) str_replace(',', '.', $state);
}),
ImportColumn::make('expgoalsaway_value')
->castStateUsing(function (string $state) {
if (blank($state)) {
return null;
}
return str_replace(',', '.', $state);
}),
ImportColumn::make('exp_handicap')
->castStateUsing(function (string $state) {
if (blank($state)) {
return null;
}
return str_replace(',', '.', $state);
}),
ImportColumn::make('exp_total')
->castStateUsing(function (string $state) {
if (blank($state)) {
return null;
}
return str_replace(',', '.', $state);
}),
];
}
public static function getCompletedNotificationBody(Import $import): string
{
$body = 'Your events import has completed and '.Number::format($import->successful_rows).' '.str('row')->plural($import->successful_rows).' imported.';
if ($failedRowsCount = $import->getFailedRowsCount()) {
$body .= ' '.Number::format($failedRowsCount).' '.str('row')->plural($failedRowsCount).' failed to import.';
}
return $body;
}
}
+11 -140
View File
@@ -2,15 +2,11 @@
namespace App\Filament\Imports;
use App\Models\StatistikoEvent;
use App\Filament\Imports\Helper\ImportBenchmark;
use App\Filament\Imports\Helper\ImportEvents;
use App\Filament\Imports\Helper\ImportFixtures;
use App\Filament\Imports\Helper\ImportPlayer;
use App\Models\StatistikoImport;
use App\Models\StatistikoPlayer;
use Filament\Notifications\Notification;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\HtmlString;
use Illuminate\Support\LazyCollection;
use Spatie\SimpleExcel\SimpleExcelReader;
class StatistikoImporter
{
@@ -33,14 +29,17 @@ class StatistikoImporter
$filePath = $record['file_name'];
$sport_id = $record['sport_id'];
self::startBenchmark($table);
ImportBenchmark::start($table);
switch ($record['data_type']) {
case 'player':
self::importPlayer($filePath);
ImportPlayer::run($filePath);
break;
case 'events':
self::importEvents($filePath, $sport_id);
ImportEvents::run($filePath, $sport_id);
break;
case 'fixtures':
ImportFixtures::run($filePath, $sport_id);
break;
case 'events_player':
break;
@@ -48,134 +47,6 @@ class StatistikoImporter
StatistikoImport::where('id', $record->id)->update(['has_run' => 1]);
self::endBenchmark($table);
}
protected static function startBenchmark(string $table): void
{
self::$startRowCount = DB::connection('mongodb')->table($table)->count();
self::$benchmarkStartTime = microtime(true);
self::$benchmarkStartMemory = memory_get_usage();
DB::connection('mongodb')->enableQueryLog();
}
protected static function endBenchmark(string $table): void
{
$executionTime = microtime(true) - self::$benchmarkStartTime;
$memoryUsage = round((memory_get_usage() - self::$benchmarkStartMemory) / 1024 / 1024, 2);
$logs = DB::connection('mongodb')->getQueryLog();
$queriesCount = \count($logs);
// Get row count after we've stopped tracking queries
$rowDiff = DB::connection('mongodb')->table($table)->count() - self::$startRowCount;
$formattedTime = match (true) {
$executionTime >= 60 => sprintf('%dm %ds', floor($executionTime / 60), $executionTime % 60),
$executionTime >= 1 => round($executionTime, 2).'s',
default => round($executionTime * 1000).'ms',
};
$str = '⚡ TIME: '.$formattedTime;
$str .= '<br/>MEM: '.$memoryUsage.'MB';
$str .= '<br/>SQL: '.number_format($queriesCount);
$str .= '<br/>ROWS: '.number_format($rowDiff);
$recipient = auth()->user();
Notification::make()
->success()
->title('Import compleated')
->body(new HtmlString($str))
->sendToDatabase($recipient);
}
private static function importPlayer(string $filePath): void
{
$now = now()->format('Y-m-d H:i:s');
SimpleExcelReader::create(storage_path('app/public/'.$filePath))
->getRows()
->chunk(1000)
->each(function (LazyCollection $chunk) use ($now) {
foreach ($chunk as $row) {
$row = array_values($row);
StatistikoPlayer::where('player_id', $row[0])
->update(
[
'name' => $row[1],
'birthdate' => Carbon::parse($row[2])->toDateString(),
'height' => $row[3],
'nationality' => $row[4],
'position' => $row[5],
'sport_radar_id' => $row[6],
'id_squadra_tm' => $row[7],
'full_name' => $row[8],
'no_specialities' => $row[9],
'rigorist' => $row[10],
'national_team_id' => $row[11],
'last_update' => Carbon::parse($row[12])->toDateString(),
'league_code' => $row[13],
'team_code' => $row[14],
'team_name' => $row[15],
'created_at' => $now,
'updated_at' => $now,
],
['upsert' => true],
);
}
});
}
private static function importEvents(string $filePath, int $sport_id): void
{
$now = now()->format('Y-m-d H:i:s');
SimpleExcelReader::create(storage_path('app/public/'.$filePath))
->getRows()
->chunk(1000)
->each(function (LazyCollection $chunk) use ($now, $sport_id) {
foreach ($chunk as $row) {
$row = array_values($row);
StatistikoEvent::where('sport_id', $sport_id)
->where('league_id', $row[0])
->where('home_id', $row[1])
->where('away_id', $row[2])
->where('starttime', Carbon::parse($row[3])->toIso8601String())
->update(
[
'season' => $row[4],
'gls_h' => $row[5],
'gls_a' => $row[6],
'crn_h' => $row[7],
'crn_a' => $row[8],
'fls_h' => $row[9],
'fls_a' => $row[10],
'ofs_h' => $row[11],
'ofs_a' => $row[12],
'snt_h' => $row[13],
'snt_a' => $row[14],
'tsh_h' => $row[15],
'tsh_a' => $row[16],
'crd_h' => $row[17],
'crd_a' => $row[18],
'expgoalshome_value' => $row[19],
'expgoalsaway_value' => $row[20],
'exp_handicap' => $row[21],
'exp_total' => $row[22],
'created_at' => $now,
'updated_at' => $now,
],
['upsert' => true],
);
}
});
ImportBenchmark::end($table);
}
}
@@ -1,130 +0,0 @@
<?php
namespace App\Filament\Imports;
use App\Models\StatistikoPlayer;
use Filament\Actions\Imports\ImportColumn;
use Filament\Actions\Imports\Importer;
use Filament\Actions\Imports\Models\Import;
use Illuminate\Support\Carbon;
use Illuminate\Support\Number;
class StatistikoPlayerImporter extends Importer
{
protected static ?string $model = StatistikoPlayer::class;
public static function useDatabaseTransactions(): bool
{
return false;
}
public function resolveRecord(): StatistikoPlayer
{
$res = StatistikoPlayer::query()
->where('id_giocatore', $this->data['id_giocatore'])
->first();
if ($res) {
return StatistikoPlayer::find($res->id);
} else {
return new StatistikoPlayer;
}
}
public static function getColumns(): array
{
return [
ImportColumn::make('id_giocatore')
->label('ID GIOCATORE'),
ImportColumn::make('giocatore')
->label('GIOCATORE')
->castStateUsing(function (string $state): ?string {
if (blank($state)) {
return null;
}
return (string) trim($state);
}),
ImportColumn::make('data_di_nascita')
->label('DATA DI NASCITA')
->castStateUsing(function ($state) {
if (blank($state)) {
return null;
}
return Carbon::parse($state);
}),
ImportColumn::make('altezza')
->label('ALTEZZA')
->numeric(),
ImportColumn::make('nazionalita')
->label('NAZIONALITA')
->castStateUsing(function (string $state): ?string {
return (string) trim($state);
}),
ImportColumn::make('posizione')
->label('POSIZIONE')
->castStateUsing(function (string $state): ?string {
if (blank($state)) {
return null;
}
return (string) trim($state);
}),
ImportColumn::make('sport_radar_id')
->label('SPORT RADAR ID'),
ImportColumn::make('id_squadra_tm')
->label('ID SQUADRA TM'),
ImportColumn::make('nome_completo')
->label('NOME COMPLETO')
->castStateUsing(function (?string $state): ?string {
if (blank($state)) {
return null;
}
return (string) trim($state);
}),
ImportColumn::make('rigorista')
->label('RIGORISTA')
->castStateUsing(function (?string $state): ?string {
if (blank($state)) {
return null;
}
return (string) trim($state);
}),
ImportColumn::make('national_team_id')
->label('NATIONAL TEAM ID'),
ImportColumn::make('data_aggiornamento')
->label('DATA AGGIORNAMENTO')
->castStateUsing(function ($state) {
return Carbon::parse($state);
}),
ImportColumn::make('league_code')
->label('LEAGUE CODE')
->castStateUsing(function (string $state): ?string {
return (string) trim($state);
}),
ImportColumn::make('team_code')
->label('TEAM CODE')
->castStateUsing(function (string $state): ?string {
return (string) trim($state);
}),
ImportColumn::make('team_name')
->label('TEAM NAME')
->castStateUsing(function (string $state): ?string {
return (string) trim($state);
}),
];
}
public static function getCompletedNotificationBody(Import $import): string
{
$body = 'Your player import has completed and '.Number::format($import->successful_rows).' '.str('row')->plural($import->successful_rows).' imported.';
if ($failedRowsCount = $import->getFailedRowsCount()) {
$body .= ' '.Number::format($failedRowsCount).' '.str('row')->plural($failedRowsCount).' failed to import.';
}
return $body;
}
}
+127
View File
@@ -0,0 +1,127 @@
<?php
namespace App\Filament\Pages;
use App\Models\CompetitionGroup;
use App\Models\Element;
use App\Models\StatisticFixtures;
use BackedEnum;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Slider;
use Filament\Pages\Dashboard as BaseDashboard;
use Filament\Pages\Dashboard\Concerns\HasFiltersForm;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Utilities\Get;
use Filament\Schemas\Schema;
class Statistic extends BaseDashboard
{
use HasFiltersForm;
protected static string|BackedEnum|null $navigationIcon = 'phosphor-chart-line-up';
protected static ?string $title = 'Statistic';
protected int|string|array $columnSpan = 'full';
public function filtersForm(Schema $schema): Schema
{
return $schema
->components([
Section::make()
->schema([
Select::make('league_id')
->prefixIcon('phosphor-trophy')
->options(function () {
$res = StatisticFixtures::where('sport_id', 1)
->groupBy('league_id')
->pluck('league_id')->toArray();
return CompetitionGroup::query()->whereIn('id', $res)->orderBy('name')->pluck('name', 'id');
})
->preload()
->searchable()
->live()
->label('League'),
Select::make('team_id')
->prefixIcon('icon-team')
->options(function (Get $get) {
if ($get('league_id')) {
$res = StatisticFixtures::where('league_id', $get('league_id'))->get();
$arr = [];
foreach ($res as $value) {
$arr[] = $value->home_id;
$arr[] = $value->away_id;
}
$arr = array_unique($arr);
return Element::query()->whereIn('id', $arr)->orderBy('name')->pluck('name', 'id');
}
})
->preload()
->searchable()
->label('Team'),
DatePicker::make('startDate'),
DatePicker::make('endDate'),
])->columns(4)
->columnSpanFull(),
Section::make()
->schema([
Select::make('palyer_id')
->label('Player')
->prefixIcon('icon-player')
->options(function () {
$res = StatisticFixtures::where('sport_id', 1)
->groupBy('league_id')
->pluck('league_id')->toArray();
return CompetitionGroup::query()->whereIn('id', $res)->orderBy('name')->pluck('name', 'id');
})
->preload()
->searchable()
->live(),
Select::make('team_position')
->prefixIcon('phosphor-map-pin')
->default('home')
->selectablePlaceholder(false)
->options([
'home' => 'Home',
'away' => 'Away',
'all' => 'All',
]),
Select::make('similar_past_matches')
->prefixIcon('phosphor-align-center-horizontal')
->default('none')
->selectablePlaceholder(false)
->options([
'none' => 'None',
'league' => 'League',
'all' => 'All Database',
]),
Select::make('statistic_type')
->prefixIcon('phosphor-cell-signal-full')
->default('none')
->selectablePlaceholder(false)
->options([
'gls' => 'Gouls',
'crn' => 'Corner',
'fls' => 'Fouls commited',
'ofs' => 'Offside',
'crd' => 'Cards',
'snt' => 'Shot on target',
'tsh' => 'Total shots',
]),
])->columns(4)
->columnSpanFull(),
Slider::make('accuracy')
->default(0.5)
->decimalPlaces(1)
->tooltips()
->fillTrack()
->range(minValue: 0, maxValue: 1)
->pips(),
]);
}
}
@@ -37,7 +37,6 @@ use Filament\Tables\Enums\FiltersLayout;
use Filament\Tables\Filters\Filter;
use Filament\Tables\Filters\Indicator;
use Filament\Tables\Table;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
+22
View File
@@ -0,0 +1,22 @@
<?php
namespace App\Filament\Widgets;
use Filament\Widgets\ChartWidget;
class StatisticChart extends ChartWidget
{
protected ?string $heading = 'Statistic Chart';
protected function getData(): array
{
return [
//
];
}
protected function getType(): string
{
return 'line';
}
}
+45
View File
@@ -0,0 +1,45 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class StatisticFixtures extends Model
{
protected $fillable = [
'sport_id',
'league_id',
'home_id',
'away_id',
'starttime',
'exp_home',
'exp_away',
];
protected function casts(): array
{
return [
'starttime' => 'date',
];
}
public function sport()
{
return $this->belongsTo(Sport::class)->where('is_active', 1);
}
public function league()
{
return $this->belongsTo(CompetitionGroup::class);
}
public function home()
{
return $this->belongsTo(Element::class);
}
public function away()
{
return $this->belongsTo(Element::class);
}
}
@@ -4,6 +4,8 @@ namespace App\Providers\Filament;
// use Agencetwogether\HooksHelper\HooksHelperPlugin;
use App\Filament\Exports\BetfairExporter;
use App\Filament\Pages\Statistic;
use App\Filament\Widgets\StatisticChart;
use App\Http\Middleware\RecordUserActivity;
use Awcodes\LightSwitch\Enums\Alignment;
use Awcodes\LightSwitch\LightSwitchPlugin;
@@ -21,8 +23,6 @@ use Filament\Http\Middleware\DispatchServingFilamentEvent;
use Filament\Panel;
use Filament\PanelProvider;
use Filament\Support\Enums\Width;
use Filament\Widgets\AccountWidget;
use Filament\Widgets\FilamentInfoWidget;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Database\Eloquent\Builder;
@@ -87,9 +87,6 @@ class AdminPanelProvider extends PanelProvider
->discoverPages(in: app_path('Filament/Pages'), for: 'App\Filament\Pages')
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\Filament\Widgets')
->discoverClusters(in: app_path('Filament/Clusters'), for: 'App\\Filament\\Clusters')
->pages([
// Dashboard::class,
])
->maxContentWidth(Width::ScreenTwoExtraLarge)
->breadcrumbs(false)
->topNavigation()
@@ -100,9 +97,11 @@ class AdminPanelProvider extends PanelProvider
->url(fn (): string => EditProfilePage::getUrl())
->icon('phosphor-user-circle-fill'),
])
->pages([
Statistic::class,
])
->widgets([
AccountWidget::class,
FilamentInfoWidget::class,
StatisticChart::class,
])
->middleware([
EncryptCookies::class,
@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('statistic_fixtures', function (Blueprint $table) {
$table->id();
$table->integer('sport_id')->index();
$table->integer('league_id')->index();
$table->integer('home_id')->index();
$table->integer('away_id')->index();
$table->dateTime('starttime')->index();
$table->decimal('exp_home', 15, 14)->default(0);
$table->decimal('exp_away', 15, 14)->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('statistic_fixtures');
}
};
@@ -0,0 +1,2 @@
<x-filament-panels::page>
</x-filament-panels::page>