1
0
mirror of https://gitlab.com/animath/si/plateforme.git synced 2024-11-30 11:33:01 +00:00

Compare commits

..

No commits in common. "620bbe78170815b35f96c01c0b8bf24ceb8e62ad" and "696863f6c365f18ddba8db339aed5d4e0cf6d6ff" have entirely different histories.

26 changed files with 580 additions and 892 deletions

View File

@ -416,7 +416,7 @@ class Pool(models.Model):
passage_pool = pool2 passage_pool = pool2
passage_position = 1 + i // 2 passage_position = 1 + i // 2
reporter = tds[line[0]].participation defender = tds[line[0]].participation
opponent = tds[line[1]].participation opponent = tds[line[1]].participation
reviewer = tds[line[2]].participation reviewer = tds[line[2]].participation
observer = tds[line[3]].participation if self.size >= 4 and settings.TFJM_APP == "ETEAM" else None observer = tds[line[3]].participation if self.size >= 4 and settings.TFJM_APP == "ETEAM" else None
@ -426,11 +426,11 @@ class Pool(models.Model):
pool=passage_pool, pool=passage_pool,
position=passage_position, position=passage_position,
solution_number=tds[line[0]].accepted, solution_number=tds[line[0]].accepted,
reporter=reporter, defender=defender,
opponent=opponent, opponent=opponent,
reviewer=reviewer, reviewer=reviewer,
observer=observer, observer=observer,
reporter_penalties=tds[line[0]].penalty_int, defender_penalties=tds[line[0]].penalty_int,
) )
# Update Google Sheets # Update Google Sheets
@ -549,7 +549,7 @@ class TeamDraw(models.Model):
@property @property
def penalty(self): def penalty(self):
""" """
The penalty multiplier on the reporter oral, in percentage, which is a malus of 25% for each penalty. The penalty multiplier on the defender oral, in percentage, which is a malus of 25% for each penalty.
""" """
return 25 * self.penalty_int return 25 * self.penalty_int

View File

