1
0
mirror of https://gitlab.com/animath/si/plateforme.git synced 2025-06-22 15:58:24 +02:00

Compare commits

..

12 Commits

17 changed files with 557 additions and 415 deletions

View File

@ -4,12 +4,10 @@ ENV PYTHONUNBUFFERED 1
ENV DJANGO_ALLOW_ASYNC_UNSAFE 1 ENV DJANGO_ALLOW_ASYNC_UNSAFE 1
RUN apk add --no-cache gettext nginx gcc git libc-dev libffi-dev libpq-dev libxml2-dev libxslt-dev \ RUN apk add --no-cache gettext nginx gcc git libc-dev libffi-dev libpq-dev libxml2-dev libxslt-dev \
npm libmagic texlive texmf-dist-fontsrecommended texmf-dist-lang texmf-dist-latexextra libmagic texlive texmf-dist-fontsrecommended texmf-dist-lang texmf-dist-latexextra uglify-js
RUN apk add --no-cache bash RUN apk add --no-cache bash
RUN npm install -g yuglify
RUN mkdir /code /code/docs RUN mkdir /code /code/docs
WORKDIR /code WORKDIR /code
COPY requirements.txt /code/requirements.txt COPY requirements.txt /code/requirements.txt

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,9 @@
# Copyright (C) 2020 by Animath # Copyright (C) 2020 by Animath
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from django.conf import settings
from django.contrib import admin from django.contrib import admin
from django.http import HttpRequest
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from .models import Note, Participation, Passage, Pool, Solution, Team, Tournament, Tweak, WrittenReview from .models import Note, Participation, Passage, Pool, Solution, Team, Tournament, Tweak, WrittenReview
@ -51,9 +53,14 @@ class PassageInline(admin.TabularInline):
model = Passage model = Passage
extra = 0 extra = 0
ordering = ('position',) ordering = ('position',)
autocomplete_fields = ('reporter', 'opponent', 'reviewer', 'observer',)
show_change_link = True show_change_link = True
def get_autocomplete_fields(self, request: HttpRequest) -> tuple[str]:
fields = ('reporter', 'opponent', 'reviewer',)
if settings.HAS_OBSERVER:
fields += ('observer',)
return fields
class NoteInline(admin.TabularInline): class NoteInline(admin.TabularInline):
model = Note model = Note
@ -113,12 +120,9 @@ class PoolAdmin(admin.ModelAdmin):
@admin.register(Passage) @admin.register(Passage)
class PassageAdmin(admin.ModelAdmin): class PassageAdmin(admin.ModelAdmin):
list_display = ('__str__', 'reporter_trigram', 'solution_number', 'opponent_trigram', 'reviewer_trigram',
'observer_trigram', 'pool_abbr', 'position', 'tournament')
list_filter = ('pool__tournament', 'pool__round', 'pool__letter', 'solution_number',) list_filter = ('pool__tournament', 'pool__round', 'pool__letter', 'solution_number',)
search_fields = ('pool__participations__team__name', 'pool__participations__team__trigram',) search_fields = ('pool__participations__team__name', 'pool__participations__team__trigram',)
ordering = ('pool__tournament', 'pool__round', 'pool__letter', 'position',) ordering = ('pool__tournament', 'pool__round', 'pool__letter', 'position',)
autocomplete_fields = ('pool', 'reporter', 'opponent', 'reviewer', 'observer',)
inlines = (NoteInline,) inlines = (NoteInline,)
@admin.display(description=_("reporter"), ordering='reporter__team__trigram') @admin.display(description=_("reporter"), ordering='reporter__team__trigram')
@ -135,7 +139,7 @@ class PassageAdmin(admin.ModelAdmin):
@admin.display(description=_("observer"), ordering='observer__team__trigram') @admin.display(description=_("observer"), ordering='observer__team__trigram')
def observer_trigram(self, record: Passage): def observer_trigram(self, record: Passage):
return record.observer.team.trigram return record.observer.team.trigram if record.observer else None
@admin.display(description=_("pool"), ordering='pool__letter') @admin.display(description=_("pool"), ordering='pool__letter')
def pool_abbr(self, record): def pool_abbr(self, record):
@ -145,15 +149,23 @@ class PassageAdmin(admin.ModelAdmin):
def tournament(self, record: Passage): def tournament(self, record: Passage):
return record.pool.tournament return record.pool.tournament
def get_list_display(self, request: HttpRequest) -> tuple[str]:
if settings.HAS_OBSERVER:
return ('__str__', 'reporter_trigram', 'solution_number', 'opponent_trigram',
'reviewer_trigram', 'observer_trigram', 'pool_abbr', 'position', 'tournament')
else:
return ('__str__', 'reporter_trigram', 'solution_number', 'opponent_trigram',
'reviewer_trigram', 'pool_abbr', 'position', 'tournament')
def get_autocomplete_fields(self, request: HttpRequest) -> tuple[str]:
fields = ('pool', 'reporter', 'opponent', 'reviewer',)
if settings.HAS_OBSERVER:
fields += ('observer',)
return fields
@admin.register(Note) @admin.register(Note)
class NoteAdmin(admin.ModelAdmin): class NoteAdmin(admin.ModelAdmin):
list_display = ('passage', 'pool', 'jury', 'reporter_writing', 'reporter_oral',
'opponent_writing', 'opponent_oral', 'reviewer_writing', 'reviewer_oral',
'observer_writing', 'observer_oral',)
list_filter = ('passage__pool__letter', 'passage__solution_number', 'jury',
'reporter_writing', 'reporter_oral', 'opponent_writing', 'opponent_oral',
'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_oral')
search_fields = ('jury__user__last_name', 'jury__user__first_name', 'passage__reporter__team__trigram',) search_fields = ('jury__user__last_name', 'jury__user__first_name', 'passage__reporter__team__trigram',)
autocomplete_fields = ('jury', 'passage',) autocomplete_fields = ('jury', 'passage',)
@ -161,6 +173,21 @@ class NoteAdmin(admin.ModelAdmin):
def pool(self, record): def pool(self, record):
return record.passage.pool.short_name return record.passage.pool.short_name
def get_list_display(self, request: HttpRequest) -> tuple[str]:
fields = ('passage', 'pool', 'jury', 'reporter_writing', 'reporter_oral',
'opponent_writing', 'opponent_oral', 'reviewer_writing', 'reviewer_oral',)
if settings.HAS_OBSERVER:
fields += ('observer_writing', 'observer_oral',)
return fields
def get_list_filter(self, request: HttpRequest) -> tuple[str]:
fields = ('passage__pool__letter', 'passage__solution_number', 'jury',
'reporter_writing', 'reporter_oral', 'opponent_writing', 'opponent_oral',
'reviewer_writing', 'reviewer_oral',)
if settings.HAS_OBSERVER:
fields += ('observer_writing', 'observer_oral',)
return fields
@admin.register(Solution) @admin.register(Solution)
class SolutionAdmin(admin.ModelAdmin): class SolutionAdmin(admin.ModelAdmin):

