From 56ec2b4b423629f4852408ccccd8e0de1fd01e3d Mon Sep 17 00:00:00 2001
From: Emmanuel Viennet <emmanuel.viennet@gmail.com>
Date: Mon, 27 Feb 2023 17:45:20 +0100
Subject: [PATCH] =?UTF-8?q?Am=C3=A9liore=20d=C3=A9tection=20d=C3=A9cisions?=
 =?UTF-8?q?=20jurys=20avant=20d=C3=A9sinscription=20ou=20suppression=20de?=
 =?UTF-8?q?=20semestre?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 app/comp/res_but.py                 | 16 +++++++++
 app/comp/res_compat.py              | 17 ++++++---
 app/models/validations.py           |  2 +-
 app/scodoc/sco_formsemestre_edit.py | 56 +++++++++++++++++++++--------
 app/scodoc/sco_pv_dict.py           |  4 +--
 app/views/notes.py                  |  6 ++--
 6 files changed, 76 insertions(+), 25 deletions(-)

diff --git a/app/comp/res_but.py b/app/comp/res_but.py
index 502cd980c..9e8719ef9 100644
--- a/app/comp/res_but.py
+++ b/app/comp/res_but.py
@@ -17,6 +17,7 @@ from app.comp.bonus_spo import BonusSport
 from app.models import ScoDocSiteConfig
 from app.models.moduleimpls import ModuleImpl
 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.sco_utils import ModuleType
@@ -261,3 +262,18 @@ class ResultatsSemestreBUT(NotesTableCompat):
         """
         s = self.ues_inscr_parcours_df.loc[etudid]
         return s.index[s.notna()]
+
+    def etud_has_decision(self, etudid):
+        """True s'il y a une décision de jury pour cet étudiant émanant de ce formsemestre.
+        prend aussi en compte les autorisations de passage.
+        Sous-classée en BUT pour les RCUEs et années.
+        """
+        return (
+            super().etud_has_decision(etudid)
+            or ApcValidationAnnee.query.filter_by(
+                formsemestre_id=self.formsemestre.id, etudid=etudid
+            ).count()
+            or ApcValidationRCUE.query.filter_by(
+                formsemestre_id=self.formsemestre.id, etudid=etudid
+            ).count()
+        )
diff --git a/app/comp/res_compat.py b/app/comp/res_compat.py
index 00b6446e6..bc501b64a 100644
--- a/app/comp/res_compat.py
+++ b/app/comp/res_compat.py
@@ -15,9 +15,7 @@ from app import log
 from app.comp import moy_sem
 from app.comp.aux_stats import StatsMoyenne
 from app.comp.res_common import ResultatsSemestre
-from app.models import FormSemestre
-from app.models import Identite
-from app.models import ModuleImpl
+from app.models import Identite, FormSemestre, ModuleImpl, ScolarAutorisationInscription
 from app.scodoc.codes_cursus import UE_SPORT, DEF
 from app.scodoc import sco_utils as scu
 
@@ -280,8 +278,17 @@ class NotesTableCompat(ResultatsSemestre):
         return True
 
     def etud_has_decision(self, etudid):
-        """True s'il y a une décision de jury pour cet étudiant"""
-        return self.get_etud_decisions_ue(etudid) or self.get_etud_decision_sem(etudid)
+        """True s'il y a une décision de jury pour cet étudiant émanant de ce formsemestre.
+        prend aussi en compte les autorisations de passage.
+        Sous-classée en BUT pour les RCUEs et années.
+        """
+        return (
+            self.get_etud_decisions_ue(etudid)
+            or self.get_etud_decision_sem(etudid)
+            or ScolarAutorisationInscription.query.filter_by(
+                origin_formsemestre_id=self.formsemestre.id, etudid=etudid
+            ).count()
+        )
 
     def get_etud_decisions_ue(self, etudid: int) -> dict:
         """Decisions du jury pour les UE de cet etudiant, ou None s'il n'y en pas eu.
diff --git a/app/models/validations.py b/app/models/validations.py
index 6f1482381..cc5565187 100644
--- a/app/models/validations.py
+++ b/app/models/validations.py
@@ -131,7 +131,7 @@ class ScolarAutorisationInscription(db.Model):
         etudid: int,
         origin_formsemestre_id: int,
     ):
-        """Efface les autorisations de cette étudiant venant du sem. origine"""
+        """Efface les autorisations de cet étudiant venant du sem. origine"""
         autorisations = cls.query.filter_by(
             etudid=etudid, origin_formsemestre_id=origin_formsemestre_id
         )
diff --git a/app/scodoc/sco_formsemestre_edit.py b/app/scodoc/sco_formsemestre_edit.py
index cd231467b..cdb8277a7 100644
--- a/app/scodoc/sco_formsemestre_edit.py
+++ b/app/scodoc/sco_formsemestre_edit.py
@@ -35,8 +35,18 @@ from flask_login import current_user
 from app import db
 from app.auth.models import User
 from app.models import APO_CODE_STR_LEN, SHORT_STR_LEN
-from app.models import Module, ModuleImpl, Evaluation, EvaluationUEPoids, UniteEns
-from app.models import ScolarNews
+from app.models import (
+    Module,
+    ModuleImpl,
+    Evaluation,
+    EvaluationUEPoids,
+    UniteEns,
+    ScolarFormSemestreValidation,
+    ScolarAutorisationInscription,
+    ApcValidationAnnee,
+    ApcValidationRCUE,
+    ScolarNews,
+)
 from app.models.formations import Formation
 from app.models.formsemestre import FormSemestre
 from app.models.but_refcomp import ApcParcours
@@ -1524,9 +1534,11 @@ Ceci n'est possible que si :
         cancelbutton="Annuler",
     )
     if tf[0] == 0:
-        if formsemestre_has_decisions_or_compensations(formsemestre_id):
+        if formsemestre_has_decisions_or_compensations(formsemestre):
             H.append(
-                """<p><b>Ce semestre ne peut pas être supprimé ! (il y a des décisions de jury ou des compensations par d'autres semestres)</b></p>"""
+                """<p><b>Ce semestre ne peut pas être supprimé !
+                (il y a des décisions de jury ou des compensations par d'autres semestres)</b>
+                </p>"""
             )
         else:
             H.append(tf[1])
