mirror of
https://gitlab.com/animath/si/plateforme.git
synced 2025-06-22 07:18:25 +02:00
Add observer team
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
@ -51,7 +51,7 @@ class PassageInline(admin.TabularInline):
|
||||
model = Passage
|
||||
extra = 0
|
||||
ordering = ('position',)
|
||||
autocomplete_fields = ('defender', 'opponent', 'reviewer',)
|
||||
autocomplete_fields = ('defender', 'opponent', 'reviewer', 'observer',)
|
||||
show_change_link = True
|
||||
|
||||
|
||||
@ -114,11 +114,11 @@ class PoolAdmin(admin.ModelAdmin):
|
||||
@admin.register(Passage)
|
||||
class PassageAdmin(admin.ModelAdmin):
|
||||
list_display = ('__str__', 'defender_trigram', 'solution_number', 'opponent_trigram', 'reviewer_trigram',
|
||||
'pool_abbr', 'position', 'tournament')
|
||||
'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',)
|
||||
autocomplete_fields = ('pool', 'defender', 'opponent', 'reviewer', 'observer',)
|
||||
inlines = (NoteInline,)
|
||||
|
||||
@admin.display(description=_("defender"), ordering='defender__team__trigram')
|
||||
@ -133,6 +133,10 @@ class PassageAdmin(admin.ModelAdmin):
|
||||
def reviewer_trigram(self, record: Passage):
|
||||
return record.reviewer.team.trigram
|
||||
|
||||
@admin.display(description=_("observer"), ordering='observer__team__trigram')
|
||||
def observer_trigram(self, record: Passage):
|
||||
return record.observer.team.trigram
|
||||
|
||||
@admin.display(description=_("pool"), ordering='pool__letter')
|
||||
def pool_abbr(self, record):
|
||||
return f"{record.pool.short_name}"
|
||||
@ -145,10 +149,11 @@ class PassageAdmin(admin.ModelAdmin):
|
||||
@admin.register(Note)
|
||||
class NoteAdmin(admin.ModelAdmin):
|
||||
list_display = ('passage', 'pool', 'jury', 'defender_writing', 'defender_oral',
|
||||
'opponent_writing', 'opponent_oral', 'reviewer_writing', 'reviewer_oral',)
|
||||
'opponent_writing', 'opponent_oral', 'reviewer_writing', 'reviewer_oral',
|
||||
'observer_writing', 'observer_oral',)
|
||||
list_filter = ('passage__pool__letter', 'passage__solution_number', 'jury',
|
||||
'defender_writing', 'defender_oral', 'opponent_writing', 'opponent_oral',
|
||||
'reviewer_writing', 'reviewer_oral')
|
||||
'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_oral')
|
||||
search_fields = ('jury__user__last_name', 'jury__user__first_name', 'passage__defender__team__trigram',)
|
||||
autocomplete_fields = ('jury', 'passage',)
|
||||
|
||||
|
@ -13,7 +13,7 @@ class NoteViewSet(ModelViewSet):
|
||||
serializer_class = NoteSerializer
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
filterset_fields = ['jury', 'passage', 'defender_writing', 'defender_oral', 'opponent_writing',
|
||||
'opponent_oral', 'reviewer_writing', 'reviewer_oral', ]
|
||||
'opponent_oral', 'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_oral', ]
|
||||
|
||||
|
||||
class ParticipationViewSet(ModelViewSet):
|
||||
@ -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', 'pool_tournament', ]
|
||||
filterset_fields = ['pool', 'solution_number', 'defender', 'opponent', 'reviewer', 'observer', 'pool_tournament', ]
|
||||
|
||||
|
||||
class PoolViewSet(ModelViewSet):
|
||||
|
@ -355,7 +355,7 @@ class PassageForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Passage
|
||||
fields = ('position', 'solution_number', 'defender', 'opponent', 'reviewer', 'defender_penalties',)
|
||||
fields = ('position', 'solution_number', 'defender', 'opponent', 'reviewer', 'opponent', 'defender_penalties',)
|
||||
|
||||
|
||||
class SynthesisForm(forms.ModelForm):
|
||||
@ -386,4 +386,4 @@ class NoteForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Note
|
||||
fields = ('defender_writing', 'defender_oral', 'opponent_writing',
|
||||
'opponent_oral', 'reviewer_writing', 'reviewer_oral', )
|
||||
'opponent_oral', 'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_oral', )
|
||||
|
@ -0,0 +1,86 @@
|
||||
# Generated by Django 5.0.6 on 2024-07-05 09:47
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("participation", "0018_rename_reporter_to_reviewer"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="note",
|
||||
name="observer_oral",
|
||||
field=models.PositiveSmallIntegerField(
|
||||
choices=[
|
||||
(-10, -10),
|
||||
(-9, -9),
|
||||
(-8, -8),
|
||||
(-7, -7),
|
||||
(-6, -6),
|
||||
(-5, -5),
|
||||
(-4, -4),
|
||||
(-3, -3),
|
||||
(-2, -2),
|
||||
(-1, -1),
|
||||
(0, 0),
|
||||
(1, 1),
|
||||
(2, 2),
|
||||
(3, 3),
|
||||
(4, 4),
|
||||
(5, 5),
|
||||
(6, 6),
|
||||
(7, 7),
|
||||
(8, 8),
|
||||
(9, 9),
|
||||
(10, 10),
|
||||
],
|
||||
default=0,
|
||||
verbose_name="observer oral note",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="note",
|
||||
name="observer_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),
|
||||
],
|
||||
default=0,
|
||||
verbose_name="observer writing note",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="passage",
|
||||
name="observer",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
default=None,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="+",
|
||||
to="participation.participation",
|
||||
verbose_name="observer",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="synthesis",
|
||||
name="type",
|
||||
field=models.PositiveSmallIntegerField(
|
||||
choices=[(1, "opponent"), (2, "reviewer"), (3, "observer")]
|
||||
),
|
||||
),
|
||||
]
|
@ -859,6 +859,8 @@ class Participation(models.Model):
|
||||
defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, defender=self)
|
||||
opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=1, opponent=self)
|
||||
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 "
|
||||
"<a href='{draw_url}'>this page</a>.</p>"
|
||||
@ -887,12 +889,26 @@ class Participation(models.Model):
|
||||
solution_url=solution_url,
|
||||
problem=reviewer_passage.solution_number, passage_url=passage_url)
|
||||
|
||||
if observer_passage:
|
||||
observer_text = _("<p>You will observe the solution of the team {observer} on the "
|
||||
"<a href='{solution_url}'>problem {problem}. "
|
||||
"You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
|
||||
solution_url = observer_passage.defended_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,
|
||||
solution_url=solution_url,
|
||||
problem=observer_passage.solution_number, passage_url=passage_url)
|
||||
else:
|
||||
observer_content = ""
|
||||
|
||||
syntheses_template_begin = f"{settings.STATIC_URL}Fiche_synthèse."
|
||||
syntheses_templates = " — ".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
|
||||
for ext in ["pdf", "tex", "odt", "docx"])
|
||||
syntheses_templates_content = f"<p>{_('Templates:')} {syntheses_templates}</p>"
|
||||
|
||||
content = defender_content + opponent_content + reviewer_content + syntheses_templates_content
|
||||
content = defender_content + opponent_content + reviewer_content + observer_content \
|
||||
+ syntheses_templates_content
|
||||
informations.append({
|
||||
'title': _("First round"),
|
||||
'type': "info",
|
||||
@ -903,6 +919,8 @@ class Participation(models.Model):
|
||||
defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, defender=self)
|
||||
opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=2, opponent=self)
|
||||
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 "
|
||||
"<a href='{solution_url}'>your solution of the problem {problem}</a>.</p>")
|
||||
@ -929,12 +947,26 @@ class Participation(models.Model):
|
||||
solution_url=solution_url,
|
||||
problem=reviewer_passage.solution_number, passage_url=passage_url)
|
||||
|
||||
if observer_passage:
|
||||
observer_text = _("<p>You will observe the solution of the team {observer} on the "
|
||||
"<a href='{solution_url}'>problem {problem}. "
|
||||
"You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
|
||||
solution_url = observer_passage.defended_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,
|
||||
solution_url=solution_url,
|
||||
problem=observer_passage.solution_number, passage_url=passage_url)
|
||||
else:
|
||||
observer_content = ""
|
||||
|
||||
syntheses_template_begin = f"{settings.STATIC_URL}Fiche_synthèse."
|
||||
syntheses_templates = " — ".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
|
||||
for ext in ["pdf", "tex", "odt", "docx"])
|
||||
syntheses_templates_content = f"<p>{_('Templates:')} {syntheses_templates}</p>"
|
||||
|
||||
content = defender_content + opponent_content + reviewer_content + syntheses_templates_content
|
||||
content = defender_content + opponent_content + reviewer_content + observer_content \
|
||||
+ syntheses_templates_content
|
||||
informations.append({
|
||||
'title': _("Second round"),
|
||||
'type': "info",
|
||||
@ -946,6 +978,8 @@ class Participation(models.Model):
|
||||
defender_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, defender=self)
|
||||
opponent_passage = Passage.objects.get(pool__tournament=self.tournament, pool__round=3, opponent=self)
|
||||
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 "
|
||||
"<a href='{solution_url}'>your solution of the problem {problem}</a>.</p>")
|
||||
@ -972,12 +1006,26 @@ class Participation(models.Model):
|
||||
solution_url=solution_url,
|
||||
problem=reviewer_passage.solution_number, passage_url=passage_url)
|
||||
|
||||
if observer_passage:
|
||||
observer_text = _("<p>You will observe the solution of the team {observer} on the "
|
||||
"<a href='{solution_url}'>problem {problem}. "
|
||||
"You can upload your synthesis sheet on <a href='{passage_url}'>this page</a>.</p>")
|
||||
solution_url = observer_passage.defended_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,
|
||||
solution_url=solution_url,
|
||||
problem=observer_passage.solution_number, passage_url=passage_url)
|
||||
else:
|
||||
observer_content = ""
|
||||
|
||||
syntheses_template_begin = f"{settings.STATIC_URL}Fiche_synthèse."
|
||||
syntheses_templates = " — ".join(f"<a href='{syntheses_template_begin}{ext}'>{ext.upper()}</a>"
|
||||
for ext in ["pdf", "tex", "odt", "docx"])
|
||||
syntheses_templates_content = f"<p>{_('Templates:')} {syntheses_templates}</p>"
|
||||
|
||||
content = defender_content + opponent_content + reviewer_content + syntheses_templates_content
|
||||
content = defender_content + opponent_content + reviewer_content + observer_content \
|
||||
+ syntheses_templates_content
|
||||
informations.append({
|
||||
'title': _("Second round"),
|
||||
'type': "info",
|
||||
@ -1560,6 +1608,16 @@ class Passage(models.Model):
|
||||
related_name="+",
|
||||
)
|
||||
|
||||
observer = models.ForeignKey(
|
||||
Participation,
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name=_("observer"),
|
||||
related_name="+",
|
||||
null=True,
|
||||
blank=True,
|
||||
default=None,
|
||||
)
|
||||
|
||||
defender_penalties = models.PositiveSmallIntegerField(
|
||||
verbose_name=_("penalties"),
|
||||
default=0,
|
||||
@ -1588,7 +1646,10 @@ class Passage(models.Model):
|
||||
|
||||
@property
|
||||
def average_defender(self) -> float:
|
||||
return self.average_defender_writing + (1.6 - 0.4 * self.defender_penalties) * self.average_defender_oral
|
||||
writing_coeff = 1 if settings.TFJM_APP == "TFJM" else 2
|
||||
oral_coeff = 1.6 if settings.TFJM_APP == "TFJM" else 3
|
||||
oral_coeff *= 1 - 0.25 * self.defender_penalties
|
||||
return writing_coeff * self.average_defender_writing + oral_coeff * self.average_defender_oral
|
||||
|
||||
@property
|
||||
def average_opponent_writing(self) -> float:
|
||||
@ -1600,7 +1661,9 @@ class Passage(models.Model):
|
||||
|
||||
@property
|
||||
def average_opponent(self) -> float:
|
||||
return 0.9 * self.average_opponent_writing + 2 * self.average_opponent_oral
|
||||
writing_coeff = 0.9 if not self.observer else 0.6
|
||||
oral_coeff = 2
|
||||
return writing_coeff * self.average_opponent_writing + oral_coeff * self.average_opponent_oral
|
||||
|
||||
@property
|
||||
def average_reviewer_writing(self) -> float:
|
||||
@ -1612,7 +1675,21 @@ class Passage(models.Model):
|
||||
|
||||
@property
|
||||
def average_reviewer(self) -> float:
|
||||
return 0.9 * self.average_reviewer_writing + self.average_reviewer_oral
|
||||
writing_coeff = 0.9 if not self.observer else 0.6
|
||||
oral_coeff = 1 if settings.TFJM_APP == "TFJM" else 1.2
|
||||
return writing_coeff * self.average_reviewer_writing + oral_coeff * self.average_reviewer_oral
|
||||
|
||||
@property
|
||||
def average_observer_writing(self) -> float:
|
||||
return self.avg(note.observer_writing for note in self.notes.all())
|
||||
|
||||
@property
|
||||
def average_observer_oral(self) -> float:
|
||||
return self.avg(note.observer_oral for note in self.notes.all())
|
||||
|
||||
@property
|
||||
def average_observer(self) -> float:
|
||||
return 0.6 * self.average_observer_writing + 0.5 * self.average_observer_oral
|
||||
|
||||
@property
|
||||
def averages(self):
|
||||
@ -1622,10 +1699,19 @@ class Passage(models.Model):
|
||||
yield self.average_opponent_oral
|
||||
yield self.average_reviewer_writing
|
||||
yield self.average_reviewer_oral
|
||||
if self.observer:
|
||||
yield self.average_observer_writing
|
||||
yield self.average_observer_oral
|
||||
|
||||
def average(self, participation):
|
||||
return self.average_defender if participation == self.defender else self.average_opponent \
|
||||
if participation == self.opponent else self.average_reviewer if participation == self.reviewer else 0
|
||||
avg = self.average_defender if participation == self.defender 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
|
||||
|
||||
if self.pool.round == 3 and settings.TFJM_APP == "ETEAM":
|
||||
avg *= math.pi - 2
|
||||
|
||||
return avg
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse_lazy("participation:passage_detail", args=(self.pk,))
|
||||
@ -1640,6 +1726,9 @@ class Passage(models.Model):
|
||||
if self.reviewer not in self.pool.participations.all():
|
||||
raise ValidationError(_("Team {trigram} is not registered in the pool.")
|
||||
.format(trigram=self.reviewer.team.trigram))
|
||||
if self.observer and self.observer not in self.pool.participations.all():
|
||||
raise ValidationError(_("Team {trigram} is not registered in the pool.")
|
||||
.format(trigram=self.observer.team.trigram))
|
||||
return super().clean()
|
||||
|
||||
def __str__(self):
|
||||
@ -1748,6 +1837,7 @@ class Synthesis(models.Model):
|
||||
choices=[
|
||||
(1, _("opponent"), ),
|
||||
(2, _("reviewer"), ),
|
||||
(3, _("observer"), ),
|
||||
]
|
||||
)
|
||||
|
||||
@ -1823,6 +1913,18 @@ class Note(models.Model):
|
||||
default=0,
|
||||
)
|
||||
|
||||
observer_writing = models.PositiveSmallIntegerField(
|
||||
verbose_name=_("observer writing note"),
|
||||
choices=[(i, i) for i in range(0, 11)],
|
||||
default=0,
|
||||
)
|
||||
|
||||
observer_oral = models.PositiveSmallIntegerField(
|
||||
verbose_name=_("observer oral note"),
|
||||
choices=[(i, i) for i in range(-10, 11)],
|
||||
default=0,
|
||||
)
|
||||
|
||||
def get_all(self):
|
||||
yield self.defender_writing
|
||||
yield self.defender_oral
|
||||
@ -1830,15 +1932,20 @@ class Note(models.Model):
|
||||
yield self.opponent_oral
|
||||
yield self.reviewer_writing
|
||||
yield self.reviewer_oral
|
||||
if self.passage.observer:
|
||||
yield self.observer_writing
|
||||
yield self.observer_oral
|
||||
|
||||
def set_all(self, defender_writing: int, defender_oral: int, opponent_writing: int, opponent_oral: int,
|
||||
reviewer_writing: int, reviewer_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.opponent_writing = opponent_writing
|
||||
self.opponent_oral = opponent_oral
|
||||
self.reviewer_writing = reviewer_writing
|
||||
self.reviewer_oral = reviewer_oral
|
||||
self.observer_writing = observer_writing
|
||||
self.observer_oral = observer_oral
|
||||
|
||||
def update_spreadsheet(self):
|
||||
if not self.has_any_note():
|
||||
|
@ -106,6 +106,8 @@ class PoolTable(tables.Table):
|
||||
|
||||
|
||||
class PassageTable(tables.Table):
|
||||
# FIXME Ne pas afficher l'équipe observatrice si non nécessaire
|
||||
|
||||
defender = tables.LinkColumn(
|
||||
"participation:passage_detail",
|
||||
args=[tables.A("id")],
|
||||
@ -121,12 +123,15 @@ class PassageTable(tables.Table):
|
||||
def render_reviewer(self, value):
|
||||
return value.team.trigram
|
||||
|
||||
def render_observer(self, value):
|
||||
return value.team.trigram
|
||||
|
||||
class Meta:
|
||||
attrs = {
|
||||
'class': 'table table-condensed table-striped text-center',
|
||||
}
|
||||
model = Passage
|
||||
fields = ('defender', 'opponent', 'reviewer', 'solution_number', )
|
||||
fields = ('defender', 'opponent', 'reviewer', 'observer', 'solution_number', )
|
||||
|
||||
|
||||
class NoteTable(tables.Table):
|
||||
@ -155,4 +160,4 @@ class NoteTable(tables.Table):
|
||||
}
|
||||
model = Note
|
||||
fields = ('jury', 'defender_writing', 'defender_oral', 'opponent_writing', 'opponent_oral',
|
||||
'reviewer_writing', 'reviewer_oral', 'update',)
|
||||
'reviewer_writing', 'reviewer_oral', 'observer_writing', 'observer_oral', 'update',)
|
||||
|
@ -31,9 +31,14 @@
|
||||
<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>
|
||||
|
||||
<dt class="col-sm-3">{% trans "reviewer:" %}</dt>
|
||||
<dt class="col-sm-3">{% trans "Reviewer:" %}</dt>
|
||||
<dd class="col-sm-9"><a href="{{ passage.reviewer.get_absolute_url }}">{{ passage.reviewer.team }}</a></dd>
|
||||
|
||||
{% if passage.observer %}
|
||||
<dt class="col-sm-3">{% trans "Observer:" %}</dt>
|
||||
<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>
|
||||
|
||||
@ -108,6 +113,20 @@
|
||||
({{ passage.reviewer.team.trigram }}) :
|
||||
</dt>
|
||||
<dd class="col-sm-4">{{ passage.average_reviewer_oral|floatformat }}/10</dd>
|
||||
|
||||
{% if passage.observer %}
|
||||
<dt class="col-sm-8">
|
||||
{% trans "Average points for the observer writing" %}
|
||||
({{ passage.observer.team.trigram }}) :
|
||||
</dt>
|
||||
<dd class="col-sm-4">{{ passage.average_observer_writing|floatformat }}/10</dd>
|
||||
|
||||
<dt class="col-sm-8">
|
||||
{% trans "Average points for the observer oral" %}
|
||||
({{ passage.observer.team.trigram }}) :
|
||||
</dt>
|
||||
<dd class="col-sm-4">{{ passage.average_observer_oral|floatformat }}/10</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
|
||||
<hr>
|
||||
@ -130,6 +149,15 @@
|
||||
({{ passage.reviewer.team.trigram }}) :
|
||||
</dt>
|
||||
<dd class="col-sm-4">{{ passage.average_reviewer|floatformat }}/19</dd>
|
||||
|
||||
{% if passage.observer %}
|
||||
<dt class="col-sm-8">
|
||||
{% trans "observer points" %}
|
||||
({{ passage.observer.team.trigram }}) :
|
||||
</dt>
|
||||
|
||||
<dd class="col-sm-4">{{ passage.average_observer|floatformat }}/10</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1992,7 +1992,8 @@ class SynthesisUploadView(LoginRequiredMixin, FormView):
|
||||
self.participation = self.request.user.registration.team.participation
|
||||
self.passage = qs.get()
|
||||
|
||||
if self.participation not in [self.passage.opponent, self.passage.reviewer]:
|
||||
if self.participation \
|
||||
and self.participation not in [self.passage.opponent, self.passage.reviewer, self.passage.observer]:
|
||||
return self.handle_no_permission()
|
||||
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
@ -2004,7 +2005,8 @@ class SynthesisUploadView(LoginRequiredMixin, FormView):
|
||||
It is discriminating whenever the team is selected for the final tournament or not.
|
||||
"""
|
||||
form_syn = form.instance
|
||||
form_syn.type = 1 if self.participation == self.passage.opponent else 2
|
||||
form_syn.type = 1 if self.participation == self.passage.opponent \
|
||||
else 2 if self.participation == self.passage.reviewer else 3
|
||||
syn_qs = Synthesis.objects.filter(participation=self.participation,
|
||||
passage=self.passage,
|
||||
type=form_syn.type).all()
|
||||
@ -2052,6 +2054,8 @@ class NoteUpdateView(VolunteerMixin, UpdateView):
|
||||
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_oral'].label += f" ({self.object.passage.reviewer.team.trigram})"
|
||||
form.fields['observer_writing'].label += f" ({self.object.passage.observer.team.trigram})"
|
||||
form.fields['observer_oral'].label += f" ({self.object.passage.observer.team.trigram})"
|
||||
return form
|
||||
|
||||
def form_valid(self, form):
|
||||
|
Reference in New Issue
Block a user