from django.shortcuts import render, get_object_or_404, redirect from django.views.generic.edit import CreateView, UpdateView, DeleteView from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.contrib.auth.decorators import login_required from django.urls import reverse_lazy from .models import Event, Training, Game, EventParticipation, GameResult from .forms import EventForm, TrainingForm, GameForm, OpenGameForm, GameResultForm from accounts.models import CustomUser # Import CustomUser for manage_participation view from clubs.models import Team from django.utils import timezone import datetime def select_event_type(request): return render(request, 'calendars/select_event_type.html') def get_all_child_teams(parent_team): """ Iteratively gets all child teams for a given team. """ children = [] teams_to_check = list(parent_team.child_teams.all()) while teams_to_check: team = teams_to_check.pop(0) if team not in children: children.append(team) teams_to_check.extend(list(team.child_teams.all())) return children class ManageableTeamsMixin: def get_form(self, form_class=None): form = super().get_form(form_class) user = self.request.user if not user.is_superuser: coached_teams_qs = user.coached_teams.all() expanded_coached_teams = list(coached_teams_qs) for team in coached_teams_qs: expanded_coached_teams.extend(get_all_child_teams(team)) assisted_teams_qs = user.assisted_teams.all() # Combine and get unique teams managed_teams_ids = {team.id for team in expanded_coached_teams} | {team.id for team in assisted_teams_qs} form.fields['team'].queryset = Team.objects.filter(id__in=managed_teams_ids) return form class CoachCheckMixin(UserPassesTestMixin): def test_func(self): user = self.request.user if user.is_superuser: return True team = self.get_object().team return user == team.head_coach or user in team.assistant_coaches.all() class EventCreateView(LoginRequiredMixin, ManageableTeamsMixin, CreateView): model = Event form_class = EventForm template_name = 'calendars/event_form.html' success_url = reverse_lazy('dashboard') class TrainingCreateView(LoginRequiredMixin, ManageableTeamsMixin, CreateView): model = Training form_class = TrainingForm template_name = 'calendars/event_form.html' success_url = reverse_lazy('dashboard') class GameCreateView(LoginRequiredMixin, ManageableTeamsMixin, CreateView): model = Game form_class = GameForm template_name = 'calendars/event_form.html' success_url = reverse_lazy('dashboard') class EventUpdateView(LoginRequiredMixin, CoachCheckMixin, UpdateView): model = Event template_name = 'calendars/event_form.html' success_url = reverse_lazy('dashboard') def get_object(self, queryset=None): # Fetch the event and then check if it's a game or training to return the specific instance obj = super().get_object(queryset) if hasattr(obj, 'game'): return obj.game if hasattr(obj, 'training'): return obj.training return obj def get_form_class(self): # self.object is now the correct child instance because of the overridden get_object if isinstance(self.object, Game): return GameForm if isinstance(self.object, Training): return TrainingForm return EventForm class EventDeleteView(LoginRequiredMixin, CoachCheckMixin, DeleteView): model = Event template_name = 'calendars/event_confirm_delete.html' success_url = reverse_lazy('dashboard') @login_required def manage_participation(request, child_id, event_id, status): child = get_object_or_404(CustomUser, id=child_id) event = get_object_or_404(Event, id=event_id) # Check if the logged-in user is a parent of the child if request.user not in child.parents.all(): # Handle unauthorized access return redirect('dashboard') # Check for parallel events if accepting a support game if status == 'attending' and hasattr(event, 'game') and child.team in event.game.opened_for_teams.all(): # A game's duration is defined as innings * 20 minutes + 1 hour travel. # I will assume 9 innings for now. game_duration = datetime.timedelta(minutes=(9 * 20 + 60)) event_start = event.start_time event_end = event.start_time + game_duration parallel_events = Event.objects.filter( eventparticipation__user=child, eventparticipation__status='attending' ).exclude(id=event.id) for pe in parallel_events: pe_duration = datetime.timedelta(minutes=(9 * 20 + 60)) # Assuming 9 innings for all games pe_start = pe.start_time pe_end = pe.start_time + pe_duration # Check for overlap with a tolerance of +/- 2 hours if (event_start < pe_end + datetime.timedelta(hours=2) and event_end > pe_start - datetime.timedelta(hours=2)): # Handle parallel event conflict return redirect('dashboard') # Or show an error message participation, created = EventParticipation.objects.get_or_create(user=child, event=event) participation.status = status participation.save() return redirect('dashboard') @login_required def open_game(request, game_id): game = get_object_or_404(Game, id=game_id) club = game.team.club # Permission check: only head coach of the team's club can open the game if not request.user.is_superuser and request.user not in club.administrators.all(): return redirect('dashboard') if request.method == 'POST': form = OpenGameForm(request.POST, club=club) if form.is_valid(): teams = form.cleaned_data['teams'] game.opened_for_teams.add(*teams) return redirect('dashboard') else: form = OpenGameForm(club=club) return render(request, 'calendars/open_game.html', {'form': form, 'game': game}) @login_required def record_results(request, game_id): game = get_object_or_404(Game, id=game_id) game_result, created = GameResult.objects.get_or_create(game=game) if request.method == 'POST': form = GameResultForm(request.POST, game=game, instance=game_result) if form.is_valid(): inning_results = {} for i in range(1, game.number_of_innings + 1): inning_results[f'inning_{i}'] = { 'home': form.cleaned_data.get(f'inning_{i}_home'), 'guest': form.cleaned_data.get(f'inning_{i}_guest'), } game_result.inning_results = inning_results game_result.save() return redirect('dashboard') else: initial_data = {} if game_result.inning_results: for inning, scores in game_result.inning_results.items(): initial_data[f'{inning}_home'] = scores.get('home') initial_data[f'{inning}_guest'] = scores.get('guest') form = GameResultForm(game=game, instance=game_result, initial=initial_data) form_fields_by_inning = [] for i in range(1, game.number_of_innings + 1): form_fields_by_inning.append({ 'inning': i, 'home': form[f'inning_{i}_home'], 'guest': form[f'inning_{i}_guest'], }) return render(request, 'calendars/record_results.html', {'form': form, 'game': game, 'form_fields_by_inning': form_fields_by_inning})