From 068951ef1de1eec36cb7e48b78d8066aa71af131 Mon Sep 17 00:00:00 2001
From: Emmanuel Viennet <emmanuel.viennet@gmail.com>
Date: Sun, 18 Jun 2023 21:20:02 +0200
Subject: [PATCH] =?UTF-8?q?Jury=20BUT:=20ajout=20colonne=20d=C3=A9cision?=
 =?UTF-8?q?=20ann=C3=A9e=20sur=20table=20r=C3=A9cap.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 app/comp/res_but.py            | 48 +++++++++++++++++++++++++++++++---
 app/scodoc/sco_recapcomplet.py |  1 +
 app/tables/jury_recap.py       | 14 +++++++++-
 3 files changed, 58 insertions(+), 5 deletions(-)

diff --git a/app/comp/res_but.py b/app/comp/res_but.py
index a91b1dbbc..2c60f24a0 100644
--- a/app/comp/res_but.py
+++ b/app/comp/res_but.py
@@ -10,17 +10,17 @@ import time
 import numpy as np
 import pandas as pd
 
-from app import log
+from app import db, log
 from app.comp import moy_ue, moy_sem, inscr_mod
 from app.comp.res_compat import NotesTableCompat
 from app.comp.bonus_spo import BonusSport
-from app.models import ScoDocSiteConfig
+from app.models import Formation, FormSemestreInscription, ScoDocSiteConfig
 from app.models.moduleimpls import ModuleImpl
 from app.models.but_refcomp import ApcParcours, ApcNiveau
 from app.models.ues import DispenseUE, UniteEns
 from app.models.but_validations import ApcValidationAnnee, ApcValidationRCUE
 from app.scodoc import sco_preferences
-from app.scodoc.codes_cursus import UE_SPORT
+from app.scodoc.codes_cursus import BUT_CODES_ORDERED, UE_SPORT
 from app.scodoc.sco_utils import ModuleType
 
 
@@ -44,7 +44,8 @@ class ResultatsSemestreBUT(NotesTableCompat):
         """Parcours de chaque étudiant { etudid : parcour_id }"""
         self.ues_ids_by_parcour: dict[set[int]] = {}
         """{ parcour_id : set }, ue_id de chaque parcours"""
-
+        self.validations_annee: dict[int, ApcValidationAnnee] = {}
+        """chargé par get_validations_annee: jury annuel BUT"""
         if not self.load_cached():
             t0 = time.time()
             self.compute()
@@ -321,3 +322,42 @@ class ResultatsSemestreBUT(NotesTableCompat):
                 formsemestre_id=self.formsemestre.id, etudid=etudid
             ).count()
         )
+
+    def get_validations_annee(self) -> dict[int, ApcValidationAnnee]:
+        """Les validations des étudiants de ce semestre
+        pour l'année BUT d'une formation compatible avec celle de ce semestre.
+        Attention:
+        1) la validation ne provient pas nécessairement de ce semestre
+        (redoublants, pair/impair, extérieurs).
+        2) l'étudiant a pu démissionner ou défaillir.
+        3) S'il y a plusieurs validations pour le même étudiant, prend la "meilleure".
+
+        Mémorise le résultat (dans l'instance, pas en cache: TODO voir au profiler)
+        """
+        if self.validations_annee:
+            return self.validations_annee
+        annee_but = (self.formsemestre.semestre_id + 1) // 2
+        validations = (
+            ApcValidationAnnee.query.filter_by(ordre=annee_but)
+            .join(Formation)
+            .filter_by(formation_code=self.formsemestre.formation.formation_code)
+            .join(
+                FormSemestreInscription,
+                db.and_(
+                    FormSemestreInscription.etudid == ApcValidationAnnee.etudid,
+                    FormSemestreInscription.formsemestre_id == self.formsemestre.id,
+                ),
+            )
+        )
+        validation_by_etud = {}
+        for validation in validations:
+            if validation.etudid in validation_by_etud:
+                # keep the "best"
+                if BUT_CODES_ORDERED.get(validation.code, 0) > BUT_CODES_ORDERED.get(
+                    validation_by_etud[validation.etudid].code, 0
+                ):
+                    validation_by_etud[validation.etudid] = validation
+            else:
+                validation_by_etud[validation.etudid] = validation
+        self.validations_annee = validation_by_etud
+        return self.validations_annee
diff --git a/app/scodoc/sco_recapcomplet.py b/app/scodoc/sco_recapcomplet.py
index 63a0d4e25..9ebe4e87d 100644
--- a/app/scodoc/sco_recapcomplet.py
+++ b/app/scodoc/sco_recapcomplet.py
@@ -251,6 +251,7 @@ def formsemestre_recapcomplet(
             <div><tt>~</tt></div><div>valeur manquante</div>
             <div><tt>=</tt></div><div>UE dispensée</div>
             <div><tt>nan</tt></div><div>valeur non disponible</div>
+            <div>📍</div><div>code jury non enregistré</div>
         </div>
     </div>
     """
diff --git a/app/tables/jury_recap.py b/app/tables/jury_recap.py
index 5d9e5b885..8d02065cf 100644
--- a/app/tables/jury_recap.py
+++ b/app/tables/jury_recap.py
@@ -81,11 +81,14 @@ class TableJury(TableRecap):
 
     def add_jury(self):
         """Ajoute la colonne code jury et le lien.
-        Le code jury est celui du semestre: cette colonne n'est montrée
+        - Le code jury est celui du semestre: cette colonne n'est montrée
         que pour les formations classiques, ce code n'est pas utilisé en BUT.
+        - En BUT, on donne la décision de jury annuelle.
         """
         res = self.res
         autorisations = res.get_autorisations_inscription()
+        if res.is_apc:
+            validations_annee = res.get_validations_annee()
         for row in self.rows:
             etud = row.etud
             if not res.is_apc:
@@ -115,6 +118,15 @@ class TableJury(TableRecap):
                 group="jury_code_sem",
                 classes=["recorded_code"],
             )
+            if res.is_apc:  # BUT
+                validation_annee = validations_annee.get(etud.id, None)
+                row.add_cell(
+                    "decision_annuelle",
+                    "Année",
+                    validation_annee.code if validation_annee else "",
+                    group="jury_code_sem",
+                    classes=["recorded_code"],
+                )
             # Lien saisie ou visu jury
             a_saisir = (not res.validations) or (not res.validations.has_decision(etud))
             row.add_cell(
-- 
GitLab