View File

@ -7,6 +7,7 @@ import re
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import Div, Field, HTML, Layout, Submit from crispy_forms.layout import Div, Field, HTML, Layout, Submit
from django import forms from django import forms
from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.validators import FileExtensionValidator from django.core.validators import FileExtensionValidator
@ -14,7 +15,6 @@ from django.utils.translation import gettext_lazy as _
import pandas import pandas
from pypdf import PdfReader from pypdf import PdfReader
from registration.models import VolunteerRegistration from registration.models import VolunteerRegistration
from tfjm import settings
from .models import Note, Participation, Passage, Pool, Solution, Team, Tournament, WrittenReview from .models import Note, Participation, Passage, Pool, Solution, Team, Tournament, WrittenReview
@ -405,6 +405,12 @@ class WrittenReviewForm(forms.ModelForm):
class NoteForm(forms.ModelForm): class NoteForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not settings.HAS_OBSERVER:
del self.fields['observer_writing']
del self.fields['observer_oral']
class Meta: class Meta:
model = Note model = Note
fields = ('reporter_writing', 'reporter_oral', 'opponent_writing', fields = ('reporter_writing', 'reporter_oral', 'opponent_writing',

View File

@ -5,11 +5,13 @@ from pathlib import Path
from django.conf import settings from django.conf import settings
from django.core.management import BaseCommand from django.core.management import BaseCommand
from django.utils.translation import activate
from participation.models import Solution, Tournament from participation.models import Solution, Tournament
class Command(BaseCommand): class Command(BaseCommand):
def handle(self, *args, **kwargs): def handle(self, *args, **kwargs):
activate(settings.PREFERRED_LANGUAGE_CODE)
base_dir = Path(__file__).parent.parent.parent.parent base_dir = Path(__file__).parent.parent.parent.parent
base_dir /= "output" base_dir /= "output"
if not base_dir.is_dir(): if not base_dir.is_dir():

View File

@ -440,6 +440,10 @@ class Tournament(models.Model):
return Participation.objects.filter(final=True) return Participation.objects.filter(final=True)
return self.participation_set return self.participation_set
@property
def organizers_and_presidents(self):
return VolunteerRegistration.objects.filter(Q(admin=True) | Q(organized_tournaments=self) | Q(pools_presided__tournament=self))
@property @property
def solutions(self): def solutions(self):
if self.final: if self.final:
@ -932,10 +936,10 @@ class Participation(models.Model):
'content': content, 'content': content,
}) })
elif timezone.now() <= tournament.reviews_first_phase_limit + timedelta(hours=2): elif timezone.now() <= tournament.reviews_first_phase_limit + timedelta(hours=2):
reporter_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, reporter=self) reporter_passage = Passage.objects.get(pool__tournament=tournament, pool__round=1, reporter=self)
opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, opponent=self) opponent_passage = Passage.objects.get(pool__tournament=tournament, pool__round=1, opponent=self)
reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, reviewer=self) reviewer_passage = Passage.objects.get(pool__tournament=tournament, pool__round=1, reviewer=self)
observer_passage = Passage.objects.filter(pool__tournament=self.tournament, pool__round=1, observer=self) observer_passage = Passage.objects.filter(pool__tournament=tournament, pool__round=1, observer=self)
observer_passage = observer_passage.get() if observer_passage.exists() else None observer_passage = observer_passage.get() if observer_passage.exists() else None
reporter_text = _("<p>The solutions draw is ended. You can check the result on " reporter_text = _("<p>The solutions draw is ended. You can check the result on "
@ -997,10 +1001,10 @@ class Participation(models.Model):
'content': content, 'content': content,
}) })
elif timezone.now() <= tournament.reviews_second_phase_limit + timedelta(hours=2): elif timezone.now() <= tournament.reviews_second_phase_limit + timedelta(hours=2):
reporter_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, reporter=self) reporter_passage = Passage.objects.get(pool__tournament=tournament, pool__round=2, reporter=self)
opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, opponent=self) opponent_passage = Passage.objects.get(pool__tournament=tournament, pool__round=2, opponent=self)
reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, reviewer=self) reviewer_passage = Passage.objects.get(pool__tournament=tournament, pool__round=2, reviewer=self)
observer_passage = Passage.objects.filter(pool__tournament=self.tournament, pool__round=2, observer=self) observer_passage = Passage.objects.filter(pool__tournament=tournament, pool__round=2, observer=self)
observer_passage = observer_passage.get() if observer_passage.exists() else None observer_passage = observer_passage.get() if observer_passage.exists() else None
reporter_text = _("<p>For the second round, you will present " reporter_text = _("<p>For the second round, you will present "
@ -1061,10 +1065,10 @@ class Participation(models.Model):
}) })
elif settings.NB_ROUNDS >= 3 \ elif settings.NB_ROUNDS >= 3 \
and timezone.now() <= tournament.reviews_third_phase_limit + timedelta(hours=2): and timezone.now() <= tournament.reviews_third_phase_limit + timedelta(hours=2):
reporter_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, reporter=self) reporter_passage = Passage.objects.get(pool__tournament=tournament, pool__round=3, reporter=self)
opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, opponent=self) opponent_passage = Passage.objects.get(pool__tournament=tournament, pool__round=3, opponent=self)
reviewer_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, reviewer=self) reviewer_passage = Passage.objects.get(pool__tournament=tournament, pool__round=3, reviewer=self)
observer_passage = Passage.objects.filter(pool__tournament=self.tournament, pool__round=3, observer=self) observer_passage = Passage.objects.filter(pool__tournament=tournament, pool__round=3, observer=self)
observer_passage = observer_passage.get() if observer_passage.exists() else None observer_passage = observer_passage.get() if observer_passage.exists() else None
reporter_text = _("<p>For the third round, you will present " reporter_text = _("<p>For the third round, you will present "

View File

@ -1,6 +1,7 @@
# Copyright (C) 2020 by Animath # Copyright (C) 2020 by Animath
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from django.conf import settings
from django.utils import formats from django.utils import formats
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.text import format_lazy from django.utils.text import format_lazy
@ -106,8 +107,6 @@ class PoolTable(tables.Table):
class PassageTable(tables.Table): class PassageTable(tables.Table):
# FIXME Ne pas afficher l'équipe observatrice si non nécessaire
reporter = tables.LinkColumn( reporter = tables.LinkColumn(
"participation:passage_detail", "participation:passage_detail",
args=[tables.A("id")], args=[tables.A("id")],
@ -131,7 +130,9 @@ class PassageTable(tables.Table):
'class': 'table table-condensed table-striped text-center', 'class': 'table table-condensed table-striped text-center',
} }
model = Passage model = Passage
fields = ('reporter', 'opponent', 'reviewer', 'observer', 'solution_number', ) fields = ('reporter', 'opponent', 'reviewer',) \
+ (('observer',) if settings.HAS_OBSERVER else ()) \
+ ('solution_number', )
class NoteTable(tables.Table): class NoteTable(tables.Table):
@ -160,4 +161,6 @@ class NoteTable(tables.Table):
} }
model = Note model = Note
fields = ('jury', 'reporter_writing', 'reporter_oral', 'opponent_writing', 'opponent_oral', fields = ('jury', 'reporter_writing', 'reporter_oral', 'opponent_writing', 'opponent_oral',
'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_oral', 'update',) 'reviewer_writing', 'reviewer_oral',) + \
(('observer_writing', 'observer_oral') if settings.HAS_OBSERVER else ()) + \
('update',)

