Improve tournament broadcast selection logic

apikeys
Razmig Sarkissian 2 months ago
parent 61a6f88e6f
commit 183d0ee6ec
  1. 31
      tournaments/models/match.py
  2. 66
      tournaments/models/tournament.py
  3. 147
      tournaments/views.py

@ -423,6 +423,37 @@ class Match(TournamentSubModel):
return timezone.now() > self.start_date
return False
def will_start(self):
"""
Returns True if match will start within the next hour
"""
if self.disabled or self.end_date:
return False
# Check start_date first
if self.start_date:
now = timezone.now()
time_until_start = (self.start_date - now).total_seconds()
return 0 < time_until_start <= 3600 # Within 1 hour (3600 seconds)
# Check planned_start_date as fallback
if self.planned_start_date:
now = timezone.now()
time_until_start = (self.planned_start_date - now).total_seconds()
return 0 < time_until_start <= 3600 # Within 1 hour (3600 seconds)
return False
def is_ongoing(self):
"""
Returns True if match has started but not finished
"""
if self.disabled:
return False
# Match is ongoing if it has started but hasn't ended
return self.started() and self.end_date is None
def should_appear(self):
if self.disabled is True:
return False

@ -1031,6 +1031,72 @@ class Tournament(BaseModel):
def will_start_soon(self, hour_delta=2):
return self.has_started(hour_delta=hour_delta)
def has_ongoing_matches(self):
"""
Returns True if tournament has any matches that are currently ongoing
"""
# Check matches in rounds
for round_obj in self.rounds.all():
for match in round_obj.matches.all():
if match.is_ongoing():
return True
# Check matches in group stages
for group_stage in self.group_stages.all():
for match in group_stage.matches.all():
if match.is_ongoing():
return True
return False
def has_matches_starting_soon(self):
"""
Returns True if tournament has any matches starting within the next hour
"""
# Check matches in rounds
for round_obj in self.rounds.all():
for match in round_obj.matches.all():
if match.will_start():
return True
# Check matches in group stages
for group_stage in self.group_stages.all():
for match in group_stage.matches.all():
if match.will_start():
return True
return False
def get_next_match_start_time(self):
"""
Returns the datetime of the earliest upcoming match, or None if no upcoming matches
"""
next_times = []
# Check matches in rounds
for round_obj in self.rounds.all():
for match in round_obj.matches.all():
if match.disabled or match.end_date:
continue
if match.start_date and match.start_date > timezone.now():
next_times.append(match.start_date)
elif match.planned_start_date and match.planned_start_date > timezone.now():
next_times.append(match.planned_start_date)
# Check matches in group stages
for group_stage in self.group_stages.all():
for match in group_stage.matches.all():
if match.disabled or match.end_date:
continue
if match.start_date and match.start_date > timezone.now():
next_times.append(match.start_date)
elif match.planned_start_date and match.planned_start_date > timezone.now():
next_times.append(match.planned_start_date)
return min(next_times) if next_times else None
def are_teams_positioned(self):
teams = self.team_registrations.all()
filtered_teams = [t for t in teams if t.is_positioned()]

