Add harmonization view

Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
Emmy D'Anello 2024-03-30 20:38:13 +01:00
parent 109b603b7a
commit 1e7bd209a1
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
6 changed files with 238 additions and 68 deletions

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: TFJM\n" "Project-Id-Version: TFJM\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-03-30 19:13+0100\n" "POT-Creation-Date: 2024-03-30 20:33+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Emmy D'Anello <emmy.danello@animath.fr>\n" "Last-Translator: Emmy D'Anello <emmy.danello@animath.fr>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -333,6 +333,7 @@ msgstr "Continuer le tirage"
#: draw/templates/draw/tournament_content.html:216 participation/admin.py:167 #: draw/templates/draw/tournament_content.html:216 participation/admin.py:167
#: participation/models.py:253 participation/models.py:653 #: participation/models.py:253 participation/models.py:653
#: participation/templates/participation/tournament_harmonize.html:15
#: registration/models.py:156 registration/models.py:609 #: registration/models.py:156 registration/models.py:609
#: registration/tables.py:39 #: registration/tables.py:39
#: registration/templates/registration/payment_form.html:52 #: registration/templates/registration/payment_form.html:52
@ -377,8 +378,8 @@ msgstr "Êtes-vous sûr·e de vouloir annuler le tirage au sort ?"
msgid "Close" msgid "Close"
msgstr "Fermer" msgstr "Fermer"
#: draw/views.py:31 participation/views.py:155 participation/views.py:469 #: draw/views.py:31 participation/views.py:156 participation/views.py:470
#: participation/views.py:500 #: participation/views.py:501
msgid "You are not in a team." msgid "You are not in a team."
msgstr "Vous n'êtes pas dans une équipe." msgstr "Vous n'êtes pas dans une équipe."
@ -490,7 +491,7 @@ msgstr "Ce trigramme est déjà utilisé."
msgid "No team was found with this access code." msgid "No team was found with this access code."
msgstr "Aucune équipe n'a été trouvée avec ce code d'accès." msgstr "Aucune équipe n'a été trouvée avec ce code d'accès."
#: participation/forms.py:59 participation/views.py:471 #: participation/forms.py:59 participation/views.py:472
msgid "The team is already validated or the validation is pending." msgid "The team is already validated or the validation is pending."
msgstr "La validation de l'équipe est déjà faite ou en cours." msgstr "La validation de l'équipe est déjà faite ou en cours."
@ -1355,6 +1356,7 @@ msgstr "Aller à la page Google Sheets de la poule"
#: participation/templates/participation/pool_detail.html:83 #: participation/templates/participation/pool_detail.html:83
#: participation/templates/participation/tournament_detail.html:98 #: participation/templates/participation/tournament_detail.html:98
#: participation/templates/participation/tournament_harmonize.html:8
msgid "Ranking" msgid "Ranking"
msgstr "Classement" msgstr "Classement"
@ -1582,7 +1584,7 @@ msgid "Invalidate"
msgstr "Invalider" msgstr "Invalider"
#: participation/templates/participation/team_detail.html:209 #: participation/templates/participation/team_detail.html:209
#: participation/views.py:326 #: participation/views.py:327
msgid "Upload motivation letter" msgid "Upload motivation letter"
msgstr "Envoyer la lettre de motivation" msgstr "Envoyer la lettre de motivation"
@ -1591,7 +1593,7 @@ msgid "Update team"
msgstr "Modifier l'équipe" msgstr "Modifier l'équipe"
#: participation/templates/participation/team_detail.html:219 #: participation/templates/participation/team_detail.html:219
#: participation/views.py:463 #: participation/views.py:464
msgid "Leave team" msgid "Leave team"
msgstr "Quitter l'équipe" msgstr "Quitter l'équipe"
@ -1685,10 +1687,41 @@ msgstr "Publier les notes pour le premier tour"
msgid "Publish notes for second round" msgid "Publish notes for second round"
msgstr "Publier les notes pour le second tour" msgstr "Publier les notes pour le second tour"
#: participation/templates/participation/tournament_detail.html:133 #: participation/templates/participation/tournament_detail.html:128
#: participation/templates/participation/tournament_detail.html:132
msgid "Harmonize"
msgstr "Harmoniser"
#: participation/templates/participation/tournament_detail.html:128
#: participation/templates/participation/tournament_detail.html:132
msgid "Day"
msgstr "Jour"
#: participation/templates/participation/tournament_detail.html:143
msgid "Files available for download" msgid "Files available for download"
msgstr "Fichiers disponibles au téléchargement" msgstr "Fichiers disponibles au téléchargement"
#: participation/templates/participation/tournament_harmonize.html:14
msgid "Rank"
msgstr "Rang"
#: participation/templates/participation/tournament_harmonize.html:16
msgid "Note"
msgstr "Note"
#: participation/templates/participation/tournament_harmonize.html:17
msgid "Including bonus / malus"
msgstr "Dont bonus / malus"
#: participation/templates/participation/tournament_harmonize.html:18
msgid "Add bonus / malus"
msgstr "Ajouter bonus / malus"
#: participation/templates/participation/tournament_harmonize.html:48
#: participation/templates/participation/tournament_payments.html:9
msgid "Back to tournament page"
msgstr "Retour à la page du tournoi"
#: participation/templates/participation/tournament_list.html:6 #: participation/templates/participation/tournament_list.html:6
#: tfjm/templates/base.html:67 #: tfjm/templates/base.html:67
msgid "All tournaments" msgid "All tournaments"
@ -1698,10 +1731,6 @@ msgstr "Tous les tournois"
msgid "Add tournament" msgid "Add tournament"
msgstr "Ajouter un tournoi" msgstr "Ajouter un tournoi"
#: participation/templates/participation/tournament_payments.html:9
msgid "Back to tournament page"
msgstr "Retour à la page du tournoi"
#: participation/templates/participation/upload_motivation_letter.html:6 #: participation/templates/participation/upload_motivation_letter.html:6
msgid "Back to the team detail" msgid "Back to the team detail"
msgstr "Retour aux détails de l'utilisateur⋅rice" msgstr "Retour aux détails de l'utilisateur⋅rice"
@ -1718,44 +1747,44 @@ msgstr "Modèles :"
msgid "Warning: non-free format" msgid "Warning: non-free format"
msgstr "Attention : format non libre" msgstr "Attention : format non libre"
#: participation/views.py:55 tfjm/templates/base.html:79 #: participation/views.py:56 tfjm/templates/base.html:79
#: tfjm/templates/navbar.html:35 #: tfjm/templates/navbar.html:35
msgid "Create team" msgid "Create team"
msgstr "Créer une équipe" msgstr "Créer une équipe"
#: participation/views.py:64 participation/views.py:105 #: participation/views.py:65 participation/views.py:106
msgid "You don't participate, so you can't create a team." msgid "You don't participate, so you can't create a team."
msgstr "Vous ne participez pas, vous ne pouvez pas créer d'équipe." msgstr "Vous ne participez pas, vous ne pouvez pas créer d'équipe."
#: participation/views.py:66 participation/views.py:107 #: participation/views.py:67 participation/views.py:108
msgid "You are already in a team." msgid "You are already in a team."
msgstr "Vous êtes déjà dans une équipe." msgstr "Vous êtes déjà dans une équipe."
#: participation/views.py:96 tfjm/templates/base.html:74 #: participation/views.py:97 tfjm/templates/base.html:74
#: tfjm/templates/navbar.html:40 #: tfjm/templates/navbar.html:40
msgid "Join team" msgid "Join team"
msgstr "Rejoindre une équipe" msgstr "Rejoindre une équipe"
#: participation/views.py:156 participation/views.py:501 #: participation/views.py:157 participation/views.py:502
msgid "You don't participate, so you don't have any team." msgid "You don't participate, so you don't have any team."
msgstr "Vous ne participez pas, vous n'avez donc pas d'équipe." msgstr "Vous ne participez pas, vous n'avez donc pas d'équipe."
#: participation/views.py:182 #: participation/views.py:183
#, python-brace-format #, python-brace-format
msgid "Detail of team {trigram}" msgid "Detail of team {trigram}"
msgstr "Détails de l'équipe {trigram}" msgstr "Détails de l'équipe {trigram}"
#: participation/views.py:211 #: participation/views.py:212
msgid "You don't participate, so you can't request the validation of the team." msgid "You don't participate, so you can't request the validation of the team."
msgstr "" msgstr ""
"Vous ne participez pas, vous ne pouvez pas demander la validation de " "Vous ne participez pas, vous ne pouvez pas demander la validation de "
"l'équipe." "l'équipe."
#: participation/views.py:214 #: participation/views.py:215
msgid "The validation of the team is already done or pending." msgid "The validation of the team is already done or pending."
msgstr "La validation de l'équipe est déjà faite ou en cours." msgstr "La validation de l'équipe est déjà faite ou en cours."
#: participation/views.py:217 #: participation/views.py:218
msgid "" msgid ""
"The team can't be validated: missing email address confirmations, " "The team can't be validated: missing email address confirmations, "
"authorizations, people, motivation letter or the tournament is not set." "authorizations, people, motivation letter or the tournament is not set."
@ -1764,158 +1793,163 @@ msgstr ""
"d'adresse e-mail, soit une autorisation, soit des personnes, soit la lettre " "d'adresse e-mail, soit une autorisation, soit des personnes, soit la lettre "
"de motivation, soit le tournoi n'a pas été choisi." "de motivation, soit le tournoi n'a pas été choisi."
#: participation/views.py:239 #: participation/views.py:240
msgid "You are not an organizer of the tournament." msgid "You are not an organizer of the tournament."
msgstr "Vous n'êtes pas un⋅e organisateur⋅rice du tournoi." msgstr "Vous n'êtes pas un⋅e organisateur⋅rice du tournoi."
#: participation/views.py:242 #: participation/views.py:243
msgid "This team has no pending validation." msgid "This team has no pending validation."
msgstr "L'équipe n'a pas de validation en attente." msgstr "L'équipe n'a pas de validation en attente."
#: participation/views.py:269 #: participation/views.py:270
msgid "You must specify if you validate the registration or not." msgid "You must specify if you validate the registration or not."
msgstr "Vous devez spécifier si vous validez l'inscription ou non." msgstr "Vous devez spécifier si vous validez l'inscription ou non."
#: participation/views.py:304 #: participation/views.py:305
#, python-brace-format #, python-brace-format
msgid "Update team {trigram}" msgid "Update team {trigram}"
msgstr "Mise à jour de l'équipe {trigram}" msgstr "Mise à jour de l'équipe {trigram}"
#: participation/views.py:365 participation/views.py:448 #: participation/views.py:366 participation/views.py:449
#, python-brace-format #, python-brace-format
msgid "Motivation letter of {team}.{ext}" msgid "Motivation letter of {team}.{ext}"
msgstr "Lettre de motivation de {team}.{ext}" msgstr "Lettre de motivation de {team}.{ext}"
#: participation/views.py:397 #: participation/views.py:398
#, python-brace-format #, python-brace-format
msgid "Authorizations of team {trigram}.zip" msgid "Authorizations of team {trigram}.zip"
msgstr "Autorisations de l'équipe {trigram}.zip" msgstr "Autorisations de l'équipe {trigram}.zip"
#: participation/views.py:401 #: participation/views.py:402
#, python-brace-format #, python-brace-format
msgid "Authorizations of {tournament}.zip" msgid "Authorizations of {tournament}.zip"
msgstr "Autorisations du tournoi {tournament}.zip" msgstr "Autorisations du tournoi {tournament}.zip"
#: participation/views.py:417 #: participation/views.py:418
#, python-brace-format #, python-brace-format
msgid "Photo authorization of {participant}.{ext}" msgid "Photo authorization of {participant}.{ext}"
msgstr "Autorisation de droit à l'image de {participant}.{ext}" msgstr "Autorisation de droit à l'image de {participant}.{ext}"
#: participation/views.py:425 #: participation/views.py:426
#, python-brace-format #, python-brace-format
msgid "Parental authorization of {participant}.{ext}" msgid "Parental authorization of {participant}.{ext}"
msgstr "Autorisation parentale de {participant}.{ext}" msgstr "Autorisation parentale de {participant}.{ext}"
#: participation/views.py:433 #: participation/views.py:434
#, python-brace-format #, python-brace-format
msgid "Health sheet of {participant}.{ext}" msgid "Health sheet of {participant}.{ext}"
msgstr "Fiche sanitaire de {participant}.{ext}" msgstr "Fiche sanitaire de {participant}.{ext}"
#: participation/views.py:441 #: participation/views.py:442
#, python-brace-format #, python-brace-format
msgid "Vaccine sheet of {participant}.{ext}" msgid "Vaccine sheet of {participant}.{ext}"
msgstr "Carnet de vaccination de {participant}.{ext}" msgstr "Carnet de vaccination de {participant}.{ext}"
#: participation/views.py:515 #: participation/views.py:516
msgid "The team is not validated yet." msgid "The team is not validated yet."
msgstr "L'équipe n'est pas encore validée." msgstr "L'équipe n'est pas encore validée."
#: participation/views.py:529 #: participation/views.py:530
#, python-brace-format #, python-brace-format
msgid "Participation of team {trigram}" msgid "Participation of team {trigram}"
msgstr "Participation de l'équipe {trigram}" msgstr "Participation de l'équipe {trigram}"
#: participation/views.py:611 #: participation/views.py:612
#, python-brace-format #, python-brace-format
msgid "Payments of {tournament}" msgid "Payments of {tournament}"
msgstr "Paiements de {tournament}" msgstr "Paiements de {tournament}"
#: participation/views.py:706 #: participation/views.py:707
msgid "Notes published!" msgid "Notes published!"
msgstr "Notes publiées !" msgstr "Notes publiées !"
#: participation/views.py:742 #: participation/views.py:738
#, python-brace-format
msgid "Harmonize notes of {tournament} - Day {round}"
msgstr "Harmoniser les notes de {tournament} - Jour {round}"
#: participation/views.py:818
msgid "You can't upload a solution after the deadline." msgid "You can't upload a solution after the deadline."
msgstr "Vous ne pouvez pas envoyer de solution après la date limite." msgstr "Vous ne pouvez pas envoyer de solution après la date limite."
#: participation/views.py:866 #: participation/views.py:942
#, python-brace-format #, python-brace-format
msgid "Solutions of team {trigram}.zip" msgid "Solutions of team {trigram}.zip"
msgstr "Solutions de l'équipe {trigram}.zip" msgstr "Solutions de l'équipe {trigram}.zip"
#: participation/views.py:866 #: participation/views.py:942
#, python-brace-format #, python-brace-format
msgid "Syntheses of team {trigram}.zip" msgid "Syntheses of team {trigram}.zip"
msgstr "Notes de synthèse de l'équipe {trigram}.zip" msgstr "Notes de synthèse de l'équipe {trigram}.zip"
#: participation/views.py:883 participation/views.py:898 #: participation/views.py:959 participation/views.py:974
#, python-brace-format #, python-brace-format
msgid "Solutions of {tournament}.zip" msgid "Solutions of {tournament}.zip"
msgstr "Solutions de {tournament}.zip" msgstr "Solutions de {tournament}.zip"
#: participation/views.py:883 participation/views.py:898 #: participation/views.py:959 participation/views.py:974
#, python-brace-format #, python-brace-format
msgid "Syntheses of {tournament}.zip" msgid "Syntheses of {tournament}.zip"
msgstr "Notes de synthèse de {tournament}.zip" msgstr "Notes de synthèse de {tournament}.zip"
#: participation/views.py:907 #: participation/views.py:983
#, python-brace-format #, python-brace-format
msgid "Solutions for pool {pool} of tournament {tournament}.zip" msgid "Solutions for pool {pool} of tournament {tournament}.zip"
msgstr "Solutions pour la poule {pool} du tournoi {tournament}.zip" msgstr "Solutions pour la poule {pool} du tournoi {tournament}.zip"
#: participation/views.py:908 #: participation/views.py:984
#, python-brace-format #, python-brace-format
msgid "Syntheses for pool {pool} of tournament {tournament}.zip" msgid "Syntheses for pool {pool} of tournament {tournament}.zip"
msgstr "Notes de synthèses pour la poule {pool} du tournoi {tournament}.zip" msgstr "Notes de synthèses pour la poule {pool} du tournoi {tournament}.zip"
#: participation/views.py:950 #: participation/views.py:1026
#, python-brace-format #, python-brace-format
msgid "Jury of pool {pool} for {tournament} with teams {teams}" msgid "Jury of pool {pool} for {tournament} with teams {teams}"
msgstr "Jury de la poule {pool} pour {tournament} avec les équipes {teams}" msgstr "Jury de la poule {pool} pour {tournament} avec les équipes {teams}"
#: participation/views.py:966 #: participation/views.py:1042
#, python-brace-format #, python-brace-format
msgid "The jury {name} is already in the pool!" msgid "The jury {name} is already in the pool!"
msgstr "{name} est déjà dans la poule !" msgstr "{name} est déjà dans la poule !"
#: participation/views.py:986 #: participation/views.py:1062
msgid "New TFJM² jury account" msgid "New TFJM² jury account"
msgstr "Nouveau compte de juré⋅e pour le TFJM²" msgstr "Nouveau compte de juré⋅e pour le TFJM²"
#: participation/views.py:1003 #: participation/views.py:1079
#, python-brace-format #, python-brace-format
msgid "The jury {name} has been successfully added!" msgid "The jury {name} has been successfully added!"
msgstr "{name} a été ajouté⋅e avec succès en tant que juré⋅e !" msgstr "{name} a été ajouté⋅e avec succès en tant que juré⋅e !"
#: participation/views.py:1039 #: participation/views.py:1115
#, python-brace-format #, python-brace-format
msgid "The jury {name} has been successfully removed!" msgid "The jury {name} has been successfully removed!"
msgstr "{name} a été retiré⋅e avec succès du jury !" msgstr "{name} a été retiré⋅e avec succès du jury !"
#: participation/views.py:1065 #: participation/views.py:1141
#, python-brace-format #, python-brace-format
msgid "The jury {name} has been successfully promoted president!" msgid "The jury {name} has been successfully promoted president!"
msgstr "{name} a été nommé⋅e président⋅e du jury !" msgstr "{name} a été nommé⋅e président⋅e du jury !"
#: participation/views.py:1093 #: participation/views.py:1169
msgid "The following user is not registered as a jury:" msgid "The following user is not registered as a jury:"
msgstr "L'utilisateur⋅rice suivant n'est pas inscrit⋅e en tant que juré⋅e :" msgstr "L'utilisateur⋅rice suivant n'est pas inscrit⋅e en tant que juré⋅e :"
#: participation/views.py:1107 #: participation/views.py:1183
msgid "Notes were successfully uploaded." msgid "Notes were successfully uploaded."
msgstr "Les notes ont bien été envoyées." msgstr "Les notes ont bien été envoyées."
#: participation/views.py:1740 #: participation/views.py:1816
#, python-brace-format #, python-brace-format
msgid "Notation sheets of pool {pool} of {tournament}.zip" msgid "Notation sheets of pool {pool} of {tournament}.zip"
msgstr "Feuilles de notations pour la poule {pool} du tournoi {tournament}.zip" msgstr "Feuilles de notations pour la poule {pool} du tournoi {tournament}.zip"
#: participation/views.py:1745 #: participation/views.py:1821
#, python-brace-format #, python-brace-format
msgid "Notation sheets of {tournament}.zip" msgid "Notation sheets of {tournament}.zip"
msgstr "Feuilles de notation de {tournament}.zip" msgstr "Feuilles de notation de {tournament}.zip"
#: participation/views.py:1924 #: participation/views.py:2000
msgid "You can't upload a synthesis after the deadline." msgid "You can't upload a synthesis after the deadline."
msgstr "Vous ne pouvez pas envoyer de note de synthèse après la date limite." msgstr "Vous ne pouvez pas envoyer de note de synthèse après la date limite."
@ -3385,12 +3419,3 @@ msgstr "Aucun résultat."
#: tfjm/templates/sidebar.html:10 tfjm/templates/sidebar.html:21 #: tfjm/templates/sidebar.html:10 tfjm/templates/sidebar.html:21
msgid "Informations" msgid "Informations"
msgstr "Informations" msgstr "Informations"
#~ msgid "Export as CSV"
#~ msgstr "Exporter en CSV"
#~ msgid "Add new pool"
#~ msgstr "Ajouter une nouvelle poule"
#~ msgid "Add pool"
#~ msgstr "Ajouter une poule"

