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_position = 1 + i // 2
defender = tds[line[0]].participation
reporter = tds[line[0]].participation
opponent = tds[line[1]].participation
reviewer = tds[line[2]].participation
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,
position=passage_position,
solution_number=tds[line[0]].accepted,
defender=defender,
reporter=reporter,
opponent=opponent,
reviewer=reviewer,
observer=observer,
defender_penalties=tds[line[0]].penalty_int,
reporter_penalties=tds[line[0]].penalty_int,
)
# Update Google Sheets
@ -549,7 +549,7 @@ class TeamDraw(models.Model):
@property
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

View File

@ -521,9 +521,9 @@ document.addEventListener('DOMContentLoaded', () => {
teamTd.innerText = team
teamTr.append(teamTd)
let defenderTd = document.createElement('td')
defenderTd.classList.add('text-center')
defenderTd.innerText = 'Déf'
let reporterTd = document.createElement('td')
reporterTd.classList.add('text-center')
reporterTd.innerText = 'Déf'
let opponentTd = document.createElement('td')
opponentTd.classList.add('text-center')
@ -537,29 +537,29 @@ document.addEventListener('DOMContentLoaded', () => {
if (poule.teams.length === 3) {
switch (i) {
case 0:
teamTr.append(defenderTd, reviewerTd, opponentTd)
teamTr.append(reporterTd, reviewerTd, opponentTd)
break
case 1:
teamTr.append(opponentTd, defenderTd, reviewerTd)
teamTr.append(opponentTd, reporterTd, reviewerTd)
break
case 2:
teamTr.append(reviewerTd, opponentTd, defenderTd)
teamTr.append(reviewerTd, opponentTd, reporterTd)
break
}
} else if (poule.teams.length === 4) {
let emptyTd = document.createElement('td')
switch (i) {
case 0:
teamTr.append(defenderTd, emptyTd, reviewerTd, opponentTd)
teamTr.append(reporterTd, emptyTd, reviewerTd, opponentTd)
break
case 1:
teamTr.append(opponentTd, defenderTd, emptyTd, reviewerTd)
teamTr.append(opponentTd, reporterTd, emptyTd, reviewerTd)
break
case 2:
teamTr.append(reviewerTd, opponentTd, defenderTd, emptyTd)
teamTr.append(reviewerTd, opponentTd, reporterTd, emptyTd)
break
case 3:
teamTr.append(emptyTd, reviewerTd, opponentTd, defenderTd)
teamTr.append(emptyTd, reviewerTd, opponentTd, reporterTd)
break
}
} else if (poule.teams.length === 5) {
@ -567,19 +567,19 @@ document.addEventListener('DOMContentLoaded', () => {
let emptyTd2 = document.createElement('td')
switch (i) {
case 0:
teamTr.append(defenderTd, emptyTd, opponentTd, reviewerTd, emptyTd2)
teamTr.append(reporterTd, emptyTd, opponentTd, reviewerTd, emptyTd2)
break
case 1:
teamTr.append(emptyTd, defenderTd, reviewerTd, emptyTd2, opponentTd)
teamTr.append(emptyTd, reporterTd, reviewerTd, emptyTd2, opponentTd)
break
case 2:
teamTr.append(opponentTd, emptyTd, defenderTd, emptyTd2, reviewerTd)
teamTr.append(opponentTd, emptyTd, reporterTd, emptyTd2, reviewerTd)
break
case 3:
teamTr.append(reviewerTd, opponentTd, emptyTd, defenderTd, emptyTd2)
teamTr.append(reviewerTd, opponentTd, emptyTd, reporterTd, emptyTd2)
break
case 4:
teamTr.append(emptyTd, reviewerTd, emptyTd2, opponentTd, defenderTd)
teamTr.append(emptyTd, reviewerTd, emptyTd2, opponentTd, reporterTd)
break
}
}
@ -662,7 +662,7 @@ document.addEventListener('DOMContentLoaded', () => {
let penaltyDiv = document.getElementById(`recap-${tid}-round-${round}-team-${team}-penalty`)
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
if (penaltyDiv === null) {
penaltyDiv = document.createElement('div')

View File

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

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: TFJM\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"
"Last-Translator: Emmy D'Anello <emmy.danello@animath.fr>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -814,6 +814,67 @@ msgstr "Pb."
msgid "Room"
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:414
msgid "Abort"
@ -920,7 +981,7 @@ msgstr "sélectionnée pour la finale"
#: participation/admin.py:124 participation/admin.py:188
#: participation/models.py:1696 participation/tables.py:114
msgid "defender"
msgid "reporter"
msgstr "défenseur⋅se"
#: 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é :"
#: 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 ""
"Les équipes défenseuse, opposante et rapportrice doivent être différent⋅es."
#: 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."
#: participation/forms.py:373
@ -1448,7 +1509,7 @@ msgstr "Tirage au sort des solutions"
#, python-brace-format
msgid ""
"<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>"
msgstr ""
"<p>Le tirage au sort des solutions est terminé. Vous pouvez consulter les "
@ -1499,7 +1560,7 @@ msgstr "Premier tour"
#: participation/models.py:986
#, python-brace-format
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>"
msgstr ""
"<p>Pour le second tour, vous défendrez <a href='{solution_url}'>votre "
@ -1513,7 +1574,7 @@ msgstr "Second tour"
#: participation/models.py:1050
#, python-brace-format
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>"
msgstr ""
"<p>Pour le troisième tour, vous défendrez <a href='{solution_url}'>votre "
@ -1638,7 +1699,7 @@ msgid "position"
msgstr "position"
#: participation/models.py:1687
msgid "defended solution"
msgid "reported solution"
msgstr "solution défendue"
#: participation/models.py:1725
@ -1647,7 +1708,7 @@ msgstr "pénalités"
#: participation/models.py:1727
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."
msgstr ""
"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
#, python-brace-format
msgid "Passage of {defender} for problem {problem}"
msgstr "Passage de {defender} pour le problème {problem}"
msgid "Passage of {reporter} for problem {problem}"
msgstr "Passage de {reporter} pour le problème {problem}"
#: participation/models.py:1871 participation/models.py:1880
#: participation/models.py:1969 participation/models.py:2012
@ -1716,10 +1777,10 @@ msgstr "solutions"
#: participation/models.py:1987
#, 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 ""
"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
msgid "written review"
@ -1734,11 +1795,11 @@ msgid "jury"
msgstr "jury"
#: participation/models.py:2017
msgid "defender writing note"
msgid "reporter writing note"
msgstr "note d'écrit défenseur⋅se"
#: participation/models.py:2023
msgid "defender oral note"
msgid "reporter oral note"
msgstr "note d'oral défenseur⋅se"
#: participation/models.py:2029
@ -1933,7 +1994,7 @@ msgid "Position:"
msgstr "Position :"
#: participation/templates/participation/passage_detail.html:28
msgid "Defender:"
msgid "Reporter:"
msgstr "Défenseur⋅se :"
#: participation/templates/participation/passage_detail.html:31
@ -1949,11 +2010,11 @@ msgid "Observer:"
msgstr "Observateur⋅rice :"
#: participation/templates/participation/passage_detail.html:42
msgid "Defended solution:"
msgid "Reported solution:"
msgstr "Solution défendue"
#: participation/templates/participation/passage_detail.html:45
msgid "Defender penalties count:"
msgid "Reporter penalties count:"
msgstr "Nombre de pénalités :"
#: participation/templates/participation/passage_detail.html:48
@ -1981,11 +2042,11 @@ msgid "Notes detail"
msgstr "Détails des notes"
#: 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"
#: 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"
#: 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"
#: participation/templates/participation/passage_detail.html:140
msgid "Defender points"
msgid "Reporter points"
msgstr "Points de l'équipe défenseuse"
#: participation/templates/participation/passage_detail.html:148
@ -2058,7 +2119,7 @@ msgid "Edit jury"
msgstr "Modifier le jury"
#: participation/templates/participation/pool_detail.html:49
msgid "Defended solutions:"
msgid "Reported solutions:"
msgstr "Solutions défendues :"
#: participation/templates/participation/pool_detail.html:55
@ -2755,7 +2816,7 @@ msgid "Notes were successfully uploaded."
msgstr "Les notes ont bien été envoyées."
#: participation/views.py:1507
msgid "Defender"
msgid "Reporter"
msgstr "Défenseur⋅se"
#: participation/views.py:1513

View File

@ -51,7 +51,7 @@ class PassageInline(admin.TabularInline):
model = Passage
extra = 0
ordering = ('position',)
autocomplete_fields = ('defender', 'opponent', 'reviewer', 'observer',)
autocomplete_fields = ('reporter', 'opponent', 'reviewer', 'observer',)
show_change_link = True
@ -113,17 +113,17 @@ class PoolAdmin(admin.ModelAdmin):
@admin.register(Passage)
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')
list_filter = ('pool__tournament', 'pool__round', 'pool__letter', 'solution_number',)
search_fields = ('pool__participations__team__name', 'pool__participations__team__trigram',)
ordering = ('pool__tournament', 'pool__round', 'pool__letter', 'position',)
autocomplete_fields = ('pool', 'defender', 'opponent', 'reviewer', 'observer',)
autocomplete_fields = ('pool', 'reporter', 'opponent', 'reviewer', 'observer',)
inlines = (NoteInline,)
@admin.display(description=_("defender"), ordering='defender__team__trigram')
def defender_trigram(self, record: Passage):
return record.defender.team.trigram
@admin.display(description=_("reporter"), ordering='reporter__team__trigram')
def reporter_trigram(self, record: Passage):
return record.reporter.team.trigram
@admin.display(description=_("opponent"), ordering='opponent__team__trigram')
def opponent_trigram(self, record: Passage):
@ -148,13 +148,13 @@ class PassageAdmin(admin.ModelAdmin):
@admin.register(Note)
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',
'observer_writing', 'observer_oral',)
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')
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',)
@admin.display(description=_("pool"))
@ -180,14 +180,14 @@ class SolutionAdmin(admin.ModelAdmin):
@admin.register(WrittenReview)
class WrittenReviewAdmin(admin.ModelAdmin):
list_display = ('participation', 'type', 'defender', 'passage',)
list_display = ('participation', 'type', 'reporter', 'passage',)
list_filter = ('participation__tournament', 'type', 'passage__solution_number',)
search_fields = ('participation__team__name', 'participation__team__trigram',)
autocomplete_fields = ('participation', 'passage',)
@admin.display(description=_("defender"))
def defender(self, record: WrittenReview):
return record.passage.defender
@admin.display(description=_("reporter"))
def reporter(self, record: WrittenReview):
return record.passage.reporter
@admin.display(description=_("problem"))
def problem(self, record: WrittenReview):

View File

@ -12,7 +12,7 @@ class NoteViewSet(ModelViewSet):
queryset = Note.objects.all()
serializer_class = NoteSerializer
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', ]
@ -27,7 +27,7 @@ class PassageViewSet(ModelViewSet):
queryset = Passage.objects.all()
serializer_class = PassageSerializer
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):

View File

@ -345,18 +345,18 @@ class UploadNotesForm(forms.Form):
class PassageForm(forms.ModelForm):
def clean(self):
cleaned_data = super().clean()
if "defender" 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:
self.add_error(None, _("The defender, the opponent and the reviewer must be different."))
if "defender" in self.cleaned_data and "solution_number" in self.cleaned_data \
and not Solution.objects.filter(participation=cleaned_data["defender"],
if "reporter" in cleaned_data and "opponent" in cleaned_data and "reviewer" in cleaned_data \
and len({cleaned_data["reporter"], cleaned_data["opponent"], cleaned_data["reviewer"]}) < 3:
self.add_error(None, _("The reporter, the opponent and the reviewer must be different."))
if "reporter" in self.cleaned_data and "solution_number" in self.cleaned_data \
and not Solution.objects.filter(participation=cleaned_data["reporter"],
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
class Meta:
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):
@ -386,5 +386,5 @@ class WrittenReviewForm(forms.ModelForm):
class NoteForm(forms.ModelForm):
class Meta:
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', )

View File

@ -51,23 +51,23 @@ class Command(BaseCommand):
team3, score3 = sorted_notes[2]
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)
reviewer_passage_1 = Passage.objects.get(pool__tournament=tournament, pool__round=1, reviewer=team2)
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)
reviewer_passage_2 = Passage.objects.get(pool__tournament=tournament, pool__round=2, reviewer=team2)
line.append(team2.team.trigram)
line.append(str(pool1.jury_president or ""))
line.append(f"Pb. {defender_passage_1.solution_number}")
line.extend([defender_passage_1.average_defender_writing, defender_passage_1.average_defender_oral,
line.append(f"Pb. {reporter_passage_1.solution_number}")
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,
reviewer_passage_1.average_reviewer_writing, reviewer_passage_1.average_reviewer_oral])
line.append(str(pool2.jury_president or ""))
line.append(f"Pb. {defender_passage_2.solution_number}")
line.extend([defender_passage_2.average_defender_writing, defender_passage_2.average_defender_oral,
line.append(f"Pb. {reporter_passage_2.solution_number}")
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,
reviewer_passage_2.average_reviewer_writing, reviewer_passage_2.average_reviewer_oral])
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})"]
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
if pool1.participations.count() != 5:
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(tweak1.diff if tweak1 else 0)
if Passage.objects.filter(pool__tournament=self, pool__round=2, defender=participation).exists():
passage2 = Passage.objects.get(pool__tournament=self, pool__round=2, defender=participation)
if Passage.objects.filter(pool__tournament=self, pool__round=2, reporter=participation).exists():
passage2 = Passage.objects.get(pool__tournament=self, pool__round=2, reporter=participation)
pool2 = passage2.pool
if pool2.participations.count() != 5:
position2 = passage2.position
@ -519,8 +519,8 @@ class Tournament(models.Model):
if settings.NB_ROUNDS >= 3:
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():
passage3 = Passage.objects.get(pool__tournament=self, pool__round=3, defender=participation)
if Passage.objects.filter(pool__tournament=self, pool__round=3, reporter=participation).exists():
passage3 = Passage.objects.get(pool__tournament=self, pool__round=3, reporter=participation)
pool3 = passage3.pool
if pool3.participations.count() != 5:
position3 = passage3.position
@ -912,36 +912,36 @@ class Participation(models.Model):
'content': content,
})
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)
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 = 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>"
"<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>")
draw_url = reverse_lazy("draw:index")
solution_url = defender_passage.defended_solution.file.url
defender_content = format_lazy(defender_text, draw_url=draw_url,
solution_url=solution_url, problem=defender_passage.solution_number)
solution_url = reporter_passage.reported_solution.file.url
reporter_content = format_lazy(reporter_text, draw_url=draw_url,
solution_url=solution_url, problem=reporter_passage.solution_number)
opponent_text = _("<p>You will oppose the solution of the team {opponent} on the "
"<a href='{solution_url}'>problem {problem}</a>. "
"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,))
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,
problem=opponent_passage.solution_number, passage_url=passage_url)
reviewer_text = _("<p>You will report the solution of the team {reviewer} on the "
"<a href='{solution_url}'>problem {problem}</a>. "
"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,))
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,
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 "
"<a href='{solution_url}'>problem {problem}</a>. "
"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,))
observer_content = format_lazy(observer_text,
observer=observer_passage.defender.team.trigram,
observer=observer_passage.reporter.team.trigram,
solution_url=solution_url,
problem=observer_passage.solution_number, passage_url=passage_url)
else:
@ -968,7 +968,7 @@ class Participation(models.Model):
for ext in ["pdf", "tex"])
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
informations.append({
'title': _("First round"),
@ -977,34 +977,34 @@ class Participation(models.Model):
'content': content,
})
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)
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 = 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>")
draw_url = reverse_lazy("draw:index")
solution_url = defender_passage.defended_solution.file.url
defender_content = format_lazy(defender_text, draw_url=draw_url,
solution_url=solution_url, problem=defender_passage.solution_number)
solution_url = reporter_passage.reported_solution.file.url
reporter_content = format_lazy(reporter_text, draw_url=draw_url,
solution_url=solution_url, problem=reporter_passage.solution_number)
opponent_text = _("<p>You will oppose the solution of the team {opponent} on the "
"<a href='{solution_url}'>problem {problem}</a>. "
"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,))
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,
problem=opponent_passage.solution_number, passage_url=passage_url)
reviewer_text = _("<p>You will report the solution of the team {reviewer} on the "
"<a href='{solution_url}'>problem {problem}</a>. "
"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,))
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,
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 "
"<a href='{solution_url}'>problem {problem}</a>. "
"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,))
observer_content = format_lazy(observer_text,
observer=observer_passage.defender.team.trigram,
observer=observer_passage.reporter.team.trigram,
solution_url=solution_url,
problem=observer_passage.solution_number, passage_url=passage_url)
else:
@ -1031,7 +1031,7 @@ class Participation(models.Model):
for ext in ["pdf", "tex"])
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
informations.append({
'title': _("Second round"),
@ -1041,34 +1041,34 @@ class Participation(models.Model):
})
elif settings.TFJM_APP == "ETEAM" \
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)
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 = 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>")
draw_url = reverse_lazy("draw:index")
solution_url = defender_passage.defended_solution.file.url
defender_content = format_lazy(defender_text, draw_url=draw_url,
solution_url=solution_url, problem=defender_passage.solution_number)
solution_url = reporter_passage.reported_solution.file.url
reporter_content = format_lazy(reporter_text, draw_url=draw_url,
solution_url=solution_url, problem=reporter_passage.solution_number)
opponent_text = _("<p>You will oppose the solution of the team {opponent} on the "
"<a href='{solution_url}'>problem {problem}</a>. "
"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,))
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,
problem=opponent_passage.solution_number, passage_url=passage_url)
reviewer_text = _("<p>You will report the solution of the team {reviewer} on the "
"<a href='{solution_url}'>problem {problem}</a>. "
"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,))
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,
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 "
"<a href='{solution_url}'>problem {problem}</a>. "
"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,))
observer_content = format_lazy(observer_text,
observer=observer_passage.defender.team.trigram,
observer=observer_passage.reporter.team.trigram,
solution_url=solution_url,
problem=observer_passage.solution_number, passage_url=passage_url)
else:
@ -1095,7 +1095,7 @@ class Participation(models.Model):
for ext in ["pdf", "tex"])
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
informations.append({
'title': _("Second round"),
@ -1204,7 +1204,7 @@ class Pool(models.Model):
@property
def solutions(self):
return [passage.defended_solution for passage in self.passages.all()]
return [passage.reported_solution for passage in self.passages.all()]
@property
def coeff(self):
@ -1251,7 +1251,7 @@ class Pool(models.Model):
header = [
sum(([str(_("Problem #{problem}").format(problem=passage.solution_number))] + (passage_width - 1) * [""]
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"{_('Reviewer')} ({passage.reviewer.team.trigram})", ""]
+ ([f"{_('Observer')} ({passage.observer.team.trigram})", ""] if has_observer else [])
@ -1268,7 +1268,7 @@ class Pool(models.Model):
line = [str(jury), jury.id]
for passage in passages:
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])
if has_observer:
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)
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_reviewer_writing, passage.coeff_reviewer_oral]
+ ([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__letter=self.letter).order_by('position', 'pool__room')
for i, passage in enumerate(all_passages):
participation = passage.defender
defender_passage = Passage.objects.get(defender=participation,
participation = passage.reporter
reporter_passage = Passage.objects.get(reporter=participation,
pool__tournament=self.tournament, pool__round=self.round)
defender_row = 5 + defender_passage.pool.juries.count()
defender_col = defender_passage.position - 1
reporter_row = 5 + reporter_passage.pool.juries.count()
reporter_col = reporter_passage.position - 1
opponent_passage = Passage.objects.get(opponent=participation,
pool__tournament=self.tournament, pool__round=self.round)
@ -1347,8 +1347,8 @@ class Pool(models.Model):
reviewer_col = reviewer_passage.position - 1
formula = "="
formula += (f"'{_('Pool')} {defender_passage.pool.short_name}'"
f"!{getcol(min_column + defender_col * passage_width)}{defender_row + 3}") # Defender
formula += (f"'{_('Pool')} {reporter_passage.pool.short_name}'"
f"!{getcol(min_column + reporter_col * passage_width)}{reporter_row + 3}") # Reporter
formula += (f" + '{_('Pool')} {opponent_passage.pool.short_name}'"
f"!{getcol(min_column + opponent_col * passage_width + 2)}{opponent_row + 3}") # Opponent
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}")
ranking.append([f"{participation.team.name} ({participation.team.trigram})", "",
f"='{_('Pool')} {defender_passage.pool.short_name}'"
f"!${getcol(3 + defender_col * passage_width)}$1",
f"='{_('Pool')} {reporter_passage.pool.short_name}'"
f"!${getcol(3 + reporter_col * passage_width)}$1",
formula,
f"=RANG(D{max_row + 6 + i}; "
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)),]
# Display penalties in red
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:
r, g, b = bg_color
format_requests.append({
@ -1684,16 +1684,16 @@ class Passage(models.Model):
)
solution_number = models.PositiveSmallIntegerField(
verbose_name=_("defended solution"),
verbose_name=_("reported solution"),
choices=[
(i, format_lazy(_("Problem #{problem}"), problem=i)) for i in range(1, len(settings.PROBLEMS) + 1)
],
)
defender = models.ForeignKey(
reporter = models.ForeignKey(
Participation,
on_delete=models.PROTECT,
verbose_name=_("defender"),
verbose_name=_("reporter"),
related_name="+",
)
@ -1721,17 +1721,17 @@ class Passage(models.Model):
default=None,
)
defender_penalties = models.PositiveSmallIntegerField(
reporter_penalties = models.PositiveSmallIntegerField(
verbose_name=_("penalties"),
default=0,
help_text=_("Number of penalties for the defender. "
"The defender will loose a 0.5 coefficient per penalty."),
help_text=_("Number of penalties for the reporter. "
"The reporter will loose a 0.5 coefficient per penalty."),
)
@property
def defended_solution(self) -> "Solution":
def reported_solution(self) -> "Solution":
return Solution.objects.get(
participation=self.defender,
participation=self.reporter,
problem=self.solution_number,
final_solution=self.pool.tournament.final)
@ -1740,27 +1740,27 @@ class Passage(models.Model):
return sum(items) / len(items) if items else 0
@property
def average_defender_writing(self) -> float:
return self.avg(note.defender_writing for note in self.notes.all())
def average_reporter_writing(self) -> float:
return self.avg(note.reporter_writing for note in self.notes.all())
@property
def coeff_defender_writing(self) -> float:
def coeff_reporter_writing(self) -> float:
return 1 if settings.TFJM_APP == "TFJM" else 2
@property
def average_defender_oral(self) -> float:
return self.avg(note.defender_oral for note in self.notes.all())
def average_reporter_oral(self) -> float:
return self.avg(note.reporter_oral for note in self.notes.all())
@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 - 0.25 * self.defender_penalties
coeff *= 1 - 0.25 * self.reporter_penalties
return coeff
@property
def average_defender(self) -> float:
return (self.coeff_defender_writing * self.average_defender_writing
+ self.coeff_defender_oral * self.average_defender_oral)
def average_reporter(self) -> float:
return (self.coeff_reporter_writing * self.average_reporter_writing
+ self.coeff_reporter_oral * self.average_reporter_oral)
@property
def average_opponent_writing(self) -> float:
@ -1827,8 +1827,8 @@ class Passage(models.Model):
@property
def averages(self):
yield self.average_defender_writing
yield self.average_defender_oral
yield self.average_reporter_writing
yield self.average_reporter_oral
yield self.average_opponent_writing
yield self.average_opponent_oral
yield self.average_reviewer_writing
@ -1838,7 +1838,7 @@ class Passage(models.Model):
yield self.average_observer_oral
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 \
else self.average_observer if participation == self.observer else 0
avg *= self.pool.coeff
@ -1849,9 +1849,9 @@ class Passage(models.Model):
return reverse_lazy("participation:passage_detail", args=(self.pk,))
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.")
.format(trigram=self.defender.team.trigram))
.format(trigram=self.reporter.team.trigram))
if self.opponent not in self.pool.participations.all():
raise ValidationError(_("Team {trigram} is not registered in the pool.")
.format(trigram=self.opponent.team.trigram))
@ -1864,8 +1864,8 @@ class Passage(models.Model):
return super().clean()
def __str__(self):
return _("Passage of {defender} for problem {problem}")\
.format(defender=self.defender.team, problem=self.solution_number)
return _("Passage of {reporter} for problem {problem}")\
.format(reporter=self.reporter.team, problem=self.solution_number)
class Meta:
verbose_name = _("passage")
@ -1984,11 +1984,11 @@ class WrittenReview(models.Model):
)
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,
type=self.get_type_display(),
problem=self.passage.solution_number,
defender=self.passage.defender.team.trigram,
reporter=self.passage.reporter.team.trigram,
)
class Meta:
@ -2013,14 +2013,14 @@ class Note(models.Model):
related_name="notes",
)
defender_writing = models.PositiveSmallIntegerField(
verbose_name=_("defender writing note"),
reporter_writing = models.PositiveSmallIntegerField(
verbose_name=_("reporter writing note"),
choices=[(i, i) for i in range(0, 21)],
default=0,
)
defender_oral = models.PositiveSmallIntegerField(
verbose_name=_("defender oral note"),
reporter_oral = models.PositiveSmallIntegerField(
verbose_name=_("reporter oral note"),
choices=[(i, i) for i in range(0, 21)],
default=0,
)
@ -2062,8 +2062,8 @@ class Note(models.Model):
)
def get_all(self):
yield self.defender_writing
yield self.defender_oral
yield self.reporter_writing
yield self.reporter_oral
yield self.opponent_writing
yield self.opponent_oral
yield self.reviewer_writing
@ -2072,10 +2072,10 @@ class Note(models.Model):
yield self.observer_writing
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):
self.defender_writing = defender_writing
self.defender_oral = defender_oral
self.reporter_writing = reporter_writing
self.reporter_oral = reporter_oral
self.opponent_writing = opponent_writing
self.opponent_oral = opponent_oral
self.reviewer_writing = reviewer_writing