View File

@ -58,7 +58,7 @@
%%%%%%%%%%%%%%%%%%%%%DEFENSEUR %%%%%%%%%%%%%%%%%%%%%DEFENSEUR
\begin{tabular}{|c|p{25mm}|p{11cm}|c|{% for passage in passages.all %}p{2cm}|{% endfor %}}\hline \begin{tabular}{|c|p{25mm}|p{11cm}|c|{% for passage in passages.all %}p{2cm}|{% endfor %}}\hline
\multicolumn{4}{|l|}{The {\bf {% trans "Reporter" %}} \normalsize presents their ideas and major results for the solution of the problem.} {% for passage in passages.all %}& P.{{ forloop.counter }} - {{ passage.reporter.team.trigram }} {% endfor %}\\ \hline \hline \multicolumn{4}{|l|}{The {\bf {% trans "Reporter" %}} \normalsize presents their ideas and major results for the solution of the problem.} {% for passage in passages.all %}& Pb. {{ passage.solution_number }} - {{ passage.reporter.team.trigram }} {% endfor %}\\ \hline \hline
%ECRIT %ECRIT
\multirow{7}{3mm}{\bf \begin{turn}{90}WRITING\end{turn}} & \multirow{3}{20mm}{ {% trans "Scientific part" %}} & {% trans "Depth and difficulty of the elements presented" %} & [0,3] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}} \multirow{7}{3mm}{\bf \begin{turn}{90}WRITING\end{turn}} & \multirow{3}{20mm}{ {% trans "Scientific part" %}} & {% trans "Depth and difficulty of the elements presented" %} & [0,3] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}}
@ -86,7 +86,7 @@
%%%%%%%%%%%%%%%%%OPPOSANT⋅E %%%%%%%%%%%%%%%%%OPPOSANT⋅E
\begin{tabular}{|c|p{25mm}|p{11cm}|c{% for passage in passages.all %}|p{2cm}{% endfor %}|}\hline \begin{tabular}{|c|p{25mm}|p{11cm}|c{% for passage in passages.all %}|p{2cm}{% endfor %}|}\hline
\multicolumn{4}{|l|}{The {\bf {% trans "Opponent" %}} \normalsize provides a critical analysis of the solution and presentation.} \multicolumn{4}{|l|}{The {\bf {% trans "Opponent" %}} \normalsize provides a critical analysis of the solution and presentation.}
{% for passage in passages.all %}& P.{{ forloop.counter }} - {{ passage.opponent.team.trigram }} {% endfor %} \\ \hline \hline {% for passage in passages.all %}& Pb. {{ passage.solution_number }} - {{ passage.opponent.team.trigram }} {% endfor %} \\ \hline \hline
%ECRIT %ECRIT
\multirow{6}{3mm}{\bf \begin{turn}{90}WRITING\end{turn}} &\multirow{4}{25mm}{ {% trans "Scientific part" %}} & {% trans "Critical thinking and perspective on the proposed solution" %} & [0,3] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}} \multirow{6}{3mm}{\bf \begin{turn}{90}WRITING\end{turn}} &\multirow{4}{25mm}{ {% trans "Scientific part" %}} & {% trans "Critical thinking and perspective on the proposed solution" %} & [0,3] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}}
@ -108,7 +108,7 @@
%%%%%%%%%%%%%%%%%%%%%%RAPPORTEUR⋅RICE %%%%%%%%%%%%%%%%%%%%%%RAPPORTEUR⋅RICE
\begin{tabular}{|c|p{25mm}|p{11cm}|c{% for passage in passages.all %}|p{2cm}{% endfor %}|}\hline \begin{tabular}{|c|p{25mm}|p{11cm}|c{% for passage in passages.all %}|p{2cm}{% endfor %}|}\hline
\multicolumn{4}{|l|}{The {\bf {% trans "Reviewer" %}} \normalsize evaluates the debate between the Reporter and the Opponent.} {% for passage in passages.all %}& P.{{ forloop.counter }} - {{ passage.reviewer.team.trigram }} {% endfor %}\\ \hline \hline \multicolumn{4}{|l|}{The {\bf {% trans "Reviewer" %}} \normalsize evaluates the debate between the Reporter and the Opponent.} {% for passage in passages.all %}& Pb. {{ passage.solution_number }} - {{ passage.reviewer.team.trigram }} {% endfor %}\\ \hline \hline
%ECRIT %ECRIT
\multirow{6}{3mm}{\bf \begin{turn}{90}WRITING\end{turn}} &\multirow{4}{25mm}{ {% trans "Scientific part" %}} & {% trans "Critical thinking and perspective on the proposed solution" %} & [0,3] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}} \multirow{6}{3mm}{\bf \begin{turn}{90}WRITING\end{turn}} &\multirow{4}{25mm}{ {% trans "Scientific part" %}} & {% trans "Critical thinking and perspective on the proposed solution" %} & [0,3] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}}
@ -131,7 +131,7 @@
{% if TFJM.APP == "ETEAM" and pool.participations.count >= 4 %} {% if TFJM.APP == "ETEAM" and pool.participations.count >= 4 %}
%%%%%%%%%%%%%%%%%%%%%%OBSERVATEUR⋅RICE %%%%%%%%%%%%%%%%%%%%%%OBSERVATEUR⋅RICE
\begin{tabular}{|c|p{25mm}|p{11cm}|c{% for passage in passages.all %}|p{2cm}{% endfor %}|}\hline \begin{tabular}{|c|p{25mm}|p{11cm}|c{% for passage in passages.all %}|p{2cm}{% endfor %}|}\hline
\multicolumn{4}{|l|}{The {\bf {% trans "Observer" %}} \normalsize makes useful remarks on crucial points missed by the other participants.} {% for passage in passages.all %}& P.{{ forloop.counter }} - {{ passage.observer.team.trigram }} {% endfor %}\\ \hline \hline \multicolumn{4}{|l|}{The {\bf {% trans "Observer" %}} \normalsize makes useful remarks on crucial points missed by the other participants.} {% for passage in passages.all %}& Pb. {{ passage.solution_number }} - {{ passage.observer.team.trigram }} {% endfor %}\\ \hline \hline
%ECRIT %ECRIT
\multirow{6}{3mm}{\bf \begin{turn}{90}WRITING\end{turn}} &\multirow{4}{25mm}{ {% trans "Scientific part" %}} & {% trans "Critical thinking and perspective on the proposed solution" %} & [0,3] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}} \multirow{6}{3mm}{\bf \begin{turn}{90}WRITING\end{turn}} &\multirow{4}{25mm}{ {% trans "Scientific part" %}} & {% trans "Critical thinking and perspective on the proposed solution" %} & [0,3] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}}

