Defender => Reporter

Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
Emmy D'Anello 2024-07-06 22:12:07 +02:00
parent 12205f953b
commit 620bbe7817
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
19 changed files with 481 additions and 287 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
defender = tds[line[0]].participation reporter = 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,
defender=defender, reporter=reporter,
opponent=opponent, opponent=opponent,
reviewer=reviewer, reviewer=reviewer,
observer=observer, observer=observer,
defender_penalties=tds[line[0]].penalty_int, reporter_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 defender oral, in percentage, which is a malus of 25% for each penalty. The penalty multiplier on the reporter 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 defenderTd = document.createElement('td') let reporterTd = document.createElement('td')
defenderTd.classList.add('text-center') reporterTd.classList.add('text-center')
defenderTd.innerText = 'Déf' reporterTd.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(defenderTd, reviewerTd, opponentTd) teamTr.append(reporterTd, reviewerTd, opponentTd)
break break
case 1: case 1:
teamTr.append(opponentTd, defenderTd, reviewerTd) teamTr.append(opponentTd, reporterTd, reviewerTd)
break break
case 2: case 2:
teamTr.append(reviewerTd, opponentTd, defenderTd) teamTr.append(reviewerTd, opponentTd, reporterTd)
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(defenderTd, emptyTd, reviewerTd, opponentTd) teamTr.append(reporterTd, emptyTd, reviewerTd, opponentTd)
break break
case 1: case 1:
teamTr.append(opponentTd, defenderTd, emptyTd, reviewerTd) teamTr.append(opponentTd, reporterTd, emptyTd, reviewerTd)
break break
case 2: case 2:
teamTr.append(reviewerTd, opponentTd, defenderTd, emptyTd) teamTr.append(reviewerTd, opponentTd, reporterTd, emptyTd)
break break
case 3: case 3:
teamTr.append(emptyTd, reviewerTd, opponentTd, defenderTd) teamTr.append(emptyTd, reviewerTd, opponentTd, reporterTd)
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(defenderTd, emptyTd, opponentTd, reviewerTd, emptyTd2) teamTr.append(reporterTd, emptyTd, opponentTd, reviewerTd, emptyTd2)
break break
case 1: case 1:
teamTr.append(emptyTd, defenderTd, reviewerTd, emptyTd2, opponentTd) teamTr.append(emptyTd, reporterTd, reviewerTd, emptyTd2, opponentTd)
break break
case 2: case 2:
teamTr.append(opponentTd, emptyTd, defenderTd, emptyTd2, reviewerTd) teamTr.append(opponentTd, emptyTd, reporterTd, emptyTd2, reviewerTd)
break break
case 3: case 3:
teamTr.append(reviewerTd, opponentTd, emptyTd, defenderTd, emptyTd2) teamTr.append(reviewerTd, opponentTd, emptyTd, reporterTd, emptyTd2)
break break
case 4: case 4:
teamTr.append(emptyTd, reviewerTd, emptyTd2, opponentTd, defenderTd) teamTr.append(emptyTd, reviewerTd, emptyTd2, opponentTd, reporterTd)
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 defender // If more than P - 5 problems were rejected, add a penalty of 25% of the coefficient of the oral reporter
// 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">Déf</td> <td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td>
<td class="text-center">Rap</td> <td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td>
<td class="text-center">Opp</td> <td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td>
{% elif forloop.counter == 2 %} {% elif forloop.counter == 2 %}
<td class="text-center">Opp</td> <td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td>
<td class="text-center">Déf</td> <td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td>
<td class="text-center">Rap</td> <td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td>
{% elif forloop.counter == 3 %} {% elif forloop.counter == 3 %}
<td class="text-center">Rap</td> <td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td>
<td class="text-center">Opp</td> <td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td>
<td class="text-center">Déf</td> <td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td>
{% endif %} {% endif %}
{% elif pool.size == 4 %} {% elif pool.size == 4 %}
{% if forloop.counter == 1 %} {% if forloop.counter == 1 %}
<td class="text-center">Déf</td> <td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td>
<td></td> <td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td>
<td class="text-center">Rap</td> <td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td>
<td class="text-center">Opp</td> <td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td>
{% elif forloop.counter == 2 %} {% elif forloop.counter == 2 %}
<td class="text-center">Opp</td> <td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td>
<td class="text-center">Déf</td> <td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td>
<td></td> <td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td>
<td class="text-center">Rap</td> <td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td>
{% elif forloop.counter == 3 %} {% elif forloop.counter == 3 %}
<td class="text-center">Rap</td> <td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td>
<td class="text-center">Opp</td> <td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td>
<td class="text-center">Déf</td> <td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td>
<td></td> <td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td>
{% elif forloop.counter == 4 %} {% elif forloop.counter == 4 %}
<td></td> <td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td>
<td class="text-center">Rap</td> <td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td>
<td class="text-center">Opp</td> <td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td>
<td class="text-center">Déf</td> <td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td>
{% endif %} {% endif %}
{% elif pool.size == 5 %} {% elif pool.size == 5 %}
{% if forloop.counter == 1 %} {% if forloop.counter == 1 %}
<td class="text-center">Déf</td> <td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td>
<td></td> <td class="text-center"></td>
<td class="text-center">Rap</td> <td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td>
<td class="text-center">Opp</td> <td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td>
<td></td> <td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td>
{% elif forloop.counter == 2 %} {% elif forloop.counter == 2 %}
<td></td> <td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td>
<td class="text-center">Déf</td> <td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td>
<td></td> <td class="text-center"></td>
<td class="text-center">Rap</td> <td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td>
<td class="text-center">Opp</td> <td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td>
{% elif forloop.counter == 3 %} {% elif forloop.counter == 3 %}
<td class="text-center">Opp</td> <td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td>
<td></td> <td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td>
<td class="text-center">Déf</td> <td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td>
<td></td> <td class="text-center"></td>
<td class="text-center">Rap</td> <td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td>
{% elif forloop.counter == 4 %} {% elif forloop.counter == 4 %}
<td class="text-center">Rap</td> <td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td>
<td class="text-center">Opp</td> <td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td>
<td></td> <td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td>
<td class="text-center">Déf</td> <td class="text-center">{% trans "Rep" context "Role abbreviation" %}</td>
<td></td> <td class="text-center"></td>
{% elif forloop.counter == 5 %} {% elif forloop.counter == 5 %}
<td></td> <td class="text-center"></td>
<td class="text-center">Rap</td> <td class="text-center">{% trans "Rev" context "Role abbreviation" %}</td>
<td class="text-center">Opp</td> <td class="text-center">{% trans "Opp" context "Role abbreviation" %}</td>
<td></td> <td class="text-center">{% if TFJM.APP == "ETEAM" %}{% trans "Obs" context "Role abbreviation" %}{% endif %}</td>
<td class="text-center">Déf</td> <td class="text-center">{% trans "Rep" context "Role abbreviation" %}</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 21:27+0200\n" "POT-Creation-Date: 2024-07-06 22:07+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"
@ -814,6 +814,67 @@ 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"
@ -920,7 +981,7 @@ 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:1696 participation/tables.py:114
msgid "defender" msgid "reporter"
msgstr "défenseur⋅se" msgstr "défenseur⋅se"
#: participation/admin.py:128 participation/models.py:1703 #: participation/admin.py:128 participation/models.py:1703
@ -1027,12 +1088,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 defender, the opponent and the reviewer must be different." msgid "The reporter, 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 defender did not work on this problem." msgid "This reporter 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
@ -1448,7 +1509,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 defend " "href='{draw_url}'>this page</a>.</p><p>For the first round, you will present "
"<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 "
@ -1499,7 +1560,7 @@ msgstr "Premier tour"
#: participation/models.py:986 #: participation/models.py:986
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>For the second round, you will defend <a href='{solution_url}'>your " "<p>For the second round, you will present <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 "
@ -1513,7 +1574,7 @@ msgstr "Second tour"
#: participation/models.py:1050 #: participation/models.py:1050
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<p>For the third round, you will defend <a href='{solution_url}'>your " "<p>For the third round, you will present <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 "
@ -1638,7 +1699,7 @@ msgid "position"
msgstr "position" msgstr "position"
#: participation/models.py:1687 #: participation/models.py:1687
msgid "defended solution" msgid "reported solution"
msgstr "solution défendue" msgstr "solution défendue"
#: participation/models.py:1725 #: participation/models.py:1725
@ -1647,7 +1708,7 @@ msgstr "pénalités"
#: participation/models.py:1727 #: participation/models.py:1727
msgid "" msgid ""
"Number of penalties for the defender. The defender will loose a 0.5 " "Number of penalties for the reporter. The reporter 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 "
@ -1661,8 +1722,8 @@ msgstr "L'équipe {trigram} n'est pas inscrite dans la poule."
#: participation/models.py:1867 #: participation/models.py:1867
#, python-brace-format #, python-brace-format
msgid "Passage of {defender} for problem {problem}" msgid "Passage of {reporter} for problem {problem}"
msgstr "Passage de {defender} pour le problème {problem}" msgstr "Passage de {reporter} pour le problème {problem}"
#: participation/models.py:1871 participation/models.py:1880 #: participation/models.py:1871 participation/models.py:1880
#: participation/models.py:1969 participation/models.py:2012 #: participation/models.py:1969 participation/models.py:2012
@ -1716,10 +1777,10 @@ msgstr "solutions"
#: participation/models.py:1987 #: participation/models.py:1987
#, python-brace-format #, python-brace-format
msgid "Written review of {team} as {type} for problem {problem} of {defender}" msgid "Written review of {team} as {type} for problem {problem} of {reporter}"
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 {defender}" "{problem} de {reporter}"
#: participation/models.py:1995 #: participation/models.py:1995
msgid "written review" msgid "written review"
@ -1734,11 +1795,11 @@ msgid "jury"
msgstr "jury" msgstr "jury"
#: participation/models.py:2017 #: participation/models.py:2017
msgid "defender writing note" msgid "reporter writing note"
msgstr "note d'écrit défenseur⋅se" msgstr "note d'écrit défenseur⋅se"
#: participation/models.py:2023 #: participation/models.py:2023
msgid "defender oral note" msgid "reporter oral note"
msgstr "note d'oral défenseur⋅se" msgstr "note d'oral défenseur⋅se"
#: participation/models.py:2029 #: participation/models.py:2029
@ -1933,7 +1994,7 @@ msgid "Position:"
msgstr "Position :" msgstr "Position :"
#: participation/templates/participation/passage_detail.html:28 #: participation/templates/participation/passage_detail.html:28
msgid "Defender:" msgid "Reporter:"
msgstr "Défenseur⋅se :" msgstr "Défenseur⋅se :"
#: participation/templates/participation/passage_detail.html:31 #: participation/templates/participation/passage_detail.html:31
@ -1949,11 +2010,11 @@ msgid "Observer:"
msgstr "Observateur⋅rice :" msgstr "Observateur⋅rice :"
#: participation/templates/participation/passage_detail.html:42 #: participation/templates/participation/passage_detail.html:42
msgid "Defended solution:" msgid "Reported solution:"
msgstr "Solution défendue" msgstr "Solution défendue"
#: participation/templates/participation/passage_detail.html:45 #: participation/templates/participation/passage_detail.html:45
msgid "Defender penalties count:" msgid "Reporter 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
@ -1981,11 +2042,11 @@ 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 defender writing" msgid "Average points for the reporter 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 defender oral" msgid "Average points for the reporter 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
@ -2013,7 +2074,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 "Defender points" msgid "Reporter 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
@ -2058,7 +2119,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 "Defended solutions:" msgid "Reported solutions:"
msgstr "Solutions défendues :" msgstr "Solutions défendues :"
#: participation/templates/participation/pool_detail.html:55 #: participation/templates/participation/pool_detail.html:55
@ -2755,7 +2816,7 @@ 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:1507
msgid "Defender" msgid "Reporter"
msgstr "Défenseur⋅se" msgstr "Défenseur⋅se"
#: participation/views.py:1513 #: participation/views.py:1513

