parent
48638e76f8
commit
dc5a033e63
@ -0,0 +1,10 @@ |
|||||||
|
from django.core.management.base import BaseCommand |
||||||
|
from tournaments.tasks import check_confirmation_deadlines |
||||||
|
|
||||||
|
class Command(BaseCommand): |
||||||
|
help = 'Run confirmation deadline check immediately' |
||||||
|
|
||||||
|
def handle(self, *args, **options): |
||||||
|
# Run the function directly (not through the task queue) |
||||||
|
check_confirmation_deadlines(schedule=0) |
||||||
|
self.stdout.write(self.style.SUCCESS('Successfully checked confirmation deadlines')) |
||||||
@ -0,0 +1,63 @@ |
|||||||
|
from django.core.management.base import BaseCommand |
||||||
|
from tournaments.tasks import check_confirmation_deadlines |
||||||
|
from django.utils import timezone |
||||||
|
import datetime |
||||||
|
import pytz |
||||||
|
|
||||||
|
class Command(BaseCommand): |
||||||
|
help = 'Schedule background tasks to run at :00 and :30 of every hour' |
||||||
|
|
||||||
|
def handle(self, *args, **options): |
||||||
|
# Clear existing tasks first to avoid duplicates |
||||||
|
from background_task.models import Task |
||||||
|
Task.objects.filter(task_name='tournaments.tasks.check_confirmation_deadlines').delete() |
||||||
|
|
||||||
|
# Get the current timezone-aware time |
||||||
|
now = timezone.now() |
||||||
|
|
||||||
|
# Get local timezone for display purposes |
||||||
|
local_timezone = timezone.get_current_timezone() |
||||||
|
local_now = now.astimezone(local_timezone) |
||||||
|
|
||||||
|
# Calculate time until next half-hour mark (either :00 or :30) |
||||||
|
current_minute = local_now.minute |
||||||
|
|
||||||
|
if current_minute < 30: |
||||||
|
# Next run at XX:30:00 |
||||||
|
next_minute = 30 |
||||||
|
next_hour = local_now.hour |
||||||
|
else: |
||||||
|
# Next run at (XX+1):00:00 |
||||||
|
next_minute = 0 |
||||||
|
next_hour = local_now.hour + 1 |
||||||
|
|
||||||
|
# Create a datetime with exactly XX:30:00 or XX:00:00 in local time |
||||||
|
first_run_local = local_now.replace( |
||||||
|
hour=next_hour, |
||||||
|
minute=next_minute + 1, #let the expiration time be off first |
||||||
|
second=0, |
||||||
|
microsecond=0 |
||||||
|
) |
||||||
|
|
||||||
|
# Handle day rollover if needed |
||||||
|
if first_run_local < local_now: # This would happen if we crossed midnight |
||||||
|
first_run_local += datetime.timedelta(days=1) |
||||||
|
|
||||||
|
# Calculate seconds from now until the first run |
||||||
|
seconds_until_first_run = (first_run_local - local_now).total_seconds() |
||||||
|
if seconds_until_first_run < 0: |
||||||
|
seconds_until_first_run = 0 # If somehow negative, run immediately |
||||||
|
|
||||||
|
# Schedule with seconds delay instead of a specific datetime |
||||||
|
check_confirmation_deadlines( |
||||||
|
schedule=int(seconds_until_first_run), # Delay in seconds before first run |
||||||
|
repeat=1800 # 30 minutes in seconds |
||||||
|
) |
||||||
|
|
||||||
|
# Show the message with proper timezone info |
||||||
|
local_timezone_name = local_timezone.tzname(local_now) |
||||||
|
self.stdout.write(self.style.SUCCESS( |
||||||
|
f'Task scheduled to first run at {first_run_local.strftime("%H:%M:%S")} {local_timezone_name} ' |
||||||
|
f'(in {int(seconds_until_first_run)} seconds) ' |
||||||
|
f'and then every 30 minutes' |
||||||
|
)) |
||||||
@ -0,0 +1,89 @@ |
|||||||
|
from background_task import background |
||||||
|
from django.utils import timezone |
||||||
|
from django.db import transaction |
||||||
|
|
||||||
|
from .models import PlayerRegistration |
||||||
|
from .models.player_registration import RegistrationStatus |
||||||
|
from .services.email_service import TournamentEmailService, TeamEmailType |
||||||
|
|
||||||
|
@background(schedule=1) # Run every 30 minutes (30*60 seconds) |
||||||
|
def check_confirmation_deadlines(): |
||||||
|
""" |
||||||
|
Periodic task to check for expired confirmation deadlines |
||||||
|
and notify the next team in the waiting list. |
||||||
|
""" |
||||||
|
now = timezone.now() |
||||||
|
print(f"[{now}] Running confirmation deadline check...") |
||||||
|
|
||||||
|
# Find players with expired confirmation deadlines |
||||||
|
expired_confirmations = PlayerRegistration.objects.filter( |
||||||
|
registration_status=RegistrationStatus.PENDING, |
||||||
|
registered_online=True, |
||||||
|
team_registration__isnull=False |
||||||
|
).select_related('team_registration', 'team_registration__tournament') |
||||||
|
|
||||||
|
print(f"Found {expired_confirmations.count()} expired confirmations") |
||||||
|
|
||||||
|
# Process each expired confirmation |
||||||
|
processed_teams = set() # To avoid processing the same team multiple times |
||||||
|
|
||||||
|
for player in expired_confirmations: |
||||||
|
team_registration = player.team_registration |
||||||
|
|
||||||
|
# Skip if we've already processed this team |
||||||
|
if team_registration.id in processed_teams: |
||||||
|
continue |
||||||
|
|
||||||
|
processed_teams.add(team_registration.id) |
||||||
|
tournament = team_registration.tournament |
||||||
|
|
||||||
|
if not tournament or not tournament.enable_online_registration: |
||||||
|
continue |
||||||
|
|
||||||
|
teams = tournament.teams(True) |
||||||
|
waiting_list_teams = tournament.waiting_list_teams(teams) |
||||||
|
if waiting_list_teams is not None: |
||||||
|
ttc = tournament.calculate_time_to_confirm(len(waiting_list_teams)) |
||||||
|
else: |
||||||
|
ttc = None |
||||||
|
first_waiting_list_team = tournament.first_waiting_list_team(teams) |
||||||
|
|
||||||
|
# Process in a transaction to ensure atomic operations |
||||||
|
with transaction.atomic(): |
||||||
|
# Get all players in this team and mark them as expired |
||||||
|
team_players = PlayerRegistration.objects.filter( |
||||||
|
team_registration=team_registration, |
||||||
|
registered_online=True |
||||||
|
) |
||||||
|
|
||||||
|
should_update_team = False |
||||||
|
should_send_mail = False |
||||||
|
for team_player in team_players: |
||||||
|
if team_player.time_to_confirm is None and first_waiting_list_team is not None: |
||||||
|
team_registration.set_time_to_confirm(ttc) |
||||||
|
team_player.save() |
||||||
|
should_send_mail = True |
||||||
|
print(team_player, "team_player.time_to_confirm is None and", ttc) |
||||||
|
elif team_player.time_to_confirm is not None and now > team_player.time_to_confirm: |
||||||
|
team_player.registration_status = RegistrationStatus.CANCELED |
||||||
|
team_player.time_to_confirm = None |
||||||
|
team_player.save() |
||||||
|
team_registration.registration_date = now |
||||||
|
print(team_player, "time_to_confirm = ", team_player.time_to_confirm) |
||||||
|
should_update_team = True |
||||||
|
# elif team_player.time_to_confirm is not None and team_player.time_to_confirm > ttc: |
||||||
|
# team_player.registration_status = RegistrationStatus.PENDING |
||||||
|
# team_player.time_to_confirm = ttc |
||||||
|
# team_player.save() |
||||||
|
# should_update_team = True |
||||||
|
|
||||||
|
if should_send_mail: |
||||||
|
TournamentEmailService.notify_team( |
||||||
|
team_registration, |
||||||
|
tournament, |
||||||
|
TeamEmailType.OUT_OF_WAITING_LIST |
||||||
|
) |
||||||
|
|
||||||
|
if should_update_team: |
||||||
|
team_registration.save() |
||||||
|
print(f"Team {team_registration} confirmation expired in tournament {tournament.id}") |
||||||
Loading…
Reference in new issue