View File

@ -52,7 +52,7 @@
%%%%%%%%%%%%%%%%%%%%%DEFENSEUR %%%%%%%%%%%%%%%%%%%%%DEFENSEUR
\begin{tabular}{|c|p{24mm}|p{11cm}|c|{% for passage in passages.all %}p{2cm}|{% endfor %}}\hline \begin{tabular}{|c|p{24mm}|p{11cm}|c|{% for passage in passages.all %}p{2cm}|{% endfor %}}\hline
\multicolumn{4}{|l|}{{\bf D\'efenseur⋅se} \normalsize pr\'esente les id\'ees et r\'esultats principaux pour la solution du probl\`eme.} {% for passage in passages.all %}& P.{{ forloop.counter }} - {{ passage.reporter.team.trigram }} {% endfor %}\\ \hline \hline \multicolumn{4}{|l|}{{\bf D\'efenseur⋅se} \normalsize pr\'esente les id\'ees et r\'esultats principaux pour la solution du probl\`eme.} {% for passage in passages.all %}& Pb. {{ passage.solution_number }} - {{ passage.reporter.team.trigram }} {% endfor %}\\ \hline \hline
%ECRIT %ECRIT
\multirow{7}{3mm}{\bf \begin{turn}{90}ÉCRIT\end{turn}} & \multirow{3}{24mm}{Partie scientifique} & Profondeur et difficulté des éléments présentés & [0,6] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}} \multirow{7}{3mm}{\bf \begin{turn}{90}ÉCRIT\end{turn}} & \multirow{3}{24mm}{Partie scientifique} & Profondeur et difficulté des éléments présentés & [0,6] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}}
@ -80,7 +80,7 @@
%%%%%%%%%%%%%%%%%OPPOSANT %%%%%%%%%%%%%%%%%OPPOSANT
\begin{tabular}{|c|p{24mm}|p{11cm}|c{% for passage in passages.all %}|p{2cm}{% endfor %}|}\hline \begin{tabular}{|c|p{24mm}|p{11cm}|c{% for passage in passages.all %}|p{2cm}{% endfor %}|}\hline
\multicolumn{4}{|l|}{L' {\bf Opposant⋅e} \normalsize fournit une analyse critique de la solution et de la pr\'esentation.} \multicolumn{4}{|l|}{L' {\bf Opposant⋅e} \normalsize fournit une analyse critique de la solution et de la pr\'esentation.}
{% for passage in passages.all %}& P.{{ forloop.counter }} - {{ passage.opponent.team.trigram }} {% endfor %} \\ \hline \hline {% for passage in passages.all %}& Pb. {{ passage.solution_number }} - {{ passage.opponent.team.trigram }} {% endfor %} \\ \hline \hline
%ECRIT %ECRIT
\multirow{6}{3mm}{\bf \begin{turn}{90}ÉCRIT\end{turn}} &\multirow{4}{24mm}{Partie scientifique} & Recul et esprit critique par rapport à la solution proposée & [0,3] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}} \multirow{6}{3mm}{\bf \begin{turn}{90}ÉCRIT\end{turn}} &\multirow{4}{24mm}{Partie scientifique} & Recul et esprit critique par rapport à la solution proposée & [0,3] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}}
@ -102,7 +102,7 @@
%%%%%%%%%%%%%%%%%%%%%%RAPPORTEUR.RICE %%%%%%%%%%%%%%%%%%%%%%RAPPORTEUR.RICE
\begin{tabular}{|c|p{24mm}|p{11cm}|c{% for passage in passages.all %}|p{2cm}{% endfor %}|}\hline \begin{tabular}{|c|p{24mm}|p{11cm}|c{% for passage in passages.all %}|p{2cm}{% endfor %}|}\hline
\multicolumn{4}{|l|}{{\bf Rapporteur⋅rice} \normalsize \'evalue le d\'ebat entre læ D\'efenseur⋅se et l'Opposant⋅e.} {% for passage in passages.all %}& P.{{ forloop.counter }} - {{ passage.reviewer.team.trigram }} {% endfor %}\\ \hline \hline \multicolumn{4}{|l|}{{\bf Rapporteur⋅rice} \normalsize \'evalue le d\'ebat entre læ D\'efenseur⋅se et l'Opposant⋅e.} {% for passage in passages.all %}& Pb. {{ passage.solution_number }} - {{ passage.reviewer.team.trigram }} {% endfor %}\\ \hline \hline
%ECRIT %ECRIT
\multirow{6}{3mm}{\bf \begin{turn}{90}ÉCRIT\end{turn}} &\multirow{4}{24mm}{Partie scientifique} & Recul et esprit critique par rapport à la solution proposée & [0,3] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}} \multirow{6}{3mm}{\bf \begin{turn}{90}ÉCRIT\end{turn}} &\multirow{4}{24mm}{Partie scientifique} & Recul et esprit critique par rapport à la solution proposée & [0,3] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}}
@ -115,7 +115,7 @@
\multirow{9}{3mm}{\bf \begin{turn}{90}ORAL\end{turn}} & \multirow{5}{24mm}{Questions et discours de læ rapporteur⋅rice} & \footnotesize Faire prendre de la hauteur au débat (par les sujets abordés, la pertinence des questions posées, les points soulevés, gestion du temps) & [0,3] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}} \multirow{9}{3mm}{\bf \begin{turn}{90}ORAL\end{turn}} & \multirow{5}{24mm}{Questions et discours de læ rapporteur⋅rice} & \footnotesize Faire prendre de la hauteur au débat (par les sujets abordés, la pertinence des questions posées, les points soulevés, gestion du temps) & [0,3] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}}
&& \footnotesize Créer un échange constructif entre les participants (formulation des questions, réaction aux réponses, articulation entre les questions, circulation de la parole) & [0,3] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}} && \footnotesize Créer un échange constructif entre les participants (formulation des questions, réaction aux réponses, articulation entre les questions, circulation de la parole) & [0,3] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}}
&& Capacité à évaluer la qualité des échanges (Défenseur⋅se-Opposant⋅e et à trois) & [0,2] {{ esp|safe }}\\ \cline{2-{{ passages.count|add:4 }}} && Capacité à évaluer la qualité des échanges (Défenseur⋅se-Opposant⋅e et à trois) & [0,2] {{ esp|safe }}\\ \cline{2-{{ passages.count|add:4 }}}
&& Réponses aux questions de læ Rapporteur⋅rice et du jury (fond et capacité à faire avancer le débat) & [0,2] {{ esp|safe }}\\ \cline{2-{{ passages.count|add:4 }}} && Réponses aux questions du jury (fond et capacité à faire avancer le débat) & [0,2] {{ esp|safe }}\\ \cline{2-{{ passages.count|add:4 }}}
& Malus & Attitude irrespectueuse ? & [-3,0] {{ esp|safe }}\\ \cline{2-{{ passages.count|add:4 }}} & Malus & Attitude irrespectueuse ? & [-3,0] {{ esp|safe }}\\ \cline{2-{{ passages.count|add:4 }}}
&\multicolumn{3}{|l|}{\bf TOTAL ORAL (/10)} {{ esp|safe }}\\ \hline &\multicolumn{3}{|l|}{\bf TOTAL ORAL (/10)} {{ esp|safe }}\\ \hline
\end{tabular} \end{tabular}

