diff --git a/application/app/Console/Commands/FixGameStartedHydraDoomEventsCommand.php b/application/app/Console/Commands/FixGameStartedHydraDoomEventsCommand.php deleted file mode 100644 index 909912f..0000000 --- a/application/app/Console/Commands/FixGameStartedHydraDoomEventsCommand.php +++ /dev/null @@ -1,116 +0,0 @@ -data, true); - if (empty($gameStartedEventData['game_id'])) { - continue; - } - - // Find the corresponding new_game event by game_id - $newGameSql = <<data, true); - - // Check if the new game was a qualifier - if (isset($newGameEventData['is_qualifier']) && $newGameEventData['is_qualifier'] === true) { - - // Update game started - $gameStartedEventData['is_qualifier'] = true; - EventData::query() - ->where('id', $gameStartedEvent->id) - ->update([ - 'data' => json_encode($gameStartedEventData), - ]); - - // Dispatch the job to re-aggregate the account stats - foreach ($gameStartedEventData['keys'] ?? [] as $key) { - dispatch(new HydraDoomAccountStatsJob($gameStartedEvent->project_id, $key)); - } - - // Update counter - $fixedCount++; - - } - - } - - } - - // Task completed - $this->info(sprintf( - 'Found %d records, and fixed %d records.', - $foundCount, - $fixedCount, - )); - - } catch (Throwable $exception) { - - $message = 'Failed to fix all game started events'; - - $this->logException($message, $exception); - - $this->error(sprintf('%s: %s', $message, $exception->getMessage())); - - } - } -} diff --git a/application/app/Console/Commands/FlagQualifiersHydraDoomEventsCommand.php b/application/app/Console/Commands/FlagQualifiersHydraDoomEventsCommand.php new file mode 100644 index 0000000..3a2d53f --- /dev/null +++ b/application/app/Console/Commands/FlagQualifiersHydraDoomEventsCommand.php @@ -0,0 +1,142 @@ +loadQualifiedPlayersByKillCount(); + + // Debug + $this->info(sprintf( + 'Found %d players who meet the required %d kill count', + count($qualifiedPlayersByKillCount), + self::REQUIRED_KILL_COUNT, + )); + + // For each qualified player + foreach ($qualifiedPlayersByKillCount as $qualifiedPlayer) + { + // Load qualified player session events + $projectAccountSessionEvents = $this->loadProjectAccountSessionEventsWithinQualificationPeriod($qualifiedPlayer->project_account_id); + + // Calculate total play time in minutes + $totalPlayTimeMinutes = $this->calculateTotalPlayTimeMinutes($projectAccountSessionEvents); + + // Update project account stats + $playedDuringQualifierPeriod = count($projectAccountSessionEvents) > 0; + $achievedRequiredKillCount = ((int) $qualifiedPlayer->total_kills >= self::REQUIRED_KILL_COUNT); + $achievedRequiredPlayMinutes = ($totalPlayTimeMinutes >= self::REQUIRED_PLAY_MINUTES); + ProjectAccountStats::query() + ->where('project_id', $qualifiedPlayer->project_id) + ->where('project_account_id', $qualifiedPlayer->project_account_id) + ->update([ + 'qualifier' => [ + 'is_qualified' => ($playedDuringQualifierPeriod && $achievedRequiredKillCount && $achievedRequiredPlayMinutes), + 'requirements' => [ + [ + 'play_from' => self::QUALIFIER_DATE_START, + 'play_to' => self::QUALIFIER_DATE_END, + 'is_met' => $playedDuringQualifierPeriod, + ], + [ + 'required_kill_count' => self::REQUIRED_KILL_COUNT, + 'actual_kill_count' => (int) $qualifiedPlayer->total_kills, + 'is_met' => $achievedRequiredKillCount, + ], + [ + 'required_play_minutes' => self::REQUIRED_PLAY_MINUTES, + 'actual_play_minutes' => $totalPlayTimeMinutes, + 'is_met' => $achievedRequiredPlayMinutes, + ], + ], + ], + ]); + + // Debug + $this->info(sprintf( + 'Done processing project account id: %d', + $qualifiedPlayer->project_account_id, + )); + } + + // Debug + $this->info('Task Completed'); + } + + private function loadQualifiedPlayersByKillCount(): array + { + $sql = <<= ? +QUERY; + + return DB::select($sql, [self::REQUIRED_KILL_COUNT]); + } + + private function loadProjectAccountSessionEventsWithinQualificationPeriod(int $projectAccountId): array + { + $sql = <<= ? AND project_account_session_events.event_timestamp <= ?) +group by project_account_session_events.game_id +QUERY; + + return DB::select($sql, [ + $projectAccountId, + strtotime(self::QUALIFIER_DATE_START) * 1000, + strtotime(self::QUALIFIER_DATE_END) * 1000 + ]); + } + + private function calculateTotalPlayTimeMinutes(array $projectAccountSessionEvents): float + { + $totalPlayTimeMinutes = 0; + + foreach ($projectAccountSessionEvents as $projectAccountSessionEvent) { + if (!empty($projectAccountSessionEvent->earliest_event_timestamp) && !empty($projectAccountSessionEvent->latest_event_timestamp)) { + $earliestEventTimestamp = Carbon::createFromTimestampMs($projectAccountSessionEvent->earliest_event_timestamp); + $latestEventTimestamp = Carbon::createFromTimestampMs($projectAccountSessionEvent->latest_event_timestamp); + $totalPlayTimeMinutes += round($earliestEventTimestamp->diffInMinutes($latestEventTimestamp), 2); + } + } + + return $totalPlayTimeMinutes; + } +} diff --git a/application/app/Models/ProjectAccountStats.php b/application/app/Models/ProjectAccountStats.php index 4b8882f..2dd6729 100644 --- a/application/app/Models/ProjectAccountStats.php +++ b/application/app/Models/ProjectAccountStats.php @@ -11,6 +11,7 @@ class ProjectAccountStats extends Model 'project_id', 'project_account_id', 'stats', + 'qualifier', ]; protected function stats(): Attribute @@ -20,4 +21,12 @@ protected function stats(): Attribute set: fn (string|null $value) => $value ? json_encode($value) : null, ); } + + protected function qualifier(): Attribute + { + return Attribute::make( + get: fn (string|null $value) => $value ? json_decode($value, true) : null, + set: fn (string|null $value) => $value ? json_encode($value) : null, + ); + } } diff --git a/application/database/migrations/2024_12_07_232428_add_qualifier_column_to_project_account_stats_table.php b/application/database/migrations/2024_12_07_232428_add_qualifier_column_to_project_account_stats_table.php new file mode 100644 index 0000000..eced6d9 --- /dev/null +++ b/application/database/migrations/2024_12_07_232428_add_qualifier_column_to_project_account_stats_table.php @@ -0,0 +1,28 @@ +json('qualifier')->nullable()->after('stats'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('project_account_stats', function (Blueprint $table) { + $table->dropColumn('qualifier'); + }); + } +};