the browser-facing portion of osu!
at master 3.4 kB view raw
1<?php 2 3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4// See the LICENCE file in the repository root for full licence text. 5 6declare(strict_types=1); 7 8namespace App\Libraries\User; 9 10use App\Models\Tournament; 11use App\Models\TournamentRegistration; 12use App\Models\User; 13use Carbon\CarbonImmutable; 14 15class CountryChangeTarget 16{ 17 const MIN_DAYS_MONTH = 15; 18 19 public static function currentMonth(): CarbonImmutable 20 { 21 $now = CarbonImmutable::now(); 22 $subMonths = $now->day > static::MIN_DAYS_MONTH ? 0 : 1; 23 24 return $now->startOfMonth()->subMonths($subMonths); 25 } 26 27 public static function get(User $user): ?string 28 { 29 if (static::isUserInTournament($user)) { 30 return null; 31 } 32 33 $until = static::currentMonth(); 34 $minMonths = static::minMonths(); 35 36 $yearMonths = $user 37 ->userCountryHistory() 38 ->whereBetween('year_month', [ 39 // one year maximum range. Offset by 1 because the range is inclusive 40 format_month_column($until->subMonths(11)), 41 format_month_column($until), 42 ])->distinct() 43 ->orderBy('year_month', 'DESC') 44 ->limit($minMonths) 45 ->pluck('year_month'); 46 47 $history = $user 48 ->userCountryHistory() 49 ->whereIn('year_month', $yearMonths) 50 ->whereHas('country') 51 ->get(); 52 53 // First group countries by year_month 54 $byMonth = []; 55 foreach ($history as $entry) { 56 $byMonth[$entry->year_month] ??= []; 57 $byMonth[$entry->year_month][] = $entry->country_acronym; 58 } 59 60 // For each year_month, summarise each countries 61 $byCountry = []; 62 foreach ($byMonth as $countries) { 63 $mixed = count($countries) > 1; 64 foreach ($countries as $country) { 65 $byCountry[$country] ??= [ 66 'total' => 0, 67 'mixed' => 0, 68 ]; 69 $byCountry[$country]['total']++; 70 if ($mixed) { 71 $byCountry[$country]['mixed']++; 72 } 73 } 74 } 75 76 // Finally find the first country which fulfills the requirement 77 foreach ($byCountry as $country => $data) { 78 if ($data['total'] === $minMonths && $data['mixed'] <= static::maxMixedMonths()) { 79 if ($user->country_acronym === $country) { 80 return null; 81 } else { 82 return $country; 83 } 84 } 85 } 86 87 return null; 88 } 89 90 public static function maxMixedMonths(): int 91 { 92 return $GLOBALS['cfg']['osu']['user']['country_change']['max_mixed_months']; 93 } 94 95 public static function minMonths(): int 96 { 97 return $GLOBALS['cfg']['osu']['user']['country_change']['min_months']; 98 } 99 100 private static function isUserInTournament(User $user): bool 101 { 102 return TournamentRegistration 103 ::where('user_id', $user->getKey()) 104 ->whereIn( 105 'tournament_id', 106 Tournament 107 ::where('end_date', '>', CarbonImmutable::now()) 108 ->orWhereNull('end_date') 109 ->select('tournament_id'), 110 )->exists(); 111 } 112}