View File

@ -108,13 +108,13 @@ class PoolTable(tables.Table):
class PassageTable(tables.Table):
# FIXME Ne pas afficher l'équipe observatrice si non nécessaire
defender = tables.LinkColumn(
reporter = tables.LinkColumn(
"participation:passage_detail",
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
def render_opponent(self, value):
@ -131,7 +131,7 @@ class PassageTable(tables.Table):
'class': 'table table-condensed table-striped text-center',
}
model = Passage
fields = ('defender', 'opponent', 'reviewer', 'observer', 'solution_number', )
fields = ('reporter', 'opponent', 'reviewer', 'observer', 'solution_number', )
class NoteTable(tables.Table):
@ -159,5 +159,5 @@ class NoteTable(tables.Table):
'class': 'table table-condensed table-striped text-center',
}
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',)

View File

@ -6,7 +6,7 @@
<form method="post">
<div id="form-content">
<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>
{% csrf_token %}
{{ form|crispy }}

View File

@ -25,8 +25,8 @@
<dt class="col-sm-3">{% trans "Position:" %}</dt>
<dd class="col-sm-9">{{ passage.position }}</dd>
<dt class="col-sm-3">{% trans "Defender:" %}</dt>
<dd class="col-sm-9"><a href="{{ passage.defender.get_absolute_url }}">{{ passage.defender.team }}</a></dd>
<dt class="col-sm-3">{% trans "Reporter:" %}</dt>
<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>
<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>
{% endif %}
<dt class="col-sm-3">{% trans "Defended solution:" %}</dt>
<dd class="col-sm-9"><a href="{{ passage.defended_solution.file.url }}">{{ passage.defended_solution }}</a></dd>
<dt class="col-sm-3">{% trans "Reported solution:" %}</dt>
<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>
<dd class="col-sm-9">{{ passage.defender_penalties }}</dd>
<dt class="col-sm-3">{% trans "Reporter penalties count:" %}</dt>
<dd class="col-sm-9">{{ passage.reporter_penalties }}</dd>
<dt class="col-sm-3">{% trans "Syntheses:" %}</dt>
<dd class="col-sm-9">
@ -79,19 +79,19 @@
<div class="card-body">
<dl class="row">
<dt class="col-sm-8">
{% trans "Average points for the defender writing" %}
({{ passage.defender.team.trigram }}) :
{% trans "Average points for the reporter writing" %}
({{ passage.reporter.team.trigram }}) :
</dt>
<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>
<dt class="col-sm-8">
{% trans "Average points for the defender oral" %}
({{ passage.defender.team.trigram }}) :
{% trans "Average points for the reporter oral" %}
({{ passage.reporter.team.trigram }}) :
</dt>
<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>
<dt class="col-sm-8">
@ -137,11 +137,11 @@
<dl class="row">
<dt class="col-sm-8">
{% trans "Defender points" %}
({{ passage.defender.team.trigram }}) :
{% trans "Reporter points" %}
({{ passage.reporter.team.trigram }}) :
</dt>
<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>
<dt class="col-sm-8">