View File

@ -201,4 +201,6 @@ class TournamentAdmin(admin.ModelAdmin):
@admin.register(Tweak) @admin.register(Tweak)
class TweakAdmin(admin.ModelAdmin): class TweakAdmin(admin.ModelAdmin):
list_display = ('participation', 'pool', 'diff',) list_display = ('participation', 'pool', 'diff',)
list_filter = ('pool__tournament', 'pool__round',)
search_fields = ('participation__team__name', 'participation__team__trigram',)
autocomplete_fields = ('participation', 'pool',) autocomplete_fields = ('participation', 'pool',)

View File

@ -104,9 +104,9 @@
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>
{% if not available_notes_1 or not available_notes_2 %}
{% if user.registration.is_admin or user.registration in tournament.organizers.all %} {% if user.registration.is_admin or user.registration in tournament.organizers.all %}
<div class="card-footer text-center"> <div class="card-footer text-center">
{% if not available_notes_1 or not available_notes_2 %}
<div class="btn-group"> <div class="btn-group">
{% if not available_notes_1 %} {% if not available_notes_1 %}
<a href="{% url 'participation:tournament_publish_notes' pk=tournament.pk round=1 %}" class="btn btn-info"> <a href="{% url 'participation:tournament_publish_notes' pk=tournament.pk round=1 %}" class="btn btn-info">
@ -121,8 +121,18 @@
</a> </a>
{% endif %} {% endif %}
</div> </div>
</div>
{% endif %} {% endif %}
<div class="btn-group">
<a href="{% url 'participation:tournament_harmonize' pk=tournament.pk round=1 %}" class="btn btn-secondary">
<i class="fas fa-ranking-star"></i>
{% trans "Harmonize" %} - {% trans "Day" %} 1
</a>
<a href="{% url 'participation:tournament_harmonize' pk=tournament.pk round=2 %}" class="btn btn-secondary">
<i class="fas fa-ranking-star"></i>
{% trans "Harmonize" %} - {% trans "Day" %} 2
</a>
</div>
</div>
{% endif %} {% endif %}
</div> </div>
{% endif %} {% endif %}

