1
0
mirror of https://gitlab.com/animath/si/plateforme.git synced 2025-02-24 14:21:19 +00:00

Compare commits

..

No commits in common. "630633bab48f3094bf4cd9618a203ce51089e850" and "29b01ebb132b1a30d6892363f394dd97495e4bd2" have entirely different histories.

6 changed files with 99 additions and 41 deletions

View File

@ -399,8 +399,6 @@ class Pool(models.Model):
passage.observer = tds[line[3]].participation passage.observer = tds[line[3]].participation
await passage.asave() await passage.asave()
await sync_to_async(self.associated_pool.update_spreadsheet)()
return self.associated_pool return self.associated_pool
def __str__(self): def __str__(self):

View File

@ -192,6 +192,24 @@ class PoolForm(forms.ModelForm):
} }
class PoolTeamsForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["participations"].queryset = self.instance.tournament.participations.all()
class Meta:
model = Pool
fields = ('participations',)
widgets = {
"participations": forms.SelectMultiple(attrs={
'class': 'selectpicker',
'data-live-search': 'true',
'data-live-search-normalize': 'true',
'data-width': 'fit',
}),
}
class AddJuryForm(forms.ModelForm): class AddJuryForm(forms.ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)

View File

@ -453,23 +453,15 @@ class Tournament(models.Model):
tweak1_qs = Tweak.objects.filter(pool=pool1, participation=participation) tweak1_qs = Tweak.objects.filter(pool=pool1, participation=participation)
tweak1 = tweak1_qs.get() if tweak1_qs.exists() else None tweak1 = tweak1_qs.get() if tweak1_qs.exists() else None
pool2 = self.pools.get(round=2, participations=participation)
passage2 = pool2.passages.get(defender=participation)
tweak2_qs = Tweak.objects.filter(pool=pool2, participation=participation)
tweak2 = tweak2_qs.get() if tweak2_qs.exists() else None
line.append(f"=SIERREUR('Poule {pool1.short_name}'!$D{pool1.juries.count() + 10 + passage1.position}; 0)") line.append(f"=SIERREUR('Poule {pool1.short_name}'!$D{pool1.juries.count() + 10 + passage1.position}; 0)")
line.append(tweak1.diff if tweak1 else 0) line.append(tweak1.diff if tweak1 else 0)
line.append(f"=SIERREUR('Poule {pool2.short_name}'!$D{pool2.juries.count() + 10 + passage2.position}; 0)")
if self.pools.filter(round=2, participations=participation).exists(): line.append(tweak2.diff if tweak2 else 0)
pool2 = self.pools.get(round=2, participations=participation)
passage2 = pool2.passages.get(defender=participation)
tweak2_qs = Tweak.objects.filter(pool=pool2, participation=participation)
tweak2 = tweak2_qs.get() if tweak2_qs.exists() else None
line.append(
f"=SIERREUR('Poule {pool2.short_name}'!$D{pool2.juries.count() + 10 + passage2.position}; 0)")
line.append(tweak2.diff if tweak2 else 0)
else:
# User has no second pool yet
line.append(0)
line.append(0)
line.append(f"=$B{i + 2} + $C{i + 2} + $D{i + 2} + E{i + 2}") line.append(f"=$B{i + 2} + $C{i + 2} + $D{i + 2} + E{i + 2}")
line.append(f"=RANG($F{i + 2}; $F$2:$F${participations.count() + 1})") line.append(f"=RANG($F{i + 2}; $F$2:$F${participations.count() + 1})")
@ -617,24 +609,21 @@ class Tournament(models.Model):
trigram = line[0][-4:-1] trigram = line[0][-4:-1]
participation = self.participations.get(team__trigram=trigram) participation = self.participations.get(team__trigram=trigram)
pool1 = self.pools.get(round=1, participations=participation) pool1 = self.pools.get(round=1, participations=participation)
pool2 = self.pools.get(round=2, participations=participation)
tweak1_qs = Tweak.objects.filter(pool=pool1, participation=participation) tweak1_qs = Tweak.objects.filter(pool=pool1, participation=participation)
tweak1_nb = int(line[2]) tweak2_qs = Tweak.objects.filter(pool=pool2, participation=participation)
tweak1_nb, tweak2_nb = int(line[2]), int(line[4])
if not tweak1_nb: if not tweak1_nb:
tweak1_qs.delete() tweak1_qs.delete()
else: else:
tweak1_qs.update_or_create(defaults={'diff': tweak1_nb}, tweak1_qs.update_or_create(defaults={'diff': tweak1_nb},
create_defaults={'diff': tweak1_nb, 'pool': pool1, create_defaults={'diff': tweak1_nb, 'pool': pool1,
'participation': participation}) 'participation': participation})
if not tweak2_nb:
if self.pools.filter(round=2, participations=participation).exists(): tweak2_qs.delete()
pool2 = self.pools.get(round=2, participations=participation) else:
tweak2_qs = Tweak.objects.filter(pool=pool2, participation=participation) tweak2_qs.update_or_create(defaults={'diff': tweak2_nb},
tweak2_nb = int(line[4]) create_defaults={'diff': tweak2_nb, 'pool': pool2,
if not tweak2_nb:
tweak2_qs.delete()
else:
tweak2_qs.update_or_create(defaults={'diff': tweak2_nb},
create_defaults={'diff': tweak2_nb, 'pool': pool2,
'participation': participation}) 'participation': participation})
def get_absolute_url(self): def get_absolute_url(self):
@ -1310,6 +1299,11 @@ class Pool(models.Model):
note.set_all(*note_line) note.set_all(*note_line)
note.save() note.save()
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
if os.getenv('GOOGLE_PRIVATE_KEY_ID', None): # Google Sheets support is enabled
self.update_juries_lines_spreadsheet()
super().save(force_insert, force_update, using, update_fields)
def __str__(self): def __str__(self):
return _("Pool of day {round} for tournament {tournament} with teams {teams}")\ return _("Pool of day {round} for tournament {tournament} with teams {teams}")\
.format(round=self.round, .format(round=self.round,

View File

@ -125,7 +125,9 @@
</div> </div>
{% if user.registration.is_volunteer %} {% if user.registration.is_volunteer %}
<div class="card-footer text-center"> <div class="card-footer text-center">
<button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#addPassageModal">{% trans "Add passage" %}</button>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#updatePoolModal">{% trans "Update" %}</button> <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#updatePoolModal">{% trans "Update" %}</button>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#updateTeamsModal">{% trans "Update teams" %}</button>
</div> </div>
{% endif %} {% endif %}
</div> </div>
@ -136,11 +138,21 @@
{% render_table passages %} {% render_table passages %}
{% trans "Add passage" as modal_title %}
{% trans "Add" as modal_button %}
{% url "participation:passage_create" pk=pool.pk as modal_action %}
{% include "base_modal.html" with modal_id="addPassage" modal_button_type="success" %}
{% trans "Update pool" as modal_title %} {% trans "Update pool" as modal_title %}
{% trans "Update" as modal_button %} {% trans "Update" as modal_button %}
{% url "participation:pool_update" pk=pool.pk as modal_action %} {% url "participation:pool_update" pk=pool.pk as modal_action %}
{% include "base_modal.html" with modal_id="updatePool" %} {% include "base_modal.html" with modal_id="updatePool" %}
{% trans "Update teams" as modal_title %}
{% trans "Update" as modal_button %}
{% url "participation:pool_update_teams" pk=pool.pk as modal_action %}
{% include "base_modal.html" with modal_id="updateTeams" %}
{% trans "Upload notes" as modal_title %} {% trans "Upload notes" as modal_title %}
{% trans "Upload" as modal_button %} {% trans "Upload" as modal_button %}
{% url "participation:pool_upload_notes" pk=pool.pk as modal_action %} {% url "participation:pool_upload_notes" pk=pool.pk as modal_action %}
@ -151,6 +163,8 @@
<script> <script>
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
initModal("updatePool", "{% url "participation:pool_update" pk=pool.pk %}") initModal("updatePool", "{% url "participation:pool_update" pk=pool.pk %}")
initModal("updateTeams", "{% url "participation:pool_update_teams" pk=pool.pk %}")
initModal("addPassage", "{% url "participation:passage_create" pk=pool.pk %}")
initModal("uploadNotes", "{% url "participation:pool_upload_notes" pk=pool.pk %}") initModal("uploadNotes", "{% url "participation:pool_upload_notes" pk=pool.pk %}")
}) })
</script> </script>