View File

@ -51,7 +51,7 @@ class PassageInline(admin.TabularInline):
model = Passage model = Passage
extra = 0 extra = 0
ordering = ('position',) ordering = ('position',)
autocomplete_fields = ('defender', 'opponent', 'reviewer', 'observer',) autocomplete_fields = ('reporter', 'opponent', 'reviewer', 'observer',)
show_change_link = True show_change_link = True
@ -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__', 'defender_trigram', 'solution_number', 'opponent_trigram', 'reviewer_trigram', list_display = ('__str__', 'reporter_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', 'defender', 'opponent', 'reviewer', 'observer',) autocomplete_fields = ('pool', 'reporter', 'opponent', 'reviewer', 'observer',)
inlines = (NoteInline,) inlines = (NoteInline,)
@admin.display(description=_("defender"), ordering='defender__team__trigram') @admin.display(description=_("reporter"), ordering='reporter__team__trigram')
def defender_trigram(self, record: Passage): def reporter_trigram(self, record: Passage):
return record.defender.team.trigram return record.reporter.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', 'defender_writing', 'defender_oral', list_display = ('passage', 'pool', 'jury', 'reporter_writing', 'reporter_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',
'defender_writing', 'defender_oral', 'opponent_writing', 'opponent_oral', 'reporter_writing', 'reporter_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__defender__team__trigram',) search_fields = ('jury__user__last_name', 'jury__user__first_name', 'passage__reporter__team__trigram',)
autocomplete_fields = ('jury', 'passage',) autocomplete_fields = ('jury', 'passage',)
@admin.display(description=_("pool")) @admin.display(description=_("pool"))
@ -180,14 +180,14 @@ class SolutionAdmin(admin.ModelAdmin):
@admin.register(WrittenReview) @admin.register(WrittenReview)
class WrittenReviewAdmin(admin.ModelAdmin): class WrittenReviewAdmin(admin.ModelAdmin):
list_display = ('participation', 'type', 'defender', 'passage',) list_display = ('participation', 'type', 'reporter', '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=_("defender")) @admin.display(description=_("reporter"))
def defender(self, record: WrittenReview): def reporter(self, record: WrittenReview):
return record.passage.defender return record.passage.reporter
@admin.display(description=_("problem")) @admin.display(description=_("problem"))
def problem(self, record: WrittenReview): def problem(self, record: WrittenReview):

View File

@ -12,7 +12,7 @@ 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', 'defender_writing', 'defender_oral', 'opponent_writing', filterset_fields = ['jury', 'passage', 'reporter_writing', 'reporter_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', 'defender', 'opponent', 'reviewer', 'observer', 'pool_tournament', ] filterset_fields = ['pool', 'solution_number', 'reporter', 'opponent', 'reviewer', 'observer', 'pool_tournament', ]
class PoolViewSet(ModelViewSet): class PoolViewSet(ModelViewSet):

View File

@ -345,18 +345,18 @@ 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 "defender" in cleaned_data and "opponent" in cleaned_data and "reviewer" in cleaned_data \ if "reporter" in cleaned_data and "opponent" in cleaned_data and "reviewer" in cleaned_data \
and len({cleaned_data["defender"], cleaned_data["opponent"], cleaned_data["reviewer"]}) < 3: and len({cleaned_data["reporter"], cleaned_data["opponent"], cleaned_data["reviewer"]}) < 3:
self.add_error(None, _("The defender, the opponent and the reviewer must be different.")) self.add_error(None, _("The reporter, the opponent and the reviewer must be different."))
if "defender" in self.cleaned_data and "solution_number" in self.cleaned_data \ if "reporter" in self.cleaned_data and "solution_number" in self.cleaned_data \
and not Solution.objects.filter(participation=cleaned_data["defender"], and not Solution.objects.filter(participation=cleaned_data["reporter"],
problem=cleaned_data["solution_number"]).exists(): problem=cleaned_data["solution_number"]).exists():
self.add_error("solution_number", _("This defender did not work on this problem.")) self.add_error("solution_number", _("This reporter did not work on this problem."))
return cleaned_data return cleaned_data
class Meta: class Meta:
model = Passage model = Passage
fields = ('position', 'solution_number', 'defender', 'opponent', 'reviewer', 'opponent', 'defender_penalties',) fields = ('position', 'solution_number', 'reporter', 'opponent', 'reviewer', 'opponent', 'reporter_penalties',)
class WrittenReviewForm(forms.ModelForm): class WrittenReviewForm(forms.ModelForm):
@ -386,5 +386,5 @@ class WrittenReviewForm(forms.ModelForm):
class NoteForm(forms.ModelForm): class NoteForm(forms.ModelForm):
class Meta: class Meta:
model = Note model = Note
fields = ('defender_writing', 'defender_oral', 'opponent_writing', fields = ('reporter_writing', 'reporter_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()
defender_passage_1 = Passage.objects.get(pool__tournament=tournament, pool__round=1, defender=team2) reporter_passage_1 = Passage.objects.get(pool__tournament=tournament, pool__round=1, reporter=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()
defender_passage_2 = Passage.objects.get(pool__tournament=tournament, pool__round=2, defender=team2) reporter_passage_2 = Passage.objects.get(pool__tournament=tournament, pool__round=2, reporter=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. {defender_passage_1.solution_number}") line.append(f"Pb. {reporter_passage_1.solution_number}")
line.extend([defender_passage_1.average_defender_writing, defender_passage_1.average_defender_oral, line.extend([reporter_passage_1.average_reporter_writing, reporter_passage_1.average_reporter_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. {defender_passage_2.solution_number}") line.append(f"Pb. {reporter_passage_2.solution_number}")
line.extend([defender_passage_2.average_defender_writing, defender_passage_2.average_defender_oral, line.extend([reporter_passage_2.average_reporter_writing, reporter_passage_2.average_reporter_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

@ -0,0 +1,133 @@
# 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

@ -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, defender=participation) passage1 = Passage.objects.get(pool__tournament=self, pool__round=1, reporter=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, defender=participation).exists(): if Passage.objects.filter(pool__tournament=self, pool__round=2, reporter=participation).exists():
passage2 = Passage.objects.get(pool__tournament=self, pool__round=2, defender=participation) passage2 = Passage.objects.get(pool__tournament=self, pool__round=2, reporter=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, defender=participation).exists(): if Passage.objects.filter(pool__tournament=self, pool__round=3, reporter=participation).exists():
passage3 = Passage.objects.get(pool__tournament=self, pool__round=3, defender=participation) passage3 = Passage.objects.get(pool__tournament=self, pool__round=3, reporter=participation)
pool3 = passage3.pool pool3 = passage3.pool
if pool3.participations.count() != 5: if pool3.participations.count() != 5:
position3 = passage3.position position3 = passage3.position
@ -912,36 +912,36 @@ class Participation(models.Model):
'content': content, 'content': content,
}) })
elif timezone.now() <= tournament.reviews_first_phase_limit + timedelta(hours=2): elif timezone.now() <= tournament.reviews_first_phase_limit + timedelta(hours=2):
defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, defender=self) reporter_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, reporter=self)
opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, opponent=self) opponent_passage = Passage.objects.get(pool__tournament=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
defender_text = _("<p>The solutions draw is ended. You can check the result on " reporter_text = _("<p>The solutions draw is ended. You can check the result on "
"<a href='{draw_url}'>this page</a>.</p>" "<a href='{draw_url}'>this page</a>.</p>"
"<p>For the first round, you will defend " "<p>For the first round, you will present "
"<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 = defender_passage.defended_solution.file.url solution_url = reporter_passage.reported_solution.file.url
defender_content = format_lazy(defender_text, draw_url=draw_url, reporter_content = format_lazy(reporter_text, draw_url=draw_url,
solution_url=solution_url, problem=defender_passage.solution_number) solution_url=solution_url, problem=reporter_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 written review on <a href='{passage_url}'>this page</a>.</p>")
solution_url = opponent_passage.defended_solution.file.url solution_url = opponent_passage.reported_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.defender.team.trigram, opponent_content = format_lazy(opponent_text, opponent=opponent_passage.reporter.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 written review on <a href='{passage_url}'>this page</a>.</p>")
solution_url = reviewer_passage.defended_solution.file.url solution_url = reviewer_passage.reported_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.defender.team.trigram, reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.reporter.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)
@ -949,10 +949,10 @@ class Participation(models.Model):
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 written review on <a href='{passage_url}'>this page</a>.</p>")
solution_url = observer_passage.defended_solution.file.url solution_url = observer_passage.reported_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.defender.team.trigram, observer=observer_passage.reporter.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:
@ -968,7 +968,7 @@ class Participation(models.Model):
for ext in ["pdf", "tex"]) for ext in ["pdf", "tex"])
reviews_templates_content = f"<p>{_('Templates:')} {reviews_templates}</p>" reviews_templates_content = f"<p>{_('Templates:')} {reviews_templates}</p>"
content = defender_content + opponent_content + reviewer_content + observer_content \ content = reporter_content + opponent_content + reviewer_content + observer_content \
+ reviews_templates_content + reviews_templates_content
informations.append({ informations.append({
'title': _("First round"), 'title': _("First round"),
@ -977,34 +977,34 @@ class Participation(models.Model):
'content': content, 'content': content,
}) })
elif timezone.now() <= tournament.reviews_second_phase_limit + timedelta(hours=2): elif timezone.now() <= tournament.reviews_second_phase_limit + timedelta(hours=2):
defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, defender=self) reporter_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, reporter=self)
opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, opponent=self) opponent_passage = Passage.objects.get(pool__tournament=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
defender_text = _("<p>For the second round, you will defend " reporter_text = _("<p>For the second round, you will present "
"<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 = defender_passage.defended_solution.file.url solution_url = reporter_passage.reported_solution.file.url
defender_content = format_lazy(defender_text, draw_url=draw_url, reporter_content = format_lazy(reporter_text, draw_url=draw_url,
solution_url=solution_url, problem=defender_passage.solution_number) solution_url=solution_url, problem=reporter_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 written review on <a href='{passage_url}'>this page</a>.</p>")
solution_url = opponent_passage.defended_solution.file.url solution_url = opponent_passage.reported_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.defender.team.trigram, opponent_content = format_lazy(opponent_text, opponent=opponent_passage.reporter.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 written review on <a href='{passage_url}'>this page</a>.</p>")
solution_url = reviewer_passage.defended_solution.file.url solution_url = reviewer_passage.reported_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.defender.team.trigram, reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.reporter.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)
@ -1012,10 +1012,10 @@ class Participation(models.Model):
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 written review on <a href='{passage_url}'>this page</a>.</p>")
solution_url = observer_passage.defended_solution.file.url solution_url = observer_passage.reported_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.defender.team.trigram, observer=observer_passage.reporter.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:
@ -1031,7 +1031,7 @@ class Participation(models.Model):
for ext in ["pdf", "tex"]) for ext in ["pdf", "tex"])
reviews_templates_content = f"<p>{_('Templates:')} {reviews_templates}</p>" reviews_templates_content = f"<p>{_('Templates:')} {reviews_templates}</p>"
content = defender_content + opponent_content + reviewer_content + observer_content \ content = reporter_content + opponent_content + reviewer_content + observer_content \
+ reviews_templates_content + reviews_templates_content
informations.append({ informations.append({
'title': _("Second round"), 'title': _("Second round"),
@ -1041,34 +1041,34 @@ class Participation(models.Model):
}) })
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.reviews_third_phase_limit + timedelta(hours=2):
defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, defender=self) reporter_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, reporter=self)
opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, opponent=self) opponent_passage = Passage.objects.get(pool__tournament=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
defender_text = _("<p>For the third round, you will defend " reporter_text = _("<p>For the third round, you will present "
"<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 = defender_passage.defended_solution.file.url solution_url = reporter_passage.reported_solution.file.url
defender_content = format_lazy(defender_text, draw_url=draw_url, reporter_content = format_lazy(reporter_text, draw_url=draw_url,
solution_url=solution_url, problem=defender_passage.solution_number) solution_url=solution_url, problem=reporter_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 written review on <a href='{passage_url}'>this page</a>.</p>")
solution_url = opponent_passage.defended_solution.file.url solution_url = opponent_passage.reported_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.defender.team.trigram, opponent_content = format_lazy(opponent_text, opponent=opponent_passage.reporter.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 written review on <a href='{passage_url}'>this page</a>.</p>")
solution_url = reviewer_passage.defended_solution.file.url solution_url = reviewer_passage.reported_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.defender.team.trigram, reviewer_content = format_lazy(reviewer_text, reviewer=reviewer_passage.reporter.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)
@ -1076,10 +1076,10 @@ class Participation(models.Model):
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 written review on <a href='{passage_url}'>this page</a>.</p>")
solution_url = observer_passage.defended_solution.file.url solution_url = observer_passage.reported_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.defender.team.trigram, observer=observer_passage.reporter.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:
@ -1095,7 +1095,7 @@ class Participation(models.Model):
for ext in ["pdf", "tex"]) for ext in ["pdf", "tex"])
reviews_templates_content = f"<p>{_('Templates:')} {reviews_templates}</p>" reviews_templates_content = f"<p>{_('Templates:')} {reviews_templates}</p>"
content = defender_content + opponent_content + reviewer_content + observer_content \ content = reporter_content + opponent_content + reviewer_content + observer_content \
+ reviews_templates_content + reviews_templates_content
informations.append({ informations.append({
'title': _("Second round"), 'title': _("Second round"),
@ -1204,7 +1204,7 @@ class Pool(models.Model):
@property @property
def solutions(self): def solutions(self):
return [passage.defended_solution for passage in self.passages.all()] return [passage.reported_solution for passage in self.passages.all()]
@property @property
def coeff(self): def coeff(self):
@ -1251,7 +1251,7 @@ 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"{_('Defender')} ({passage.defender.team.trigram})", "", sum(([f"{_('Reporter')} ({passage.reporter.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 [])
@ -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.defender_writing, note.defender_oral, note.opponent_writing, note.opponent_oral, line.extend([note.reporter_writing, note.reporter_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_defender_writing, passage.coeff_defender_oral, coeffs = sum(([passage.coeff_reporter_writing, passage.coeff_reporter_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.defender participation = passage.reporter
defender_passage = Passage.objects.get(defender=participation, reporter_passage = Passage.objects.get(reporter=participation,
pool__tournament=self.tournament, pool__round=self.round) pool__tournament=self.tournament, pool__round=self.round)
defender_row = 5 + defender_passage.pool.juries.count() reporter_row = 5 + reporter_passage.pool.juries.count()
defender_col = defender_passage.position - 1 reporter_col = reporter_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')} {defender_passage.pool.short_name}'" formula += (f"'{_('Pool')} {reporter_passage.pool.short_name}'"
f"!{getcol(min_column + defender_col * passage_width)}{defender_row + 3}") # Defender f"!{getcol(min_column + reporter_col * passage_width)}{reporter_row + 3}") # Reporter
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')} {defender_passage.pool.short_name}'" f"='{_('Pool')} {reporter_passage.pool.short_name}'"
f"!${getcol(3 + defender_col * passage_width)}$1", f"!${getcol(3 + reporter_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(defender_penalties__gte=1).all()] for passage in self.passages.filter(reporter_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=_("defended solution"), verbose_name=_("reported 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)
], ],
) )
defender = models.ForeignKey( reporter = models.ForeignKey(
Participation, Participation,
on_delete=models.PROTECT, on_delete=models.PROTECT,
verbose_name=_("defender"), verbose_name=_("reporter"),
related_name="+", related_name="+",
) )
@ -1721,17 +1721,17 @@ class Passage(models.Model):
default=None, default=None,
) )
defender_penalties = models.PositiveSmallIntegerField( reporter_penalties = models.PositiveSmallIntegerField(
verbose_name=_("penalties"), verbose_name=_("penalties"),
default=0, default=0,
help_text=_("Number of penalties for the defender. " help_text=_("Number of penalties for the reporter. "
"The defender will loose a 0.5 coefficient per penalty."), "The reporter will loose a 0.5 coefficient per penalty."),
) )
@property @property
def defended_solution(self) -> "Solution": def reported_solution(self) -> "Solution":
return Solution.objects.get( return Solution.objects.get(
participation=self.defender, participation=self.reporter,
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_defender_writing(self) -> float: def average_reporter_writing(self) -> float:
return self.avg(note.defender_writing for note in self.notes.all()) return self.avg(note.reporter_writing for note in self.notes.all())
@property @property
def coeff_defender_writing(self) -> float: def coeff_reporter_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_defender_oral(self) -> float: def average_reporter_oral(self) -> float:
return self.avg(note.defender_oral for note in self.notes.all()) return self.avg(note.reporter_oral for note in self.notes.all())
@property @property
def coeff_defender_oral(self) -> float: def coeff_reporter_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.defender_penalties coeff *= 1 - 0.25 * self.reporter_penalties
return coeff return coeff
@property @property
def average_defender(self) -> float: def average_reporter(self) -> float:
return (self.coeff_defender_writing * self.average_defender_writing return (self.coeff_reporter_writing * self.average_reporter_writing
+ self.coeff_defender_oral * self.average_defender_oral) + self.coeff_reporter_oral * self.average_reporter_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_defender_writing yield self.average_reporter_writing
yield self.average_defender_oral yield self.average_reporter_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_defender if participation == self.defender else self.average_opponent \ avg = self.average_reporter if participation == self.reporter 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.defender not in self.pool.participations.all(): if self.reporter 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.defender.team.trigram)) .format(trigram=self.reporter.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 {defender} for problem {problem}")\ return _("Passage of {reporter} for problem {problem}")\
.format(defender=self.defender.team, problem=self.solution_number) .format(reporter=self.reporter.team, problem=self.solution_number)
class Meta: class Meta:
verbose_name = _("passage") verbose_name = _("passage")
@ -1984,11 +1984,11 @@ class WrittenReview(models.Model):
) )
def __str__(self): def __str__(self):
return _("Written review of {team} as {type} for problem {problem} of {defender}").format( return _("Written review of {team} as {type} for problem {problem} of {reporter}").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,
defender=self.passage.defender.team.trigram, reporter=self.passage.reporter.team.trigram,
) )
class Meta: class Meta:
@ -2013,14 +2013,14 @@ class Note(models.Model):
related_name="notes", related_name="notes",
) )
defender_writing = models.PositiveSmallIntegerField( reporter_writing = models.PositiveSmallIntegerField(
verbose_name=_("defender writing note"), verbose_name=_("reporter writing note"),
choices=[(i, i) for i in range(0, 21)], choices=[(i, i) for i in range(0, 21)],
default=0, default=0,
) )
defender_oral = models.PositiveSmallIntegerField( reporter_oral = models.PositiveSmallIntegerField(
verbose_name=_("defender oral note"), verbose_name=_("reporter 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 +2062,8 @@ class Note(models.Model):
) )
def get_all(self): def get_all(self):
yield self.defender_writing yield self.reporter_writing
yield self.defender_oral yield self.reporter_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 +2072,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, defender_writing: int, defender_oral: int, opponent_writing: int, opponent_oral: int, def set_all(self, reporter_writing: int, reporter_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.defender_writing = defender_writing self.reporter_writing = reporter_writing
self.defender_oral = defender_oral self.reporter_oral = reporter_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
defender = tables.LinkColumn( reporter = tables.LinkColumn(
"participation:passage_detail", "participation:passage_detail",
args=[tables.A("id")], args=[tables.A("id")],
verbose_name=_("defender").capitalize, verbose_name=_("reporter").capitalize,
) )
def render_defender(self, value): def render_reporter(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 = ('defender', 'opponent', 'reviewer', 'observer', 'solution_number', ) fields = ('reporter', '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', 'defender_writing', 'defender_oral', 'opponent_writing', 'opponent_oral', fields = ('jury', 'reporter_writing', 'reporter_oral', 'opponent_writing', 'opponent_oral',
'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_oral', 'update',) 'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_oral', '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.defender.team.trigram }}, {% trans "Pb." %} {{ note.passage.solution_number }}</h5> <h5>{% trans "Defense of" %} {{ note.passage.reporter.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 "Defender:" %}</dt> <dt class="col-sm-3">{% trans "Reporter:" %}</dt>
<dd class="col-sm-9"><a href="{{ passage.defender.get_absolute_url }}">{{ passage.defender.team }}</a></dd> <dd class="col-sm-9"><a href="{{ passage.reporter.get_absolute_url }}">{{ passage.reporter.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,11 +39,11 @@
<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 "Defended solution:" %}</dt> <dt class="col-sm-3">{% trans "Reported solution:" %}</dt>
<dd class="col-sm-9"><a href="{{ passage.defended_solution.file.url }}">{{ passage.defended_solution }}</a></dd> <dd class="col-sm-9"><a href="{{ passage.reported_solution.file.url }}">{{ passage.reported_solution }}</a></dd>
<dt class="col-sm-3">{% trans "Defender penalties count:" %}</dt> <dt class="col-sm-3">{% trans "Reporter penalties count:" %}</dt>
<dd class="col-sm-9">{{ passage.defender_penalties }}</dd> <dd class="col-sm-9">{{ passage.reporter_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">
@ -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 defender writing" %} {% trans "Average points for the reporter writing" %}
({{ passage.defender.team.trigram }}) : ({{ passage.reporter.team.trigram }}) :
</dt> </dt>
<dd class="col-sm-4"> <dd class="col-sm-4">
{{ passage.average_defender_writing|floatformat }}/{% if TFJM_APP == "TFJM" %}20{% else %}10{% endif %} {{ passage.average_reporter_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 defender oral" %} {% trans "Average points for the reporter oral" %}
({{ passage.defender.team.trigram }}) : ({{ passage.reporter.team.trigram }}) :
</dt> </dt>
<dd class="col-sm-4"> <dd class="col-sm-4">
{{ passage.average_defender_oral|floatformat }}/{% if TFJM_APP == "TFJM" %}20{% else %}10{% endif %} {{ passage.average_reporter_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 "Defender points" %} {% trans "Reporter points" %}
({{ passage.defender.team.trigram }}) : ({{ passage.reporter.team.trigram }}) :
</dt> </dt>
<dd class="col-sm-4"> <dd class="col-sm-4">
{{ passage.average_defender|floatformat }}/{% if TFJM_APP == "TFJM" %}52{% else %}50{% endif %} {{ passage.average_reporter|floatformat }}/{% if TFJM_APP == "TFJM" %}52{% else %}50{% endif %}
</dd> </dd>
<dt class="col-sm-8"> <dt class="col-sm-8">

View File

@ -46,10 +46,10 @@
</a> </a>
</dd> </dd>
<dt class="col-sm-3">{% trans "Defended solutions:" %}</dt> <dt class="col-sm-3">{% trans "Reported 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.defended_solution.file.url }}">{{ passage.defender.team.trigram }} — {{ passage.get_solution_number_display }}</a>{% if not forloop.last %}, {% endif %} <a href="{{ passage.reported_solution.file.url }}">{{ passage.reporter.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,7 +61,7 @@
<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.defender.team.trigram }} — {{ passage.get_solution_number_display }} : {{ passage.reporter.team.trigram }} — {{ passage.get_solution_number_display }} :
{% for review in passage.written_reviews.all %} {% for review in passage.written_reviews.all %}
<a href="{{ review.file.url }}">{{ review.participation.team.trigram }} ({{ review.get_type_display }})</a>{% if not forloop.last %}, {% endif %} <a href="{{ review.file.url }}">{{ review.participation.team.trigram }} ({{ review.get_type_display }})</a>{% if not forloop.last %}, {% endif %}
{% empty %} {% empty %}
@ -70,7 +70,7 @@
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
<a href="{% url 'participation:pool_download_written_review' pool_id=pool.id %}" class="badge rounded-pill text-bg-secondary"> <a href="{% url 'participation:pool_download_written_reviews' 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.defender.team.trigram }}~}} $\qquad$ probl\`eme \underline{~{{ passage.solution_number }}~} \item D\'efenseur\textperiodcentered{}se au passage {{ forloop.counter }} : \underline{\texttt{~{{ passage.reporter.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.defender.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.reporter.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.defender.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.reporter.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

@ -1503,10 +1503,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):
defender_tc = TableCell(valuetype="string", stylename=title_style_left) reporter_tc = TableCell(valuetype="string", stylename=title_style_left)
defender_tc.addElement(P(text=_("Defender"))) reporter_tc.addElement(P(text=_("Reporter")))
defender_tc.setAttribute('numbercolumnsspanned', "2") reporter_tc.setAttribute('numbercolumnsspanned', "2")
header_role.addElement(defender_tc) header_role.addElement(reporter_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 +1539,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):
defender_w_tc = TableCell(valuetype="string", stylename=title_style_botleft) reporter_w_tc = TableCell(valuetype="string", stylename=title_style_botleft)
defender_w_tc.addElement(P(text=f"{_('Writing')} (/{20 if settings.TFJM_APP == 'TFJM' else 10})")) reporter_w_tc.addElement(P(text=f"{_('Writing')} (/{20 if settings.TFJM_APP == 'TFJM' else 10})"))
header_notes.addElement(defender_w_tc) header_notes.addElement(reporter_w_tc)
defender_o_tc = TableCell(valuetype="string", stylename=title_style_bot) reporter_o_tc = TableCell(valuetype="string", stylename=title_style_bot)
defender_o_tc.addElement(P(text=f"{_('Oral')} (/{20 if settings.TFJM_APP == 'TFJM' else 10})")) reporter_o_tc.addElement(P(text=f"{_('Oral')} (/{20 if settings.TFJM_APP == 'TFJM' else 10})"))
header_notes.addElement(defender_o_tc) header_notes.addElement(reporter_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 +1626,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():
defender_w_tc = TableCell(valuetype="float", value=passage.coeff_defender_writing, stylename=style_left) reporter_w_tc = TableCell(valuetype="float", value=passage.coeff_reporter_writing, stylename=style_left)
defender_w_tc.addElement(P(text=str(passage.coeff_defender_writing))) reporter_w_tc.addElement(P(text=str(passage.coeff_reporter_writing)))
coeff_row.addElement(defender_w_tc) coeff_row.addElement(reporter_w_tc)
defender_o_tc = TableCell(valuetype="float", value=passage.coeff_defender_oral, stylename=style) reporter_o_tc = TableCell(valuetype="float", value=passage.coeff_reporter_oral, stylename=style)
defender_o_tc.addElement(P(text=str(passage.coeff_defender_oral))) reporter_o_tc.addElement(P(text=str(passage.coeff_reporter_oral)))
coeff_row.addElement(defender_o_tc) coeff_row.addElement(reporter_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 +1671,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)
defender_tc = TableCell(valuetype="float", value=passage.average_defender, stylename=style_botleft) reporter_tc = TableCell(valuetype="float", value=passage.average_reporter, stylename=style_botleft)
defender_tc.addElement(P(text=str(passage.average_defender))) reporter_tc.addElement(P(text=str(passage.average_reporter)))
defender_tc.setAttribute('numbercolumnsspanned', "2") reporter_tc.setAttribute('numbercolumnsspanned', "2")
defender_tc.setAttribute("formula", f"of:=[.{def_w_col}{max_row + 1}] * [.{def_w_col}{max_row + 2}]" reporter_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(defender_tc) subtotal_row.addElement(reporter_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 +1748,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.defender.team.name} ({passage.defender.team.trigram})")) team_tc.addElement(P(text=f"{passage.reporter.team.name} ({passage.reporter.team.trigram})"))
team_tc.setAttribute('numbercolumnsspanned', "2") team_tc.setAttribute('numbercolumnsspanned', "2")
team_row.addElement(team_tc) team_row.addElement(team_tc)
@ -1758,17 +1758,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)
defender_pos = passage.position - 1 reporter_pos = passage.position - 1
opponent_pos = self.object.passages.get(opponent=passage.defender).position - 1 opponent_pos = self.object.passages.get(opponent=passage.reporter).position - 1
reviewer_pos = self.object.passages.get(reviewer=passage.defender).position - 1 reviewer_pos = self.object.passages.get(reviewer=passage.reporter).position - 1
observer_pos = self.object.passages.get(observer=passage.defender).position - 1 \ observer_pos = self.object.passages.get(observer=passage.reporter).position - 1 \
if has_observer else None if has_observer else None
score_tc = TableCell(valuetype="float", value=self.object.average(passage.defender), score_tc = TableCell(valuetype="float", value=self.object.average(passage.reporter),
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.defender))) score_tc.addElement(P(text=self.object.average(passage.reporter)))
formula = "of:=" formula = "of:="
formula += getcol(min_column + defender_pos * passage_width) + str(max_row + 3) # Defender formula += getcol(min_column + reporter_pos * passage_width) + str(max_row + 3) # Reporter
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 +1778,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.defender) + 1, rank_tc = TableCell(valuetype="float", value=sorted_participations.index(passage.reporter) + 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.defender) + 1))) rank_tc.addElement(P(text=str(sorted_participations.index(passage.reporter) + 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 +1984,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.defender, passage.opponent, passage.reviewer, passage.observer]: and reg.team.participation in [passage.reporter, 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 +2005,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['defender_writing'].column.verbose_name += f" ({passage.defender.team.trigram})" context['notes'].columns['reporter_writing'].column.verbose_name += f" ({passage.reporter.team.trigram})"
context['notes'].columns['defender_oral'].column.verbose_name += f" ({passage.defender.team.trigram})" context['notes'].columns['reporter_oral'].column.verbose_name += f" ({passage.reporter.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})"
@ -2101,8 +2101,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['defender_writing'].label += f" ({self.object.passage.defender.team.trigram})" form.fields['reporter_writing'].label += f" ({self.object.passage.reporter.team.trigram})"
form.fields['defender_oral'].label += f" ({self.object.passage.defender.team.trigram})" form.fields['reporter_oral'].label += f" ({self.object.passage.reporter.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

@ -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(defender=user.registration.team.participation) passage_participant_qs = Passage.objects.filter(Q(reporter=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),
defender=solution.participation, reporter=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()),
defender=solution.participation, reporter=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
@ -881,20 +881,20 @@ class WrittenReviewView(LoginRequiredMixin, View):
path = f"media/reviews/{filename}" path = f"media/reviews/{filename}"
if not os.path.exists(path): if not os.path.exists(path):
raise Http404 raise Http404
reviews = WrittenReview.objects.get(file__endswith=filename) review = WrittenReview.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 reviews.passage.pool.juries.all() and (user.registration in review.passage.pool.juries.all()
or user.registration in reviews.passage.pool.tournament.organizers.all() or user.registration in review.passage.pool.tournament.organizers.all()
or user.registration.pools_presided.filter(tournament=reviews.passage.pool.tournament).exists()) or user.registration.pools_presided.filter(tournament=review.passage.pool.tournament).exists())
or user.registration.participates and user.registration.team == reviews.participation.team): or user.registration.participates and user.registration.team == review.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(reviews) + f".{ext}" true_file_name = str(review) + 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{~~~~} defended by team \underline{~~~~~~~~~~~~~~~~~~~~~~~~~~~~} Problem \underline{~~~~} reported by team \underline{~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
\medskip \medskip