View File

@ -23,44 +23,80 @@
<dd class="col-sm-6">{% if tournament.price %}{{ tournament.price }} €{% else %}{% trans "Free" %}{% endif %}</dd> <dd class="col-sm-6">{% if tournament.price %}{{ tournament.price }} €{% else %}{% trans "Free" %}{% endif %}</dd>
{% endif %} {% endif %}
<dt class="col-sm-6 text-sm-end">{% trans 'remote'|capfirst %}</dt> <dt class="col-sm-6 text-sm-end">{% trans "remote"|capfirst %}</dt>
<dd class="col-sm-6">{{ tournament.remote|yesno }}</dd> <dd class="col-sm-6">{{ tournament.remote|yesno }}</dd>
<dt class="col-sm-6 text-sm-end">{% trans 'dates'|capfirst %}</dt> <dt class="col-sm-6 text-sm-end">{% trans "dates"|capfirst %}</dt>
<dd class="col-sm-6">{% trans "From" %} {{ tournament.date_start }} {% trans "to" %} {{ tournament.date_end }}</dd> <dd class="col-sm-6">{% trans "From" %} {{ tournament.date_start }} {% trans "to" %} {{ tournament.date_end }}</dd>
<dt class="col-sm-6 text-sm-end">{% trans 'date of registration closing'|capfirst %}</dt> <dt class="col-sm-6 text-sm-end">{% trans "date of registration closing"|capfirst %}</dt>
<dd class="col-sm-6">{{ tournament.inscription_limit }}</dd> <dd class="col-sm-6">{{ tournament.inscription_limit }}</dd>
<dt class="col-sm-6 text-sm-end">{% trans 'date of maximal solution submission'|capfirst %}</dt> <dt class="col-sm-6 text-sm-end">{% trans "date of maximal solution submission"|capfirst %}</dt>
<dd class="col-sm-6">{{ tournament.solution_limit }}</dd> <dd class="col-sm-6">{{ tournament.solution_limit }}</dd>
<dt class="col-sm-6 text-sm-end">{% trans 'date of the random draw'|capfirst %}</dt> <dt class="col-sm-6 text-sm-end">{% trans "date of the random draw"|capfirst %}</dt>
<dd class="col-sm-6">{{ tournament.solutions_draw }}</dd> <dd class="col-sm-6">{{ tournament.solutions_draw }}</dd>
<dt class="col-sm-6 text-sm-end">{% trans 'date of maximal written reviews submission for the first round'|capfirst %}</dt> <dt class="col-sm-6 text-sm-end">{% trans "date of maximal written reviews submission for the first round"|capfirst %}</dt>
<dd class="col-sm-6">{{ tournament.reviews_first_phase_limit }}</dd> <dd class="col-sm-6">{{ tournament.reviews_first_phase_limit }}</dd>
<dt class="col-sm-6 text-sm-end">{% trans 'date of maximal written reviews submission for the second round'|capfirst %}</dt> <dt class="col-sm-6 text-sm-end">{% trans "Solutions available for the second round" %}</dt>
<dd class="col-sm-6">
{{ tournament.solutions_available_second_phase|yesno }}
{% if user.is_authenticated and user.registration in tournament.organizers_and_presidents.all %}
{% now 'Y-m-d' as today %}
{% if not tournament.solutions_available_second_phase %}
{% if today >= tournament.date_first_phase|date:"Y-m-d" %}
<a href="{% url 'participation:tournament_publish_solutions' pk=tournament.pk round=2 %}" class="btn btn-sm btn-info"><i class="fas fa-eye"></i> {% trans "Publish" %}</a>
{% endif %}
{% else %}
{% if today <= tournament.date_second_phase|date:"Y-m-d" %}
<a href="{% url 'participation:tournament_publish_solutions' pk=tournament.pk round=2 %}?hide" class="btn btn-sm bg-danger"><i class="fas fa-eye-slash"></i> {% trans "Unpublish" %}</a>
{% endif %}
{% endif %}
{% endif %}
</dd>
<dt class="col-sm-6 text-sm-end">{% trans "date of maximal written reviews submission for the second round"|capfirst %}</dt>
<dd class="col-sm-6">{{ tournament.reviews_second_phase_limit }}</dd> <dd class="col-sm-6">{{ tournament.reviews_second_phase_limit }}</dd>
{% if TFJM.APP == "ETEAM" %} {% if TFJM.NB_ROUNDS == 3 %}
<dt class="col-sm-6 text-sm-end">{% trans 'date of maximal written reviews submission for the third round'|capfirst %}</dt> <dt class="col-sm-6 text-sm-end">{% trans "Solutions available for the third round" %}</dt>
<dd class="col-sm-6">
{{ tournament.solutions_available_third_phase|yesno }}
{% if tournament.solutions_available_second_phase and user.is_authenticated and user.registration in tournament.organizers_and_presidents.all %}
{% now 'Y-m-d' as today %}
{% if not tournament.solutions_available_third_phase %}
{% if today >= tournament.date_second_phase|date:"Y-m-d" %}
<a href="{% url 'participation:tournament_publish_solutions' pk=tournament.pk round=3 %}" class="btn btn-sm btn-info"><i class="fas fa-eye"></i> {% trans "Publish" %}</a>
{% endif %}
{% else %}
{% if today <= tournament.date_third_phase|date:"Y-m-d" %}
<a href="{% url 'participation:tournament_publish_solutions' pk=tournament.pk round=3 %}?hide" class="btn btn-sm bg-danger"><i class="fas fa-eye-slash"></i> {% trans "Unpublish" %}</a>
{% endif %}
{% endif %}
{% endif %}
</dd>
<dt class="col-sm-6 text-sm-end">{% trans "date of maximal written reviews submission for the third round"|capfirst %}</dt>
<dd class="col-sm-6">{{ tournament.reviews_third_phase_limit }}</dd> <dd class="col-sm-6">{{ tournament.reviews_third_phase_limit }}</dd>
{% endif %} {% endif %}
<dt class="col-sm-6 text-sm-end">{% trans 'description'|capfirst %}</dt> <dt class="col-sm-6 text-sm-end">{% trans "description"|capfirst %}</dt>
<dd class="col-sm-6">{{ tournament.description }}</dd> <dd class="col-sm-6">{{ tournament.description }}</dd>
{% if TFJM.ML_MANAGEMENT %} {% if TFJM.ML_MANAGEMENT %}
<dt class="col-sm-6 text-sm-end">{% trans 'To contact organizers' %}</dt> <dt class="col-sm-6 text-sm-end">{% trans "To contact organizers" %}</dt>
<dd class="col-sm-6"><a href="mailto:{{ tournament.organizers_email }}">{{ tournament.organizers_email }}</a></dd> <dd class="col-sm-6"><a href="mailto:{{ tournament.organizers_email }}">{{ tournament.organizers_email }}</a></dd>
<dt class="col-sm-6 text-sm-end">{% trans 'To contact juries' %}</dt> {% if user.is_authenticated and user.registration.is_volunteer %}
<dd class="col-sm-6"><a href="mailto:{{ tournament.jurys_email }}">{{ tournament.jurys_email }}</a></dd> <dt class="col-sm-6 text-sm-end">{% trans "To contact juries" %}</dt>
<dd class="col-sm-6"><a href="mailto:{{ tournament.jurys_email }}">{{ tournament.jurys_email }}</a></dd>
<dt class="col-sm-6 text-sm-end">{% trans 'To contact valid teams' %}</dt> <dt class="col-sm-6 text-sm-end">{% trans "To contact valid teams" %}</dt>
<dd class="col-sm-6"><a href="mailto:{{ tournament.teams_email }}">{{ tournament.teams_email }}</a></dd> <dd class="col-sm-6"><a href="mailto:{{ tournament.teams_email }}">{{ tournament.teams_email }}</a></dd>
{% endif %}
{% endif %} {% endif %}
</dl> </dl>
</div> </div>
@ -199,7 +235,7 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% endif %} {% endif %}
{% if user.registration.is_admin or user.registration in tournament.organizers.all %} {% if user.registration.is_admin or user.registration in tournament.organizers.all %}

