mirror of
				https://gitlab.com/animath/si/plateforme.git
				synced 2025-11-04 16:02:26 +01:00 
			
		
		
		
	Update ODS note sheets
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
		@@ -17,6 +17,7 @@ from django.db.models.functions import Concat
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from pypdf import PdfReader
 | 
			
		||||
 | 
			
		||||
from registration.models import VolunteerRegistration
 | 
			
		||||
from .models import Note, Participation, Passage, Pool, Solution, Synthesis, Team, Tournament
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -291,7 +292,7 @@ class UploadNotesForm(forms.Form):
 | 
			
		||||
 | 
			
		||||
    def process(self, csvfile: Iterable[str], cleaned_data: dict):
 | 
			
		||||
        parsed_notes = {}
 | 
			
		||||
        valid_lengths = [1 + 6 * 3, 1 + 7 * 4, 1 + 6 * 5]  # Per pool sizes
 | 
			
		||||
        valid_lengths = [2 + 6 * 3, 2 + 7 * 4, 2 + 6 * 5]  # Per pool sizes
 | 
			
		||||
        pool_size = 0
 | 
			
		||||
        line_length = 0
 | 
			
		||||
        for line in csvfile:
 | 
			
		||||
@@ -310,29 +311,24 @@ class UploadNotesForm(forms.Form):
 | 
			
		||||
            name = line[0]
 | 
			
		||||
            if name.lower() in ["rôle", "juré", "moyenne", "coefficient", "sous-total", "équipe", "equipe"]:
 | 
			
		||||
                continue
 | 
			
		||||
            notes = line[1:line_length]
 | 
			
		||||
            notes = line[2:line_length]
 | 
			
		||||
            if not all(s.isnumeric() or s[0] == '-' and s[1:].isnumeric() for s in notes):
 | 
			
		||||
                continue
 | 
			
		||||
            notes = list(map(int, notes))
 | 
			
		||||
 | 
			
		||||
            max_notes = pool_size * ([20, 16, 9, 10, 9, 10] + ([4] if pool_size == 4 else []))
 | 
			
		||||
            max_notes = pool_size * ([20, 20, 10, 10, 10, 10] + ([4] if pool_size == 4 else []))
 | 
			
		||||
            for n, max_n in zip(notes, max_notes):
 | 
			
		||||
                if n > max_n:
 | 
			
		||||
                    self.add_error('file',
 | 
			
		||||
                                   _("The following note is higher of the maximum expected value:")
 | 
			
		||||
                                   + str(n) + " > " + str(max_n))
 | 
			
		||||
 | 
			
		||||
            # Search by "{first_name} {last_name}"
 | 
			
		||||
            jury = User.objects.annotate(full_name=Concat('first_name', Value(' '), 'last_name',
 | 
			
		||||
                                                          output_field=CharField())) \
 | 
			
		||||
                .filter(full_name=name.replace('’', '\''), registration__volunteerregistration__isnull=False)
 | 
			
		||||
            # Search by volunteer id
 | 
			
		||||
            jury = VolunteerRegistration.objects.filter(pk=line[1])
 | 
			
		||||
            if jury.count() != 1:
 | 
			
		||||
                self.add_error('file', _("The following user was not found:") + " " + name)
 | 
			
		||||
                continue
 | 
			
		||||
                raise ValidationError({'file': _("The following user was not found:") + " " + name})
 | 
			
		||||
            jury = jury.get()
 | 
			
		||||
 | 
			
		||||
            vr = jury.registration
 | 
			
		||||
            parsed_notes[vr] = notes
 | 
			
		||||
            parsed_notes[jury] = notes
 | 
			
		||||
 | 
			
		||||
        cleaned_data['parsed_notes'] = parsed_notes
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -680,7 +680,7 @@ class Passage(models.Model):
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def average_defender(self) -> float:
 | 
			
		||||
        return self.average_defender_writing + (2 - 0.5 * self.defender_penalties) * self.average_defender_oral
 | 
			
		||||
        return self.average_defender_writing + (1.6 - 0.4 * self.defender_penalties) * self.average_defender_oral
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def average_opponent_writing(self) -> float:
 | 
			
		||||
@@ -692,7 +692,7 @@ class Passage(models.Model):
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def average_opponent(self) -> float:
 | 
			
		||||
        return self.average_opponent_writing + 2 * self.average_opponent_oral
 | 
			
		||||
        return 0.9 * self.average_opponent_writing + 2 * self.average_opponent_oral
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def average_reporter_writing(self) -> float:
 | 
			
		||||
@@ -704,7 +704,7 @@ class Passage(models.Model):
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def average_reporter(self) -> float:
 | 
			
		||||
        return self.average_reporter_writing + self.average_reporter_oral
 | 
			
		||||
        return 0.9 * self.average_reporter_writing + self.average_reporter_oral
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def average_observer(self) -> float:
 | 
			
		||||
 
 | 
			
		||||
@@ -1176,6 +1176,10 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
 | 
			
		||||
        first_col_style.addElement(TableColumnProperties(columnwidth="9cm", breakbefore="auto"))
 | 
			
		||||
        doc.automaticstyles.addElement(first_col_style)
 | 
			
		||||
 | 
			
		||||
        jury_id_style = Style(name="co_jury_id", family="table-column")
 | 
			
		||||
        jury_id_style.addElement(TableColumnProperties(columnwidth="1cm", breakbefore="auto"))
 | 
			
		||||
        doc.automaticstyles.addElement(jury_id_style)
 | 
			
		||||
 | 
			
		||||
        col_style = Style(name="co2", family="table-column")
 | 
			
		||||
        col_style.addElement(TableColumnProperties(columnwidth="2.6cm", breakbefore="auto"))
 | 
			
		||||
        doc.automaticstyles.addElement(col_style)
 | 
			
		||||
@@ -1188,6 +1192,7 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
 | 
			
		||||
        doc.spreadsheet.addElement(table)
 | 
			
		||||
 | 
			
		||||
        table.addElement(TableColumn(stylename=first_col_style))
 | 
			
		||||
        table.addElement(TableColumn(stylename=jury_id_style))
 | 
			
		||||
 | 
			
		||||
        for i in range(line_length):
 | 
			
		||||
            table.addElement(TableColumn(stylename=obs_col_style if pool_size == 4
 | 
			
		||||
@@ -1198,7 +1203,9 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
 | 
			
		||||
        table.addElement(header_pb)
 | 
			
		||||
        problems_tc = TableCell(valuetype="string", stylename=title_style_topleft)
 | 
			
		||||
        problems_tc.addElement(P(text="Problème"))
 | 
			
		||||
        problems_tc.setAttribute('numbercolumnsspanned', "2")
 | 
			
		||||
        header_pb.addElement(problems_tc)
 | 
			
		||||
        header_pb.addElement(CoveredTableCell())
 | 
			
		||||
        for passage in self.object.passages.all():
 | 
			
		||||
            tc = TableCell(valuetype="string", stylename=title_style_topleftright)
 | 
			
		||||
            tc.addElement(P(text=f"Problème {passage.solution_number}"))
 | 
			
		||||
@@ -1212,7 +1219,9 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
 | 
			
		||||
        table.addElement(header_role)
 | 
			
		||||
        role_tc = TableCell(valuetype="string", stylename=title_style_left)
 | 
			
		||||
        role_tc.addElement(P(text="Rôle"))
 | 
			
		||||
        role_tc.setAttribute('numbercolumnsspanned', "2")
 | 
			
		||||
        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="Défenseur⋅se"))
 | 
			
		||||
@@ -1243,7 +1252,9 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
 | 
			
		||||
        table.addElement(header_notes)
 | 
			
		||||
        jury_tc = TableCell(valuetype="string", value="Juré⋅e", stylename=title_style_botleft)
 | 
			
		||||
        jury_tc.addElement(P(text="Juré⋅e"))
 | 
			
		||||
        jury_tc.setAttribute('numbercolumnsspanned', "2")
 | 
			
		||||
        header_notes.addElement(jury_tc)
 | 
			
		||||
        header_notes.addElement(CoveredTableCell())
 | 
			
		||||
 | 
			
		||||
        for i in range(pool_size):
 | 
			
		||||
            defender_w_tc = TableCell(valuetype="string", stylename=title_style_botleft)
 | 
			
		||||