@ -521,9 +521,9 @@ document.addEventListener('DOMContentLoaded', () => {
teamTd.innerText = team teamTd.innerText = team
teamTr.append(teamTd) teamTr.append(teamTd)
let reporterTd = document.createElement('td') let defenderTd = document.createElement('td')
reporterTd.classList.add('text-center') defenderTd.classList.add('text-center')
reporterTd.innerText = 'Déf' defenderTd.innerText = 'Déf'
let opponentTd = document.createElement('td') let opponentTd = document.createElement('td')
opponentTd.classList.add('text-center') opponentTd.classList.add('text-center')
@ -537,29 +537,29 @@ document.addEventListener('DOMContentLoaded', () => {
if (poule.teams.length === 3) { if (poule.teams.length === 3) {
switch (i) { switch (i) {
case 0: case 0:
teamTr.append(reporterTd, reviewerTd, opponentTd) teamTr.append(defenderTd, reviewerTd, opponentTd)
break break
case 1: case 1:
teamTr.append(opponentTd, reporterTd, reviewerTd) teamTr.append(opponentTd, defenderTd, reviewerTd)
break break
case 2: case 2:
teamTr.append(reviewerTd, opponentTd, reporterTd) teamTr.append(reviewerTd, opponentTd, defenderTd)
break break
} }
} else if (poule.teams.length === 4) { } else if (poule.teams.length === 4) {
let emptyTd = document.createElement('td') let emptyTd = document.createElement('td')
switch (i) { switch (i) {
case 0: case 0:
teamTr.append(reporterTd, emptyTd, reviewerTd, opponentTd) teamTr.append(defenderTd, emptyTd, reviewerTd, opponentTd)
break break
case 1: case 1:
teamTr.append(opponentTd, reporterTd, emptyTd, reviewerTd) teamTr.append(opponentTd, defenderTd, emptyTd, reviewerTd)
break break
case 2: case 2:
teamTr.append(reviewerTd, opponentTd, reporterTd, emptyTd) teamTr.append(reviewerTd, opponentTd, defenderTd, emptyTd)
break break
case 3: case 3:
teamTr.append(emptyTd, reviewerTd, opponentTd, reporterTd) teamTr.append(emptyTd, reviewerTd, opponentTd, defenderTd)
break break
} }
} else if (poule.teams.length === 5) { } else if (poule.teams.length === 5) {
@ -567,19 +567,19 @@ document.addEventListener('DOMContentLoaded', () => {
let emptyTd2 = document.createElement('td') let emptyTd2 = document.createElement('td')
switch (i) { switch (i) {
case 0: case 0:
teamTr.append(reporterTd, emptyTd, opponentTd, reviewerTd, emptyTd2) teamTr.append(defenderTd, emptyTd, opponentTd, reviewerTd, emptyTd2)
break break
case 1: case 1:
teamTr.append(emptyTd, reporterTd, reviewerTd, emptyTd2, opponentTd) teamTr.append(emptyTd, defenderTd, reviewerTd, emptyTd2, opponentTd)
break break
case 2: case 2:
teamTr.append(opponentTd, emptyTd, reporterTd, emptyTd2, reviewerTd) teamTr.append(opponentTd, emptyTd, defenderTd, emptyTd2, reviewerTd)
break break
case 3: case 3:
teamTr.append(reviewerTd, opponentTd, emptyTd, reporterTd, emptyTd2) teamTr.append(reviewerTd, opponentTd, emptyTd, defenderTd, emptyTd2)
break break
case 4: case 4:
teamTr.append(emptyTd, reviewerTd, emptyTd2, opponentTd, reporterTd) teamTr.append(emptyTd, reviewerTd, emptyTd2, opponentTd, defenderTd)
break break
} }
} }
@ -662,7 +662,7 @@ document.addEventListener('DOMContentLoaded', () => {
let penaltyDiv = document.getElementById(`recap-${tid}-round-${round}-team-${team}-penalty`) let penaltyDiv = document.getElementById(`recap-${tid}-round-${round}-team-${team}-penalty`)
if (rejected.length > problems_count - RECOMMENDED_SOLUTIONS_COUNT) { if (rejected.length > problems_count - RECOMMENDED_SOLUTIONS_COUNT) {
// If more than P - 5 problems were rejected, add a penalty of 25% of the coefficient of the oral reporter // If more than P - 5 problems were rejected, add a penalty of 25% of the coefficient of the oral defender
// This is P - 6 for the ETEAM // This is P - 6 for the ETEAM
if (penaltyDiv === null) { if (penaltyDiv === null) {
penaltyDiv = document.createElement('div') penaltyDiv = document.createElement('div')

View File

@ -307,71 +307,71 @@
<td class="text-center">{{ td.participation.team.trigram }}</td> <td class="text-center">{{ td.participation.team.trigram }}</td>
{% if pool.size == 3 %} {% if pool.size == 3 %}
{% if forloop.counter == 1 %} {% if forloop.counter == 1 %}
<td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td> <td class="text-center">Déf</td>
<td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td> <td class="text-center">Rap</td>
<td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td> <td class="text-center">Opp</td>
{% elif forloop.counter == 2 %} {% elif forloop.counter == 2 %}
<td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td> <td class="text-center">Opp</td>
<td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td> <td class="text-center">Déf</td>
<td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td> <td class="text-center">Rap</td>
{% elif forloop.counter == 3 %} {% elif forloop.counter == 3 %}
<td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td> <td class="text-center">Rap</td>
<td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td> <td class="text-center">Opp</td>
<td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td> <td class="text-center">Déf</td>
{% endif %} {% endif %}
{% elif pool.size == 4 %} {% elif pool.size == 4 %}
{% if forloop.counter == 1 %} {% if forloop.counter == 1 %}
<td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td> <td class="text-center">Déf</td>
<td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td> <td></td>
<td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td> <td class="text-center">Rap</td>
<td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td> <td class="text-center">Opp</td>
{% elif forloop.counter == 2 %} {% elif forloop.counter == 2 %}
<td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td> <td class="text-center">Opp</td>
<td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td> <td class="text-center">Déf</td>
<td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td> <td></td>
<td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td> <td class="text-center">Rap</td>
{% elif forloop.counter == 3 %} {% elif forloop.counter == 3 %}
<td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td> <td class="text-center">Rap</td>
<td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td> <td class="text-center">Opp</td>
<td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td> <td class="text-center">Déf</td>
<td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td> <td></td>
{% elif forloop.counter == 4 %} {% elif forloop.counter == 4 %}
<td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td> <td></td>
<td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td> <td class="text-center">Rap</td>
<td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td> <td class="text-center">Opp</td>
<td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td> <td class="text-center">Déf</td>
{% endif %} {% endif %}
{% elif pool.size == 5 %} {% elif pool.size == 5 %}
{% if forloop.counter == 1 %} {% if forloop.counter == 1 %}
<td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td> <td class="text-center">Déf</td>
<td class="text-center"></td> <td></td>
<td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td> <td class="text-center">Rap</td>
<td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td> <td class="text-center">Opp</td>
<td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td> <td></td>
{% elif forloop.counter == 2 %} {% elif forloop.counter == 2 %}
<td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td> <td></td>
<td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td> <td class="text-center">Déf</td>
<td class="text-center"></td> <td></td>
<td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td> <td class="text-center">Rap</td>
<td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td> <td class="text-center">Opp</td>
{% elif forloop.counter == 3 %} {% elif forloop.counter == 3 %}
<td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td> <td class="text-center">Opp</td>
<td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td> <td></td>
<td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td> <td class="text-center">Déf</td>
<td class="text-center"></td> <td></td>
<td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td> <td class="text-center">Rap</td>
{% elif forloop.counter == 4 %} {% elif forloop.counter == 4 %}
<td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td> <td class="text-center">Rap</td>
<td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td> <td class="text-center">Opp</td>
<td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td> <td></td>
<td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td> <td class="text-center">Déf</td>
<td class="text-center"></td> <td></td>
{% elif forloop.counter == 5 %} {% elif forloop.counter == 5 %}
<td class="text-center"></td> <td></td>
<td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td> <td class="text-center">Rap</td>
<td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td> <td class="text-center">Opp</td>
<td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td> <td></td>
<td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td> <td class="text-center">Déf</td>
{% endif %} {% endif %}
{% endif %} {% endif %}
</tr> </tr>

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-07-06 22:07+0200\n" "POT-Creation-Date: 2024-07-06 10:16+0200\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"
@ -79,7 +79,7 @@ msgstr "Type de permission nécessaire pour écrire un message dans un canal."
#: chat/models.py:62 draw/admin.py:53 draw/admin.py:71 draw/admin.py:88 #: chat/models.py:62 draw/admin.py:53 draw/admin.py:71 draw/admin.py:88
#: draw/models.py:27 participation/admin.py:79 participation/admin.py:144 #: draw/models.py:27 participation/admin.py:79 participation/admin.py:144
#: participation/admin.py:176 participation/models.py:783 #: participation/admin.py:176 participation/models.py:783
#: participation/models.py:807 participation/models.py:1131 #: participation/models.py:807 participation/models.py:1116
#: registration/models.py:763 #: registration/models.py:763
#: registration/templates/registration/payment_form.html:53 #: registration/templates/registration/payment_form.html:53
msgid "tournament" msgid "tournament"
@ -95,7 +95,7 @@ msgstr ""
#: chat/models.py:73 draw/models.py:446 draw/models.py:473 #: chat/models.py:73 draw/models.py:446 draw/models.py:473
#: participation/admin.py:140 participation/admin.py:160 #: participation/admin.py:140 participation/admin.py:160
#: participation/models.py:1666 participation/models.py:1675 #: participation/models.py:1651 participation/models.py:1660
#: participation/tables.py:84 #: participation/tables.py:84
msgid "pool" msgid "pool"
msgstr "poule" msgstr "poule"
@ -265,7 +265,7 @@ msgid "teams"
msgstr "équipes" msgstr "équipes"
#: draw/admin.py:92 draw/models.py:245 draw/models.py:465 #: draw/admin.py:92 draw/models.py:245 draw/models.py:465
#: participation/models.py:1135 #: participation/models.py:1120
msgid "round" msgid "round"
msgstr "tour" msgstr "tour"
@ -634,7 +634,7 @@ msgstr "Le numéro du tour doit être entre 1 et {nb}."
msgid "rounds" msgid "rounds"
msgstr "tours" msgstr "tours"
#: draw/models.py:268 participation/models.py:1143 #: draw/models.py:268 participation/models.py:1128
msgid "letter" msgid "letter"
msgstr "lettre" msgstr "lettre"
@ -672,12 +672,12 @@ msgstr "L'instance complète de la poule."
msgid "Pool {letter}{number}" msgid "Pool {letter}{number}"
msgstr "Poule {letter}{number}" msgstr "Poule {letter}{number}"
#: draw/models.py:447 participation/models.py:1667 #: draw/models.py:447 participation/models.py:1652
msgid "pools" msgid "pools"
msgstr "poules" msgstr "poules"
#: draw/models.py:459 participation/models.py:1121 participation/models.py:1886 #: draw/models.py:459 participation/models.py:1106 participation/models.py:1871
#: participation/models.py:1920 participation/models.py:1962 #: participation/models.py:1901 participation/models.py:1943
msgid "participation" msgid "participation"
msgstr "participation" msgstr "participation"
@ -701,9 +701,9 @@ msgid ""
msgstr "" msgstr ""
"L'ordre de choix dans la poule, entre 0 et la taille de la poule moins 1." "L'ordre de choix dans la poule, entre 0 et la taille de la poule moins 1."
#: draw/models.py:496 draw/models.py:519 participation/models.py:1252 #: draw/models.py:496 draw/models.py:519 participation/models.py:1237
#: participation/models.py:1689 participation/models.py:1927 #: participation/models.py:1674 participation/models.py:1908
#: participation/views.py:1492 participation/views.py:1757 #: participation/views.py:1489 participation/views.py:1754
#, python-brace-format #, python-brace-format
msgid "Problem #{problem}" msgid "Problem #{problem}"
msgstr "Problème n°{problem}" msgstr "Problème n°{problem}"
@ -814,67 +814,6 @@ msgstr "Pb."
msgid "Room" msgid "Room"
msgstr "Salle" msgstr "Salle"
#: draw/templates/draw/tournament_content.html:310
#: draw/templates/draw/tournament_content.html:315
#: draw/templates/draw/tournament_content.html:320
#: draw/templates/draw/tournament_content.html:324
#: draw/templates/draw/tournament_content.html:330
#: draw/templates/draw/tournament_content.html:336
#: draw/templates/draw/tournament_content.html:342
#: draw/templates/draw/tournament_content.html:346
#: draw/templates/draw/tournament_content.html:353
#: draw/templates/draw/tournament_content.html:360
#: draw/templates/draw/tournament_content.html:367
#: draw/templates/draw/tournament_content.html:374
msgctxt "Role abbreviation"
msgid "Rep"
msgstr "Déf"
#: draw/templates/draw/tournament_content.html:311
#: draw/templates/draw/tournament_content.html:316
#: draw/templates/draw/tournament_content.html:318
#: draw/templates/draw/tournament_content.html:326
#: draw/templates/draw/tournament_content.html:332
#: draw/templates/draw/tournament_content.html:334
#: draw/templates/draw/tournament_content.html:340
#: draw/templates/draw/tournament_content.html:348
#: draw/templates/draw/tournament_content.html:355
#: draw/templates/draw/tournament_content.html:362
#: draw/templates/draw/tournament_content.html:364
#: draw/templates/draw/tournament_content.html:371
msgctxt "Role abbreviation"
msgid "Rev"
msgstr "Rap"
#: draw/templates/draw/tournament_content.html:312
#: draw/templates/draw/tournament_content.html:314
#: draw/templates/draw/tournament_content.html:319
#: draw/templates/draw/tournament_content.html:327
#: draw/templates/draw/tournament_content.html:329
#: draw/templates/draw/tournament_content.html:335
#: draw/templates/draw/tournament_content.html:341
#: draw/templates/draw/tournament_content.html:349
#: draw/templates/draw/tournament_content.html:356
#: draw/templates/draw/tournament_content.html:358
#: draw/templates/draw/tournament_content.html:365
#: draw/templates/draw/tournament_content.html:372
msgctxt "Role abbreviation"
msgid "Opp"
msgstr "Opp"
#: draw/templates/draw/tournament_content.html:325
#: draw/templates/draw/tournament_content.html:331
#: draw/templates/draw/tournament_content.html:337
#: draw/templates/draw/tournament_content.html:339
#: draw/templates/draw/tournament_content.html:350
#: draw/templates/draw/tournament_content.html:352
#: draw/templates/draw/tournament_content.html:359
#: draw/templates/draw/tournament_content.html:366
#: draw/templates/draw/tournament_content.html:373
msgctxt "Role abbreviation"
msgid "Obs"
msgstr "Obs"
#: draw/templates/draw/tournament_content.html:395 #: draw/templates/draw/tournament_content.html:395
#: draw/templates/draw/tournament_content.html:414 #: draw/templates/draw/tournament_content.html:414
msgid "Abort" msgid "Abort"
@ -980,26 +919,26 @@ msgid "selected for final"
msgstr "sélectionnée pour la finale" msgstr "sélectionnée pour la finale"
#: participation/admin.py:124 participation/admin.py:188 #: participation/admin.py:124 participation/admin.py:188
#: participation/models.py:1696 participation/tables.py:114 #: participation/models.py:1681 participation/tables.py:114
msgid "reporter" msgid "defender"
msgstr "défenseur⋅se" msgstr "défenseur⋅se"
#: participation/admin.py:128 participation/models.py:1703 #: participation/admin.py:128 participation/models.py:1688
#: participation/models.py:1974 #: participation/models.py:1955
msgid "opponent" msgid "opponent"
msgstr "opposant⋅e" msgstr "opposant⋅e"
#: participation/admin.py:132 participation/models.py:1710 #: participation/admin.py:132 participation/models.py:1695
#: participation/models.py:1975 #: participation/models.py:1956
msgid "reviewer" msgid "reviewer"
msgstr "rapporteur⋅rice" msgstr "rapporteur⋅rice"
#: participation/admin.py:136 participation/models.py:1717 #: participation/admin.py:136 participation/models.py:1702
#: participation/models.py:1976 #: participation/models.py:1957
msgid "observer" msgid "observer"
msgstr "observateur⋅rice" msgstr "observateur⋅rice"
#: participation/admin.py:192 participation/models.py:1925 #: participation/admin.py:192 participation/models.py:1906
msgid "problem" msgid "problem"
msgstr "numéro de problème" msgstr "numéro de problème"
@ -1088,12 +1027,12 @@ msgid "The following user was not found:"
msgstr "L'utilisateur⋅rice suivant n'a pas été trouvé :" msgstr "L'utilisateur⋅rice suivant n'a pas été trouvé :"
#: participation/forms.py:350 #: participation/forms.py:350
msgid "The reporter, the opponent and the reviewer must be different." msgid "The defender, the opponent and the reviewer must be different."
msgstr "" msgstr ""
"Les équipes défenseuse, opposante et rapportrice doivent être différent⋅es." "Les équipes défenseuse, opposante et rapportrice doivent être différent⋅es."
#: participation/forms.py:354 #: participation/forms.py:354
msgid "This reporter did not work on this problem." msgid "This defender did not work on this problem."
msgstr "Ce⋅tte défenseur⋅se ne travaille pas sur ce problème." msgstr "Ce⋅tte défenseur⋅se ne travaille pas sur ce problème."
#: participation/forms.py:373 #: participation/forms.py:373
@ -1300,7 +1239,7 @@ msgid "first phase date"
msgstr "date du premier tour" msgstr "date du premier tour"
#: participation/models.py:327 #: participation/models.py:327
msgid "limit date to upload the written reviews for the first phase" msgid "limit date to upload the syntheses for the first phase"
msgstr "date limite pour envoyer les notes de synthèses pour la première phase" msgstr "date limite pour envoyer les notes de synthèses pour la première phase"
#: participation/models.py:332 #: participation/models.py:332
@ -1313,7 +1252,7 @@ msgstr ""
"cocher la case lorsque les solutions pour le second tour sont accessibles" "cocher la case lorsque les solutions pour le second tour sont accessibles"
#: participation/models.py:342 #: participation/models.py:342
msgid "limit date to upload the written reviews for the second phase" msgid "limit date to upload the syntheses for the second phase"
msgstr "date limite d'envoi des notes de synthèse pour la seconde phase" msgstr "date limite d'envoi des notes de synthèse pour la seconde phase"
#: participation/models.py:347 #: participation/models.py:347
@ -1326,7 +1265,7 @@ msgstr ""
"cocher la case lorsque les solutions pour le second tour sont accessibles" "cocher la case lorsque les solutions pour le second tour sont accessibles"
#: participation/models.py:357 #: participation/models.py:357
msgid "limit date to upload the written reviews for the third phase" msgid "limit date to upload the syntheses for the third phase"
msgstr "" msgstr ""
"date limite pour envoyer les notes de synthèses pour la troisième phase" "date limite pour envoyer les notes de synthèses pour la troisième phase"
@ -1355,7 +1294,7 @@ msgid "Final ranking"
msgstr "Classement final" msgstr "Classement final"
#: participation/models.py:481 participation/models.py:553 #: participation/models.py:481 participation/models.py:553
#: participation/models.py:1327 participation/views.py:1731 #: participation/models.py:1312 participation/views.py:1728
msgid "Team" msgid "Team"
msgstr "Équipe" msgstr "Équipe"
@ -1387,15 +1326,15 @@ msgstr "Scores jour 3"
msgid "Tweaks day 3" msgid "Tweaks day 3"
msgstr "Ajustements 3" msgstr "Ajustements 3"
#: participation/models.py:485 participation/models.py:1327 #: participation/models.py:485 participation/models.py:1312
#: participation/views.py:1738 #: participation/views.py:1735
msgid "Total" msgid "Total"
msgstr "Total" msgstr "Total"
#: participation/models.py:485 participation/models.py:553 #: participation/models.py:485 participation/models.py:553
#: participation/models.py:1327 #: participation/models.py:1312
#: participation/templates/participation/tournament_harmonize.html:14 #: participation/templates/participation/tournament_harmonize.html:14
#: participation/views.py:1741 #: participation/views.py:1738
msgid "Rank" msgid "Rank"
msgstr "Rang" msgstr "Rang"
@ -1407,7 +1346,7 @@ msgstr "Score"
msgid "Mention" msgid "Mention"
msgstr "Mention" msgstr "Mention"
#: participation/models.py:698 participation/models.py:1596 #: participation/models.py:698 participation/models.py:1581
msgid "Don't update the table structure for a better automated integration." msgid "Don't update the table structure for a better automated integration."
msgstr "" msgstr ""
"Ne pas mettre à jour la structure de la table pour une meilleure intégration " "Ne pas mettre à jour la structure de la table pour une meilleure intégration "
@ -1509,7 +1448,7 @@ msgstr "Tirage au sort des solutions"
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>The solutions draw is ended. You can check the result on <a " "<p>The solutions draw is ended. You can check the result on <a "
"href='{draw_url}'>this page</a>.</p><p>For the first round, you will present " "href='{draw_url}'>this page</a>.</p><p>For the first round, you will defend "
"<a href='{solution_url}'>your solution of the problem {problem}</a>.</p>" "<a href='{solution_url}'>your solution of the problem {problem}</a>.</p>"
msgstr "" msgstr ""
"<p>Le tirage au sort des solutions est terminé. Vous pouvez consulter les " "<p>Le tirage au sort des solutions est terminé. Vous pouvez consulter les "
@ -1517,70 +1456,70 @@ msgstr ""
"tour, vous défendrez <a href='{solution_url}'>votre solution du problème " "tour, vous défendrez <a href='{solution_url}'>votre solution du problème "
"{problem}</a>.</p>" "{problem}</a>.</p>"
#: participation/models.py:930 participation/models.py:993 #: participation/models.py:930 participation/models.py:988
#: participation/models.py:1057 #: participation/models.py:1047
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>You will oppose the solution of the team {opponent} on the <a " "<p>You will oppose the solution of the team {opponent} on the <a "
"href='{solution_url}'>problem {problem}</a>. You can upload your written " "href='{solution_url}'>problem {problem}</a>. You can upload your synthesis "
"review on <a href='{passage_url}'>this page</a>.</p>" "sheet on <a href='{passage_url}'>this page</a>.</p>"
msgstr "" msgstr ""
"<p>Vous opposerez la solution de l'équipe {opponent} sur le <a " "<p>Vous opposerez la solution de l'équipe {opponent} sur le <a "
"href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note " "href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note "
"de synthèse sur <a href='{passage_url}'>cette page</a>.</p>" "de synthèse sur <a href='{passage_url}'>cette page</a>.</p>"
#: participation/models.py:939 participation/models.py:1002 #: participation/models.py:939 participation/models.py:997
#: participation/models.py:1066 #: participation/models.py:1056
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>You will report the solution of the team {reviewer} on the <a " "<p>You will report the solution of the team {reviewer} on the <a "
"href='{solution_url}'>problem {problem}</a>. You can upload your written " "href='{solution_url}'>problem {problem}. You can upload your synthesis sheet "
"review on <a href='{passage_url}'>this page</a>.</p>" "on <a href='{passage_url}'>this page</a>.</p>"
msgstr "" msgstr ""
"<p>Vous rapporterez la solution de l'équipe {reviewer} sur le <a " "<p>Vous rapporterez la solution de l'équipe {reviewer} sur le <a "
"href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note " "href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note "
"de synthèse sur <a href='{passage_url}'>cette page</a>.</p>" "de synthèse sur <a href='{passage_url}'>cette page</a>.</p>"
#: participation/models.py:949 participation/models.py:1012 #: participation/models.py:949 participation/models.py:1007
#: participation/models.py:1076 #: participation/models.py:1066
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>You will observe the solution of the team {observer} on the <a " "<p>You will observe the solution of the team {observer} on the <a "
"href='{solution_url}'>problem {problem}</a>. You can upload your written " "href='{solution_url}'>problem {problem}. You can upload your synthesis sheet "
"review on <a href='{passage_url}'>this page</a>.</p>" "on <a href='{passage_url}'>this page</a>.</p>"
msgstr "" msgstr ""
"<p>Vous observerez la solution de l'équipe {observer} sur le <a " "<p>Vous observerez la solution de l'équipe {observer} sur le <a "
"href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note " "href='{solution_url}'>problème {problem}</a>. Vous pouvez envoyer votre note "
"de synthèse sur <a href='{passage_url}'>cette page</a>.</p>" "de synthèse sur <a href='{passage_url}'>cette page</a>.</p>"
#: participation/models.py:974 registration/models.py:629 #: participation/models.py:969 registration/models.py:629
msgid "First round" msgid "First round"
msgstr "Premier tour" msgstr "Premier tour"
#: participation/models.py:986 #: participation/models.py:981
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>For the second round, you will present <a href='{solution_url}'>your " "<p>For the second round, you will defend <a href='{solution_url}'>your "
"solution of the problem {problem}</a>.</p>" "solution of the problem {problem}</a>.</p>"
msgstr "" msgstr ""
"<p>Pour le second tour, vous défendrez <a href='{solution_url}'>votre " "<p>Pour le second tour, vous défendrez <a href='{solution_url}'>votre "
"solution du problème {problem}</a>.</p>" "solution du problème {problem}</a>.</p>"
#: participation/models.py:1037 participation/models.py:1101 #: participation/models.py:1027 participation/models.py:1086
#: registration/models.py:640 #: registration/models.py:640
msgid "Second round" msgid "Second round"
msgstr "Second tour" msgstr "Second tour"
#: participation/models.py:1050 #: participation/models.py:1040
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>For the third round, you will present <a href='{solution_url}'>your " "<p>For the third round, you will defend <a href='{solution_url}'>your "
"solution of the problem {problem}</a>.</p>" "solution of the problem {problem}</a>.</p>"
msgstr "" msgstr ""
"<p>Pour le troisième tour, vous défendrez <a href='{solution_url}'>votre " "<p>Pour le troisième tour, vous défendrez <a href='{solution_url}'>votre "
"solution du problème {problem}</a>.</p>" "solution du problème {problem}</a>.</p>"
#: participation/models.py:1107 #: participation/models.py:1092
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>The tournament {tournament} is ended. You can check the results on the <a " "<p>The tournament {tournament} is ended. You can check the results on the <a "
@ -1589,57 +1528,57 @@ msgstr ""
"<p>Le tournoi {tournament} est terminé. Vous pouvez consulter les résultats " "<p>Le tournoi {tournament} est terminé. Vous pouvez consulter les résultats "
"sur la <a href='{url}'>page du tournoi</a>.</p>" "sur la <a href='{url}'>page du tournoi</a>.</p>"
#: participation/models.py:1112 #: participation/models.py:1097
msgid "Tournament ended" msgid "Tournament ended"
msgstr "Tournoi terminé" msgstr "Tournoi terminé"
#: participation/models.py:1122 participation/models.py:1165 #: participation/models.py:1107 participation/models.py:1150
msgid "participations" msgid "participations"
msgstr "participations" msgstr "participations"
#: participation/models.py:1137 participation/models.py:1138 #: participation/models.py:1122 participation/models.py:1123
#: participation/models.py:1139 #: participation/models.py:1124
#, python-brace-format #, python-brace-format
msgid "Round {round}" msgid "Round {round}"
msgstr "Tour {round}" msgstr "Tour {round}"
#: participation/models.py:1153 #: participation/models.py:1138
msgid "room" msgid "room"
msgstr "salle" msgstr "salle"
#: participation/models.py:1155 #: participation/models.py:1140
msgid "Room 1" msgid "Room 1"
msgstr "Salle 1" msgstr "Salle 1"
#: participation/models.py:1156 #: participation/models.py:1141
msgid "Room 2" msgid "Room 2"
msgstr "Salle 2" msgstr "Salle 2"
#: participation/models.py:1159 #: participation/models.py:1144
msgid "For 5-teams pools only" msgid "For 5-teams pools only"
msgstr "Pour les poules de 5 équipe uniquement" msgstr "Pour les poules de 5 équipe uniquement"
#: participation/models.py:1171 #: participation/models.py:1156
msgid "juries" msgid "juries"
msgstr "jurys" msgstr "jurys"
#: participation/models.py:1180 #: participation/models.py:1165
msgid "president of the jury" msgid "president of the jury"
msgstr "président⋅e du jury" msgstr "président⋅e du jury"
#: participation/models.py:1187 #: participation/models.py:1172
msgid "BigBlueButton URL" msgid "BigBlueButton URL"
msgstr "Lien BigBlueButton" msgstr "Lien BigBlueButton"
#: participation/models.py:1188 #: participation/models.py:1173
msgid "The link of the BBB visio for this pool." msgid "The link of the BBB visio for this pool."
msgstr "Le lien du salon BBB pour cette poule." msgstr "Le lien du salon BBB pour cette poule."
#: participation/models.py:1193 #: participation/models.py:1178
msgid "results available" msgid "results available"
msgstr "résultats disponibles" msgstr "résultats disponibles"
#: participation/models.py:1194 #: participation/models.py:1179
msgid "" msgid ""
"Check this case when results become accessible to teams. They stay " "Check this case when results become accessible to teams. They stay "
"accessible to you. Only averages are given." "accessible to you. Only averages are given."
@ -1648,194 +1587,194 @@ msgstr ""
"Ils restent toujours accessibles pour vous. Seules les moyennes sont " "Ils restent toujours accessibles pour vous. Seules les moyennes sont "
"communiquées." "communiquées."
#: participation/models.py:1226 #: participation/models.py:1211
msgid "The president of the jury must be part of the jury." msgid "The president of the jury must be part of the jury."
msgstr "Læ président⋅e du jury doit faire partie du jury." msgstr "Læ président⋅e du jury doit faire partie du jury."
#: participation/models.py:1253 participation/models.py:1327 #: participation/models.py:1238 participation/models.py:1312
#: participation/views.py:1486 participation/views.py:1735 #: participation/views.py:1483 participation/views.py:1732
msgid "Problem" msgid "Problem"
msgstr "Problème" msgstr "Problème"
#: participation/models.py:1258 participation/views.py:1501 #: participation/models.py:1243 participation/views.py:1498
msgid "Role" msgid "Role"
msgstr "Rôle" msgstr "Rôle"
#: participation/models.py:1263 participation/views.py:1535 #: participation/models.py:1248 participation/views.py:1532
#: participation/views.py:1536 #: participation/views.py:1533
msgid "Juree" msgid "Juree"
msgstr "Juré⋅e" msgstr "Juré⋅e"
#: participation/models.py:1286 participation/models.py:1612 #: participation/models.py:1271 participation/models.py:1597
#: participation/models.py:1634 participation/views.py:1605 #: participation/models.py:1619 participation/views.py:1602
msgid "Average" msgid "Average"
msgstr "Moyenne" msgstr "Moyenne"
#: participation/models.py:1292 participation/views.py:1624 #: participation/models.py:1277 participation/views.py:1621
msgid "Coefficient" msgid "Coefficient"
msgstr "Coefficien" msgstr "Coefficien"
#: participation/models.py:1293 participation/views.py:1667 #: participation/models.py:1278 participation/views.py:1664
msgid "Subtotal" msgid "Subtotal"
msgstr "Sous-total" msgstr "Sous-total"
#: participation/models.py:1559 #: participation/models.py:1544
#, python-brace-format #, python-brace-format
msgid "Input must be a valid integer between {min_note} and {max_note}." msgid "Input must be a valid integer between {min_note} and {max_note}."
msgstr "L'entrée doit être un entier valide entre {min_note} et {max_note}." msgstr "L'entrée doit être un entier valide entre {min_note} et {max_note}."
#: participation/models.py:1647 #: participation/models.py:1632
#, python-brace-format #, python-brace-format
msgid "The jury {jury} is not part of the jury for this pool." msgid "The jury {jury} is not part of the jury for this pool."
msgstr "{jury} ne fait pas partie du jury pour cette poule." msgstr "{jury} ne fait pas partie du jury pour cette poule."
#: participation/models.py:1660 #: participation/models.py:1645
#, python-brace-format #, python-brace-format
msgid "Pool {code} for tournament {tournament} with teams {teams}" msgid "Pool {code} for tournament {tournament} with teams {teams}"
msgstr "Poule {code} du tournoi {tournament} avec les équipes {teams}" msgstr "Poule {code} du tournoi {tournament} avec les équipes {teams}"
#: participation/models.py:1680 #: participation/models.py:1665
msgid "position" msgid "position"
msgstr "position" msgstr "position"
#: participation/models.py:1687 #: participation/models.py:1672
msgid "reported solution" msgid "defended solution"
msgstr "solution défendue" msgstr "solution défendue"
#: participation/models.py:1725 #: participation/models.py:1710
msgid "penalties" msgid "penalties"
msgstr "pénalités" msgstr "pénalités"
#: participation/models.py:1727 #: participation/models.py:1712
msgid "" msgid ""
"Number of penalties for the reporter. The reporter will loose a 0.5 " "Number of penalties for the defender. The defender will loose a 0.5 "
"coefficient per penalty." "coefficient per penalty."
msgstr "" msgstr ""
"Nombre de pénalités pour l'équipe défenseuse. Elle perd un coefficient 0.5 " "Nombre de pénalités pour l'équipe défenseuse. Elle perd un coefficient 0.5 "
"sur sa présentation orale par pénalité." "sur sa présentation orale par pénalité."
#: participation/models.py:1853 participation/models.py:1856 #: participation/models.py:1838 participation/models.py:1841
#: participation/models.py:1859 participation/models.py:1862 #: participation/models.py:1844 participation/models.py:1847
#, python-brace-format #, python-brace-format
msgid "Team {trigram} is not registered in the pool." msgid "Team {trigram} is not registered in the pool."
msgstr "L'équipe {trigram} n'est pas inscrite dans la poule." msgstr "L'équipe {trigram} n'est pas inscrite dans la poule."
#: participation/models.py:1867 #: participation/models.py:1852
#, python-brace-format #, python-brace-format
msgid "Passage of {reporter} for problem {problem}" msgid "Passage of {defender} for problem {problem}"
msgstr "Passage de {reporter} pour le problème {problem}" msgstr "Passage de {defender} pour le problème {problem}"
#: participation/models.py:1871 participation/models.py:1880 #: participation/models.py:1856 participation/models.py:1865
#: participation/models.py:1969 participation/models.py:2012 #: participation/models.py:1950 participation/models.py:1993
msgid "passage" msgid "passage"
msgstr "passage" msgstr "passage"
#: participation/models.py:1872 #: participation/models.py:1857
msgid "passages" msgid "passages"
msgstr "passages" msgstr "passages"
#: participation/models.py:1891 #: participation/models.py:1876
msgid "difference" msgid "difference"
msgstr "différence" msgstr "différence"
#: participation/models.py:1892 #: participation/models.py:1877
msgid "Score to add/remove on the final score" msgid "Score to add/remove on the final score"
msgstr "Score à ajouter/retrancher au score final" msgstr "Score à ajouter/retrancher au score final"
#: participation/models.py:1899 #: participation/models.py:1884
msgid "tweak" msgid "tweak"
msgstr "harmonisation" msgstr "harmonisation"
#: participation/models.py:1900 #: participation/models.py:1885
msgid "tweaks" msgid "tweaks"
msgstr "harmonisations" msgstr "harmonisations"
#: participation/models.py:1932 #: participation/models.py:1913
msgid "solution for the final tournament" msgid "solution for the final tournament"
msgstr "solution pour la finale" msgstr "solution pour la finale"
#: participation/models.py:1937 participation/models.py:1981 #: participation/models.py:1918 participation/models.py:1962
msgid "file" msgid "file"
msgstr "fichier" msgstr "fichier"
#: participation/models.py:1947 #: participation/models.py:1928
#, python-brace-format #, python-brace-format
msgid "Solution of team {team} for problem {problem}" msgid "Solution of team {team} for problem {problem}"
msgstr "Solution de l'équipe {team} pour le problème {problem}" msgstr "Solution de l'équipe {team} pour le problème {problem}"
#: participation/models.py:1949 #: participation/models.py:1930
msgid "for final" msgid "for final"
msgstr "pour la finale" msgstr "pour la finale"
#: participation/models.py:1952 #: participation/models.py:1933
msgid "solution" msgid "solution"
msgstr "solution" msgstr "solution"
#: participation/models.py:1953 #: participation/models.py:1934
msgid "solutions" msgid "solutions"
msgstr "solutions" msgstr "solutions"
#: participation/models.py:1987 #: participation/models.py:1968
#, python-brace-format #, python-brace-format
msgid "Written review of {team} as {type} for problem {problem} of {reporter}" msgid "Synthesis of {team} as {type} for problem {problem} of {defender}"
msgstr "" msgstr ""
"Note de synthèse de l'équipe {team} en tant que {type} pour le problème " "Note de synthèse de l'équipe {team} en tant que {type} pour le problème "
"{problem} de {reporter}" "{problem} de {defender}"
#: participation/models.py:1995 #: participation/models.py:1976
msgid "written review" msgid "synthesis"
msgstr "note de synthèse" msgstr "note de synthèse"
#: participation/models.py:1996 #: participation/models.py:1977
msgid "written reviews" msgid "syntheses"
msgstr "notes de synthèse" msgstr "notes de synthèse"
#: participation/models.py:2005 #: participation/models.py:1986
msgid "jury" msgid "jury"
msgstr "jury" msgstr "jury"
#: participation/models.py:2017 #: participation/models.py:1998
msgid "reporter writing note" msgid "defender writing note"
msgstr "note d'écrit défenseur⋅se" msgstr "note d'écrit défenseur⋅se"
#: participation/models.py:2023 #: participation/models.py:2004
msgid "reporter oral note" msgid "defender oral note"
msgstr "note d'oral défenseur⋅se" msgstr "note d'oral défenseur⋅se"
#: participation/models.py:2029 #: participation/models.py:2010
msgid "opponent writing note" msgid "opponent writing note"
msgstr "note d'écrit opposant⋅e" msgstr "note d'écrit opposant⋅e"
#: participation/models.py:2035 #: participation/models.py:2016
msgid "opponent oral note" msgid "opponent oral note"
msgstr "note d'oral opposant⋅e" msgstr "note d'oral opposant⋅e"
#: participation/models.py:2041 #: participation/models.py:2022
msgid "reviewer writing note" msgid "reviewer writing note"
msgstr "note d'écrit rapporteur⋅rice" msgstr "note d'écrit rapporteur⋅rice"
#: participation/models.py:2047 #: participation/models.py:2028
msgid "reviewer oral note" msgid "reviewer oral note"
msgstr "note d'oral du rapporteur⋅rice" msgstr "note d'oral du rapporteur⋅rice"
#: participation/models.py:2053 #: participation/models.py:2034
msgid "observer writing note" msgid "observer writing note"
msgstr "note d'écrit de l'observateur⋅rice" msgstr "note d'écrit de l'observateur⋅rice"
#: participation/models.py:2059 #: participation/models.py:2040
msgid "observer oral note" msgid "observer oral note"
msgstr "note d'oral de l'observateur⋅rice" msgstr "note d'oral de l'observateur⋅rice"
#: participation/models.py:2124 #: participation/models.py:2105
#, python-brace-format #, python-brace-format
msgid "Notes of {jury} for {passage}" msgid "Notes of {jury} for {passage}"
msgstr "Notes de {jury} pour le {passage}" msgstr "Notes de {jury} pour le {passage}"
#: participation/models.py:2127 #: participation/models.py:2108
msgid "note" msgid "note"
msgstr "note" msgstr "note"
#: participation/models.py:2128 #: participation/models.py:2109
msgid "notes" msgid "notes"
msgstr "notes" msgstr "notes"
@ -1971,7 +1910,7 @@ msgstr "Envoyer une solution"
#: participation/templates/participation/upload_motivation_letter.html:13 #: participation/templates/participation/upload_motivation_letter.html:13
#: participation/templates/participation/upload_notes.html:24 #: participation/templates/participation/upload_notes.html:24
#: participation/templates/participation/upload_solution.html:11 #: participation/templates/participation/upload_solution.html:11
#: participation/templates/participation/upload_written_review.html:23 #: participation/templates/participation/upload_synthesis.html:18
#: registration/templates/registration/upload_health_sheet.html:17 #: registration/templates/registration/upload_health_sheet.html:17
#: registration/templates/registration/upload_parental_authorization.html:17 #: registration/templates/registration/upload_parental_authorization.html:17
#: registration/templates/registration/upload_photo_authorization.html:18 #: registration/templates/registration/upload_photo_authorization.html:18
@ -1994,7 +1933,7 @@ msgid "Position:"
msgstr "Position :" msgstr "Position :"
#: participation/templates/participation/passage_detail.html:28 #: participation/templates/participation/passage_detail.html:28
msgid "Reporter:" msgid "Defender:"
msgstr "Défenseur⋅se :" msgstr "Défenseur⋅se :"
#: participation/templates/participation/passage_detail.html:31 #: participation/templates/participation/passage_detail.html:31
@ -2010,11 +1949,11 @@ msgid "Observer:"
msgstr "Observateur⋅rice :" msgstr "Observateur⋅rice :"
#: participation/templates/participation/passage_detail.html:42 #: participation/templates/participation/passage_detail.html:42
msgid "Reported solution:" msgid "Defended solution:"
msgstr "Solution défendue" msgstr "Solution défendue"
#: participation/templates/participation/passage_detail.html:45 #: participation/templates/participation/passage_detail.html:45
msgid "Reporter penalties count:" msgid "Defender penalties count:"
msgstr "Nombre de pénalités :" msgstr "Nombre de pénalités :"
#: participation/templates/participation/passage_detail.html:48 #: participation/templates/participation/passage_detail.html:48
@ -2024,7 +1963,7 @@ msgstr "Notes de synthèse :"
#: participation/templates/participation/passage_detail.html:53 #: participation/templates/participation/passage_detail.html:53
#: participation/templates/participation/pool_detail.html:68 #: participation/templates/participation/pool_detail.html:68
msgid "No review was uploaded yet." msgid "No synthesis was uploaded yet."
msgstr "Aucune note de synthèse n'a encore été envoyée." msgstr "Aucune note de synthèse n'a encore été envoyée."
#: participation/templates/participation/passage_detail.html:61 #: participation/templates/participation/passage_detail.html:61
@ -2034,19 +1973,19 @@ msgstr "Modifier les notes"
#: participation/templates/participation/passage_detail.html:66 #: participation/templates/participation/passage_detail.html:66
#: participation/templates/participation/passage_detail.html:187 #: participation/templates/participation/passage_detail.html:187
msgid "Upload review" msgid "Upload synthesis"
msgstr "Envoyer la note de synthèse" msgstr "Envoyer une note de synthèse"
#: participation/templates/participation/passage_detail.html:74 #: participation/templates/participation/passage_detail.html:74
msgid "Notes detail" msgid "Notes detail"
msgstr "Détails des notes" msgstr "Détails des notes"
#: participation/templates/participation/passage_detail.html:82 #: participation/templates/participation/passage_detail.html:82
msgid "Average points for the reporter writing" msgid "Average points for the defender writing"
msgstr "Moyenne de l'écrit de l'équipe défenseuse" msgstr "Moyenne de l'écrit de l'équipe défenseuse"
#: participation/templates/participation/passage_detail.html:90 #: participation/templates/participation/passage_detail.html:90
msgid "Average points for the reporter oral" msgid "Average points for the defender oral"
msgstr "Moyenne de l'oral de l'équipe défenseuse" msgstr "Moyenne de l'oral de l'équipe défenseuse"
#: participation/templates/participation/passage_detail.html:98 #: participation/templates/participation/passage_detail.html:98
@ -2074,7 +2013,7 @@ msgid "Average points for the observer oral"
msgstr "Moyenne de l'oral de l'équipe observatrice" msgstr "Moyenne de l'oral de l'équipe observatrice"
#: participation/templates/participation/passage_detail.html:140 #: participation/templates/participation/passage_detail.html:140
msgid "Reporter points" msgid "Defender points"
msgstr "Points de l'équipe défenseuse" msgstr "Points de l'équipe défenseuse"
#: participation/templates/participation/passage_detail.html:148 #: participation/templates/participation/passage_detail.html:148
@ -2119,7 +2058,7 @@ msgid "Edit jury"
msgstr "Modifier le jury" msgstr "Modifier le jury"
#: participation/templates/participation/pool_detail.html:49 #: participation/templates/participation/pool_detail.html:49
msgid "Reported solutions:" msgid "Defended solutions:"
msgstr "Solutions défendues :" msgstr "Solutions défendues :"
#: participation/templates/participation/pool_detail.html:55 #: participation/templates/participation/pool_detail.html:55
@ -2435,15 +2374,15 @@ msgid "date of the random draw"
msgstr "date du tirage au sort" msgstr "date du tirage au sort"
#: participation/templates/participation/tournament_detail.html:41 #: participation/templates/participation/tournament_detail.html:41
msgid "date of maximal written reviews submission for the first round" msgid "date of maximal syntheses submission for the first round"
msgstr "date limite de soumission des notes de synthèse pour le premier tour" msgstr "date limite de soumission des notes de synthèse pour le premier tour"
#: participation/templates/participation/tournament_detail.html:44 #: participation/templates/participation/tournament_detail.html:44
msgid "date of maximal written reviews submission for the second round" msgid "date of maximal syntheses submission for the second round"
msgstr "date limite de soumission des notes de synthèse pour le second tour" msgstr "date limite de soumission des notes de synthèse pour le second tour"
#: participation/templates/participation/tournament_detail.html:48 #: participation/templates/participation/tournament_detail.html:48
msgid "date of maximal written reviews submission for the third round" msgid "date of maximal syntheses submission for the third round"
msgstr "date limite de soumission des notes de synthèse pour le troisième tour" msgstr "date limite de soumission des notes de synthèse pour le troisième tour"
#: participation/templates/participation/tournament_detail.html:56 #: participation/templates/participation/tournament_detail.html:56
@ -2523,42 +2462,6 @@ msgstr "Dépublier les notes pour le troisième tour"
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_detail.html:236
msgid "Validated team participant data spreadsheet"
msgstr "Tableur des données des équipes validées"
#: participation/templates/participation/tournament_detail.html:241
msgid "All teams participant data spreadsheet"
msgstr "Tableur des données de toutes les équipes"
#: participation/templates/participation/tournament_detail.html:246
msgid "Archive of all authorisations sorted by team and person"
msgstr "Archive de toutes les autorisations triées par équipe et personne"
#: participation/templates/participation/tournament_detail.html:251
msgid "Archive of all submitted solutions sorted by team"
msgstr "Archive de toutes les solutions envoyées triées par équipe"
#: participation/templates/participation/tournament_detail.html:256
msgid "Archive of all sent solutions sorted by problem"
msgstr "Archive de toutes les solutions envoyées triées par problème"
#: participation/templates/participation/tournament_detail.html:261
msgid "Archive of all sent solutions sorted by pool"
msgstr "Archive de toutes les solutions envoyées triées par poule"
#: participation/templates/participation/tournament_detail.html:266
msgid "Archive of all summary notes sorted by pool and passage"
msgstr "Archive de toutes les notes de synthèse triées par poule et passage"
#: participation/templates/participation/tournament_detail.html:272
msgid "Note spreadsheet on Google Sheets"
msgstr "Tableur de notes sur Google Sheets"
#: participation/templates/participation/tournament_detail.html:277
msgid "Archive of all printable note sheets sorted by pool"
msgstr "Archive de toutes les fiches de notes imprimables triées par poule"
#: participation/templates/participation/tournament_harmonize.html:16 #: participation/templates/participation/tournament_harmonize.html:16
#: registration/models.py:655 #: registration/models.py:655
msgid "Note" msgid "Note"
@ -2601,11 +2504,11 @@ msgstr ""
msgid "Download empty notation sheet" msgid "Download empty notation sheet"
msgstr "Télécharger la fiche de notation vierge" msgstr "Télécharger la fiche de notation vierge"
#: participation/templates/participation/upload_written_review.html:9 #: participation/templates/participation/upload_synthesis.html:9
msgid "Templates:" msgid "Templates:"
msgstr "Modèles :" msgstr "Modèles :"
#: participation/templates/participation/upload_written_review.html:14 #: participation/templates/participation/upload_synthesis.html:13
msgid "Warning: non-free format" msgid "Warning: non-free format"
msgstr "Attention : format non libre" msgstr "Attention : format non libre"
@ -2753,96 +2656,96 @@ msgstr "Vous ne pouvez pas envoyer de solution après la date limite."
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:1023 #: participation/views.py:1022
#, python-brace-format #, python-brace-format
msgid "Written reviews 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:1040 participation/views.py:1056 #: participation/views.py:1039 participation/views.py:1054
#, 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:1041 participation/views.py:1057 #: participation/views.py:1039 participation/views.py:1054
#, python-brace-format #, python-brace-format
msgid "Written reviews 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:1066 #: participation/views.py:1063
#, 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:1067 #: participation/views.py:1064
#, python-brace-format #, python-brace-format
msgid "Written reviews 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:1109 #: participation/views.py:1106
#, 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:1125 #: participation/views.py:1122
#, 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:1145 #: participation/views.py:1142
msgid "New jury account" msgid "New jury account"
msgstr "Nouveau compte de juré⋅e" msgstr "Nouveau compte de juré⋅e"
#: participation/views.py:1166 #: participation/views.py:1163
#, 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:1202 #: participation/views.py:1199
#, 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:1228 #: participation/views.py:1225
#, 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:1256 #: participation/views.py:1253
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:1272 #: participation/views.py:1269
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:1507 #: participation/views.py:1504
msgid "Reporter" msgid "Defender"
msgstr "Défenseur⋅se" msgstr "Défenseur⋅se"
#: participation/views.py:1513 #: participation/views.py:1510
msgid "Opponent" msgid "Opponent"
msgstr "Opposant⋅e" msgstr "Opposant⋅e"
#: participation/views.py:1520 #: participation/views.py:1517
msgid "Reviewer" msgid "Reviewer"
msgstr "Rapporteur⋅rice" msgstr "Rapporteur⋅rice"
#: participation/views.py:1527 #: participation/views.py:1524
msgid "Observer" msgid "Observer"
msgstr "Observateur⋅rice" msgstr "Observateur⋅rice"
#: participation/views.py:1898 #: participation/views.py:1895
#, 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:1903 #: participation/views.py:1900
#, 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:2070 #: participation/views.py:2067
msgid "You can't upload a written review 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."
#: registration/admin.py:53 registration/admin.py:69 registration/admin.py:85 #: registration/admin.py:53 registration/admin.py:69 registration/admin.py:85

View File

@ -4,7 +4,7 @@
from django.contrib import admin from django.contrib import admin
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, Synthesis, Team, Tournament, Tweak
class ParticipationInline(admin.StackedInline): class ParticipationInline(admin.StackedInline):
@ -32,8 +32,8 @@ class SolutionInline(admin.TabularInline):
show_change_link = True show_change_link = True
class WrittenReviewInline(admin.TabularInline): class SynthesisInline(admin.TabularInline):
model = WrittenReview model = Synthesis
extra = 0 extra = 0
ordering = ('passage__solution_number', 'type',) ordering = ('passage__solution_number', 'type',)
autocomplete_fields = ('passage',) autocomplete_fields = ('passage',)
@ -51,7 +51,7 @@ class PassageInline(admin.TabularInline):
model = Passage model = Passage
extra = 0 extra = 0
ordering = ('position',) ordering = ('position',)
autocomplete_fields = ('reporter', 'opponent', 'reviewer', 'observer',) autocomplete_fields = ('defender', 'opponent', 'reviewer', 'observer',)
show_change_link = True show_change_link = True
@ -95,7 +95,7 @@ class ParticipationAdmin(admin.ModelAdmin):
search_fields = ('team__name', 'team__trigram',) search_fields = ('team__name', 'team__trigram',)
list_filter = ('valid', 'tournament',) list_filter = ('valid', 'tournament',)
autocomplete_fields = ('team', 'tournament',) autocomplete_fields = ('team', 'tournament',)
inlines = (SolutionInline, WrittenReviewInline,) inlines = (SolutionInline, SynthesisInline,)
@admin.register(Pool) @admin.register(Pool)
@ -113,17 +113,17 @@ 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', list_display = ('__str__', 'defender_trigram', 'solution_number', 'opponent_trigram', 'reviewer_trigram',
'observer_trigram', 'pool_abbr', 'position', 'tournament') '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',) autocomplete_fields = ('pool', 'defender', 'opponent', 'reviewer', 'observer',)
inlines = (NoteInline,) inlines = (NoteInline,)
@admin.display(description=_("reporter"), ordering='reporter__team__trigram') @admin.display(description=_("defender"), ordering='defender__team__trigram')
def reporter_trigram(self, record: Passage): def defender_trigram(self, record: Passage):
return record.reporter.team.trigram return record.defender.team.trigram
@admin.display(description=_("opponent"), ordering='opponent__team__trigram') @admin.display(description=_("opponent"), ordering='opponent__team__trigram')
def opponent_trigram(self, record: Passage): def opponent_trigram(self, record: Passage):
@ -148,13 +148,13 @@ class PassageAdmin(admin.ModelAdmin):
@admin.register(Note) @admin.register(Note)
class NoteAdmin(admin.ModelAdmin): class NoteAdmin(admin.ModelAdmin):
list_display = ('passage', 'pool', 'jury', 'reporter_writing', 'reporter_oral', list_display = ('passage', 'pool', 'jury', 'defender_writing', 'defender_oral',
'opponent_writing', 'opponent_oral', 'reviewer_writing', 'reviewer_oral', 'opponent_writing', 'opponent_oral', 'reviewer_writing', 'reviewer_oral',
'observer_writing', 'observer_oral',) 'observer_writing', 'observer_oral',)
list_filter = ('passage__pool__letter', 'passage__solution_number', 'jury', list_filter = ('passage__pool__letter', 'passage__solution_number', 'jury',
'reporter_writing', 'reporter_oral', 'opponent_writing', 'opponent_oral', 'defender_writing', 'defender_oral', 'opponent_writing', 'opponent_oral',
'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_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__defender__team__trigram',)
autocomplete_fields = ('jury', 'passage',) autocomplete_fields = ('jury', 'passage',)
@admin.display(description=_("pool")) @admin.display(description=_("pool"))
@ -178,19 +178,19 @@ class SolutionAdmin(admin.ModelAdmin):
return Tournament.final_tournament() if record.final_solution else record.participation.tournament return Tournament.final_tournament() if record.final_solution else record.participation.tournament
@admin.register(WrittenReview) @admin.register(Synthesis)
class WrittenReviewAdmin(admin.ModelAdmin): class SynthesisAdmin(admin.ModelAdmin):
list_display = ('participation', 'type', 'reporter', 'passage',) list_display = ('participation', 'type', 'defender', 'passage',)
list_filter = ('participation__tournament', 'type', 'passage__solution_number',) list_filter = ('participation__tournament', 'type', 'passage__solution_number',)
search_fields = ('participation__team__name', 'participation__team__trigram',) search_fields = ('participation__team__name', 'participation__team__trigram',)
autocomplete_fields = ('participation', 'passage',) autocomplete_fields = ('participation', 'passage',)
@admin.display(description=_("reporter")) @admin.display(description=_("defender"))
def reporter(self, record: WrittenReview): def defender(self, record: Synthesis):
return record.passage.reporter return record.passage.defender
@admin.display(description=_("problem")) @admin.display(description=_("problem"))
def problem(self, record: WrittenReview): def problem(self, record: Synthesis):
return record.passage.solution_number return record.passage.solution_number

View File

@ -3,7 +3,7 @@
from rest_framework import serializers from rest_framework import serializers
from ..models import Note, Participation, Passage, Pool, Solution, Team, Tournament, WrittenReview from ..models import Note, Participation, Passage, Pool, Solution, Synthesis, Team, Tournament
class NoteSerializer(serializers.ModelSerializer): class NoteSerializer(serializers.ModelSerializer):
@ -38,9 +38,9 @@ class SolutionSerializer(serializers.ModelSerializer):
fields = '__all__' fields = '__all__'
class WrittenReviewSerializer(serializers.ModelSerializer): class SynthesisSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = WrittenReview model = Synthesis
fields = '__all__' fields = '__all__'
@ -58,9 +58,9 @@ class TournamentSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Tournament model = Tournament
fields = ('id', 'pk', 'name', 'date_start', 'date_end', 'place', 'max_teams', 'price', 'remote', fields = ('id', 'pk', 'name', 'date_start', 'date_end', 'place', 'max_teams', 'price', 'remote',
'inscription_limit', 'solution_limit', 'solutions_draw', 'reviews_first_phase_limit', 'inscription_limit', 'solution_limit', 'solutions_draw', 'syntheses_first_phase_limit',
'solutions_available_second_phase', 'reviews_second_phase_limit', 'solutions_available_second_phase', 'syntheses_second_phase_limit',
'solutions_available_third_phase', 'reviews_third_phase_limit', 'solutions_available_third_phase', 'syntheses_third_phase_limit',
'description', 'organizers', 'final', 'participations',) 'description', 'organizers', 'final', 'participations',)

View File

@ -2,7 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from .views import NoteViewSet, ParticipationViewSet, PassageViewSet, PoolViewSet, \ from .views import NoteViewSet, ParticipationViewSet, PassageViewSet, PoolViewSet, \
SolutionViewSet, TeamViewSet, TournamentViewSet, TweakViewSet, WrittenReviewViewSet SolutionViewSet, SynthesisViewSet, TeamViewSet, TournamentViewSet, TweakViewSet
def register_participation_urls(router, path): def register_participation_urls(router, path):
@ -13,8 +13,8 @@ def register_participation_urls(router, path):
router.register(path + "/participation", ParticipationViewSet) router.register(path + "/participation", ParticipationViewSet)
router.register(path + "/passage", PassageViewSet) router.register(path + "/passage", PassageViewSet)
router.register(path + "/pool", PoolViewSet) router.register(path + "/pool", PoolViewSet)
router.register(path + "/review", WrittenReviewViewSet)
router.register(path + "/solution", SolutionViewSet) router.register(path + "/solution", SolutionViewSet)
router.register(path + "/synthesis", SynthesisViewSet)
router.register(path + "/team", TeamViewSet) router.register(path + "/team", TeamViewSet)
router.register(path + "/tournament", TournamentViewSet) router.register(path + "/tournament", TournamentViewSet)
router.register(path + "/tweak", TweakViewSet) router.register(path + "/tweak", TweakViewSet)

View File

@ -4,15 +4,15 @@ from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from .serializers import NoteSerializer, ParticipationSerializer, PassageSerializer, PoolSerializer, \ from .serializers import NoteSerializer, ParticipationSerializer, PassageSerializer, PoolSerializer, \
SolutionSerializer, TeamSerializer, TournamentSerializer, TweakSerializer, WrittenReviewSerializer SolutionSerializer, SynthesisSerializer, TeamSerializer, TournamentSerializer, TweakSerializer
from ..models import Note, Participation, Passage, Pool, Solution, Team, Tournament, Tweak, WrittenReview from ..models import Note, Participation, Passage, Pool, Solution, Synthesis, Team, Tournament, Tweak
class NoteViewSet(ModelViewSet): class NoteViewSet(ModelViewSet):
queryset = Note.objects.all() queryset = Note.objects.all()
serializer_class = NoteSerializer serializer_class = NoteSerializer
filter_backends = [DjangoFilterBackend] filter_backends = [DjangoFilterBackend]
filterset_fields = ['jury', 'passage', 'reporter_writing', 'reporter_oral', 'opponent_writing', filterset_fields = ['jury', 'passage', 'defender_writing', 'defender_oral', 'opponent_writing',
'opponent_oral', 'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_oral', ] 'opponent_oral', 'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_oral', ]
@ -27,7 +27,7 @@ class PassageViewSet(ModelViewSet):
queryset = Passage.objects.all() queryset = Passage.objects.all()
serializer_class = PassageSerializer serializer_class = PassageSerializer
filter_backends = [DjangoFilterBackend] filter_backends = [DjangoFilterBackend]
filterset_fields = ['pool', 'solution_number', 'reporter', 'opponent', 'reviewer', 'observer', 'pool_tournament', ] filterset_fields = ['pool', 'solution_number', 'defender', 'opponent', 'reviewer', 'observer', 'pool_tournament', ]
class PoolViewSet(ModelViewSet): class PoolViewSet(ModelViewSet):
@ -44,9 +44,9 @@ class SolutionViewSet(ModelViewSet):
filterset_fields = ['participation', 'number', 'problem', 'final_solution', ] filterset_fields = ['participation', 'number', 'problem', 'final_solution', ]
class WrittenReviewViewSet(ModelViewSet): class SynthesisViewSet(ModelViewSet):
queryset = WrittenReview.objects.all() queryset = Synthesis.objects.all()
serializer_class = WrittenReviewSerializer serializer_class = SynthesisSerializer
filter_backends = [DjangoFilterBackend] filter_backends = [DjangoFilterBackend]
filterset_fields = ['participation', 'number', 'passage', 'type', ] filterset_fields = ['participation', 'number', 'passage', 'type', ]
@ -64,9 +64,9 @@ class TournamentViewSet(ModelViewSet):
serializer_class = TournamentSerializer serializer_class = TournamentSerializer
filter_backends = [DjangoFilterBackend] filter_backends = [DjangoFilterBackend]
filterset_fields = ['name', 'date_start', 'date_end', 'place', 'max_teams', 'price', 'remote', filterset_fields = ['name', 'date_start', 'date_end', 'place', 'max_teams', 'price', 'remote',
'inscription_limit', 'solution_limit', 'solutions_draw', 'reviews_first_phase_limit', 'inscription_limit', 'solution_limit', 'solutions_draw', 'syntheses_first_phase_limit',
'solutions_available_second_phase', 'reviews_second_phase_limit', 'solutions_available_second_phase', 'syntheses_second_phase_limit',
'solutions_available_third_phase', 'reviews_third_phase_limit', 'solutions_available_third_phase', 'syntheses_third_phase_limit',
'description', 'organizers', 'final', ] 'description', 'organizers', 'final', ]

View File

@ -16,7 +16,7 @@ from pypdf import PdfReader
from registration.models import VolunteerRegistration from registration.models import VolunteerRegistration
from tfjm import settings from tfjm import settings
from .models import Note, Participation, Passage, Pool, Solution, Team, Tournament, WrittenReview from .models import Note, Participation, Passage, Pool, Solution, Synthesis, Team, Tournament
class TeamForm(forms.ModelForm): class TeamForm(forms.ModelForm):
@ -137,7 +137,7 @@ class TournamentForm(forms.ModelForm):
if settings.NB_ROUNDS < 3: if settings.NB_ROUNDS < 3:
del self.fields['date_third_phase'] del self.fields['date_third_phase']
del self.fields['solutions_available_third_phase'] del self.fields['solutions_available_third_phase']
del self.fields['reviews_third_phase_limit'] del self.fields['syntheses_third_phase_limit']
if not settings.PAYMENT_MANAGEMENT: if not settings.PAYMENT_MANAGEMENT:
del self.fields['price'] del self.fields['price']
@ -151,13 +151,13 @@ class TournamentForm(forms.ModelForm):
'solution_limit': forms.DateTimeInput(attrs={'type': 'datetime-local'}, format='%Y-%m-%d %H:%M'), 'solution_limit': forms.DateTimeInput(attrs={'type': 'datetime-local'}, format='%Y-%m-%d %H:%M'),
'solutions_draw': forms.DateTimeInput(attrs={'type': 'datetime-local'}, format='%Y-%m-%d %H:%M'), 'solutions_draw': forms.DateTimeInput(attrs={'type': 'datetime-local'}, format='%Y-%m-%d %H:%M'),
'date_first_phase': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'), 'date_first_phase': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
'reviews_first_phase_limit': forms.DateTimeInput(attrs={'type': 'datetime-local'}, 'syntheses_first_phase_limit': forms.DateTimeInput(attrs={'type': 'datetime-local'},
format='%Y-%m-%d %H:%M'), format='%Y-%m-%d %H:%M'),
'date_second_phase': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'), 'date_second_phase': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
'reviews_second_phase_limit': forms.DateTimeInput(attrs={'type': 'datetime-local'}, 'syntheses_second_phase_limit': forms.DateTimeInput(attrs={'type': 'datetime-local'},
format='%Y-%m-%d %H:%M'), format='%Y-%m-%d %H:%M'),
'date_third_phase': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'), 'date_third_phase': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
'reviews_third_phase_limit': forms.DateTimeInput(attrs={'type': 'datetime-local'}, 'syntheses_third_phase_limit': forms.DateTimeInput(attrs={'type': 'datetime-local'},
format='%Y-%m-%d %H:%M'), format='%Y-%m-%d %H:%M'),
'organizers': forms.SelectMultiple(attrs={ 'organizers': forms.SelectMultiple(attrs={
'class': 'selectpicker', 'class': 'selectpicker',
@ -345,21 +345,21 @@ class UploadNotesForm(forms.Form):
class PassageForm(forms.ModelForm): class PassageForm(forms.ModelForm):
def clean(self): def clean(self):
cleaned_data = super().clean() cleaned_data = super().clean()
if "reporter" in cleaned_data and "opponent" in cleaned_data and "reviewer" in cleaned_data \ if "defender" in cleaned_data and "opponent" in cleaned_data and "reviewer" in cleaned_data \
and len({cleaned_data["reporter"], cleaned_data["opponent"], cleaned_data["reviewer"]}) < 3: and len({cleaned_data["defender"], cleaned_data["opponent"], cleaned_data["reviewer"]}) < 3:
self.add_error(None, _("The reporter, the opponent and the reviewer must be different.")) self.add_error(None, _("The defender, the opponent and the reviewer must be different."))
if "reporter" in self.cleaned_data and "solution_number" in self.cleaned_data \ if "defender" in self.cleaned_data and "solution_number" in self.cleaned_data \
and not Solution.objects.filter(participation=cleaned_data["reporter"], and not Solution.objects.filter(participation=cleaned_data["defender"],
problem=cleaned_data["solution_number"]).exists(): problem=cleaned_data["solution_number"]).exists():
self.add_error("solution_number", _("This reporter did not work on this problem.")) self.add_error("solution_number", _("This defender did not work on this problem."))
return cleaned_data return cleaned_data
class Meta: class Meta:
model = Passage model = Passage
fields = ('position', 'solution_number', 'reporter', 'opponent', 'reviewer', 'opponent', 'reporter_penalties',) fields = ('position', 'solution_number', 'defender', 'opponent', 'reviewer', 'opponent', 'defender_penalties',)
class WrittenReviewForm(forms.ModelForm): class SynthesisForm(forms.ModelForm):
def clean_file(self): def clean_file(self):
if "file" in self.files: if "file" in self.files:
file = self.files["file"] file = self.files["file"]
@ -375,16 +375,16 @@ class WrittenReviewForm(forms.ModelForm):
def save(self, commit=True): def save(self, commit=True):
""" """
Don't save a written review with this way. Use a view instead Don't save a synthesis with this way. Use a view instead
""" """
class Meta: class Meta:
model = WrittenReview model = Synthesis
fields = ('file',) fields = ('file',)
class NoteForm(forms.ModelForm): class NoteForm(forms.ModelForm):
class Meta: class Meta:
model = Note model = Note
fields = ('reporter_writing', 'reporter_oral', 'opponent_writing', fields = ('defender_writing', 'defender_oral', 'opponent_writing',
'opponent_oral', 'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_oral', ) 'opponent_oral', 'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_oral', )

View File

@ -51,23 +51,23 @@ class Command(BaseCommand):
team3, score3 = sorted_notes[2] team3, score3 = sorted_notes[2]
pool1 = tournament.pools.filter(round=1, participations=team2).first() pool1 = tournament.pools.filter(round=1, participations=team2).first()
reporter_passage_1 = Passage.objects.get(pool__tournament=tournament, pool__round=1, reporter=team2) defender_passage_1 = Passage.objects.get(pool__tournament=tournament, pool__round=1, defender=team2)
opponent_passage_1 = Passage.objects.get(pool__tournament=tournament, pool__round=1, opponent=team2) opponent_passage_1 = Passage.objects.get(pool__tournament=tournament, pool__round=1, opponent=team2)
reviewer_passage_1 = Passage.objects.get(pool__tournament=tournament, pool__round=1, reviewer=team2) reviewer_passage_1 = Passage.objects.get(pool__tournament=tournament, pool__round=1, reviewer=team2)
pool2 = tournament.pools.filter(round=2, participations=team2).first() pool2 = tournament.pools.filter(round=2, participations=team2).first()
reporter_passage_2 = Passage.objects.get(pool__tournament=tournament, pool__round=2, reporter=team2) defender_passage_2 = Passage.objects.get(pool__tournament=tournament, pool__round=2, defender=team2)
opponent_passage_2 = Passage.objects.get(pool__tournament=tournament, pool__round=2, opponent=team2) opponent_passage_2 = Passage.objects.get(pool__tournament=tournament, pool__round=2, opponent=team2)
reviewer_passage_2 = Passage.objects.get(pool__tournament=tournament, pool__round=2, reviewer=team2) reviewer_passage_2 = Passage.objects.get(pool__tournament=tournament, pool__round=2, reviewer=team2)
line.append(team2.team.trigram) line.append(team2.team.trigram)
line.append(str(pool1.jury_president or "")) line.append(str(pool1.jury_president or ""))
line.append(f"Pb. {reporter_passage_1.solution_number}") line.append(f"Pb. {defender_passage_1.solution_number}")
line.extend([reporter_passage_1.average_reporter_writing, reporter_passage_1.average_reporter_oral, line.extend([defender_passage_1.average_defender_writing, defender_passage_1.average_defender_oral,
opponent_passage_1.average_opponent_writing, opponent_passage_1.average_opponent_oral, opponent_passage_1.average_opponent_writing, opponent_passage_1.average_opponent_oral,
reviewer_passage_1.average_reviewer_writing, reviewer_passage_1.average_reviewer_oral]) reviewer_passage_1.average_reviewer_writing, reviewer_passage_1.average_reviewer_oral])
line.append(str(pool2.jury_president or "")) line.append(str(pool2.jury_president or ""))
line.append(f"Pb. {reporter_passage_2.solution_number}") line.append(f"Pb. {defender_passage_2.solution_number}")
line.extend([reporter_passage_2.average_reporter_writing, reporter_passage_2.average_reporter_oral, line.extend([defender_passage_2.average_defender_writing, defender_passage_2.average_defender_oral,
opponent_passage_2.average_opponent_writing, opponent_passage_2.average_opponent_oral, opponent_passage_2.average_opponent_writing, opponent_passage_2.average_opponent_oral,
reviewer_passage_2.average_reviewer_writing, reviewer_passage_2.average_reviewer_oral]) reviewer_passage_2.average_reviewer_writing, reviewer_passage_2.average_reviewer_oral])
line.extend([score2, f"{score1:.1f} ({team1.team.trigram})", line.extend([score2, f"{score1:.1f} ({team1.team.trigram})",

View File

@ -1,75 +0,0 @@
# Generated by Django 5.0.6 on 2024-07-06 19:19
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("participation", "0019_note_observer_oral_note_observer_writing_and_more"),
]
operations = [
migrations.RenameModel(
old_name="Synthesis",
new_name="WrittenReview",
),
migrations.AlterModelOptions(
name="writtenreview",
options={
"ordering": ("passage__pool__round", "type"),
"verbose_name": "written review",
"verbose_name_plural": "written reviews",
},
),
migrations.RenameField(
model_name="tournament",
old_name="syntheses_first_phase_limit",
new_name="reviews_first_phase_limit",
),
migrations.RenameField(
model_name="tournament",
old_name="syntheses_second_phase_limit",
new_name="reviews_second_phase_limit",
),
migrations.RenameField(
model_name="tournament",
old_name="syntheses_third_phase_limit",
new_name="reviews_third_phase_limit",
),
migrations.AlterField(
model_name="tournament",
name="reviews_first_phase_limit",
field=models.DateTimeField(
default=django.utils.timezone.now,
verbose_name="limit date to upload the written reviews for the first phase",
),
),
migrations.AlterField(
model_name="tournament",
name="reviews_second_phase_limit",
field=models.DateTimeField(
default=django.utils.timezone.now,
verbose_name="limit date to upload the written reviews for the second phase",
),
),
migrations.AlterField(
model_name="tournament",
name="reviews_third_phase_limit",
field=models.DateTimeField(
default=django.utils.timezone.now,
verbose_name="limit date to upload the written reviews for the third phase",
),
),
migrations.AlterField(
model_name="writtenreview",
name="passage",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="written_reviews",
to="participation.passage",
verbose_name="passage",
),
),
]

View File

@ -1,133 +0,0 @@
# Generated by Django 5.0.6 on 2024-07-06 20:00
import django
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("participation", "0020_rename_synthesis_writtenreview_and_more"),
]
operations = [
migrations.RenameField(
model_name="note",
old_name="defender_oral",
new_name="reporter_oral",
),
migrations.RenameField(
model_name="note",
old_name="defender_writing",
new_name="reporter_writing",
),
migrations.RenameField(
model_name="passage",
old_name="defender",
new_name="reporter",
),
migrations.RenameField(
model_name="passage",
old_name="defender_penalties",
new_name="reporter_penalties",
),
migrations.AlterField(
model_name="passage",
name="solution_number",
field=models.PositiveSmallIntegerField(
choices=[
(1, "Problem #1"),
(2, "Problem #2"),
(3, "Problem #3"),
(4, "Problem #4"),
(5, "Problem #5"),
(6, "Problem #6"),
(7, "Problem #7"),
(8, "Problem #8"),
(9, "Problem #9"),
(10, "Problem #10"),
],
verbose_name="reported solution",
),
),
migrations.AlterField(
model_name="note",
name="reporter_oral",
field=models.PositiveSmallIntegerField(
choices=[
(0, 0),
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(5, 5),
(6, 6),
(7, 7),
(8, 8),
(9, 9),
(10, 10),
(11, 11),
(12, 12),
(13, 13),
(14, 14),
(15, 15),
(16, 16),
(17, 17),
(18, 18),
(19, 19),
(20, 20),
],
default=0,
verbose_name="reporter oral note",
),
),
migrations.AlterField(
model_name="note",
name="reporter_writing",
field=models.PositiveSmallIntegerField(
choices=[
(0, 0),
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(5, 5),
(6, 6),
(7, 7),
(8, 8),
(9, 9),
(10, 10),
(11, 11),
(12, 12),
(13, 13),
(14, 14),
(15, 15),
(16, 16),
(17, 17),
(18, 18),
(19, 19),
(20, 20),
],
default=0,
verbose_name="reporter writing note",
),
),
migrations.AlterField(
model_name="passage",
name="reporter",
field=models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="+",
to="participation.participation",
verbose_name="reporter",
),
),
migrations.AlterField(
model_name="passage",
name="reporter_penalties",
field=models.PositiveSmallIntegerField(
default=0,
help_text="Number of penalties for the reporter. The reporter will loose a 0.5 coefficient per penalty.",
verbose_name="penalties",
),
),
]

View File

@ -323,8 +323,8 @@ class Tournament(models.Model):
default=date.today, default=date.today,
) )
reviews_first_phase_limit = models.DateTimeField( syntheses_first_phase_limit = models.DateTimeField(
verbose_name=_("limit date to upload the written reviews for the first phase"), verbose_name=_("limit date to upload the syntheses for the first phase"),
default=timezone.now, default=timezone.now,
) )
@ -338,8 +338,8 @@ class Tournament(models.Model):
default=False, default=False,
) )
reviews_second_phase_limit = models.DateTimeField( syntheses_second_phase_limit = models.DateTimeField(
verbose_name=_("limit date to upload the written reviews for the second phase"), verbose_name=_("limit date to upload the syntheses for the second phase"),
default=timezone.now, default=timezone.now,
) )
@ -353,8 +353,8 @@ class Tournament(models.Model):
default=False, default=False,
) )
reviews_third_phase_limit = models.DateTimeField( syntheses_third_phase_limit = models.DateTimeField(
verbose_name=_("limit date to upload the written reviews for the third phase"), verbose_name=_("limit date to upload the syntheses for the third phase"),
default=timezone.now, default=timezone.now,
) )
@ -442,10 +442,10 @@ class Tournament(models.Model):
return Solution.objects.filter(participation__tournament=self) return Solution.objects.filter(participation__tournament=self)
@property @property
def written_reviews(self): def syntheses(self):
if self.final: if self.final:
return WrittenReview.objects.filter(final_solution=True) return Synthesis.objects.filter(final_solution=True)
return WrittenReview.objects.filter(participation__tournament=self) return Synthesis.objects.filter(participation__tournament=self)
@property @property
def best_format(self): def best_format(self):
@ -490,7 +490,7 @@ class Tournament(models.Model):
line = [f"{participation.team.name} ({participation.team.trigram})"] line = [f"{participation.team.name} ({participation.team.trigram})"]
lines.append(line) lines.append(line)
passage1 = Passage.objects.get(pool__tournament=self, pool__round=1, reporter=participation) passage1 = Passage.objects.get(pool__tournament=self, pool__round=1, defender=participation)
pool1 = passage1.pool pool1 = passage1.pool
if pool1.participations.count() != 5: if pool1.participations.count() != 5:
position1 = passage1.position position1 = passage1.position
@ -502,8 +502,8 @@ class Tournament(models.Model):
line.append(f"=SIERREUR('{_('Pool')} {pool1.short_name}'!$D{pool1.juries.count() + 10 + position1}; 0)") line.append(f"=SIERREUR('{_('Pool')} {pool1.short_name}'!$D{pool1.juries.count() + 10 + position1}; 0)")
line.append(tweak1.diff if tweak1 else 0) line.append(tweak1.diff if tweak1 else 0)
if Passage.objects.filter(pool__tournament=self, pool__round=2, reporter=participation).exists(): if Passage.objects.filter(pool__tournament=self, pool__round=2, defender=participation).exists():
passage2 = Passage.objects.get(pool__tournament=self, pool__round=2, reporter=participation) passage2 = Passage.objects.get(pool__tournament=self, pool__round=2, defender=participation)
pool2 = passage2.pool pool2 = passage2.pool
if pool2.participations.count() != 5: if pool2.participations.count() != 5:
position2 = passage2.position position2 = passage2.position
@ -519,8 +519,8 @@ class Tournament(models.Model):
if settings.NB_ROUNDS >= 3: if settings.NB_ROUNDS >= 3:
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}")
if Passage.objects.filter(pool__tournament=self, pool__round=3, reporter=participation).exists(): if Passage.objects.filter(pool__tournament=self, pool__round=3, defender=participation).exists():
passage3 = Passage.objects.get(pool__tournament=self, pool__round=3, reporter=participation) passage3 = Passage.objects.get(pool__tournament=self, pool__round=3, defender=participation)
pool3 = passage3.pool pool3 = passage3.pool
if pool3.participations.count() != 5: if pool3.participations.count() != 5:
position3 = passage3.position position3 = passage3.position
@ -911,128 +911,128 @@ class Participation(models.Model):
'priority': 1, 'priority': 1,
'content': content, 'content': content,
}) })
elif timezone.now() <= tournament.reviews_first_phase_limit + timedelta(hours=2): elif timezone.now() <= tournament.syntheses_first_phase_limit + timedelta(hours=2):
reporter_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, reporter=self) defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, defender=self)
opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, opponent=self) opponent_passage = Passage.objects.get(pool__tournament=self.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=self.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=self.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 " defender_text = _("<p>The solutions draw is ended. You can check the result on "
"<a href='{draw_url}'>this page</a>.</p>" "<a href='{draw_url}'>this page</a>.</p>"
"<p>For the first round, you will present " "<p>For the first round, you will defend "
"<a href='{solution_url}'>your solution of the problem {problem}</a>.</p>") "<a href='{solution_url}'>your solution of the problem {problem}</a>.</p>")
draw_url = reverse_lazy("draw:index") draw_url = reverse_lazy("draw:index")
solution_url = reporter_passage.reported_solution.file.url solution_url = defender_passage.defended_solution.file.url
reporter_content = format_lazy(reporter_text, draw_url=draw_url, defender_content = format_lazy(defender_text, draw_url=draw_url,
solution_url=solution_url, problem=reporter_passage.solution_number) solution_url=solution_url, problem=defender_passage.solution_number)
opponent_text = _("<p>You will oppose the solution of the team {opponent} on the " opponent_text = _("<p>You will oppose the solution of the team {opponent} on the "
"<a href='{solution_url}'>problem {problem}</a>. " "<a href='{solution_url}'>problem {problem}</a>. "
"You can upload your written review on <a href='{passage_url}'>this page</a>.</p>") "You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
solution_url = opponent_passage.reported_solution.file.url solution_url = opponent_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(opponent_passage.pk,)) passage_url = reverse_lazy("participation:passage_detail", args=(opponent_passage.pk,))
opponent_content = format_lazy(opponent_text, opponent=opponent_passage.reporter.team.trigram, opponent_content = format_lazy(opponent_text, opponent=opponent_passage.defender.team.trigram,
solution_url=solution_url, solution_url=solution_url,
problem=opponent_passage.solution_number, passage_url=passage_url) problem=opponent_passage.solution_number, passage_url=passage_url)
reviewer_text = _("<p>You will report the solution of the team {reviewer} on the " reviewer_text = _("<p>You will report the solution of the team {reviewer} on the "
"<a href='{solution_url}'>problem {problem}</a>. " "<a href='{solution_url}'>problem {problem}</a>. "
"You can upload your written review on <a href='{passage_url}'>this page</a>.</p>") "You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
solution_url = reviewer_passage.reported_solution.file.url solution_url = reviewer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(reviewer_passage.pk,)) passage_url = reverse_lazy("participation:passage_detail", args=(reviewer_passage.pk,))
reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.reporter.team.trigram, reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.defender.team.trigram,
solution_url=solution_url, solution_url=solution_url,
problem=reviewer_passage.solution_number, passage_url=passage_url) problem=reviewer_passage.solution_number, passage_url=passage_url)
if observer_passage: if observer_passage:
observer_text = _("<p>You will observe the solution of the team {observer} on the " observer_text = _("<p>You will observe the solution of the team {observer} on the "
"<a href='{solution_url}'>problem {problem}</a>. " "<a href='{solution_url}'>problem {problem}</a>. "
"You can upload your written review on <a href='{passage_url}'>this page</a>.</p>") "You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
solution_url = observer_passage.reported_solution.file.url solution_url = observer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(observer_passage.pk,)) passage_url = reverse_lazy("participation:passage_detail", args=(observer_passage.pk,))
observer_content = format_lazy(observer_text, observer_content = format_lazy(observer_text,
observer=observer_passage.reporter.team.trigram, observer=observer_passage.defender.team.trigram,
solution_url=solution_url, solution_url=solution_url,
problem=observer_passage.solution_number, passage_url=passage_url) problem=observer_passage.solution_number, passage_url=passage_url)
else: else:
observer_content = "" observer_content = ""
if settings.TFJM_APP == "TFJM": if settings.TFJM_APP == "TFJM":
reviews_template_begin = f"{settings.STATIC_URL}tfjm/Fiche_synthèse." syntheses_template_begin = f"{settings.STATIC_URL}tfjm/Fiche_synthèse."
reviews_templates = "".join(f"<a href='{reviews_template_begin}{ext}'>{ext.upper()}</a>" syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex", "odt", "docx"]) for ext in ["pdf", "tex", "odt", "docx"])
else: else:
reviews_template_begin = f"{settings.STATIC_URL}eteam/Written_review." syntheses_template_begin = f"{settings.STATIC_URL}eteam/Written_review."
reviews_templates = "".join(f"<a href='{reviews_template_begin}{ext}'>{ext.upper()}</a>" syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex"]) for ext in ["pdf", "tex"])
reviews_templates_content = f"<p>{_('Templates:')} {reviews_templates}</p>" syntheses_templates_content = f"<p>{_('Templates:')} {syntheses_templates}</p>"
content = reporter_content + opponent_content + reviewer_content + observer_content \ content = defender_content + opponent_content + reviewer_content + observer_content \
+ reviews_templates_content + syntheses_templates_content
informations.append({ informations.append({
'title': _("First round"), 'title': _("First round"),
'type': "info", 'type': "info",
'priority': 1, 'priority': 1,
'content': content, 'content': content,
}) })
elif timezone.now() <= tournament.reviews_second_phase_limit + timedelta(hours=2): elif timezone.now() <= tournament.syntheses_second_phase_limit + timedelta(hours=2):
reporter_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, reporter=self) defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, defender=self)
opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, opponent=self) opponent_passage = Passage.objects.get(pool__tournament=self.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=self.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=self.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 " defender_text = _("<p>For the second round, you will defend "
"<a href='{solution_url}'>your solution of the problem {problem}</a>.</p>") "<a href='{solution_url}'>your solution of the problem {problem}</a>.</p>")
draw_url = reverse_lazy("draw:index") draw_url = reverse_lazy("draw:index")
solution_url = reporter_passage.reported_solution.file.url solution_url = defender_passage.defended_solution.file.url
reporter_content = format_lazy(reporter_text, draw_url=draw_url, defender_content = format_lazy(defender_text, draw_url=draw_url,
solution_url=solution_url, problem=reporter_passage.solution_number) solution_url=solution_url, problem=defender_passage.solution_number)
opponent_text = _("<p>You will oppose the solution of the team {opponent} on the " opponent_text = _("<p>You will oppose the solution of the team {opponent} on the "
"<a href='{solution_url}'>problem {problem}</a>. " "<a href='{solution_url}'>problem {problem}</a>. "
"You can upload your written review on <a href='{passage_url}'>this page</a>.</p>") "You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
solution_url = opponent_passage.reported_solution.file.url solution_url = opponent_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(opponent_passage.pk,)) passage_url = reverse_lazy("participation:passage_detail", args=(opponent_passage.pk,))
opponent_content = format_lazy(opponent_text, opponent=opponent_passage.reporter.team.trigram, opponent_content = format_lazy(opponent_text, opponent=opponent_passage.defender.team.trigram,
solution_url=solution_url, solution_url=solution_url,
problem=opponent_passage.solution_number, passage_url=passage_url) problem=opponent_passage.solution_number, passage_url=passage_url)
reviewer_text = _("<p>You will report the solution of the team {reviewer} on the " reviewer_text = _("<p>You will report the solution of the team {reviewer} on the "
"<a href='{solution_url}'>problem {problem}</a>. " "<a href='{solution_url}'>problem {problem}</a>. "
"You can upload your written review on <a href='{passage_url}'>this page</a>.</p>") "You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
solution_url = reviewer_passage.reported_solution.file.url solution_url = reviewer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(reviewer_passage.pk,)) passage_url = reverse_lazy("participation:passage_detail", args=(reviewer_passage.pk,))
reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.reporter.team.trigram, reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.defender.team.trigram,
solution_url=solution_url, solution_url=solution_url,
problem=reviewer_passage.solution_number, passage_url=passage_url) problem=reviewer_passage.solution_number, passage_url=passage_url)
if observer_passage: if observer_passage:
observer_text = _("<p>You will observe the solution of the team {observer} on the " observer_text = _("<p>You will observe the solution of the team {observer} on the "
"<a href='{solution_url}'>problem {problem}</a>. " "<a href='{solution_url}'>problem {problem}</a>. "
"You can upload your written review on <a href='{passage_url}'>this page</a>.</p>") "You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
solution_url = observer_passage.reported_solution.file.url solution_url = observer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(observer_passage.pk,)) passage_url = reverse_lazy("participation:passage_detail", args=(observer_passage.pk,))
observer_content = format_lazy(observer_text, observer_content = format_lazy(observer_text,
observer=observer_passage.reporter.team.trigram, observer=observer_passage.defender.team.trigram,
solution_url=solution_url, solution_url=solution_url,
problem=observer_passage.solution_number, passage_url=passage_url) problem=observer_passage.solution_number, passage_url=passage_url)
else: else:
observer_content = "" observer_content = ""
if settings.TFJM_APP == "TFJM": if settings.TFJM_APP == "TFJM":
reviews_template_begin = f"{settings.STATIC_URL}tfjm/Fiche_synthèse." syntheses_template_begin = f"{settings.STATIC_URL}tfjm/Fiche_synthèse."
reviews_templates = "".join(f"<a href='{reviews_template_begin}{ext}'>{ext.upper()}</a>" syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex", "odt", "docx"]) for ext in ["pdf", "tex", "odt", "docx"])
else: else:
reviews_template_begin = f"{settings.STATIC_URL}eteam/Written_review." syntheses_template_begin = f"{settings.STATIC_URL}eteam/Written_review."
reviews_templates = "".join(f"<a href='{reviews_template_begin}{ext}'>{ext.upper()}</a>" syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex"]) for ext in ["pdf", "tex"])
reviews_templates_content = f"<p>{_('Templates:')} {reviews_templates}</p>" syntheses_templates_content = f"<p>{_('Templates:')} {syntheses_templates}</p>"
content = reporter_content + opponent_content + reviewer_content + observer_content \ content = defender_content + opponent_content + reviewer_content + observer_content \
+ reviews_templates_content + syntheses_templates_content
informations.append({ informations.append({
'title': _("Second round"), 'title': _("Second round"),
'type': "info", 'type': "info",
@ -1040,63 +1040,63 @@ class Participation(models.Model):
'content': content, 'content': content,
}) })
elif settings.TFJM_APP == "ETEAM" \ elif settings.TFJM_APP == "ETEAM" \
and timezone.now() <= tournament.reviews_third_phase_limit + timedelta(hours=2): and timezone.now() <= tournament.syntheses_third_phase_limit + timedelta(hours=2):
reporter_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, reporter=self) defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, defender=self)
opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, opponent=self) opponent_passage = Passage.objects.get(pool__tournament=self.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=self.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=self.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 " defender_text = _("<p>For the third round, you will defend "
"<a href='{solution_url}'>your solution of the problem {problem}</a>.</p>") "<a href='{solution_url}'>your solution of the problem {problem}</a>.</p>")
draw_url = reverse_lazy("draw:index") draw_url = reverse_lazy("draw:index")
solution_url = reporter_passage.reported_solution.file.url solution_url = defender_passage.defended_solution.file.url
reporter_content = format_lazy(reporter_text, draw_url=draw_url, defender_content = format_lazy(defender_text, draw_url=draw_url,
solution_url=solution_url, problem=reporter_passage.solution_number) solution_url=solution_url, problem=defender_passage.solution_number)
opponent_text = _("<p>You will oppose the solution of the team {opponent} on the " opponent_text = _("<p>You will oppose the solution of the team {opponent} on the "
"<a href='{solution_url}'>problem {problem}</a>. " "<a href='{solution_url}'>problem {problem}</a>. "
"You can upload your written review on <a href='{passage_url}'>this page</a>.</p>") "You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
solution_url = opponent_passage.reported_solution.file.url solution_url = opponent_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(opponent_passage.pk,)) passage_url = reverse_lazy("participation:passage_detail", args=(opponent_passage.pk,))
opponent_content = format_lazy(opponent_text, opponent=opponent_passage.reporter.team.trigram, opponent_content = format_lazy(opponent_text, opponent=opponent_passage.defender.team.trigram,
solution_url=solution_url, solution_url=solution_url,
problem=opponent_passage.solution_number, passage_url=passage_url) problem=opponent_passage.solution_number, passage_url=passage_url)
reviewer_text = _("<p>You will report the solution of the team {reviewer} on the " reviewer_text = _("<p>You will report the solution of the team {reviewer} on the "
"<a href='{solution_url}'>problem {problem}</a>. " "<a href='{solution_url}'>problem {problem}</a>. "
"You can upload your written review on <a href='{passage_url}'>this page</a>.</p>") "You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
solution_url = reviewer_passage.reported_solution.file.url solution_url = reviewer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(reviewer_passage.pk,)) passage_url = reverse_lazy("participation:passage_detail", args=(reviewer_passage.pk,))
reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.reporter.team.trigram, reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.defender.team.trigram,
solution_url=solution_url, solution_url=solution_url,
problem=reviewer_passage.solution_number, passage_url=passage_url) problem=reviewer_passage.solution_number, passage_url=passage_url)
if observer_passage: if observer_passage:
observer_text = _("<p>You will observe the solution of the team {observer} on the " observer_text = _("<p>You will observe the solution of the team {observer} on the "
"<a href='{solution_url}'>problem {problem}</a>. " "<a href='{solution_url}'>problem {problem}</a>. "
"You can upload your written review on <a href='{passage_url}'>this page</a>.</p>") "You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
solution_url = observer_passage.reported_solution.file.url solution_url = observer_passage.defended_solution.file.url
passage_url = reverse_lazy("participation:passage_detail", args=(observer_passage.pk,)) passage_url = reverse_lazy("participation:passage_detail", args=(observer_passage.pk,))
observer_content = format_lazy(observer_text, observer_content = format_lazy(observer_text,
observer=observer_passage.reporter.team.trigram, observer=observer_passage.defender.team.trigram,
solution_url=solution_url, solution_url=solution_url,
problem=observer_passage.solution_number, passage_url=passage_url) problem=observer_passage.solution_number, passage_url=passage_url)
else: else:
observer_content = "" observer_content = ""
if settings.TFJM_APP == "TFJM": if settings.TFJM_APP == "TFJM":
reviews_template_begin = f"{settings.STATIC_URL}tfjm/Fiche_synthèse." syntheses_template_begin = f"{settings.STATIC_URL}tfjm/Fiche_synthèse."
reviews_templates = "".join(f"<a href='{reviews_template_begin}{ext}'>{ext.upper()}</a>" syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex", "odt", "docx"]) for ext in ["pdf", "tex", "odt", "docx"])
else: else:
reviews_template_begin = f"{settings.STATIC_URL}eteam/Written_review." syntheses_template_begin = f"{settings.STATIC_URL}eteam/Written_review."
reviews_templates = "".join(f"<a href='{reviews_template_begin}{ext}'>{ext.upper()}</a>" syntheses_templates = "".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
for ext in ["pdf", "tex"]) for ext in ["pdf", "tex"])
reviews_templates_content = f"<p>{_('Templates:')} {reviews_templates}</p>" syntheses_templates_content = f"<p>{_('Templates:')} {syntheses_templates}</p>"
content = reporter_content + opponent_content + reviewer_content + observer_content \ content = defender_content + opponent_content + reviewer_content + observer_content \
+ reviews_templates_content + syntheses_templates_content
informations.append({ informations.append({
'title': _("Second round"), 'title': _("Second round"),
'type': "info", 'type': "info",
@ -1204,7 +1204,7 @@ class Pool(models.Model):
@property @property
def solutions(self): def solutions(self):
return [passage.reported_solution for passage in self.passages.all()] return [passage.defended_solution for passage in self.passages.all()]
@property @property
def coeff(self): def coeff(self):
@ -1251,12 +1251,12 @@ class Pool(models.Model):
header = [ header = [
sum(([str(_("Problem #{problem}").format(problem=passage.solution_number))] + (passage_width - 1) * [""] sum(([str(_("Problem #{problem}").format(problem=passage.solution_number))] + (passage_width - 1) * [""]
for passage in passages), start=[str(_("Problem")), ""]), for passage in passages), start=[str(_("Problem")), ""]),
sum(([f"{_('Reporter')} ({passage.reporter.team.trigram})", "", sum(([f"{_('Defender')} ({passage.defender.team.trigram})", "",
f"{_('Opponent')} ({passage.opponent.team.trigram})", "", f"{_('Opponent')} ({passage.opponent.team.trigram})", "",
f"{_('Reviewer')} ({passage.reviewer.team.trigram})", ""] f"{_('Reviewer')} ({passage.reviewer.team.trigram})", ""]
+ ([f"{_('Observer')} ({passage.observer.team.trigram})", ""] if has_observer else []) + ([f"{_('Observer')} ({passage.observer.team.trigram})", ""] if has_observer else [])
for passage in passages), start=[str(_("Role")), ""]), for passage in passages), start=[str(_("Role")), ""]),
sum(([f"{_('Writing')} (/{20 if settings.TFJM_APP == 'TFJM' else 10})", sum(([f"{_('Writing')} (/{20 if settings.TFJM_APP == "TFJM" else 10})",
f"{_('Oral')} (/{20 if settings.TFJM_APP == 'TFJM' else 10})", f"{_('Oral')} (/{20 if settings.TFJM_APP == 'TFJM' else 10})",
f"{_('Writing')} (/10)", f"{_('Oral')} (/10)", f"{_('Writing')} (/10)", f"{_('Oral')} (/10)"] f"{_('Writing')} (/10)", f"{_('Oral')} (/10)", f"{_('Writing')} (/10)", f"{_('Oral')} (/10)"]
+ ([f"{_('Writing')} (/10)", f"{_('Oral')} (/10)"] if has_observer else []) + ([f"{_('Writing')} (/10)", f"{_('Oral')} (/10)"] if has_observer else [])
@ -1268,7 +1268,7 @@ class Pool(models.Model):
line = [str(jury), jury.id] line = [str(jury), jury.id]
for passage in passages: for passage in passages:
note = passage.notes.filter(jury=jury).first() note = passage.notes.filter(jury=jury).first()
line.extend([note.reporter_writing, note.reporter_oral, note.opponent_writing, note.opponent_oral, line.extend([note.defender_writing, note.defender_oral, note.opponent_writing, note.opponent_oral,
note.reviewer_writing, note.reviewer_oral]) note.reviewer_writing, note.reviewer_oral])
if has_observer: if has_observer:
line.extend([note.observer_writing, note.observer_oral]) line.extend([note.observer_writing, note.observer_oral])
@ -1284,7 +1284,7 @@ class Pool(models.Model):
return getcol((number - 1) // 26) + chr(65 + (number - 1) % 26) return getcol((number - 1) // 26) + chr(65 + (number - 1) % 26)
average = [str(_("Average")), ""] average = [str(_("Average")), ""]
coeffs = sum(([passage.coeff_reporter_writing, passage.coeff_reporter_oral, coeffs = sum(([passage.coeff_defender_writing, passage.coeff_defender_oral,
passage.coeff_opponent_writing, passage.coeff_opponent_oral, passage.coeff_opponent_writing, passage.coeff_opponent_oral,
passage.coeff_reviewer_writing, passage.coeff_reviewer_oral] passage.coeff_reviewer_writing, passage.coeff_reviewer_oral]
+ ([passage.coeff_observer_writing, passage.coeff_observer_oral] if has_observer else []) + ([passage.coeff_observer_writing, passage.coeff_observer_oral] if has_observer else [])
@ -1330,11 +1330,11 @@ class Pool(models.Model):
pool__round=self.round, pool__round=self.round,
pool__letter=self.letter).order_by('position', 'pool__room') pool__letter=self.letter).order_by('position', 'pool__room')
for i, passage in enumerate(all_passages): for i, passage in enumerate(all_passages):
participation = passage.reporter participation = passage.defender
reporter_passage = Passage.objects.get(reporter=participation, defender_passage = Passage.objects.get(defender=participation,
pool__tournament=self.tournament, pool__round=self.round) pool__tournament=self.tournament, pool__round=self.round)
reporter_row = 5 + reporter_passage.pool.juries.count() defender_row = 5 + defender_passage.pool.juries.count()
reporter_col = reporter_passage.position - 1 defender_col = defender_passage.position - 1
opponent_passage = Passage.objects.get(opponent=participation, opponent_passage = Passage.objects.get(opponent=participation,
pool__tournament=self.tournament, pool__round=self.round) pool__tournament=self.tournament, pool__round=self.round)
@ -1347,8 +1347,8 @@ class Pool(models.Model):
reviewer_col = reviewer_passage.position - 1 reviewer_col = reviewer_passage.position - 1
formula = "=" formula = "="
formula += (f"'{_('Pool')} {reporter_passage.pool.short_name}'" formula += (f"'{_('Pool')} {defender_passage.pool.short_name}'"
f"!{getcol(min_column + reporter_col * passage_width)}{reporter_row + 3}") # Reporter f"!{getcol(min_column + defender_col * passage_width)}{defender_row + 3}") # Defender
formula += (f" + '{_('Pool')} {opponent_passage.pool.short_name}'" formula += (f" + '{_('Pool')} {opponent_passage.pool.short_name}'"
f"!{getcol(min_column + opponent_col * passage_width + 2)}{opponent_row + 3}") # Opponent f"!{getcol(min_column + opponent_col * passage_width + 2)}{opponent_row + 3}") # Opponent
formula += (f" + '{_('Pool')} {reviewer_passage.pool.short_name}'" formula += (f" + '{_('Pool')} {reviewer_passage.pool.short_name}'"
@ -1362,8 +1362,8 @@ class Pool(models.Model):
f"!{getcol(min_column + observer_col * passage_width + 6)}{observer_row + 3}") f"!{getcol(min_column + observer_col * passage_width + 6)}{observer_row + 3}")
ranking.append([f"{participation.team.name} ({participation.team.trigram})", "", ranking.append([f"{participation.team.name} ({participation.team.trigram})", "",
f"='{_('Pool')} {reporter_passage.pool.short_name}'" f"='{_('Pool')} {defender_passage.pool.short_name}'"
f"!${getcol(3 + reporter_col * passage_width)}$1", f"!${getcol(3 + defender_col * passage_width)}$1",
formula, formula,
f"=RANG(D{max_row + 6 + i}; " f"=RANG(D{max_row + 6 + i}; "
f"D${max_row + 6}:D${max_row + 5 + pool_size})"]) f"D${max_row + 6}:D${max_row + 5 + pool_size})"])
@ -1430,7 +1430,7 @@ class Pool(models.Model):
(f"A{max_row + 6}:E{max_row + 5 + pool_size}", (0.9, 0.9, 0.9)),] (f"A{max_row + 6}:E{max_row + 5 + pool_size}", (0.9, 0.9, 0.9)),]
# Display penalties in red # Display penalties in red
bg_colors += [(f"{getcol(2 + (passage.position - 1) * passage_width + 2)}{max_row + 2}", (1.0, 0.7, 0.7)) bg_colors += [(f"{getcol(2 + (passage.position - 1) * passage_width + 2)}{max_row + 2}", (1.0, 0.7, 0.7))
for passage in self.passages.filter(reporter_penalties__gte=1).all()] for passage in self.passages.filter(defender_penalties__gte=1).all()]
for bg_range, bg_color in bg_colors: for bg_range, bg_color in bg_colors:
r, g, b = bg_color r, g, b = bg_color
format_requests.append({ format_requests.append({
@ -1684,16 +1684,16 @@ class Passage(models.Model):
) )
solution_number = models.PositiveSmallIntegerField( solution_number = models.PositiveSmallIntegerField(
verbose_name=_("reported solution"), verbose_name=_("defended solution"),
choices=[ choices=[
(i, format_lazy(_("Problem #{problem}"), problem=i)) for i in range(1, len(settings.PROBLEMS) + 1) (i, format_lazy(_("Problem #{problem}"), problem=i)) for i in range(1, len(settings.PROBLEMS) + 1)
], ],
) )
reporter = models.ForeignKey( defender = models.ForeignKey(
Participation, Participation,
on_delete=models.PROTECT, on_delete=models.PROTECT,
verbose_name=_("reporter"), verbose_name=_("defender"),
related_name="+", related_name="+",
) )
@ -1721,17 +1721,17 @@ class Passage(models.Model):
default=None, default=None,
) )
reporter_penalties = models.PositiveSmallIntegerField( defender_penalties = models.PositiveSmallIntegerField(
verbose_name=_("penalties"), verbose_name=_("penalties"),
default=0, default=0,
help_text=_("Number of penalties for the reporter. " help_text=_("Number of penalties for the defender. "
"The reporter will loose a 0.5 coefficient per penalty."), "The defender will loose a 0.5 coefficient per penalty."),
) )
@property @property
def reported_solution(self) -> "Solution": def defended_solution(self) -> "Solution":
return Solution.objects.get( return Solution.objects.get(
participation=self.reporter, participation=self.defender,
problem=self.solution_number, problem=self.solution_number,
final_solution=self.pool.tournament.final) final_solution=self.pool.tournament.final)
@ -1740,27 +1740,27 @@ class Passage(models.Model):
return sum(items) / len(items) if items else 0 return sum(items) / len(items) if items else 0
@property @property
def average_reporter_writing(self) -> float: def average_defender_writing(self) -> float:
return self.avg(note.reporter_writing for note in self.notes.all()) return self.avg(note.defender_writing for note in self.notes.all())
@property @property
def coeff_reporter_writing(self) -> float: def coeff_defender_writing(self) -> float:
return 1 if settings.TFJM_APP == "TFJM" else 2 return 1 if settings.TFJM_APP == "TFJM" else 2
@property @property
def average_reporter_oral(self) -> float: def average_defender_oral(self) -> float:
return self.avg(note.reporter_oral for note in self.notes.all()) return self.avg(note.defender_oral for note in self.notes.all())
@property @property
def coeff_reporter_oral(self) -> float: def coeff_defender_oral(self) -> float:
coeff = 1.6 if settings.TFJM_APP == "TFJM" else 3 coeff = 1.6 if settings.TFJM_APP == "TFJM" else 3
coeff *= 1 - 0.25 * self.reporter_penalties coeff *= 1 - 0.25 * self.defender_penalties
return coeff return coeff
@property @property
def average_reporter(self) -> float: def average_defender(self) -> float:
return (self.coeff_reporter_writing * self.average_reporter_writing return (self.coeff_defender_writing * self.average_defender_writing
+ self.coeff_reporter_oral * self.average_reporter_oral) + self.coeff_defender_oral * self.average_defender_oral)
@property @property
def average_opponent_writing(self) -> float: def average_opponent_writing(self) -> float:
@ -1827,8 +1827,8 @@ class Passage(models.Model):
@property @property
def averages(self): def averages(self):
yield self.average_reporter_writing yield self.average_defender_writing
yield self.average_reporter_oral yield self.average_defender_oral
yield self.average_opponent_writing yield self.average_opponent_writing
yield self.average_opponent_oral yield self.average_opponent_oral
yield self.average_reviewer_writing yield self.average_reviewer_writing
@ -1838,7 +1838,7 @@ class Passage(models.Model):
yield self.average_observer_oral yield self.average_observer_oral
def average(self, participation): def average(self, participation):
avg = self.average_reporter if participation == self.reporter else self.average_opponent \ avg = self.average_defender if participation == self.defender else self.average_opponent \
if participation == self.opponent else self.average_reviewer if participation == self.reviewer \ if participation == self.opponent else self.average_reviewer if participation == self.reviewer \
else self.average_observer if participation == self.observer else 0 else self.average_observer if participation == self.observer else 0
avg *= self.pool.coeff avg *= self.pool.coeff
@ -1849,9 +1849,9 @@ class Passage(models.Model):
return reverse_lazy("participation:passage_detail", args=(self.pk,)) return reverse_lazy("participation:passage_detail", args=(self.pk,))
def clean(self): def clean(self):
if self.reporter not in self.pool.participations.all(): if self.defender not in self.pool.participations.all():
raise ValidationError(_("Team {trigram} is not registered in the pool.") raise ValidationError(_("Team {trigram} is not registered in the pool.")
.format(trigram=self.reporter.team.trigram)) .format(trigram=self.defender.team.trigram))
if self.opponent not in self.pool.participations.all(): if self.opponent not in self.pool.participations.all():
raise ValidationError(_("Team {trigram} is not registered in the pool.") raise ValidationError(_("Team {trigram} is not registered in the pool.")
.format(trigram=self.opponent.team.trigram)) .format(trigram=self.opponent.team.trigram))
@ -1864,8 +1864,8 @@ class Passage(models.Model):
return super().clean() return super().clean()
def __str__(self): def __str__(self):
return _("Passage of {reporter} for problem {problem}")\ return _("Passage of {defender} for problem {problem}")\
.format(reporter=self.reporter.team, problem=self.solution_number) .format(defender=self.defender.team, problem=self.solution_number)
class Meta: class Meta:
verbose_name = _("passage") verbose_name = _("passage")
@ -1905,12 +1905,8 @@ def get_solution_filename(instance, filename):
+ ("_final" if instance.final_solution else "") + ("_final" if instance.final_solution else "")
def get_review_filename(instance, filename):
return f"reviews/{instance.participation.team.trigram}_{instance.type}_{instance.passage.pk}"
def get_synthesis_filename(instance, filename): def get_synthesis_filename(instance, filename):
return get_review_filename(instance, filename) return f"syntheses/{instance.participation.team.trigram}_{instance.type}_{instance.passage.pk}"
class Solution(models.Model): class Solution(models.Model):
@ -1955,7 +1951,7 @@ class Solution(models.Model):
ordering = ('participation__team__trigram', 'final_solution', 'problem',) ordering = ('participation__team__trigram', 'final_solution', 'problem',)
class WrittenReview(models.Model): class Synthesis(models.Model):
participation = models.ForeignKey( participation = models.ForeignKey(
Participation, Participation,
on_delete=models.CASCADE, on_delete=models.CASCADE,
@ -1965,7 +1961,7 @@ class WrittenReview(models.Model):
passage = models.ForeignKey( passage = models.ForeignKey(
Passage, Passage,
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name="written_reviews", related_name="syntheses",
verbose_name=_("passage"), verbose_name=_("passage"),
) )
@ -1984,16 +1980,16 @@ class WrittenReview(models.Model):
) )
def __str__(self): def __str__(self):
return _("Written review of {team} as {type} for problem {problem} of {reporter}").format( return _("Synthesis of {team} as {type} for problem {problem} of {defender}").format(
team=self.participation.team.trigram, team=self.participation.team.trigram,
type=self.get_type_display(), type=self.get_type_display(),
problem=self.passage.solution_number, problem=self.passage.solution_number,
reporter=self.passage.reporter.team.trigram, defender=self.passage.defender.team.trigram,
) )
class Meta: class Meta:
verbose_name = _("written review") verbose_name = _("synthesis")
verbose_name_plural = _("written reviews") verbose_name_plural = _("syntheses")
unique_together = (('participation', 'passage', 'type', ), ) unique_together = (('participation', 'passage', 'type', ), )
ordering = ('passage__pool__round', 'type',) ordering = ('passage__pool__round', 'type',)
@ -2013,14 +2009,14 @@ class Note(models.Model):
related_name="notes", related_name="notes",
) )
reporter_writing = models.PositiveSmallIntegerField( defender_writing = models.PositiveSmallIntegerField(
verbose_name=_("reporter writing note"), verbose_name=_("defender writing note"),
choices=[(i, i) for i in range(0, 21)], choices=[(i, i) for i in range(0, 21)],
default=0, default=0,
) )
reporter_oral = models.PositiveSmallIntegerField( defender_oral = models.PositiveSmallIntegerField(
verbose_name=_("reporter oral note"), verbose_name=_("defender oral note"),
choices=[(i, i) for i in range(0, 21)], choices=[(i, i) for i in range(0, 21)],
default=0, default=0,
) )
@ -2062,8 +2058,8 @@ class Note(models.Model):
) )
def get_all(self): def get_all(self):
yield self.reporter_writing yield self.defender_writing
yield self.reporter_oral yield self.defender_oral
yield self.opponent_writing yield self.opponent_writing
yield self.opponent_oral yield self.opponent_oral
yield self.reviewer_writing yield self.reviewer_writing
@ -2072,10 +2068,10 @@ class Note(models.Model):
yield self.observer_writing yield self.observer_writing
yield self.observer_oral yield self.observer_oral
def set_all(self, reporter_writing: int, reporter_oral: int, opponent_writing: int, opponent_oral: int, def set_all(self, defender_writing: int, defender_oral: int, opponent_writing: int, opponent_oral: int,
reviewer_writing: int, reviewer_oral: int, observer_writing: int = 0, observer_oral: int = 0): reviewer_writing: int, reviewer_oral: int, observer_writing: int = 0, observer_oral: int = 0):
self.reporter_writing = reporter_writing self.defender_writing = defender_writing
self.reporter_oral = reporter_oral self.defender_oral = defender_oral
self.opponent_writing = opponent_writing self.opponent_writing = opponent_writing
self.opponent_oral = opponent_oral self.opponent_oral = opponent_oral
self.reviewer_writing = reviewer_writing self.reviewer_writing = reviewer_writing

View File

@ -108,13 +108,13 @@ class PoolTable(tables.Table):
class PassageTable(tables.Table): class PassageTable(tables.Table):
# FIXME Ne pas afficher l'équipe observatrice si non nécessaire # FIXME Ne pas afficher l'équipe observatrice si non nécessaire
reporter = tables.LinkColumn( defender = tables.LinkColumn(
"participation:passage_detail", "participation:passage_detail",
args=[tables.A("id")], args=[tables.A("id")],
verbose_name=_("reporter").capitalize, verbose_name=_("defender").capitalize,
) )
def render_reporter(self, value): def render_defender(self, value):
return value.team.trigram return value.team.trigram
def render_opponent(self, value): def render_opponent(self, value):
@ -131,7 +131,7 @@ 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 = ('defender', 'opponent', 'reviewer', 'observer', 'solution_number', )
class NoteTable(tables.Table): class NoteTable(tables.Table):
@ -159,5 +159,5 @@ class NoteTable(tables.Table):
'class': 'table table-condensed table-striped text-center', 'class': 'table table-condensed table-striped text-center',
} }
model = Note model = Note
fields = ('jury', 'reporter_writing', 'reporter_oral', 'opponent_writing', 'opponent_oral', fields = ('jury', 'defender_writing', 'defender_oral', 'opponent_writing', 'opponent_oral',
'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_oral', 'update',) 'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_oral', 'update',)

View File

@ -6,7 +6,7 @@
<form method="post"> <form method="post">
<div id="form-content"> <div id="form-content">
<h4>{% trans "Notes of" %} {{ note.jury }}</h4> <h4>{% trans "Notes of" %} {{ note.jury }}</h4>
<h5>{% trans "Defense of" %} {{ note.passage.reporter.team.trigram }}, {% trans "Pb." %} {{ note.passage.solution_number }}</h5> <h5>{% trans "Defense of" %} {{ note.passage.defender.team.trigram }}, {% trans "Pb." %} {{ note.passage.solution_number }}</h5>
<hr> <hr>
{% csrf_token %} {% csrf_token %}
{{ form|crispy }} {{ form|crispy }}

View File

@ -25,8 +25,8 @@
<dt class="col-sm-3">{% trans "Position:" %}</dt> <dt class="col-sm-3">{% trans "Position:" %}</dt>
<dd class="col-sm-9">{{ passage.position }}</dd> <dd class="col-sm-9">{{ passage.position }}</dd>
<dt class="col-sm-3">{% trans "Reporter:" %}</dt> <dt class="col-sm-3">{% trans "Defender:" %}</dt>
<dd class="col-sm-9"><a href="{{ passage.reporter.get_absolute_url }}">{{ passage.reporter.team }}</a></dd> <dd class="col-sm-9"><a href="{{ passage.defender.get_absolute_url }}">{{ passage.defender.team }}</a></dd>
<dt class="col-sm-3">{% trans "Opponent:" %}</dt> <dt class="col-sm-3">{% trans "Opponent:" %}</dt>
<dd class="col-sm-9"><a href="{{ passage.opponent.get_absolute_url }}">{{ passage.opponent.team }}</a></dd> <dd class="col-sm-9"><a href="{{ passage.opponent.get_absolute_url }}">{{ passage.opponent.team }}</a></dd>
@ -39,18 +39,18 @@
<dd class="col-sm-9"><a href="{{ passage.observer.get_absolute_url }}">{{ passage.observer.team }}</a></dd> <dd class="col-sm-9"><a href="{{ passage.observer.get_absolute_url }}">{{ passage.observer.team }}</a></dd>
{% endif %} {% endif %}
<dt class="col-sm-3">{% trans "Reported solution:" %}</dt> <dt class="col-sm-3">{% trans "Defended solution:" %}</dt>
<dd class="col-sm-9"><a href="{{ passage.reported_solution.file.url }}">{{ passage.reported_solution }}</a></dd> <dd class="col-sm-9"><a href="{{ passage.defended_solution.file.url }}">{{ passage.defended_solution }}</a></dd>
<dt class="col-sm-3">{% trans "Reporter penalties count:" %}</dt> <dt class="col-sm-3">{% trans "Defender penalties count:" %}</dt>
<dd class="col-sm-9">{{ passage.reporter_penalties }}</dd> <dd class="col-sm-9">{{ passage.defender_penalties }}</dd>
<dt class="col-sm-3">{% trans "Syntheses:" %}</dt> <dt class="col-sm-3">{% trans "Syntheses:" %}</dt>
<dd class="col-sm-9"> <dd class="col-sm-9">
{% for review in passage.written_reviews.all %} {% for synthesis in passage.syntheses.all %}
<a href="{{ review.file.url }}">{{ review }}{% if not forloop.last %}, {% endif %}</a> <a href="{{ synthesis.file.url }}">{{ synthesis }}{% if not forloop.last %}, {% endif %}</a>
{% empty %} {% empty %}
{% trans "No review was uploaded yet." %} {% trans "No synthesis was uploaded yet." %}
{% endfor %} {% endfor %}
</dd> </dd>
</dl> </dl>
@ -63,7 +63,7 @@
</div> </div>
{% elif user.registration.participates %} {% elif user.registration.participates %}
<div class="card-footer text-center"> <div class="card-footer text-center">
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#uploadWrittenReviewModal">{% trans "Upload review" %}</button> <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#uploadSynthesisModal">{% trans "Upload synthesis" %}</button>
</div> </div>
{% endif %} {% endif %}
</div> </div>
@ -79,19 +79,19 @@
<div class="card-body"> <div class="card-body">
<dl class="row"> <dl class="row">
<dt class="col-sm-8"> <dt class="col-sm-8">
{% trans "Average points for the reporter writing" %} {% trans "Average points for the defender writing" %}
({{ passage.reporter.team.trigram }}) : ({{ passage.defender.team.trigram }}) :
</dt> </dt>
<dd class="col-sm-4"> <dd class="col-sm-4">
{{ passage.average_reporter_writing|floatformat }}/{% if TFJM_APP == "TFJM" %}20{% else %}10{% endif %} {{ passage.average_defender_writing|floatformat }}/{% if TFJM_APP == "TFJM" %}20{% else %}10{% endif %}
</dd> </dd>
<dt class="col-sm-8"> <dt class="col-sm-8">
{% trans "Average points for the reporter oral" %} {% trans "Average points for the defender oral" %}
({{ passage.reporter.team.trigram }}) : ({{ passage.defender.team.trigram }}) :
</dt> </dt>
<dd class="col-sm-4"> <dd class="col-sm-4">
{{ passage.average_reporter_oral|floatformat }}/{% if TFJM_APP == "TFJM" %}20{% else %}10{% endif %} {{ passage.average_defender_oral|floatformat }}/{% if TFJM_APP == "TFJM" %}20{% else %}10{% endif %}
</dd> </dd>
<dt class="col-sm-8"> <dt class="col-sm-8">
@ -137,11 +137,11 @@
<dl class="row"> <dl class="row">
<dt class="col-sm-8"> <dt class="col-sm-8">
{% trans "Reporter points" %} {% trans "Defender points" %}
({{ passage.reporter.team.trigram }}) : ({{ passage.defender.team.trigram }}) :
</dt> </dt>
<dd class="col-sm-4"> <dd class="col-sm-4">
{{ passage.average_reporter|floatformat }}/{% if TFJM_APP == "TFJM" %}52{% else %}50{% endif %} {{ passage.average_defender|floatformat }}/{% if TFJM_APP == "TFJM" %}52{% else %}50{% endif %}
</dd> </dd>
<dt class="col-sm-8"> <dt class="col-sm-8">
@ -184,10 +184,10 @@
{% include "base_modal.html" with modal_id=note.modal_name %} {% include "base_modal.html" with modal_id=note.modal_name %}
{% endfor %} {% endfor %}
{% elif user.registration.participates %} {% elif user.registration.participates %}
{% trans "Upload review" as modal_title %} {% trans "Upload synthesis" as modal_title %}
{% trans "Upload" as modal_button %} {% trans "Upload" as modal_button %}
{% url "participation:upload_review" pk=passage.pk as modal_action %} {% url "participation:upload_synthesis" pk=passage.pk as modal_action %}
{% include "base_modal.html" with modal_id="uploadWrittenReview" modal_enctype="multipart/form-data" %} {% include "base_modal.html" with modal_id="uploadSynthesis" modal_enctype="multipart/form-data" %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}
@ -201,8 +201,8 @@
initModal("{{ note.modal_name }}", "{% url "participation:update_notes" pk=note.pk %}") initModal("{{ note.modal_name }}", "{% url "participation:update_notes" pk=note.pk %}")
{% endfor %} {% endfor %}
{% elif user.registration.participates %} {% elif user.registration.participates %}
initModal("uploadWrittenReview", "{% url "participation:upload_review" pk=passage.pk %}") initModal("uploadSynthesis", "{% url "participation:upload_synthesis" pk=passage.pk %}")
{% endif %} {% endif %}
}) });
</script> </script>
{% endblock %} {% endblock %}

View File

@ -46,10 +46,10 @@
</a> </a>
</dd> </dd>
<dt class="col-sm-3">{% trans "Reported solutions:" %}</dt> <dt class="col-sm-3">{% trans "Defended solutions:" %}</dt>
<dd class="col-sm-9"> <dd class="col-sm-9">
{% for passage in pool.passages.all %} {% for passage in pool.passages.all %}
<a href="{{ passage.reported_solution.file.url }}">{{ passage.reporter.team.trigram }} — {{ passage.get_solution_number_display }}</a>{% if not forloop.last %}, {% endif %} <a href="{{ passage.defended_solution.file.url }}">{{ passage.defender.team.trigram }} — {{ passage.get_solution_number_display }}</a>{% if not forloop.last %}, {% endif %}
{% endfor %} {% endfor %}
<a href="{% url 'participation:pool_download_solutions' pool_id=pool.id %}" class="badge rounded-pill text-bg-secondary"> <a href="{% url 'participation:pool_download_solutions' pool_id=pool.id %}" class="badge rounded-pill text-bg-secondary">
<i class="fas fa-download"></i> {% trans "Download all" %} <i class="fas fa-download"></i> {% trans "Download all" %}
@ -61,16 +61,16 @@
<ul class="list-group list-group-flush"> <ul class="list-group list-group-flush">
{% for passage in pool.passages.all %} {% for passage in pool.passages.all %}
<li class="list-group-item"> <li class="list-group-item">
{{ passage.reporter.team.trigram }} — {{ passage.get_solution_number_display }} : {{ passage.defender.team.trigram }} — {{ passage.get_solution_number_display }} :
{% for review in passage.written_reviews.all %} {% for synthesis in passage.syntheses.all %}
<a href="{{ review.file.url }}">{{ review.participation.team.trigram }} ({{ review.get_type_display }})</a>{% if not forloop.last %}, {% endif %} <a href="{{ synthesis.file.url }}">{{ synthesis.participation.team.trigram }} ({{ synthesis.get_type_display }})</a>{% if not forloop.last %}, {% endif %}
{% empty %} {% empty %}
{% trans "No review was uploaded yet." %} {% trans "No synthesis was uploaded yet." %}
{% endfor %} {% endfor %}
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
<a href="{% url 'participation:pool_download_written_reviews' pool_id=pool.id %}" class="badge rounded-pill text-bg-secondary"> <a href="{% url 'participation:pool_download_syntheses' pool_id=pool.id %}" class="badge rounded-pill text-bg-secondary">
<i class="fas fa-download"></i> {% trans "Download all" %} <i class="fas fa-download"></i> {% trans "Download all" %}
</a> </a>
</dd> </dd>

View File

@ -41,7 +41,7 @@
\begin{center} \begin{center}
\begin{itemize} \begin{itemize}
{% for passage in passages.all %} {% for passage in passages.all %}
\item D\'efenseur\textperiodcentered{}se au passage {{ forloop.counter }} : \underline{\texttt{~{{ passage.reporter.team.trigram }}~}} $\qquad$ probl\`eme \underline{~{{ passage.solution_number }}~} \item D\'efenseur\textperiodcentered{}se au passage {{ forloop.counter }} : \underline{\texttt{~{{ passage.defender.team.trigram }}~}} $\qquad$ probl\`eme \underline{~{{ passage.solution_number }}~}
{% endfor %} {% endfor %}
\end{itemize} \end{itemize}
\end{center} \end{center}
@ -50,7 +50,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\textperiodcentered{}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\textperiodcentered{}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.defender.team.trigram }} {% endfor %}\\ \hline \hline
%ECRIT %ECRIT
\multirow{6}{3mm}{\centering \bf\'E\\ C\\ R\\ I\\ T} & \multirow{3}{20mm}{Partie scientifique} & Profondeur et difficulté des éléments présentés & [0,6] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}} \multirow{6}{3mm}{\centering \bf\'E\\ C\\ R\\ I\\ T} & \multirow{3}{20mm}{Partie scientifique} & Profondeur et difficulté des éléments présentés & [0,6] {{ esp|safe }}\\ \cline{3-{{ passages.count|add:4 }}}

View File

@ -46,7 +46,7 @@ Tour {{ pool.round }} \;-- Poule {{ pool.get_letter_display }}{% if pool.partici
\begin{tabular}{|p{40mm}{% for passage in passages.all %}{% if passages.count == 3 %}|p{3cm}|p{3cm}{% else %}|p{2.5cm}|p{2.5cm}{% endif %}{% endfor %}|}\hline \begin{tabular}{|p{40mm}{% for passage in passages.all %}{% if passages.count == 3 %}|p{3cm}|p{3cm}{% else %}|p{2.5cm}|p{2.5cm}{% endif %}{% endfor %}|}\hline
\multirow{2}{40mm}{\LARGE R\^ole} {% for passage in passages.all %}& \multicolumn{2}{c|}{ \Large Probl\`eme {{ passage.solution_number }}}{% endfor %} \\ \cline{2-{{ passages.count|add:passages.count|add:1 }}} \multirow{2}{40mm}{\LARGE R\^ole} {% for passage in passages.all %}& \multicolumn{2}{c|}{ \Large Probl\`eme {{ passage.solution_number }}}{% endfor %} \\ \cline{2-{{ passages.count|add:passages.count|add:1 }}}
{% for passage in passages.all %}& \hspace{4mm} {\Large \'ECRIT} & \hspace{4mm} {\Large ORAL}{% endfor %} \\ \hline {% for passage in passages.all %}& \hspace{4mm} {\Large \'ECRIT} & \hspace{4mm} {\Large ORAL}{% endfor %} \\ \hline
\multirow{2}{35mm}{\LARGE D\'efenseur\textperiodcentered{}se} {% for passage in passages.all %}& \multicolumn{2}{c|}{\Large {{ passage.reporter.team.trigram }}}{% endfor %} \\ \cline{2-{{ passages.count|add:passages.count|add:1 }}} \multirow{2}{35mm}{\LARGE D\'efenseur\textperiodcentered{}se} {% for passage in passages.all %}& \multicolumn{2}{c|}{\Large {{ passage.defender.team.trigram }}}{% endfor %} \\ \cline{2-{{ passages.count|add:passages.count|add:1 }}}
{% for passage in passages.all %} {% for passage in passages.all %}
& \phantom{asd asd} \phantom{asd asd} \centering \normalsize$0\leq x\leq 20$ & \phantom{asd asd} \phantom{asd asd} \centering \normalsize$0\leq x\leq 20$
& \phantom{asd asd} \phantom{asd asd} \centering \normalsize$0\leq x\leq 20$ & \phantom{asd asd} \phantom{asd asd} \centering \normalsize$0\leq x\leq 20$

View File

@ -38,15 +38,15 @@
<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 syntheses submission for the first round'|capfirst %}</dt>
<dd class="col-sm-6">{{ tournament.reviews_first_phase_limit }}</dd> <dd class="col-sm-6">{{ tournament.syntheses_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 'date of maximal syntheses submission for the second round'|capfirst %}</dt>
<dd class="col-sm-6">{{ tournament.reviews_second_phase_limit }}</dd> <dd class="col-sm-6">{{ tournament.syntheses_second_phase_limit }}</dd>
{% if TFJM.APP == "ETEAM" %} {% if TFJM.APP == "ETEAM" %}
<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 'date of maximal syntheses submission for the third round'|capfirst %}</dt>
<dd class="col-sm-6">{{ tournament.reviews_third_phase_limit }}</dd> <dd class="col-sm-6">{{ tournament.syntheses_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>
@ -233,48 +233,48 @@
<ul> <ul>
<li> <li>
<a href="{% url "participation:tournament_csv" pk=tournament.pk %}"> <a href="{% url "participation:tournament_csv" pk=tournament.pk %}">
{% trans "Validated team participant data spreadsheet" %} Validated team participant data spreadsheet
</a> </a>
</li> </li>
<li> <li>
<a href="{% url "participation:tournament_csv" pk=tournament.pk %}?all"> <a href="{% url "participation:tournament_csv" pk=tournament.pk %}?all">
{% trans "All teams participant data spreadsheet" %} All teams participant data spreadsheet
</a> </a>
</li> </li>
<li> <li>
<a href="{% url "participation:tournament_authorizations" tournament_id=tournament.id %}"> <a href="{% url "participation:tournament_authorizations" tournament_id=tournament.id %}">
{% trans "Archive of all authorisations sorted by team and person" %} Archive of all authorisations sorted by team and person
</a> </a>
</li> </li>
<li> <li>
<a href="{% url "participation:tournament_solutions" tournament_id=tournament.id %}"> <a href="{% url "participation:tournament_solutions" tournament_id=tournament.id %}">
{% trans "Archive of all submitted solutions sorted by team" %} Archive of all submitted solutions sorted by team
</a> </a>
</li> </li>
<li> <li>
<a href="{% url "participation:tournament_solutions" tournament_id=tournament.id %}?sort_by=problem"> <a href="{% url "participation:tournament_solutions" tournament_id=tournament.id %}?sort_by=problem">
{% trans "Archive of all sent solutions sorted by problem" %} Archive of all sent solutions sorted by problem
</a> </a>
</li> </li>
<li> <li>
<a href="{% url "participation:tournament_solutions" tournament_id=tournament.id %}?sort_by=pool"> <a href="{% url "participation:tournament_solutions" tournament_id=tournament.id %}?sort_by=pool">
{% trans "Archive of all sent solutions sorted by pool" %} Archive of all sent solutions sorted by pool
</a> </a>
</li> </li>
<li> <li>
<a href="{% url "participation:tournament_written_reviews" tournament_id=tournament.id %}?sort_by=pool"> <a href="{% url "participation:tournament_syntheses" tournament_id=tournament.id %}?sort_by=pool">
{% trans "Archive of all summary notes sorted by pool and passage" %} Archive of all summary notes sorted by pool and passage
</a> </a>
</li> </li>
<li> <li>
<a href="https://docs.google.com/spreadsheets/d/{{ tournament.notes_sheet_id }}/edit"> <a href="https://docs.google.com/spreadsheets/d/{{ tournament.notes_sheet_id }}/edit">
<i class="fas fa-table"></i> <i class="fas fa-table"></i>
{% trans "Note spreadsheet on Google Sheets" %} Note spreadsheet on Google Sheets
</a> </a>
</li> </li>
<li> <li>
<a href="{% url "participation:tournament_notation_sheets" tournament_id=tournament.id %}"> <a href="{% url "participation:tournament_notation_sheets" tournament_id=tournament.id %}">
{% trans "Archive of all printable note sheets sorted by pool" %} Archive of all printable note sheets sorted by pool
</a> </a>
</li> </li>
</ul> </ul>

View File

@ -8,11 +8,11 @@ from .views import CreateTeamView, FinalNotationSheetTemplateView, GSheetNotific
PassageDetailView, PassageUpdateView, PoolCreateView, PoolDetailView, PoolJuryView, PoolNotesTemplateView, \ PassageDetailView, PassageUpdateView, PoolCreateView, PoolDetailView, PoolJuryView, PoolNotesTemplateView, \
PoolPresideJuryView, PoolRemoveJuryView, PoolUpdateView, PoolUploadNotesView, \ PoolPresideJuryView, PoolRemoveJuryView, PoolUpdateView, PoolUploadNotesView, \
ScaleNotationSheetTemplateView, SelectTeamFinalView, \ ScaleNotationSheetTemplateView, SelectTeamFinalView, \
SolutionsDownloadView, SolutionUploadView, \ SolutionsDownloadView, SolutionUploadView, SynthesisUploadView, \
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, TournamentUpdateView
app_name = "participation" app_name = "participation"
@ -42,8 +42,8 @@ urlpatterns = [
name="tournament_authorizations"), name="tournament_authorizations"),
path("tournament/<int:tournament_id>/solutions/", SolutionsDownloadView.as_view(), path("tournament/<int:tournament_id>/solutions/", SolutionsDownloadView.as_view(),
name="tournament_solutions"), name="tournament_solutions"),
path("tournament/<int:tournament_id>/written_reviews/", SolutionsDownloadView.as_view(), path("tournament/<int:tournament_id>/syntheses/", SolutionsDownloadView.as_view(),
name="tournament_written_reviews"), name="tournament_syntheses"),
path("tournament/<int:tournament_id>/notation/sheets/", NotationSheetsArchiveView.as_view(), path("tournament/<int:tournament_id>/notation/sheets/", NotationSheetsArchiveView.as_view(),
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(),
@ -60,7 +60,7 @@ urlpatterns = [
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"),
path("pools/<int:pool_id>/solutions/", SolutionsDownloadView.as_view(), name="pool_download_solutions"), path("pools/<int:pool_id>/solutions/", SolutionsDownloadView.as_view(), name="pool_download_solutions"),
path("pools/<int:pool_id>/written_reviews/", SolutionsDownloadView.as_view(), name="pool_download_written_reviews"), path("pools/<int:pool_id>/syntheses/", SolutionsDownloadView.as_view(), name="pool_download_syntheses"),
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"),
@ -71,6 +71,6 @@ urlpatterns = [
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/<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>/written_review/", WrittenReviewUploadView.as_view(), name="upload_written_review"), path("pools/passages/<int:pk>/solution/", SynthesisUploadView.as_view(), name="upload_synthesis"),
path("pools/passages/notes/<int:pk>/", NoteUpdateView.as_view(), name="update_notes"), path("pools/passages/notes/<int:pk>/", NoteUpdateView.as_view(), name="update_notes"),
] ]

View File

@ -46,9 +46,9 @@ 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, TeamForm, TournamentForm, UploadNotesForm, \ PoolForm, RequestValidationForm, SolutionForm, SynthesisForm, TeamForm, TournamentForm, \
ValidateParticipationForm, WrittenReviewForm UploadNotesForm, ValidateParticipationForm
from .models import Note, Participation, Passage, Pool, Solution, Team, Tournament, Tweak, WrittenReview 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
@ -977,7 +977,7 @@ class PoolUpdateView(VolunteerMixin, UpdateView):
class SolutionsDownloadView(VolunteerMixin, View): class SolutionsDownloadView(VolunteerMixin, View):
""" """
Download all solutions or written reviews as a ZIP archive. Download all solutions or syntheses as a ZIP archive.
""" """
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
@ -1018,12 +1018,11 @@ class SolutionsDownloadView(VolunteerMixin, View):
if 'team_id' in kwargs: if 'team_id' in kwargs:
team = Team.objects.get(pk=kwargs["team_id"]) team = Team.objects.get(pk=kwargs["team_id"])
solutions = Solution.objects.filter(participation=team.participation).all() solutions = Solution.objects.filter(participation=team.participation).all()
written_reviews = WrittenReview.objects.filter(participation=team.participation).all() syntheses = Synthesis.objects.filter(participation=team.participation).all()
filename = _("Solutions of team {trigram}.zip") if is_solution \ filename = _("Solutions of team {trigram}.zip") if is_solution else _("Syntheses of team {trigram}.zip")
else _("Written reviews of team {trigram}.zip")
filename = filename.format(trigram=team.trigram) filename = filename.format(trigram=team.trigram)
def prefix(s: Solution | WrittenReview) -> str: def prefix(s: Solution | Synthesis) -> str:
return "" return ""
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"])
@ -1036,12 +1035,11 @@ class SolutionsDownloadView(VolunteerMixin, View):
for sol in pool.solutions: for sol in pool.solutions:
sol.pool = pool sol.pool = pool
solutions.append(sol) solutions.append(sol)
written_reviews = WrittenReview.objects.filter(passage__pool__tournament=tournament).all() syntheses = Synthesis.objects.filter(passage__pool__tournament=tournament).all()
filename = _("Solutions of {tournament}.zip") if is_solution \ filename = _("Solutions of {tournament}.zip") if is_solution else _("Syntheses of {tournament}.zip")
else _("Written reviews of {tournament}.zip")
filename = filename.format(tournament=tournament.name) filename = filename.format(tournament=tournament.name)
def prefix(s: Solution | WrittenReview) -> str: def prefix(s: Solution | Synthesis) -> str:
pool = s.pool if is_solution else s.passage.pool pool = s.pool if is_solution else s.passage.pool
p = f"Poule {pool.short_name}/" p = f"Poule {pool.short_name}/"
if not is_solution: if not is_solution:
@ -1052,28 +1050,27 @@ class SolutionsDownloadView(VolunteerMixin, View):
solutions = Solution.objects.filter(participation__tournament=tournament).all() solutions = Solution.objects.filter(participation__tournament=tournament).all()
else: else:
solutions = Solution.objects.filter(final_solution=True).all() solutions = Solution.objects.filter(final_solution=True).all()
written_reviews = WrittenReview.objects.filter(passage__pool__tournament=tournament).all() syntheses = Synthesis.objects.filter(passage__pool__tournament=tournament).all()
filename = _("Solutions of {tournament}.zip") if is_solution \ filename = _("Solutions of {tournament}.zip") if is_solution else _("Syntheses of {tournament}.zip")
else _("Written reviews of {tournament}.zip")
filename = filename.format(tournament=tournament.name) filename = filename.format(tournament=tournament.name)
def prefix(s: Solution | WrittenReview) -> str: def prefix(s: Solution | Synthesis) -> str:
return f"{s.participation.team.trigram}/" if sort_by == "team" else f"Problème {s.problem}/" return f"{s.participation.team.trigram}/" if sort_by == "team" else f"Problème {s.problem}/"
else: else:
pool = Pool.objects.get(pk=kwargs["pool_id"]) pool = Pool.objects.get(pk=kwargs["pool_id"])
solutions = pool.solutions solutions = pool.solutions
written_reviews = WrittenReview.objects.filter(passage__pool=pool).all() syntheses = Synthesis.objects.filter(passage__pool=pool).all()
filename = _("Solutions for pool {pool} of tournament {tournament}.zip") \ filename = _("Solutions for pool {pool} of tournament {tournament}.zip") \
if is_solution else _("Written reviews for pool {pool} of tournament {tournament}.zip") if is_solution else _("Syntheses for pool {pool} of tournament {tournament}.zip")
filename = filename.format(pool=pool.short_name, filename = filename.format(pool=pool.short_name,
tournament=pool.tournament.name) tournament=pool.tournament.name)
def prefix(s: Solution | WrittenReview) -> str: def prefix(s: Solution | Synthesis) -> str:
return "" return ""
output = BytesIO() output = BytesIO()
zf = ZipFile(output, "w") zf = ZipFile(output, "w")
for s in (solutions if is_solution else written_reviews): for s in (solutions if is_solution else syntheses):
if s.file.storage.exists(s.file.path): if s.file.storage.exists(s.file.path):
zf.write("media/" + s.file.name, prefix(s) + f"{s}.pdf") zf.write("media/" + s.file.name, prefix(s) + f"{s}.pdf")
@ -1503,10 +1500,10 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
header_role.addElement(role_tc) header_role.addElement(role_tc)
header_role.addElement(CoveredTableCell()) header_role.addElement(CoveredTableCell())
for i in range(pool_size): for i in range(pool_size):
reporter_tc = TableCell(valuetype="string", stylename=title_style_left) defender_tc = TableCell(valuetype="string", stylename=title_style_left)
reporter_tc.addElement(P(text=_("Reporter"))) defender_tc.addElement(P(text=_("Defender")))
reporter_tc.setAttribute('numbercolumnsspanned', "2") defender_tc.setAttribute('numbercolumnsspanned', "2")
header_role.addElement(reporter_tc) header_role.addElement(defender_tc)
header_role.addElement(CoveredTableCell()) header_role.addElement(CoveredTableCell())
opponent_tc = TableCell(valuetype="string", stylename=title_style) opponent_tc = TableCell(valuetype="string", stylename=title_style)
@ -1539,13 +1536,13 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
header_notes.addElement(CoveredTableCell()) header_notes.addElement(CoveredTableCell())
for i in range(pool_size): for i in range(pool_size):
reporter_w_tc = TableCell(valuetype="string", stylename=title_style_botleft) defender_w_tc = TableCell(valuetype="string", stylename=title_style_botleft)
reporter_w_tc.addElement(P(text=f"{_('Writing')} (/{20 if settings.TFJM_APP == 'TFJM' else 10})")) defender_w_tc.addElement(P(text=f"{_('Writing')} (/{20 if settings.TFJM_APP == 'TFJM' else 10})"))
header_notes.addElement(reporter_w_tc) header_notes.addElement(defender_w_tc)
reporter_o_tc = TableCell(valuetype="string", stylename=title_style_bot) defender_o_tc = TableCell(valuetype="string", stylename=title_style_bot)
reporter_o_tc.addElement(P(text=f"{_('Oral')} (/{20 if settings.TFJM_APP == 'TFJM' else 10})")) defender_o_tc.addElement(P(text=f"{_('Oral')} (/{20 if settings.TFJM_APP == 'TFJM' else 10})"))
header_notes.addElement(reporter_o_tc) header_notes.addElement(defender_o_tc)
opponent_w_tc = TableCell(valuetype="string", stylename=title_style_bot) opponent_w_tc = TableCell(valuetype="string", stylename=title_style_bot)
opponent_w_tc.addElement(P(text=f"{_('Writing')} (/10)")) opponent_w_tc.addElement(P(text=f"{_('Writing')} (/10)"))
@ -1626,13 +1623,13 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
coeff_row.addElement(coeff_tc) coeff_row.addElement(coeff_tc)
coeff_row.addElement(CoveredTableCell()) coeff_row.addElement(CoveredTableCell())
for passage in self.object.passages.all(): for passage in self.object.passages.all():
reporter_w_tc = TableCell(valuetype="float", value=passage.coeff_reporter_writing, stylename=style_left) defender_w_tc = TableCell(valuetype="float", value=passage.coeff_defender_writing, stylename=style_left)
reporter_w_tc.addElement(P(text=str(passage.coeff_reporter_writing))) defender_w_tc.addElement(P(text=str(passage.coeff_defender_writing)))
coeff_row.addElement(reporter_w_tc) coeff_row.addElement(defender_w_tc)
reporter_o_tc = TableCell(valuetype="float", value=passage.coeff_reporter_oral, stylename=style) defender_o_tc = TableCell(valuetype="float", value=passage.coeff_defender_oral, stylename=style)
reporter_o_tc.addElement(P(text=str(passage.coeff_reporter_oral))) defender_o_tc.addElement(P(text=str(passage.coeff_defender_oral)))
coeff_row.addElement(reporter_o_tc) coeff_row.addElement(defender_o_tc)
opponent_w_tc = TableCell(valuetype="float", value=passage.coeff_opponent_writing, stylename=style) opponent_w_tc = TableCell(valuetype="float", value=passage.coeff_opponent_writing, stylename=style)
opponent_w_tc.addElement(P(text=str(passage.coeff_opponent_writing))) opponent_w_tc.addElement(P(text=str(passage.coeff_opponent_writing)))
@ -1671,12 +1668,12 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
for i, passage in enumerate(self.object.passages.all()): for i, passage in enumerate(self.object.passages.all()):
def_w_col = getcol(min_column + passage_width * i) def_w_col = getcol(min_column + passage_width * i)
def_o_col = getcol(min_column + passage_width * i + 1) def_o_col = getcol(min_column + passage_width * i + 1)
reporter_tc = TableCell(valuetype="float", value=passage.average_reporter, stylename=style_botleft) defender_tc = TableCell(valuetype="float", value=passage.average_defender, stylename=style_botleft)
reporter_tc.addElement(P(text=str(passage.average_reporter))) defender_tc.addElement(P(text=str(passage.average_defender)))
reporter_tc.setAttribute('numbercolumnsspanned', "2") defender_tc.setAttribute('numbercolumnsspanned', "2")
reporter_tc.setAttribute("formula", f"of:=[.{def_w_col}{max_row + 1}] * [.{def_w_col}{max_row + 2}]" defender_tc.setAttribute("formula", f"of:=[.{def_w_col}{max_row + 1}] * [.{def_w_col}{max_row + 2}]"
f" + [.{def_o_col}{max_row + 1}] * [.{def_o_col}{max_row + 2}]") f" + [.{def_o_col}{max_row + 1}] * [.{def_o_col}{max_row + 2}]")
subtotal_row.addElement(reporter_tc) subtotal_row.addElement(defender_tc)
subtotal_row.addElement(CoveredTableCell()) subtotal_row.addElement(CoveredTableCell())
opp_w_col = getcol(min_column + passage_width * i + 2) opp_w_col = getcol(min_column + passage_width * i + 2)
@ -1748,7 +1745,7 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
team_tc = TableCell(valuetype="string", team_tc = TableCell(valuetype="string",
stylename=style_botleft if passage.position == pool_size else style_left) stylename=style_botleft if passage.position == pool_size else style_left)
team_tc.addElement(P(text=f"{passage.reporter.team.name} ({passage.reporter.team.trigram})")) team_tc.addElement(P(text=f"{passage.defender.team.name} ({passage.defender.team.trigram})"))
team_tc.setAttribute('numbercolumnsspanned', "2") team_tc.setAttribute('numbercolumnsspanned', "2")
team_row.addElement(team_tc) team_row.addElement(team_tc)
@ -1758,17 +1755,17 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
problem_tc.setAttribute("formula", f"of:=[.B{3 + passage_width * (passage.position - 1)}]") problem_tc.setAttribute("formula", f"of:=[.B{3 + passage_width * (passage.position - 1)}]")
team_row.addElement(problem_tc) team_row.addElement(problem_tc)
reporter_pos = passage.position - 1 defender_pos = passage.position - 1
opponent_pos = self.object.passages.get(opponent=passage.reporter).position - 1 opponent_pos = self.object.passages.get(opponent=passage.defender).position - 1
reviewer_pos = self.object.passages.get(reviewer=passage.reporter).position - 1 reviewer_pos = self.object.passages.get(reviewer=passage.defender).position - 1
observer_pos = self.object.passages.get(observer=passage.reporter).position - 1 \ observer_pos = self.object.passages.get(observer=passage.defender).position - 1 \
if has_observer else None if has_observer else None
score_tc = TableCell(valuetype="float", value=self.object.average(passage.reporter), score_tc = TableCell(valuetype="float", value=self.object.average(passage.defender),
stylename=style_bot if passage.position == pool_size else style) stylename=style_bot if passage.position == pool_size else style)
score_tc.addElement(P(text=self.object.average(passage.reporter))) score_tc.addElement(P(text=self.object.average(passage.defender)))
formula = "of:=" formula = "of:="
formula += getcol(min_column + reporter_pos * passage_width) + str(max_row + 3) # Reporter formula += getcol(min_column + defender_pos * passage_width) + str(max_row + 3) # Defender
formula += " + " + getcol(min_column + opponent_pos * passage_width + 2) + str(max_row + 3) # Opponent formula += " + " + getcol(min_column + opponent_pos * passage_width + 2) + str(max_row + 3) # Opponent
formula += " + " + getcol(min_column + reviewer_pos * passage_width + 4) + str(max_row + 3) # Reviewer formula += " + " + getcol(min_column + reviewer_pos * passage_width + 4) + str(max_row + 3) # Reviewer
if has_observer: if has_observer:
@ -1778,9 +1775,9 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
team_row.addElement(score_tc) team_row.addElement(score_tc)
score_col = 'C' score_col = 'C'
rank_tc = TableCell(valuetype="float", value=sorted_participations.index(passage.reporter) + 1, rank_tc = TableCell(valuetype="float", value=sorted_participations.index(passage.defender) + 1,
stylename=style_botright if passage.position == pool_size else style_right) stylename=style_botright if passage.position == pool_size else style_right)
rank_tc.addElement(P(text=str(sorted_participations.index(passage.reporter) + 1))) rank_tc.addElement(P(text=str(sorted_participations.index(passage.defender) + 1)))
rank_tc.setAttribute("formula", f"of:=RANK([.{score_col}{max_row + 5 + passage.position}]; " rank_tc.setAttribute("formula", f"of:=RANK([.{score_col}{max_row + 5 + passage.position}]; "
f"[.{score_col}${max_row + 6}]:" f"[.{score_col}${max_row + 6}]:"
f"[.{score_col}${max_row + 5 + pool_size}])") f"[.{score_col}${max_row + 5 + pool_size}])")
@ -1984,7 +1981,7 @@ class PassageDetailView(LoginRequiredMixin, DetailView):
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 \
and reg.team.participation in [passage.reporter, passage.opponent, passage.reviewer, passage.observer]: and reg.team.participation in [passage.defender, passage.opponent, passage.reviewer, passage.observer]:
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
return self.handle_no_permission() return self.handle_no_permission()
@ -2005,8 +2002,8 @@ class PassageDetailView(LoginRequiredMixin, DetailView):
if 'notes' in context and not self.request.user.registration.is_admin: if 'notes' in context and not self.request.user.registration.is_admin:
context['notes']._sequence.remove('update') context['notes']._sequence.remove('update')
context['notes'].columns['reporter_writing'].column.verbose_name += f" ({passage.reporter.team.trigram})" context['notes'].columns['defender_writing'].column.verbose_name += f" ({passage.defender.team.trigram})"
context['notes'].columns['reporter_oral'].column.verbose_name += f" ({passage.reporter.team.trigram})" context['notes'].columns['defender_oral'].column.verbose_name += f" ({passage.defender.team.trigram})"
context['notes'].columns['opponent_writing'].column.verbose_name += f" ({passage.opponent.team.trigram})" context['notes'].columns['opponent_writing'].column.verbose_name += f" ({passage.opponent.team.trigram})"
context['notes'].columns['opponent_oral'].column.verbose_name += f" ({passage.opponent.team.trigram})" context['notes'].columns['opponent_oral'].column.verbose_name += f" ({passage.opponent.team.trigram})"
context['notes'].columns['reviewer_writing'].column.verbose_name += f" ({passage.reviewer.team.trigram})" context['notes'].columns['reviewer_writing'].column.verbose_name += f" ({passage.reviewer.team.trigram})"
@ -2031,9 +2028,9 @@ class PassageUpdateView(VolunteerMixin, UpdateView):
return self.handle_no_permission() return self.handle_no_permission()
class WrittenReviewUploadView(LoginRequiredMixin, FormView): class SynthesisUploadView(LoginRequiredMixin, FormView):
template_name = "participation/upload_written_review.html" template_name = "participation/upload_synthesis.html"
form_class = WrittenReviewForm form_class = SynthesisForm
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated or not request.user.registration.participates: if not request.user.is_authenticated or not request.user.registration.participates:
@ -2060,14 +2057,14 @@ class WrittenReviewUploadView(LoginRequiredMixin, FormView):
form_syn = form.instance form_syn = form.instance
form_syn.type = 1 if self.participation == self.passage.opponent \ form_syn.type = 1 if self.participation == self.passage.opponent \
else 2 if self.participation == self.passage.reviewer else 3 else 2 if self.participation == self.passage.reviewer else 3
syn_qs = WrittenReview.objects.filter(participation=self.participation, syn_qs = Synthesis.objects.filter(participation=self.participation,
passage=self.passage, passage=self.passage,
type=form_syn.type).all() type=form_syn.type).all()
deadline = self.passage.pool.tournament.reviews_first_phase_limit if self.passage.pool.round == 1 \ deadline = self.passage.pool.tournament.syntheses_first_phase_limit if self.passage.pool.round == 1 \
else self.passage.pool.tournament.reviews_second_phase_limit else self.passage.pool.tournament.syntheses_second_phase_limit
if syn_qs.exists() and timezone.now() > deadline: if syn_qs.exists() and timezone.now() > deadline:
form.add_error(None, _("You can't upload a written review after the deadline.")) form.add_error(None, _("You can't upload a synthesis after the deadline."))
return self.form_invalid(form) return self.form_invalid(form)
# Drop previous solution if existing # Drop previous solution if existing
@ -2101,8 +2098,8 @@ class NoteUpdateView(VolunteerMixin, UpdateView):
def get_form(self, form_class=None): def get_form(self, form_class=None):
form = super().get_form(form_class) form = super().get_form(form_class)
form.fields['reporter_writing'].label += f" ({self.object.passage.reporter.team.trigram})" form.fields['defender_writing'].label += f" ({self.object.passage.defender.team.trigram})"
form.fields['reporter_oral'].label += f" ({self.object.passage.reporter.team.trigram})" form.fields['defender_oral'].label += f" ({self.object.passage.defender.team.trigram})"
form.fields['opponent_writing'].label += f" ({self.object.passage.opponent.team.trigram})" form.fields['opponent_writing'].label += f" ({self.object.passage.opponent.team.trigram})"
form.fields['opponent_oral'].label += f" ({self.object.passage.opponent.team.trigram})" form.fields['opponent_oral'].label += f" ({self.object.passage.opponent.team.trigram})"
form.fields['reviewer_writing'].label += f" ({self.object.passage.reviewer.team.trigram})" form.fields['reviewer_writing'].label += f" ({self.object.passage.reviewer.team.trigram})"

View File

@ -26,7 +26,7 @@ from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, DetailView, RedirectView, TemplateView, UpdateView, View from django.views.generic import CreateView, DetailView, RedirectView, TemplateView, UpdateView, View
from django_tables2 import SingleTableView from django_tables2 import SingleTableView
from magic import Magic from magic import Magic
from participation.models import Passage, Solution, Tournament, WrittenReview from participation.models import Passage, Solution, Synthesis, Tournament
from tfjm.tokens import email_validation_token from tfjm.tokens import email_validation_token
from tfjm.views import UserMixin, UserRegistrationMixin, VolunteerMixin from tfjm.views import UserMixin, UserRegistrationMixin, VolunteerMixin
@ -837,11 +837,11 @@ class SolutionView(LoginRequiredMixin, View):
solution = Solution.objects.get(file__endswith=filename) solution = Solution.objects.get(file__endswith=filename)
user = request.user user = request.user
if user.registration.participates and user.registration.team.participation: if user.registration.participates and user.registration.team.participation:
passage_participant_qs = Passage.objects.filter(Q(reporter=user.registration.team.participation) passage_participant_qs = Passage.objects.filter(Q(defender=user.registration.team.participation)
| Q(opponent=user.registration.team.participation) | Q(opponent=user.registration.team.participation)
| Q(reviewer=user.registration.team.participation) | Q(reviewer=user.registration.team.participation)
| Q(observer=user.registration.team.participation), | Q(observer=user.registration.team.participation),
reporter=solution.participation, defender=solution.participation,
solution_number=solution.problem) solution_number=solution.problem)
else: else:
passage_participant_qs = Passage.objects.none() passage_participant_qs = Passage.objects.none()
@ -853,7 +853,7 @@ class SolutionView(LoginRequiredMixin, View):
or user.registration.is_volunteer or user.registration.is_volunteer
and Passage.objects.filter(Q(pool__juries=user.registration) and Passage.objects.filter(Q(pool__juries=user.registration)
| Q(pool__tournament__in=user.registration.organized_tournaments.all()), | Q(pool__tournament__in=user.registration.organized_tournaments.all()),
reporter=solution.participation, defender=solution.participation,
solution_number=solution.problem).exists() solution_number=solution.problem).exists()
or user.registration.participates and user.registration.team or user.registration.participates and user.registration.team
and (solution.participation.team == user.registration.team or and (solution.participation.team == user.registration.team or
@ -871,30 +871,30 @@ class SolutionView(LoginRequiredMixin, View):
return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name) return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)
class WrittenReviewView(LoginRequiredMixin, View): class SynthesisView(LoginRequiredMixin, View):
""" """
Display the sent written reviews. Display the sent synthesis.
""" """
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
filename = kwargs["filename"] filename = kwargs["filename"]
path = f"media/reviews/{filename}" path = f"media/syntheses/{filename}"
if not os.path.exists(path): if not os.path.exists(path):
raise Http404 raise Http404
review = WrittenReview.objects.get(file__endswith=filename) synthesis = Synthesis.objects.get(file__endswith=filename)
user = request.user user = request.user
if not (user.registration.is_admin or user.registration.is_volunteer if not (user.registration.is_admin or user.registration.is_volunteer
and (user.registration in review.passage.pool.juries.all() and (user.registration in synthesis.passage.pool.juries.all()
or user.registration in review.passage.pool.tournament.organizers.all() or user.registration in synthesis.passage.pool.tournament.organizers.all()
or user.registration.pools_presided.filter(tournament=review.passage.pool.tournament).exists()) or user.registration.pools_presided.filter(tournament=synthesis.passage.pool.tournament).exists())
or user.registration.participates and user.registration.team == review.participation.team): or user.registration.participates and user.registration.team == synthesis.participation.team):
raise PermissionDenied raise PermissionDenied
# Guess mime type of the file # Guess mime type of the file
mime = Magic(mime=True) mime = Magic(mime=True)
mime_type = mime.from_file(path) mime_type = mime.from_file(path)
ext = mime_type.split("/")[1].replace("jpeg", "jpg") ext = mime_type.split("/")[1].replace("jpeg", "jpg")
# Replace file name # Replace file name
true_file_name = str(review) + f".{ext}" true_file_name = str(synthesis) + f".{ext}"
return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name) return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)

View File

@ -32,7 +32,7 @@ Round \underline{~~~~} pool \underline{~~~~}
\medskip \medskip
Problem \underline{~~~~} reported by team \underline{~~~~~~~~~~~~~~~~~~~~~~~~~~~~} Problem \underline{~~~~} defended by team \underline{~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
\medskip \medskip

View File

@ -24,7 +24,7 @@ from django.views.defaults import bad_request, page_not_found, permission_denied
from django.views.generic import TemplateView from django.views.generic import TemplateView
from participation.views import MotivationLetterView from participation.views import MotivationLetterView
from registration.views import HealthSheetView, ParentalAuthorizationView, PhotoAuthorizationView, \ from registration.views import HealthSheetView, ParentalAuthorizationView, PhotoAuthorizationView, \
ReceiptView, SolutionView, VaccineSheetView, WrittenReviewView ReceiptView, SolutionView, SynthesisView, VaccineSheetView
from .views import AdminSearchView from .views import AdminSearchView
@ -61,8 +61,8 @@ urlpatterns = [
path('media/solutions/<str:filename>/', SolutionView.as_view(), path('media/solutions/<str:filename>/', SolutionView.as_view(),
name='solution'), name='solution'),
path('media/reviews/<str:filename>/', WrittenReviewView.as_view(), path('media/syntheses/<str:filename>/', SynthesisView.as_view(),
name='reviews'), name='synthesis'),
] ]
if settings.DEBUG: if settings.DEBUG: