diff --git a/app/scodoc/sco_apogee_csv.py b/app/scodoc/sco_apogee_csv.py
index c56f497f037167cbdaf9b0c449a51470ccc1cb09..f2dba69c97bb2f61db34f912de5d65feef0bc2d4 100644
--- a/app/scodoc/sco_apogee_csv.py
+++ b/app/scodoc/sco_apogee_csv.py
@@ -221,7 +221,8 @@ class ApoEtud(dict):
except KeyError as exc:
raise ScoFormatError(
f"""Fichier Apogee invalide : ligne mal formatée ? <br>colonne <tt>{
- col_id}</tt> non déclarée ?"""
+ col_id}</tt> non déclarée ?""",
+ safe=True,
) from exc
else:
try:
@@ -326,9 +327,14 @@ class ApoEtud(dict):
self.log.append("export étape désactivé")
return VOID_APO_RES
+ # Element passage
+ res_passage = self.search_elt_passage(code, res)
+ if res_passage:
+ return res_passage
+
# Elements UE
res_ue = self.search_elt_ue(code, res)
- if res_ue != {}:
+ if res_ue:
return res_ue
# Elements Modules
@@ -403,6 +409,25 @@ class ApoEtud(dict):
return VOID_APO_RES
return {} # no UE result found for this code
+ def search_elt_passage(self, code: str, res: NotesTableCompat) -> dict:
+ """Cherche un résultat de type "passage" pour ce code Apogée.
+ dict vide si pas de résultat trouvé pour ce code.
+ L'élement est rempli si:
+ - code est dans les codes passage du formsemestre (sem)
+ - autorisation d'inscription enregistre de sem vers sem d'indice suivant
+ """
+ if res.formsemestre.semestre_id < 1:
+ return {}
+ next_semestre_id = res.formsemestre.semestre_id + 1
+ if code in res.formsemestre.get_codes_apogee(category="passage"):
+ if next_semestre_id in res.get_autorisations_inscription().get(
+ self.etud.id, set()
+ ):
+ return dict(
+ N="", B=20, J="", R=ScoDocSiteConfig.get_code_apo("ADM"), M=""
+ )
+ return {}
+
def comp_elt_semestre(self, nt: NotesTableCompat, decision: dict, etudid: int):
"""Calcul résultat apo semestre.
Toujours vide pour en BUT/APC.
@@ -703,7 +728,8 @@ class ApoData:
filename = self.orig_filename or e.filename
raise ScoFormatError(
f"""<h3>Erreur lecture du fichier Apogée <tt>{filename}</tt></h3>
- <p>{e.args[0]}</p>"""
+ <p>{e.args[0]}</p>""",
+ safe=True,
) from e
self.etape_apogee = self.get_etape_apogee() # 'V1RT'
self.vdi_apogee = self.get_vdi_apogee() # '111'
@@ -795,7 +821,9 @@ class ApoData:
self.sems_periode = None
def get_etape_apogee(self) -> str:
- """Le code etape: 'V1RT', donné par le code de l'élément VET"""
+ """Le code etape: 'V1RT', donné par le code de l'élément VET.
+ Le VET doit être parmi les colonnes de la section XX-APO_COLONNES-XX
+ """
for elt in self.apo_csv.apo_elts.values():
if elt.type_objet == "VET":
return elt.code
@@ -860,7 +888,8 @@ class ApoData:
log(f"Colonnes presentes: {present}")
raise ScoFormatError(
f"""Fichier Apogee invalide<br>Colonnes declarees: <tt>{declared}</tt>
- <br>Colonnes presentes: <tt>{present}</tt>"""
+ <br>Colonnes presentes: <tt>{present}</tt>""",
+ safe=True,
) from exc
# l'ensemble de tous les codes des elements apo des semestres:
sem_elems = reduce(set.union, list(self.get_codes_by_sem().values()), set())
diff --git a/app/scodoc/sco_apogee_reader.py b/app/scodoc/sco_apogee_reader.py
index 44ab7fa6551df9ad04c90ecd35537f71017bb145..32c7f872870371dfef96568d47cf8b0c6ed4c022 100644
--- a/app/scodoc/sco_apogee_reader.py
+++ b/app/scodoc/sco_apogee_reader.py
@@ -299,11 +299,14 @@ class ApoCSVReadWrite:
for i, field in enumerate(fields):
cols[self.col_ids[i]] = field
except IndexError as exc:
- raise
raise ScoFormatError(
f"Fichier Apogee incorrect (colonnes excédentaires ? (<tt>{i}/{field}</tt>))",
filename=self.get_filename(),
+ safe=True,
) from exc
+ # Ajoute colonnes vides manquantes, pratique si on a édité le fichier Apo à la main...
+ for i in range(len(fields), len(self.col_ids)):
+ cols[self.col_ids[i]] = ""
etud_tuples.append(
ApoEtudTuple(
nip=fields[0], # id etudiant
@@ -337,6 +340,8 @@ class ApoCSVReadWrite:
fields = line.split(APO_SEP)
if len(fields) == 2:
k, v = fields
+ elif len(fields) == 1:
+ k, v = fields[0], ""
else:
log(f"Error read CSV: \nline={line}\nfields={fields}")
log(dir(f))
diff --git a/app/scodoc/sco_exceptions.py b/app/scodoc/sco_exceptions.py
index 2e61608c32559f5b693a7cf9127fea2568186749..53b1711c2e103eb2ad8de4fbbbab1d5f5f6f2669 100644
--- a/app/scodoc/sco_exceptions.py
+++ b/app/scodoc/sco_exceptions.py
@@ -61,12 +61,12 @@ class ScoValueError(ScoException):
class ScoPermissionDenied(ScoValueError):
"""Permission non accordée (appli web)"""
- def __init__(self, msg=None, dest_url=None):
+ def __init__(self, msg=None, dest_url=None, safe=False):
if msg is None:
msg = f"""Opération non autorisée pour {
current_user.get_nomcomplet() if current_user else "?"
}. Pas la permission, ou objet verrouillé."""
- super().__init__(msg, dest_url=dest_url)
+ super().__init__(msg, dest_url=dest_url, safe=safe)
class ScoBugCatcher(ScoException):
@@ -84,8 +84,8 @@ class InvalidEtudId(NoteProcessError):
class ScoFormatError(ScoValueError):
"Erreur lecture d'un fichier fourni par l'utilisateur"
- def __init__(self, msg, filename="", dest_url=None):
- super().__init__(msg, dest_url=dest_url)
+ def __init__(self, msg, filename="", dest_url=None, safe=False):
+ super().__init__(msg, dest_url=dest_url, safe=safe)
self.filename = filename
@@ -95,15 +95,15 @@ class ScoInvalidParamError(ScoValueError):
(id strings, ...)
"""
- def __init__(self, msg=None, dest_url=None):
+ def __init__(self, msg=None, dest_url=None, safe=False):
msg = msg or "Adresse invalide. Vérifiez vos signets."
- super().__init__(msg, dest_url=dest_url)
+ super().__init__(msg, dest_url=dest_url, safe=safe)
class ScoPDFFormatError(ScoValueError):
"erreur génération PDF (templates platypus, ...)"
- def __init__(self, msg, dest_url=None):
+ def __init__(self, msg, dest_url=None, safe=False):
super().__init__(
f"""Erreur dans un format pdf:
<p>{msg}</p>
@@ -112,6 +112,7 @@ class ScoPDFFormatError(ScoValueError):
</p>
""",
dest_url=dest_url,
+ safe=safe,
)
@@ -130,33 +131,33 @@ class ScoConfigurationError(ScoValueError):
class ScoLockedFormError(ScoValueError):
"Modification d'une formation verrouillée"
- def __init__(self, msg="", dest_url=None):
+ def __init__(self, msg="", dest_url=None, safe=False):
msg = (
"Cette formation est verrouillée (car il y a un semestre verrouillé qui s'y réfère). "
+ str(msg)
)
- super().__init__(msg=msg, dest_url=dest_url)
+ super().__init__(msg=msg, dest_url=dest_url, safe=safe)
class ScoLockedSemError(ScoValueError):
"Modification d'un formsemestre verrouillé"
- def __init__(self, msg="", dest_url=None):
+ def __init__(self, msg="", dest_url=None, safe=False):
msg = "Ce semestre est verrouillé ! " + str(msg)
- super().__init__(msg=msg, dest_url=dest_url)
+ super().__init__(msg=msg, dest_url=dest_url, safe=safe)
class ScoNonEmptyFormationObject(ScoValueError):
"""On ne peut pas supprimer un module/matiere ou UE si des formsemestre s'y réfèrent"""
- def __init__(self, type_objet="objet'", msg="", dest_url=None):
+ def __init__(self, type_objet="objet'", msg="", dest_url=None, safe=False):
msg = f"""<h3>{type_objet} "{msg}" utilisé(e) dans des semestres: suppression impossible.</h3>
<p class="help">Il faut d'abord supprimer le semestre (ou en retirer ce {type_objet}).
Mais il est peut-être préférable de laisser ce programme intact et d'en créer une
nouvelle version pour la modifier sans affecter les semestres déjà en place.
</p>
"""
- super().__init__(msg=msg, dest_url=dest_url)
+ super().__init__(msg=msg, dest_url=dest_url, safe=safe)
class ScoInvalidIdType(ScoValueError):