View File

@ -12,7 +12,7 @@ from .views import CreateTeamView, FinalNotationSheetTemplateView, GSheetNotific
TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, \ TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, \
TeamUploadMotivationLetterView, TournamentCreateView, TournamentDetailView, TournamentExportCSVView, \ TeamUploadMotivationLetterView, TournamentCreateView, TournamentDetailView, TournamentExportCSVView, \
TournamentHarmonizeNoteView, TournamentHarmonizeView, TournamentListView, TournamentPaymentsView, \ TournamentHarmonizeNoteView, TournamentHarmonizeView, TournamentListView, TournamentPaymentsView, \
TournamentPublishNotesView, TournamentUpdateView, WrittenReviewUploadView TournamentPublishNotesView, TournamentPublishSolutionsView, TournamentUpdateView, WrittenReviewUploadView
app_name = "participation" app_name = "participation"
@ -48,6 +48,8 @@ urlpatterns = [
name="tournament_notation_sheets"), name="tournament_notation_sheets"),
path("tournament/<int:pk>/notation/notifications/", GSheetNotificationsView.as_view(), path("tournament/<int:pk>/notation/notifications/", GSheetNotificationsView.as_view(),
name="tournament_gsheet_notifications"), name="tournament_gsheet_notifications"),
path("tournament/<int:pk>/publish-solutions/<int:round>/", TournamentPublishSolutionsView.as_view(),
name="tournament_publish_solutions"),
path("tournament/<int:pk>/publish-notes/<int:round>/", TournamentPublishNotesView.as_view(), path("tournament/<int:pk>/publish-notes/<int:round>/", TournamentPublishNotesView.as_view(),
name="tournament_publish_notes"), name="tournament_publish_notes"),
path("tournament/<int:pk>/harmonize/<int:round>/", TournamentHarmonizeView.as_view(), path("tournament/<int:pk>/harmonize/<int:round>/", TournamentHarmonizeView.as_view(),

View File

@ -672,7 +672,7 @@ class TournamentPaymentsView(VolunteerMixin, SingleTableMixin, DetailView):
if self.object.final: if self.object.final:
payments = Payment.objects.filter(final=True) payments = Payment.objects.filter(final=True)
else: else:
payments = Payment.objects.filter(registrations__team__participation__tournament=self.get_object()) payments = Payment.objects.filter(registrations__team__participation__tournament=self.get_object(), final=False)
return payments.annotate(team_id=F('registrations__team')).order_by('-valid', 'registrations__team__trigram') \ return payments.annotate(team_id=F('registrations__team')).order_by('-valid', 'registrations__team__trigram') \
.distinct().all() .distinct().all()
@ -747,12 +747,12 @@ class TournamentPublishNotesView(VolunteerMixin, SingleObjectMixin, RedirectView
return self.handle_no_permission() return self.handle_no_permission()
tournament = self.get_object() tournament = self.get_object()
reg = request.user.registration reg = request.user.registration
if not reg.is_admin and (not reg.is_volunteer or tournament not in reg.organized_tournaments.all()): if not reg.is_volunteer or reg not in tournament.organizers_and_presidents.all():
return self.handle_no_permission() return self.handle_no_permission()
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
if int(kwargs["round"]) not in (1, 2): if int(kwargs["round"]) not in range(1, settings.NB_ROUNDS + 1):
raise Http404 raise Http404
tournament = Tournament.objects.get(pk=kwargs["pk"]) tournament = Tournament.objects.get(pk=kwargs["pk"])
@ -767,6 +767,45 @@ class TournamentPublishNotesView(VolunteerMixin, SingleObjectMixin, RedirectView
return reverse_lazy("participation:tournament_detail", args=(kwargs['pk'],)) return reverse_lazy("participation:tournament_detail", args=(kwargs['pk'],))
class TournamentPublishSolutionsView(VolunteerMixin, SingleObjectMixin, RedirectView):
"""
On rend les solutions du tour suivant accessibles aux équipes.
"""
model = Tournament
def dispatch(self, request, *args, **kwargs):
"""
Les admins, orgas et PJ peuvent rendre les solutions accessibles.
"""
if not request.user.is_authenticated:
return self.handle_no_permission()
tournament = self.get_object()
reg = request.user.registration
if not reg.is_volunteer or reg not in tournament.organizers_and_presidents.all():
return self.handle_no_permission()
return super().dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
if int(kwargs["round"]) not in range(2, settings.NB_ROUNDS + 1):
raise Http404
tournament = Tournament.objects.get(pk=kwargs["pk"])
publish_solutions = 'hide' not in request.GET
if int(kwargs['round']) == 2:
tournament.solutions_available_second_phase = publish_solutions
elif int(kwargs['round']) == 3:
tournament.solutions_available_third_phase = publish_solutions
tournament.save()
if 'hide' not in request.GET:
messages.success(request, _("Solutions are now available to teams!"))
else:
messages.warning(request, _("Solutions are not available to teams anymore."))
return super().get(request, *args, **kwargs)
def get_redirect_url(self, *args, **kwargs):
return reverse_lazy("participation:tournament_detail", args=(kwargs['pk'],))
class TournamentHarmonizeView(VolunteerMixin, DetailView): class TournamentHarmonizeView(VolunteerMixin, DetailView):
""" """
Harmonize the notes of a tournament. Harmonize the notes of a tournament.
@ -779,7 +818,7 @@ class TournamentHarmonizeView(VolunteerMixin, DetailView):
return self.handle_no_permission() return self.handle_no_permission()
tournament = self.get_object() tournament = self.get_object()
reg = request.user.registration reg = request.user.registration
if not reg.is_admin and (not reg.is_volunteer or tournament not in reg.organized_tournaments.all()): if not reg.is_volunteer or reg not in tournament.organizers_and_presidents.all():
return self.handle_no_permission() return self.handle_no_permission()
if self.kwargs['round'] not in range(1, settings.NB_ROUNDS + 1): if self.kwargs['round'] not in range(1, settings.NB_ROUNDS + 1):
raise Http404 raise Http404
@ -812,7 +851,7 @@ class TournamentHarmonizeNoteView(VolunteerMixin, DetailView):
return self.handle_no_permission() return self.handle_no_permission()
tournament = self.get_object() tournament = self.get_object()
reg = request.user.registration reg = request.user.registration
if not reg.is_admin and (not reg.is_volunteer or tournament not in reg.organized_tournaments.all()): if not reg.is_volunteer or reg not in tournament.organizers_and_presidents.all():
return self.handle_no_permission() return self.handle_no_permission()
if self.kwargs['round'] not in range(1, settings.NB_ROUNDS + 1) \ if self.kwargs['round'] not in range(1, settings.NB_ROUNDS + 1) \
or self.kwargs['action'] not in ('add', 'remove') \ or self.kwargs['action'] not in ('add', 'remove') \
@ -852,7 +891,7 @@ class SelectTeamFinalView(VolunteerMixin, DetailView):
return self.handle_no_permission() return self.handle_no_permission()
tournament = self.get_object() tournament = self.get_object()
reg = request.user.registration reg = request.user.registration
if not reg.is_admin and (not reg.is_volunteer or tournament not in reg.organized_tournaments.all()): if not reg.is_volunteer or reg not in tournament.organizers_and_presidents.all():
return self.handle_no_permission() return self.handle_no_permission()
participation_qs = tournament.participations.filter(pk=self.kwargs["participation_id"]) participation_qs = tournament.participations.filter(pk=self.kwargs["participation_id"])
if not participation_qs.exists(): if not participation_qs.exists():
@ -1003,17 +1042,14 @@ class SolutionsDownloadView(VolunteerMixin, View):
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
elif 'tournament_id' in kwargs: elif 'tournament_id' in kwargs:
tournament = Tournament.objects.get(pk=kwargs["tournament_id"]) tournament = Tournament.objects.get(pk=kwargs["tournament_id"])
if reg.is_volunteer \ if reg.is_volunteer and reg in tournament.organizers_and_presidents.all():
and (tournament in reg.organized_tournaments.all()
or reg.pools_presided.filter(tournament=tournament).exists()):
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
else: else:
pool = Pool.objects.get(pk=kwargs["pool_id"]) pool = Pool.objects.get(pk=kwargs["pool_id"])
tournament = pool.tournament tournament = pool.tournament
if reg.is_volunteer \ if reg.is_volunteer \
and (reg in tournament.organizers.all() and (reg in tournament.organizers_and_presidents.all()
or reg in pool.juries.all() or reg in pool.juries.all()):
or reg.pools_presided.filter(tournament=tournament).exists()):
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
return self.handle_no_permission() return self.handle_no_permission()
@ -2001,7 +2037,7 @@ class PassageDetailView(LoginRequiredMixin, DetailView):
reg = request.user.registration reg = request.user.registration
passage = self.get_object() passage = self.get_object()
if reg.is_admin or reg.is_volunteer \ if reg.is_admin or reg.is_volunteer \
and (self.get_object().pool.tournament in reg.organized_tournaments.all() and (reg in self.get_object().pool.tournament.organizers_and_presidents.all()
or reg in passage.pool.juries.all() or reg in passage.pool.juries.all()
or reg.pools_presided.filter(tournament=passage.pool.tournament).exists()) \ or reg.pools_presided.filter(tournament=passage.pool.tournament).exists()) \
or reg.participates and reg.team \ or reg.participates and reg.team \

View File

@ -1,3 +0,0 @@
{{ object.user.last_name }}
{{ object.user.first_name }}
{{ object.user.email }}

View File

@ -70,7 +70,7 @@ class Survey(models.Model):
teams = Team.objects.filter(participation__valid=True) teams = Team.objects.filter(participation__valid=True)
if self.tournament: if self.tournament:
teams = teams.filter(participation__tournament=self.tournament) teams = teams.filter(participation__tournament=self.tournament)
return teams.all() return teams.order_by('participation__tournament__name', 'trigram').all()
else: else:
if self.invite_coaches: if self.invite_coaches:
registrations = ParticipantRegistration.objects.filter(team__participation__valid=True) registrations = ParticipantRegistration.objects.filter(team__participation__valid=True)
@ -78,7 +78,7 @@ class Survey(models.Model):
registrations = StudentRegistration.objects.filter(team__participation__valid=True) registrations = StudentRegistration.objects.filter(team__participation__valid=True)
if self.tournament: if self.tournament:
registrations = registrations.filter(team__participation__tournament=self.tournament) registrations = registrations.filter(team__participation__tournament=self.tournament)
return registrations.all() return registrations.order_by('team__participation__tournament__name', 'team__trigram').all()
@property @property
def completed(self): def completed(self):

View File

@ -48,6 +48,7 @@
<thead> <thead>
<tr> <tr>
<th>{% trans "participant"|capfirst %}</th> <th>{% trans "participant"|capfirst %}</th>
<th>{% trans "tournament"|capfirst %}</th>
<th>{% trans "completed"|capfirst %}</th> <th>{% trans "completed"|capfirst %}</th>
</tr> </tr>
</thead> </thead>
@ -56,8 +57,10 @@
<tr class="{% if participant in survey.completed.all %}table-success{% else %}table-danger{% endif %}"> <tr class="{% if participant in survey.completed.all %}table-success{% else %}table-danger{% endif %}">
{% if survey.invite_team %} {% if survey.invite_team %}
<td>{% trans "Team" %} {{ participant.name }} ({{ participant.trigram }})</td> <td>{% trans "Team" %} {{ participant.name }} ({{ participant.trigram }})</td>
<td>{{ participant.participation.tournament.name }}</td>
{% else %} {% else %}
<td>{{ participant.user.first_name }} {{ participant.user.last_name }} ({% trans "team" %} {{ participant.team.trigram }})</td> <td>{{ participant.user.first_name }} {{ participant.user.last_name }} ({% trans "team" %} {{ participant.team.trigram }})</td>
<td>{{ participant.team.participation.tournament.name }}</td>
{% endif %} {% endif %}
{% if participant in survey.completed.all %} {% if participant in survey.completed.all %}
<td>{% trans "Yes" %}</td> <td>{% trans "Yes" %}</td>

View File

@ -213,6 +213,7 @@ STATICFILES_FINDERS = (
PIPELINE = { PIPELINE = {
'DISABLE_WRAPPER': True, 'DISABLE_WRAPPER': True,
'JS_COMPRESSOR': 'pipeline.compressors.uglifyjs.UglifyJSCompressor',
'JAVASCRIPT': { 'JAVASCRIPT': {
'main': { 'main': {
'source_filenames': ( 'source_filenames': (

View File

@ -23,7 +23,7 @@
</div> </div>
<div id="sidebar-card" class="collapse d-lg-block"> <div id="sidebar-card" class="collapse d-lg-block">
<div class="card-body"> <div class="card-body px-2 py-1">
{% for information in user.registration.important_informations %} {% for information in user.registration.important_informations %}
<div class="card my-2"> <div class="card my-2">
<div class="card-header bg-dark-subtle"> <div class="card-header bg-dark-subtle">