View File

@ -5,9 +5,9 @@ from django.urls import path
from django.views.generic import TemplateView from django.views.generic import TemplateView
from .views import CreateTeamView, FinalNotationSheetTemplateView, JoinTeamView, MyParticipationDetailView, \ from .views import CreateTeamView, FinalNotationSheetTemplateView, JoinTeamView, MyParticipationDetailView, \
MyTeamDetailView, NotationSheetsArchiveView, NoteUpdateView, ParticipationDetailView, \ MyTeamDetailView, NotationSheetsArchiveView, NoteUpdateView, ParticipationDetailView, PassageCreateView, \
PassageDetailView, PassageUpdateView, PoolCreateView, PoolDetailView, PoolJuryView, PoolNotesTemplateView, \ PassageDetailView, PassageUpdateView, PoolCreateView, PoolDetailView, PoolJuryView, PoolNotesTemplateView, \
PoolPresideJuryView, PoolRemoveJuryView, PoolUpdateView, PoolUploadNotesView, \ PoolPresideJuryView, PoolRemoveJuryView, PoolUpdateTeamsView, PoolUpdateView, PoolUploadNotesView, \
ScaleNotationSheetTemplateView, SolutionsDownloadView, SolutionUploadView, SynthesisUploadView, \ ScaleNotationSheetTemplateView, SolutionsDownloadView, SolutionUploadView, SynthesisUploadView, \
TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, \ TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamListView, TeamUpdateView, \
TeamUploadMotivationLetterView, TournamentCreateView, TournamentDetailView, TournamentExportCSVView, \ TeamUploadMotivationLetterView, TournamentCreateView, TournamentDetailView, TournamentExportCSVView, \
@ -60,11 +60,13 @@ urlpatterns = [
path("pools/<int:pk>/notation/scale/", ScaleNotationSheetTemplateView.as_view(), name="pool_scale_note_sheet"), path("pools/<int:pk>/notation/scale/", ScaleNotationSheetTemplateView.as_view(), name="pool_scale_note_sheet"),
path("pools/<int:pk>/notation/final/", FinalNotationSheetTemplateView.as_view(), name="pool_final_note_sheet"), path("pools/<int:pk>/notation/final/", FinalNotationSheetTemplateView.as_view(), name="pool_final_note_sheet"),
path("pools/<int:pool_id>/notation/sheets/", NotationSheetsArchiveView.as_view(), name="pool_notation_sheets"), path("pools/<int:pool_id>/notation/sheets/", NotationSheetsArchiveView.as_view(), name="pool_notation_sheets"),
path("pools/<int:pk>/update-teams/", PoolUpdateTeamsView.as_view(), name="pool_update_teams"),
path("pools/<int:pk>/jury/", PoolJuryView.as_view(), name="pool_jury"), path("pools/<int:pk>/jury/", PoolJuryView.as_view(), name="pool_jury"),
path("pools/<int:pk>/jury/remove/<int:jury_id>/", PoolRemoveJuryView.as_view(), name="pool_remove_jury"), path("pools/<int:pk>/jury/remove/<int:jury_id>/", PoolRemoveJuryView.as_view(), name="pool_remove_jury"),
path("pools/<int:pk>/jury/preside/<int:jury_id>/", PoolPresideJuryView.as_view(), name="pool_preside"), path("pools/<int:pk>/jury/preside/<int:jury_id>/", PoolPresideJuryView.as_view(), name="pool_preside"),
path("pools/<int:pk>/upload-notes/", PoolUploadNotesView.as_view(), name="pool_upload_notes"), path("pools/<int:pk>/upload-notes/", PoolUploadNotesView.as_view(), name="pool_upload_notes"),
path("pools/<int:pk>/upload-notes/template/", PoolNotesTemplateView.as_view(), name="pool_notes_template"), path("pools/<int:pk>/upload-notes/template/", PoolNotesTemplateView.as_view(), name="pool_notes_template"),
path("pools/passages/add/<int:pk>/", PassageCreateView.as_view(), name="passage_create"),
path("pools/passages/<int:pk>/", PassageDetailView.as_view(), name="passage_detail"), path("pools/passages/<int:pk>/", PassageDetailView.as_view(), name="passage_detail"),
path("pools/passages/<int:pk>/update/", PassageUpdateView.as_view(), name="passage_update"), path("pools/passages/<int:pk>/update/", PassageUpdateView.as_view(), name="passage_update"),
path("pools/passages/<int:pk>/solution/", SynthesisUploadView.as_view(), name="upload_synthesis"), path("pools/passages/<int:pk>/solution/", SynthesisUploadView.as_view(), name="upload_synthesis"),

View File

@ -40,7 +40,7 @@ from tfjm.lists import get_sympa_client
from tfjm.views import AdminMixin, VolunteerMixin from tfjm.views import AdminMixin, VolunteerMixin
from .forms import AddJuryForm, JoinTeamForm, MotivationLetterForm, NoteForm, ParticipationForm, PassageForm, \ from .forms import AddJuryForm, JoinTeamForm, MotivationLetterForm, NoteForm, ParticipationForm, PassageForm, \
PoolForm, RequestValidationForm, SolutionForm, SynthesisForm, TeamForm, TournamentForm, \ PoolForm, PoolTeamsForm, RequestValidationForm, SolutionForm, SynthesisForm, TeamForm, TournamentForm, \
UploadNotesForm, ValidateParticipationForm UploadNotesForm, ValidateParticipationForm
from .models import Note, Participation, Passage, Pool, Solution, Synthesis, Team, Tournament, Tweak from .models import Note, Participation, Passage, Pool, Solution, Synthesis, Team, Tournament, Tweak
from .tables import NoteTable, ParticipationTable, PassageTable, PoolTable, TeamTable, TournamentTable from .tables import NoteTable, ParticipationTable, PassageTable, PoolTable, TeamTable, TournamentTable
@ -880,12 +880,19 @@ class PoolUpdateView(VolunteerMixin, UpdateView):
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
return self.handle_no_permission() return self.handle_no_permission()
def form_valid(self, form):
ret = super().form_valid(form) class PoolUpdateTeamsView(VolunteerMixin, UpdateView):
# Update Google Sheets juries lines model = Pool
if os.getenv('GOOGLE_PRIVATE_KEY_ID', None): form_class = PoolTeamsForm
self.object.update_juries_lines_spreadsheet()
return ret 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()):
return super().dispatch(request, *args, **kwargs)
return self.handle_no_permission()
class SolutionsDownloadView(VolunteerMixin, View): class SolutionsDownloadView(VolunteerMixin, View):
@ -1068,10 +1075,6 @@ class PoolJuryView(VolunteerMixin, FormView, DetailView):
self.object.juries.add(reg) self.object.juries.add(reg)
self.object.save() self.object.save()
# Update Google Sheets juries lines
if os.getenv('GOOGLE_PRIVATE_KEY_ID', None):
self.object.update_juries_lines_spreadsheet()
# Add notification # Add notification
messages.success(self.request, _("The jury {name} has been successfully added!") messages.success(self.request, _("The jury {name} has been successfully added!")
.format(name=f"{user.first_name} {user.last_name}")) .format(name=f"{user.first_name} {user.last_name}"))
@ -1867,6 +1870,35 @@ class NotationSheetsArchiveView(VolunteerMixin, DetailView):
return response return response
class PassageCreateView(VolunteerMixin, CreateView):
model = Passage
form_class = PassageForm
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return self.handle_no_permission()
qs = Pool.objects.filter(pk=self.kwargs["pk"])
if not qs.exists():
raise Http404
self.pool = qs.get()
if request.user.registration.is_admin or request.user.registration.is_volunteer \
and (self.pool.tournament in request.user.registration.organized_tournaments.all()
or request.user.registration in self.pool.juries.all()):
return super().dispatch(request, *args, **kwargs)
return self.handle_no_permission()
def get_form(self, form_class=None):
form = super().get_form(form_class)
form.instance.pool = self.pool
form.fields["defender"].queryset = self.pool.participations.all()
form.fields["opponent"].queryset = self.pool.participations.all()
form.fields["reporter"].queryset = self.pool.participations.all()
return form
class PassageDetailView(LoginRequiredMixin, DetailView): class PassageDetailView(LoginRequiredMixin, DetailView):
model = Passage model = Passage