View File

@ -0,0 +1,52 @@
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<div class="card bg-body shadow">
<div class="card-header text-center">
<h5>{% trans "Ranking" %}</h5>
</div>
<div class="card-body">
<table class="table table-striped text-center">
<thead>
<tr>
<th>{% trans "Rank" %}</th>
<th>{% trans "team"|capfirst %}</th>
<th>{% trans "Note" %}</th>
<th>{% trans "Including bonus / malus" %}</th>
<th>{% trans "Add bonus / malus" %}</th>
</tr>
</thead>
<tbody>
{% for participation, note in notes %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ participation.team }}</td>
<td>{{ note.note|floatformat }}</td>
<td>{% if note.tweak >= 0 %}+{% endif %}{{ note.tweak }}</td>
<td>
<div class="btn-group">
<a href="{% url 'participation:tournament_harmonize_note' pk=tournament.pk round=round action="add" trigram=participation.team.trigram %}"
class="btn btn-sm btn-success">
+1
</a>
<a href="{% url 'participation:tournament_harmonize_note' pk=tournament.pk round=round action="remove" trigram=participation.team.trigram %}"
class="btn btn-sm btn-danger">
-1
</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="card-footer text-center">
<a class="btn btn-secondary" href="{% url 'participation:tournament_detail' pk=tournament.pk %}">
<i class="fas fa-arrow-left-long"></i>
{% trans "Back to tournament page" %}
</a>
</div>
</div>
{% endblock %}

