diff --git a/participation/forms.py b/participation/forms.py index 06d37d1..b6c5bd0 100644 --- a/participation/forms.py +++ b/participation/forms.py @@ -178,8 +178,13 @@ class SolutionForm(forms.ModelForm): class PoolForm(forms.ModelForm): class Meta: model = Pool - fields = ('tournament', 'round', 'letter', 'bbb_url', 'results_available', 'juries',) + fields = ('tournament', 'round', 'letter', 'bbb_url', 'results_available', 'jury_president', 'juries',) widgets = { + "jury_president": forms.Select(attrs={ + 'class': 'selectpicker', + 'data-live-search': 'true', + 'data-live-search-normalize': 'true', + }), "juries": forms.SelectMultiple(attrs={ 'class': 'selectpicker', 'data-live-search': 'true', diff --git a/participation/models.py b/participation/models.py index 4617e1a..cc237fe 100644 --- a/participation/models.py +++ b/participation/models.py @@ -816,6 +816,10 @@ class Solution(models.Model): unique=True, ) + @property + def tournament(self): + return Tournament.final_tournament() if self.final_solution else self.participation.tournament + def __str__(self): return _("Solution of team {team} for problem {problem}")\ .format(team=self.participation.team.name, problem=self.problem)\ @@ -954,6 +958,9 @@ class Note(models.Model): def modal_name(self): return f"updateNotes{self.pk}" + def has_any_note(self): + return any(self.get_all()) + def __str__(self): return _("Notes of {jury} for {passage}").format(jury=self.jury, passage=self.passage) diff --git a/participation/views.py b/participation/views.py index 7db55f4..aa54845 100644 --- a/participation/views.py +++ b/participation/views.py @@ -721,8 +721,7 @@ class PoolUpdateView(VolunteerMixin, UpdateView): if not request.user.is_authenticated: return self.handle_no_permission() if request.user.registration.is_admin or request.user.registration.is_volunteer \ - and (self.get_object().tournament in request.user.registration.organized_tournaments.all() - or request.user.registration in self.get_object().juries.all()): + and self.get_object().tournament in request.user.registration.organized_tournaments.all(): return super().dispatch(request, *args, **kwargs) return self.handle_no_permission() @@ -750,9 +749,11 @@ class PoolDownloadView(VolunteerMixin, DetailView): def dispatch(self, request, *args, **kwargs): if not request.user.is_authenticated: return self.handle_no_permission() - if request.user.registration.is_admin or request.user.registration.is_volunteer \ - and (self.get_object().tournament in request.user.registration.organized_tournaments.all() - or request.user.registration in self.get_object().juries.all()): + reg = request.user.registration + if reg.is_admin or reg.is_volunteer \ + and (self.get_object().tournament in reg.organized_tournaments.all() + or reg in self.get_object().juries.all() + or reg.pools_presided.filter(tournament=self.get_object().tournament).exists()): return super().dispatch(request, *args, **kwargs) return self.handle_no_permission() @@ -785,12 +786,16 @@ class PoolJuryView(VolunteerMixin, FormView, DetailView): form_class = AddJuryForm template_name = 'participation/pool_jury.html' + def dispatch(self, request, *args, **kwargs): - if not request.user.is_authenticated: - return self.handle_no_permission() - if request.user.registration.is_admin or request.user.registration.is_volunteer \ - and self.get_object().tournament in request.user.registration.organized_tournaments.all(): + self.object = self.get_object() + + if request.user.is_authenticated and \ + (request.user.registration.is_admin or request.user.registration.is_volunteer + and (self.object.tournament in request.user.registration.organized_tournaments.all() + or request.user.registration == self.object.jury_president)): return super().dispatch(request, *args, **kwargs) + return self.handle_no_permission() def get_context_data(self, **kwargs): @@ -866,11 +871,14 @@ class PoolRemoveJuryView(VolunteerMixin, DetailView): model = Pool def dispatch(self, request, *args, **kwargs): - if not request.user.is_authenticated: - return self.handle_no_permission() - if request.user.registration.is_admin or request.user.registration.is_volunteer \ - and self.get_object().tournament in request.user.registration.organized_tournaments.all(): + self.object = self.get_object() + + if request.user.is_authenticated and \ + (request.user.registration.is_admin or request.user.registration.is_volunteer + and (self.object.tournament in request.user.registration.organized_tournaments.all() + or request.user.registration == self.object.jury_president)): return super().dispatch(request, *args, **kwargs) + return self.handle_no_permission() def get(self, request, *args, **kwargs): @@ -889,11 +897,14 @@ class PoolPresideJuryView(VolunteerMixin, DetailView): model = Pool def dispatch(self, request, *args, **kwargs): - if not request.user.is_authenticated: - return self.handle_no_permission() - if request.user.registration.is_admin or request.user.registration.is_volunteer \ - and self.get_object().tournament in request.user.registration.organized_tournaments.all(): + self.object = self.get_object() + + if request.user.is_authenticated and \ + (request.user.registration.is_admin or request.user.registration.is_volunteer + and (self.object.tournament in request.user.registration.organized_tournaments.all() + or request.user.registration == self.object.jury_president)): return super().dispatch(request, *args, **kwargs) + return self.handle_no_permission() def get(self, request, *args, **kwargs): @@ -919,7 +930,7 @@ class PoolUploadNotesView(VolunteerMixin, FormView, DetailView): if request.user.is_authenticated and \ (request.user.registration.is_admin or request.user.registration.is_volunteer and (self.object.tournament in request.user.registration.organized_tournaments.all() - or request.user.registration in self.object.juries.all())): + or request.user.registration == self.object.jury_president)): return super().dispatch(request, *args, **kwargs) return self.handle_no_permission() @@ -958,6 +969,17 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView): """ model = Pool + def dispatch(self, request, *args, **kwargs): + self.object = self.get_object() + + if request.user.is_authenticated and \ + (request.user.registration.is_admin or request.user.registration.is_volunteer + and (self.object.tournament in request.user.registration.organized_tournaments.all() + or request.user.registration == self.object.jury_president)): + return super().dispatch(request, *args, **kwargs) + + return self.handle_no_permission() + def render_to_response(self, context, **response_kwargs): # noqa: C901 pool_size = self.object.passages.count() passage_width = 7 if pool_size == 4 else 6 @@ -1536,26 +1558,34 @@ class PassageDetailView(LoginRequiredMixin, DetailView): def dispatch(self, request, *args, **kwargs): if not request.user.is_authenticated: return self.handle_no_permission() - if request.user.registration.is_admin or request.user.registration.is_volunteer \ - and (self.get_object().pool.tournament in request.user.registration.organized_tournaments.all() - or request.user.registration in self.get_object().pool.juries.all()) \ - or request.user.registration.participates and request.user.registration.team \ - and request.user.registration.team.participation in [self.get_object().defender, - self.get_object().opponent, - self.get_object().reporter]: + reg = request.user.registration + passage = self.get_object() + if reg.is_admin or reg.is_volunteer \ + and (self.get_object().pool.tournament in reg.organized_tournaments.all() + or reg in passage.pool.juries.all() + or reg.pools_presided.filter(tournament=passage.pool.tournament).exists()) \ + or reg.participates and reg.team \ + and reg.team.participation in [passage.defender, passage.opponent, passage.reporter, passage.observer]: return super().dispatch(request, *args, **kwargs) return self.handle_no_permission() def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - if self.request.user.registration in self.object.pool.juries.all(): + reg = self.request.user.registration + if reg in self.object.pool.juries.all(): context["my_note"] = Note.objects.get_or_create(passage=self.object, jury=self.request.user.registration)[0] - context["notes"] = NoteTable(self.object.notes.all()) - elif self.request.user.registration.is_admin: - context["notes"] = NoteTable(self.object.notes.all()) - if 'notes' in context and not self.object.observer: + if reg.is_volunteer: + notes = self.object.notes.all() + if not reg.is_admin \ + or (reg != self.object.pool.jury_president + and reg not in self.object.pool.tournament.organizers.all()): + notes = [note for note in notes if note.has_any_note()] + context["notes"] = NoteTable(notes) + # Only display the observer column for 4-teams pools - context['notes']._sequence.pop() + context['notes']._sequence.remove('observer_oral') + if 'notes' in context and not self.request.user.registration.is_admin: + context['notes']._sequence.remove('update') return context @@ -1634,8 +1664,9 @@ class NoteUpdateView(VolunteerMixin, UpdateView): if not request.user.is_authenticated: return self.handle_no_permission() - if request.user.registration.is_admin or request.user.registration.is_volunteer \ - and self.get_object().jury == request.user.registration: + reg = request.user.registration + note = self.get_object() + if reg.is_admin or reg.is_volunteer and (note.jury == reg or note.passage.pool.jury_president == reg): return super().dispatch(request, *args, **kwargs) return self.handle_no_permission() diff --git a/registration/views.py b/registration/views.py index b959ac3..19fce8d 100644 --- a/registration/views.py +++ b/registration/views.py @@ -797,9 +797,10 @@ class SolutionView(LoginRequiredMixin, View): else: passage_participant_qs = Passage.objects.none() if not (user.registration.is_admin - or user.registration.is_volunteer and user.registration - in (solution.participation.tournament - if not solution.final_solution else Tournament.final_tournament()).organizers.all() + or (user.registration.is_volunteer + and user.registration in solution.tournament.organizers.all()) + or (user.registration.is_volunteer + and user.registration.presided_pools.filter(tournament=solution.tournament).exists()) or user.registration.is_volunteer and Passage.objects.filter(Q(pool__juries=user.registration) | Q(pool__tournament__in=user.registration.organized_tournaments.all()), @@ -834,7 +835,8 @@ class SynthesisView(LoginRequiredMixin, View): user = request.user if not (user.registration.is_admin or user.registration.is_volunteer and (user.registration in synthesis.passage.pool.juries.all() - or user.registration in synthesis.passage.pool.tournament.organizers.all()) + or user.registration in synthesis.passage.pool.tournament.organizers.all() + or user.registration.presided_pools.filter(tournament=synthesis.passage.pool.tournament).exists()) or user.registration.participates and user.registration.team == synthesis.participation.team): raise PermissionDenied # Guess mime type of the file