Behebt einen Fehler in der , bei dem beim Bearbeiten eines Events (Spiel oder Training) das Formular leer angezeigt wurde anstatt mit den vorhandenen Daten des Events vorbefüllt zu sein. Die Ursache war eine Inkonsistenz bei der Übergabe der Modellinstanz: Die erhielt ein übergeordnetes -Objekt, während die für oder eine spezifische Child-Instanz erwartete. Änderungen: - Überschreibung der Methode in , um die korrekte untergeordnete Instanz ( oder ) des Events abzurufen und an das Formular zu übergeben. - Aktualisierung der Methode in zur Verwendung von für eine robustere Typüberprüfung des Objekts.
194 lines
7.6 KiB
Python
194 lines
7.6 KiB
Python
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})
|