View File

@ -11,7 +11,8 @@ from .views import CreateTeamView, FinalNotationSheetTemplateView, JoinTeamView,
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, \
TournamentListView, TournamentPaymentsView, TournamentPublishNotesView, TournamentUpdateView TournamentHarmonizeView, TournamentHarmonizeNoteView, TournamentListView, TournamentPaymentsView, \
TournamentPublishNotesView, TournamentUpdateView
app_name = "participation" app_name = "participation"
@ -47,6 +48,10 @@ urlpatterns = [
name="tournament_notation_sheets"), name="tournament_notation_sheets"),
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(),
name="tournament_harmonize"),
path("tournament/<int:pk>/harmonize/<int:round>/<str:action>/<str:trigram>/", TournamentHarmonizeNoteView.as_view(),
name="tournament_harmonize_note"),
path("pools/create/", PoolCreateView.as_view(), name="pool_create"), path("pools/create/", PoolCreateView.as_view(), name="pool_create"),
path("pools/<int:pk>/", PoolDetailView.as_view(), name="pool_detail"), path("pools/<int:pk>/", PoolDetailView.as_view(), name="pool_detail"),
path("pools/<int:pk>/update/", PoolUpdateView.as_view(), name="pool_update"), path("pools/<int:pk>/update/", PoolUpdateView.as_view(), name="pool_update"),