@ -534,28 +534,24 @@ def automatic_broadcast_event(request, event_id):
event = get_object_or_404(Event, pk=event_id)
now = timezone.now()
# Initial time windows
past_cutoff = now - timedelta(hours=4)
future_cutoff = now + timedelta(hours=4)
# Get all tournaments for the event (expanded time range for better coverage)
past_cutoff = now - timedelta(hours=12) # Extended past search
future_cutoff = now + timedelta(hours=12) # Extended future search
# Get recent/current tournaments (started but not too old)
recent_tournaments = Tournament.objects.filter(
all_tournaments = Tournament.objects.filter(
event=event,
start_date__gte=past_cutoff,
start_date__lte=now
).order_by('start_date')
# Get near-future tournaments
future_tournaments = Tournament.objects.filter(
event=event,
start_date__gt=now,
start_date__lte=future_cutoff
).order_by('start_date')
# Filter recent tournaments that have content
recent_with_content = []
for tournament in recent_tournaments:
# Categorize tournaments by priority
ongoing_tournaments = []
starting_soon_tournaments = []
other_tournaments_with_content = []
for tournament in all_tournaments:
try:
# First check if tournament has broadcast content
content = tournament.broadcast_content()
has_content = (
len(content.get('matches', [])) > 0 or
@ -563,59 +559,92 @@ def automatic_broadcast_event(request, event_id):
len(content.get('summons', [])) > 0 or
len(content.get('rankings', [])) > 0
)
if has_content:
recent_with_content.append(tournament)
except:
# Categorize based on match activity (highest priority first)
if tournament.has_ongoing_matches():
ongoing_tournaments.append(tournament)
elif tournament.has_matches_starting_soon():
starting_soon_tournaments.append(tournament)
else:
other_tournaments_with_content.append(tournament)
except Exception as e:
print(f"Error processing tournament {tournament.id}: {e}")
continue
tournaments_to_display = recent_with_content[:]
# Sort each category by next match start time and tournament start time
def sort_by_next_match_time(tournament):
next_match_time = tournament.get_next_match_start_time()
return (
next_match_time if next_match_time else tournament.start_date,
tournament.start_date
)
# If we have 0 or 1 recent tournaments, extend the search
if len(recent_with_content) <= 1:
needed = 2 - len(recent_with_content)
# Sort ongoing tournaments by next match time (for consistency)
ongoing_tournaments.sort(key=sort_by_next_match_time)
# First, try to add future tournaments (within 4 hours)
added_from_future = 0
for tournament in future_tournaments:
if added_from_future >= needed:
break
tournaments_to_display.append(tournament)
added_from_future += 1
# Sort starting soon tournaments by next match time (earliest first)
starting_soon_tournaments.sort(key=sort_by_next_match_time)
# If still not enough, extend past search (beyond 4 hours)
if len(tournaments_to_display) < 2:
extended_past_tournaments = Tournament.objects.filter(
event=event,
start_date__lt=past_cutoff # Older than 4 hours ago
).order_by('-start_date') # Most recent first
still_needed = 2 - len(tournaments_to_display)
for tournament in extended_past_tournaments[:still_needed]:
# Check if this past tournament has content
try:
content = tournament.broadcast_content()
has_content = (
len(content.get('matches', [])) > 0 or
len(content.get('group_stages', [])) > 0 or
len(content.get('summons', [])) > 0 or
len(content.get('rankings', [])) > 0
)
if has_content:
tournaments_to_display.append(tournament)
except:
continue
# If still not enough, extend future search (beyond 4 hours)
if len(tournaments_to_display) < 2:
extended_future_tournaments = Tournament.objects.filter(
event=event,
start_date__gt=future_cutoff # More than 4 hours in the future
).order_by('start_date')
# Sort other tournaments by tournament start time
other_tournaments_with_content.sort(key=lambda t: t.start_date)
still_needed = 2 - len(tournaments_to_display)
for tournament in extended_future_tournaments[:still_needed]:
# Build final list prioritizing ongoing matches above all else
tournaments_to_display = []
# Priority 1: Tournaments with ongoing matches (take max 2)
tournaments_to_display.extend(ongoing_tournaments[:2])
# If we need more tournaments and have space
if len(tournaments_to_display) < 2:
remaining_slots = 2 - len(tournaments_to_display)
# Priority 2: Tournaments with matches starting soon
tournaments_to_display.extend(starting_soon_tournaments[:remaining_slots])
# If still need more tournaments
if len(tournaments_to_display) < 2:
remaining_slots = 2 - len(tournaments_to_display)
# Priority 3: Other tournaments with content
tournaments_to_display.extend(other_tournaments_with_content[:remaining_slots])
# Fallback: If still don't have enough tournaments, expand search
if len(tournaments_to_display) < 2:
# Get tournaments from wider time range
extended_tournaments = Tournament.objects.filter(
event=event
).exclude(
id__in=[t.id for t in tournaments_to_display]
).order_by('-start_date') # Most recent first
remaining_needed = 2 - len(tournaments_to_display)
for tournament in extended_tournaments[:remaining_needed]:
try:
# Check if tournament has any content
content = tournament.broadcast_content()
has_content = (
len(content.get('matches', [])) > 0 or
len(content.get('group_stages', [])) > 0 or
len(content.get('summons', [])) > 0 or
len(content.get('rankings', [])) > 0
)
if has_content:
tournaments_to_display.append(tournament)
except:
# If no content, add anyway as last resort
tournaments_to_display.append(tournament)
# Ensure we have exactly what we need (limit to 2)
tournaments_to_display = tournaments_to_display[:2]
# Log the prioritization for debugging
print(f"Event {event_id} broadcast prioritization:")
print(f" - Ongoing tournaments: {len(ongoing_tournaments)}")
print(f" - Starting soon tournaments: {len(starting_soon_tournaments)}")
print(f" - Other tournaments with content: {len(other_tournaments_with_content)}")
print(f" - Final selection: {[str(t.id) for t in tournaments_to_display]}")
tournament_ids = [str(tournament.id) for tournament in tournaments_to_display]
# Create QR code URL for the event broadcast

Loading…
Cancel
Save