@@ -1560,18 +1572,32 @@ def formsemestre_delete2(formsemestre_id, dialog_confirmed=False):
     return flask.redirect(scu.ScoURL() + "?head_message=Semestre%20supprimé")
 
 
-def formsemestre_has_decisions_or_compensations(formsemestre_id):
-    """True if decision de jury dans ce semestre
-    ou bien compensation de ce semestre par d'autre ssemestres.
+def formsemestre_has_decisions_or_compensations(formsemestre: FormSemestre):
+    """True if decision de jury (sem. UE, RCUE, année) émanant de ce semestre
+    ou compensation de ce semestre par d'autres semestres
+    ou autorisations de passage.
     """
-    r = ndb.SimpleDictFetch(
-        """SELECT v.id AS formsemestre_validation_id, v.* 
-        FROM scolar_formsemestre_validation v 
-        WHERE v.formsemestre_id = %(formsemestre_id)s 
-        OR v.compense_formsemestre_id = %(formsemestre_id)s""",
-        {"formsemestre_id": formsemestre_id},
-    )
-    return r
+    # Validations de semestre ou d'UEs
+    if ScolarFormSemestreValidation.query.filter_by(
+        formsemestre_id=formsemestre.id
+    ).count():
+        return True
+    if ScolarFormSemestreValidation.query.filter_by(
+        compense_formsemestre_id=formsemestre.id
+    ).count():
+        return True
+    # Autorisations d'inscription:
+    if ScolarAutorisationInscription.query.filter_by(
+        origin_formsemestre_id=formsemestre.id
+    ).count():
+        return True
+    # Validations d'années BUT
+    if ApcValidationAnnee.query.filter_by(formsemestre_id=formsemestre.id).count():
+        return True
+    # Validations de RCUEs
+    if ApcValidationRCUE.query.filter_by(formsemestre_id=formsemestre.id).count():
+        return True
+    return False
 
 
 def do_formsemestre_delete(formsemestre_id):
diff --git a/app/scodoc/sco_pv_dict.py b/app/scodoc/sco_pv_dict.py
index 9c059b62a..d597f12b6 100644
--- a/app/scodoc/sco_pv_dict.py
+++ b/app/scodoc/sco_pv_dict.py
@@ -154,7 +154,7 @@ def dict_pvjury(
             etudid=etudid, origin_formsemestre_id=formsemestre_id
         ).all()
         d["autorisations"] = [a.to_dict() for a in autorisations]
-        d["autorisations_descr"] = _descr_autorisations(autorisations)
+        d["autorisations_descr"] = descr_autorisations(autorisations)
 
         d["validation_parcours"] = Se.parcours_validated()
         d["parcours"] = Se.get_cursus_descr(filter_futur=True)
@@ -259,7 +259,7 @@ def _comp_ects_by_ue_code(nt, decisions_ue):
     return ects_by_ue_code
 
 
-def _descr_autorisations(autorisations: list[ScolarAutorisationInscription]) -> str:
+def descr_autorisations(autorisations: list[ScolarAutorisationInscription]) -> str:
     "résumé textuel des autorisations d'inscription (-> 'S1, S3' )"
     return ", ".join([f"S{a.semestre_id}" for a in autorisations])
 
diff --git a/app/views/notes.py b/app/views/notes.py
index 13f971639..ae2a69d37 100644
--- a/app/views/notes.py
+++ b/app/views/notes.py
@@ -50,7 +50,7 @@ from app.but import jury_but_view
 
 from app.comp import res_sem
 from app.comp.res_compat import NotesTableCompat
-from app.models import ScolarNews, Scolog
+from app.models import ScolarAutorisationInscription, ScolarNews, Scolog
 from app.models.but_refcomp import ApcNiveau, ApcParcours
 from app.models.config import ScoDocSiteConfig
 from app.models.etudiants import Identite
@@ -137,8 +137,8 @@ from app.scodoc import sco_tag_module
 from app.scodoc import sco_ue_external
 from app.scodoc import sco_undo_notes
 from app.scodoc import sco_users
-from app.scodoc import sco_xml
 from app.scodoc.gen_tables import GenTable
+from app.scodoc.sco_pv_dict import descr_autorisations
 from app.scodoc.sco_permissions import Permission
 from app.scodoc.TrivialFormulator import TrivialFormulator
 from app.views import ScoData
@@ -2770,6 +2770,8 @@ def formsemestre_validation_suppress_etud(
         <li>Année BUT: {descr_annee}</li>
         <li>UEs : {", ".join(descr_ues)}</li>
         <li>RCUEs: {len(d.get("decision_rcue", []))} décisions</li>
+        <li>Autorisations: {descr_autorisations(ScolarAutorisationInscription.query.filter_by(origin_formsemestre_id=formsemestre_id,
+            etudid=etudid))}
         </ul>
         """
         return scu.confirm_dialog(
-- 
GitLab