View File

@ -9,6 +9,7 @@ from tempfile import mkdtemp
from typing import Any, Dict from typing import Any, Dict
from zipfile import ZipFile from zipfile import ZipFile
import gspread
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
@ -41,7 +42,7 @@ 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, PoolTeamsForm, 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 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
@ -710,6 +711,81 @@ class TournamentPublishNotesView(VolunteerMixin, SingleObjectMixin, RedirectView
return reverse_lazy("participation:tournament_detail", args=(kwargs['pk'],)) return reverse_lazy("participation:tournament_detail", args=(kwargs['pk'],))
class TournamentHarmonizeView(VolunteerMixin, DetailView):
"""
Harmonize the notes of a tournament.
"""
model = Tournament
template_name = "participation/tournament_harmonize.html"
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return self.handle_no_permission()
tournament = self.get_object()
reg = request.user.registration
if not reg.is_admin and (not reg.is_volunteer or tournament not in reg.organized_tournaments.all()):
return self.handle_no_permission()
if self.kwargs['round'] not in (1, 2):
raise Http404
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
tournament = self.get_object()
context['round'] = self.kwargs['round']
context['pools'] = tournament.pools.filter(round=context["round"]).all()
context['title'] = _("Harmonize notes of {tournament} - Day {round}") \
.format(tournament=tournament, round=context["round"])
notes = dict()
for participation in self.object.participations.all():
note = sum(pool.average(participation) for pool in context['pools'])
tweak = sum(tweak.diff for tweak in participation.tweaks.filter(pool__in=context['pools']).all())
notes[participation] = {'note': note, 'tweak': tweak}
context["notes"] = sorted(notes.items(), key=lambda x: x[1]['note'], reverse=True)
return context
class TournamentHarmonizeNoteView(VolunteerMixin, DetailView):
model = Tournament
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return self.handle_no_permission()
tournament = self.get_object()
reg = request.user.registration
if not reg.is_admin and (not reg.is_volunteer or tournament not in reg.organized_tournaments.all()):
return self.handle_no_permission()
if self.kwargs['round'] not in (1, 2) or self.kwargs['action'] not in ('add', 'remove') \
or self.kwargs['trigram'] not in [p.team.trigram for p in tournament.participations.all()]:
raise Http404
return super().dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
tournament = self.get_object()
participation = tournament.participations.get(team__trigram=kwargs['trigram'])
pool = tournament.pools.get(round=kwargs['round'], participations=participation)
tweak_qs = Tweak.objects.filter(participation=participation, pool=pool)
old_diff = tweak_qs.first().diff if tweak_qs.exists() else 0
new_diff = old_diff + (1 if kwargs['action'] == 'add' else -1)
if new_diff == 0:
tweak_qs.delete()
else:
tweak_qs.update_or_create(defaults={'diff': new_diff},
create_defaults={'diff': new_diff, 'participation': participation, 'pool': pool})
gc = gspread.service_account_from_dict(settings.GOOGLE_SERVICE_CLIENT)
spreadsheet = gc.open_by_key(tournament.notes_sheet_id)
worksheet = spreadsheet.worksheet("Classement final")
column = 3 if kwargs['round'] == '1' else 5
row = worksheet.find(f"{participation.team.name} ({participation.team.trigram})", in_column=1).row
worksheet.update_cell(row, column, new_diff)
return redirect(reverse_lazy("participation:tournament_harmonize", args=(tournament.pk, kwargs['round'],)))
class SolutionUploadView(LoginRequiredMixin, FormView): class SolutionUploadView(LoginRequiredMixin, FormView):
template_name = "participation/upload_solution.html" template_name = "participation/upload_solution.html"
form_class = SolutionForm form_class = SolutionForm