View File

@ -46,10 +46,10 @@
</a>
</dd>
<dt class="col-sm-3">{% trans "Defended solutions:" %}</dt>
<dt class="col-sm-3">{% trans "Reported solutions:" %}</dt>
<dd class="col-sm-9">
{% 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 %}
<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" %}
@ -61,7 +61,7 @@
<ul class="list-group list-group-flush">
{% for passage in pool.passages.all %}
<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 %}
<a href="{{ review.file.url }}">{{ review.participation.team.trigram }} ({{ review.get_type_display }})</a>{% if not forloop.last %}, {% endif %}
{% empty %}
@ -70,7 +70,7 @@
</li>
{% endfor %}
</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" %}
</a>
</dd>

View File

@ -41,7 +41,7 @@
\begin{center}
\begin{itemize}
{% 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 %}
\end{itemize}
\end{center}
@ -50,7 +50,7 @@
%%%%%%%%%%%%%%%%%%%%%DEFENSEUR
\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
\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
\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
\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 %}
& \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(CoveredTableCell())
for i in range(pool_size):
defender_tc = TableCell(valuetype="string", stylename=title_style_left)
defender_tc.addElement(P(text=_("Defender")))
defender_tc.setAttribute('numbercolumnsspanned', "2")
header_role.addElement(defender_tc)
reporter_tc = TableCell(valuetype="string", stylename=title_style_left)
reporter_tc.addElement(P(text=_("Reporter")))
reporter_tc.setAttribute('numbercolumnsspanned', "2")
header_role.addElement(reporter_tc)
header_role.addElement(CoveredTableCell())
opponent_tc = TableCell(valuetype="string", stylename=title_style)
@ -1539,13 +1539,13 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
header_notes.addElement(CoveredTableCell())
for i in range(pool_size):
defender_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})"))
header_notes.addElement(defender_w_tc)
reporter_w_tc = TableCell(valuetype="string", stylename=title_style_botleft)
reporter_w_tc.addElement(P(text=f"{_('Writing')} (/{20 if settings.TFJM_APP == 'TFJM' else 10})"))
header_notes.addElement(reporter_w_tc)
defender_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})"))
header_notes.addElement(defender_o_tc)
reporter_o_tc = TableCell(valuetype="string", stylename=title_style_bot)
reporter_o_tc.addElement(P(text=f"{_('Oral')} (/{20 if settings.TFJM_APP == 'TFJM' else 10})"))
header_notes.addElement(reporter_o_tc)
opponent_w_tc = TableCell(valuetype="string", stylename=title_style_bot)
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(CoveredTableCell())
for passage in self.object.passages.all():
defender_w_tc = TableCell(valuetype="float", value=passage.coeff_defender_writing, stylename=style_left)
defender_w_tc.addElement(P(text=str(passage.coeff_defender_writing)))
coeff_row.addElement(defender_w_tc)
reporter_w_tc = TableCell(valuetype="float", value=passage.coeff_reporter_writing, stylename=style_left)
reporter_w_tc.addElement(P(text=str(passage.coeff_reporter_writing)))
coeff_row.addElement(reporter_w_tc)
defender_o_tc = TableCell(valuetype="float", value=passage.coeff_defender_oral, stylename=style)
defender_o_tc.addElement(P(text=str(passage.coeff_defender_oral)))
coeff_row.addElement(defender_o_tc)
reporter_o_tc = TableCell(valuetype="float", value=passage.coeff_reporter_oral, stylename=style)
reporter_o_tc.addElement(P(text=str(passage.coeff_reporter_oral)))
coeff_row.addElement(reporter_o_tc)
opponent_w_tc = TableCell(valuetype="float", value=passage.coeff_opponent_writing, stylename=style)
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()):
def_w_col = getcol(min_column + passage_width * i)
def_o_col = getcol(min_column + passage_width * i + 1)
defender_tc = TableCell(valuetype="float", value=passage.average_defender, stylename=style_botleft)
defender_tc.addElement(P(text=str(passage.average_defender)))
defender_tc.setAttribute('numbercolumnsspanned', "2")
defender_tc.setAttribute("formula", f"of:=[.{def_w_col}{max_row + 1}] * [.{def_w_col}{max_row + 2}]"
reporter_tc = TableCell(valuetype="float", value=passage.average_reporter, stylename=style_botleft)
reporter_tc.addElement(P(text=str(passage.average_reporter)))
reporter_tc.setAttribute('numbercolumnsspanned', "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}]")
subtotal_row.addElement(defender_tc)
subtotal_row.addElement(reporter_tc)
subtotal_row.addElement(CoveredTableCell())
opp_w_col = getcol(min_column + passage_width * i + 2)
@ -1748,7 +1748,7 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
team_tc = TableCell(valuetype="string",
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_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)}]")
team_row.addElement(problem_tc)
defender_pos = passage.position - 1
opponent_pos = self.object.passages.get(opponent=passage.defender).position - 1
reviewer_pos = self.object.passages.get(reviewer=passage.defender).position - 1
observer_pos = self.object.passages.get(observer=passage.defender).position - 1 \
reporter_pos = passage.position - 1
opponent_pos = self.object.passages.get(opponent=passage.reporter).position - 1
reviewer_pos = self.object.passages.get(reviewer=passage.reporter).position - 1
observer_pos = self.object.passages.get(observer=passage.reporter).position - 1 \
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)
score_tc.addElement(P(text=self.object.average(passage.defender)))
score_tc.addElement(P(text=self.object.average(passage.reporter)))
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 + reviewer_pos * passage_width + 4) + str(max_row + 3) # Reviewer
if has_observer:
@ -1778,9 +1778,9 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
team_row.addElement(score_tc)
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)
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}]; "
f"[.{score_col}${max_row + 6}]:"
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.pools_presided.filter(tournament=passage.pool.tournament).exists()) \
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 self.handle_no_permission()
@ -2005,8 +2005,8 @@ class PassageDetailView(LoginRequiredMixin, DetailView):
if 'notes' in context and not self.request.user.registration.is_admin:
context['notes']._sequence.remove('update')
context['notes'].columns['defender_writing'].column.verbose_name += f" ({passage.defender.team.trigram})"
context['notes'].columns['defender_oral'].column.verbose_name += f" ({passage.defender.team.trigram})"
context['notes'].columns['reporter_writing'].column.verbose_name += f" ({passage.reporter.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_oral'].column.verbose_name += f" ({passage.opponent.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):
form = super().get_form(form_class)
form.fields['defender_writing'].label += f" ({self.object.passage.defender.team.trigram})"
form.fields['defender_oral'].label += f" ({self.object.passage.defender.team.trigram})"
form.fields['reporter_writing'].label += f" ({self.object.passage.reporter.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_oral'].label += f" ({self.object.passage.opponent.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)
user = request.user
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(reviewer=user.registration.team.participation)
| Q(observer=user.registration.team.participation),
defender=solution.participation,
reporter=solution.participation,
solution_number=solution.problem)
else:
passage_participant_qs = Passage.objects.none()
@ -853,7 +853,7 @@ class SolutionView(LoginRequiredMixin, View):
or user.registration.is_volunteer
and Passage.objects.filter(Q(pool__juries=user.registration)
| Q(pool__tournament__in=user.registration.organized_tournaments.all()),
defender=solution.participation,
reporter=solution.participation,
solution_number=solution.problem).exists()
or user.registration.participates and user.registration.team
and (solution.participation.team == user.registration.team or
@ -881,20 +881,20 @@ class WrittenReviewView(LoginRequiredMixin, View):
path = f"media/reviews/{filename}"
if not os.path.exists(path):
raise Http404
reviews = WrittenReview.objects.get(file__endswith=filename)
review = WrittenReview.objects.get(file__endswith=filename)
user = request.user
if not (user.registration.is_admin or user.registration.is_volunteer
and (user.registration in reviews.passage.pool.juries.all()
or user.registration in reviews.passage.pool.tournament.organizers.all()
or user.registration.pools_presided.filter(tournament=reviews.passage.pool.tournament).exists())
or user.registration.participates and user.registration.team == reviews.participation.team):
and (user.registration in review.passage.pool.juries.all()
or user.registration in review.passage.pool.tournament.organizers.all()
or user.registration.pools_presided.filter(tournament=review.passage.pool.tournament).exists())
or user.registration.participates and user.registration.team == review.participation.team):
raise PermissionDenied
# Guess mime type of the file
mime = Magic(mime=True)
mime_type = mime.from_file(path)
ext = mime_type.split("/")[1].replace("jpeg", "jpg")
# 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)

View File

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