@@ -1251,11 +1262,11 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
 | 
			
		||||
            header_notes.addElement(defender_w_tc)
 | 
			
		||||
 | 
			
		||||
            defender_o_tc = TableCell(valuetype="string", stylename=title_style_bot)
 | 
			
		||||
            defender_o_tc.addElement(P(text="Oral (/16)"))
 | 
			
		||||
            defender_o_tc.addElement(P(text="Oral (/20)"))
 | 
			
		||||
            header_notes.addElement(defender_o_tc)
 | 
			
		||||
 | 
			
		||||
            opponent_w_tc = TableCell(valuetype="string", stylename=title_style_bot)
 | 
			
		||||
            opponent_w_tc.addElement(P(text="Écrit (/9)"))
 | 
			
		||||
            opponent_w_tc.addElement(P(text="Écrit (/10)"))
 | 
			
		||||
            header_notes.addElement(opponent_w_tc)
 | 
			
		||||
 | 
			
		||||
            opponent_o_tc = TableCell(valuetype="string", stylename=title_style_bot)
 | 
			
		||||
@@ -1263,7 +1274,7 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
 | 
			
		||||
            header_notes.addElement(opponent_o_tc)
 | 
			
		||||
 | 
			
		||||
            reporter_w_tc = TableCell(valuetype="string", stylename=title_style_bot)
 | 
			
		||||
            reporter_w_tc.addElement(P(text="Écrit (/9)"))
 | 
			
		||||
            reporter_w_tc.addElement(P(text="Écrit (/10)"))
 | 
			
		||||
            header_notes.addElement(reporter_w_tc)
 | 
			
		||||
 | 
			
		||||
            reporter_o_tc = TableCell(valuetype="string",
 | 
			
		||||
@@ -1285,6 +1296,9 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
 | 
			
		||||
            name_tc = TableCell(valuetype="string", stylename=style_leftright)
 | 
			
		||||
            name_tc.addElement(P(text=f"{jury.user.first_name} {jury.user.last_name}"))
 | 
			
		||||
            jury_row.addElement(name_tc)
 | 
			
		||||
            jury_id_tc = TableCell(valuetype="float", value=jury.pk, stylename=style_leftright)
 | 
			
		||||
            jury_id_tc.addElement(P(text=str(jury.pk)))
 | 
			
		||||
            jury_row.addElement(jury_id_tc)
 | 
			
		||||
 | 
			
		||||
            for passage in self.object.passages.all():
 | 
			
		||||
                notes = Note.objects.get(jury=jury, passage=passage)
 | 
			
		||||
@@ -1297,14 +1311,16 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
 | 
			
		||||
        jury_size = self.object.juries.count()
 | 
			
		||||
        min_row = 4
 | 
			
		||||
        max_row = 4 + jury_size - 1
 | 
			
		||||
        min_column = 2
 | 
			
		||||
        min_column = 3
 | 
			
		||||
 | 
			
		||||
        # Add line for averages
 | 
			
		||||
        average_row = TableRow()
 | 
			
		||||
        table.addElement(average_row)
 | 
			
		||||
        average_tc = TableCell(valuetype="string", stylename=title_style_topleftright)
 | 
			
		||||
        average_tc.addElement(P(text="Moyenne"))
 | 
			
		||||
        average_tc.setAttribute('numbercolumnsspanned', "2")
 | 
			
		||||
        average_row.addElement(average_tc)
 | 
			
		||||
        average_row.addElement(CoveredTableCell())
 | 
			
		||||
        for i, passage in enumerate(self.object.passages.all()):
 | 
			
		||||
            for j, note in enumerate(passage.averages):
 | 
			
		||||
                tc = TableCell(valuetype="float", value=note,
 | 
			
		||||
@@ -1321,17 +1337,19 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
 | 
			
		||||
        table.addElement(coeff_row)
 | 
			
		||||
        coeff_tc = TableCell(valuetype="string", stylename=title_style_leftright)
 | 
			
		||||
        coeff_tc.addElement(P(text="Coefficient"))
 | 
			
		||||
        coeff_tc.setAttribute('numbercolumnsspanned', "2")
 | 
			
		||||
        coeff_row.addElement(coeff_tc)
 | 
			
		||||
        coeff_row.addElement(CoveredTableCell())
 | 
			
		||||
        for passage in self.object.passages.all():
 | 
			
		||||
            defender_w_tc = TableCell(valuetype="float", value=1, stylename=style_left)
 | 
			
		||||
            defender_w_tc.addElement(P(text="1"))
 | 
			
		||||
            coeff_row.addElement(defender_w_tc)
 | 
			
		||||
 | 
			
		||||
            defender_o_tc = TableCell(valuetype="float", value=2 - 0.5 * passage.defender_penalties, stylename=style)
 | 
			
		||||
            defender_o_tc.addElement(P(text=str(2 - 0.5 * passage.defender_penalties)))
 | 
			
		||||
            defender_o_tc = TableCell(valuetype="float", value=1.6 - 0.4 * passage.defender_penalties, stylename=style)
 | 
			
		||||
            defender_o_tc.addElement(P(text=str(2 - 0.4 * passage.defender_penalties)))
 | 
			
		||||
            coeff_row.addElement(defender_o_tc)
 | 
			
		||||
 | 
			
		||||
            opponent_w_tc = TableCell(valuetype="float", value=1, stylename=style)
 | 
			
		||||
            opponent_w_tc = TableCell(valuetype="float", value=0.9, stylename=style)
 | 
			
		||||
            opponent_w_tc.addElement(P(text="1"))
 | 
			
		||||
            coeff_row.addElement(opponent_w_tc)
 | 
			
		||||
 | 
			
		||||
@@ -1339,7 +1357,7 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
 | 
			
		||||
            opponent_o_tc.addElement(P(text="2"))
 | 
			
		||||
            coeff_row.addElement(opponent_o_tc)
 | 
			
		||||
 | 
			
		||||
            reporter_w_tc = TableCell(valuetype="float", value=1, stylename=style)
 | 
			
		||||
            reporter_w_tc = TableCell(valuetype="float", value=0.9, stylename=style)
 | 
			
		||||
            reporter_w_tc.addElement(P(text="1"))
 | 
			
		||||
            coeff_row.addElement(reporter_w_tc)
 | 
			
		||||
 | 
			
		||||
@@ -1358,7 +1376,9 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
 | 
			
		||||
        table.addElement(subtotal_row)
 | 
			
		||||
        subtotal_tc = TableCell(valuetype="string", stylename=title_style_botleft)
 | 
			
		||||
        subtotal_tc.addElement(P(text="Sous-total"))
 | 
			
		||||
        subtotal_tc.setAttribute('numbercolumnsspanned', "2")
 | 
			
		||||
        subtotal_row.addElement(subtotal_tc)
 | 
			
		||||
        subtotal_row.addElement(CoveredTableCell())
 | 
			
		||||
        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)
 | 
			
		||||
@@ -1406,6 +1426,7 @@ class PoolNotesTemplateView(VolunteerMixin, DetailView):
 | 
			
		||||
        table.addElement(scores_header)
 | 
			
		||||
        team_tc = TableCell(valuetype="string", stylename=title_style_topbotleft)
 | 
			
		||||
        team_tc.addElement(P(text="Équipe"))
 | 
			
		||||
        team_tc.setAttribute('numbercolumnsspanned', "2")
 | 
			
		||||
        scores_header.addElement(team_tc)
 | 
			
		||||
        problem_tc = TableCell(valuetype="string", stylename=title_style_topbot)
 | 
			
		||||
        problem_tc.addElement(P(text="Problème"))
 | 
			
		||||
@@ -1452,6 +1473,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.setAttribute('numbercolumnsspanned', "2")
 | 
			
		||||
            team_row.addElement(team_tc)
 | 
			
		||||
 | 
			
		||||
            problem_tc = TableCell(valuetype="string",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user