%(nomprenom)s (%(inscription)s)
-
+%(etat_civil)s
%(emaillink)s
|
%(etudfoto)s
diff --git a/app/views/scolar.py b/app/views/scolar.py
index 6268c7e2..3a4e89d3 100644
--- a/app/views/scolar.py
+++ b/app/views/scolar.py
@@ -557,6 +557,7 @@ def etud_info(etudid=None, format="xml"):
"nom_usuel",
"prenom",
"nomprenom",
+ "prenom_etat_civil",
"email",
"emailperso",
"domicile",
@@ -577,6 +578,9 @@ def etud_info(etudid=None, format="xml"):
):
d[a] = etud[a] # ne pas quoter car ElementTree.tostring quote déjà
d["civilite"] = etud["civilite_str"] # exception: ne sort pas la civilite brute
+ d["civilite_etat_civil"] = etud[
+ "civilite_etat_civil_str"
+ ] # exception: ne sort pas la civilite brute
d["sexe"] = d["civilite"] # backward compat pour anciens clients
d["photo_url"] = sco_photos.etud_photo_url(etud)
@@ -1442,6 +1446,25 @@ def _etudident_create_or_edit_form(edit):
"title": "Civilité",
},
),
+ (
+ "prenom_etat_civil",
+ {
+ "size": 25,
+ "title": "Prénom (état-civil)",
+ "allow_null": True,
+ "explanation": "Si précisé, remplace le prénom d'usage dans les documents officiels",
+ },
+ ),
+ (
+ "civilite_etat_civil",
+ {
+ "input_type": "menu",
+ "labels": ["Homme", "Femme", "Autre/neutre"],
+ "allowed_values": ["M", "F", "X"],
+ "title": "Civilité (état-civil)",
+ "explanation": "Si précisé: remplace la civilité d'usage dans les documents officiels",
+ },
+ ),
(
"date_naissance",
{
diff --git a/migrations/versions/cf29790ca6f6_ajout_prenom_civilite__etat_civil.py b/migrations/versions/cf29790ca6f6_ajout_prenom_civilite__etat_civil.py
new file mode 100644
index 00000000..8b13136a
--- /dev/null
+++ b/migrations/versions/cf29790ca6f6_ajout_prenom_civilite__etat_civil.py
@@ -0,0 +1,33 @@
+"""ajout (prenom,civilite)_etat_civil
+
+Revision ID: cf29790ca6f6
+Revises: 6520faf67508
+Create Date: 2023-02-25 10:55:42.831526
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = "cf29790ca6f6"
+down_revision = "6520faf67508"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - checked ###
+ op.add_column(
+ "identite",
+ sa.Column("civilite_etat_civil", sa.Text(), nullable=True, server_default="X"),
+ )
+ op.add_column("identite", sa.Column("prenom_etat_civil", sa.Text(), nullable=True))
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - checked ###
+ op.drop_column("identite", "prenom_etat_civil")
+ op.drop_column("identite", "civilite_etat_civil")
+ # ### end Alembic commands ###
diff --git a/tools/format_import_etudiants.txt b/tools/format_import_etudiants.txt
index 3ba04f50..ad18c275 100644
--- a/tools/format_import_etudiants.txt
+++ b/tools/format_import_etudiants.txt
@@ -9,6 +9,8 @@ nom; text; identite; 0; nom de l'etudiant;
nom_usuel; text; identite; 1; nom usuel (si different);
prenom; text; identite; 0; prenom de l'etudiant
civilite; text; identite; 1; sexe ('M', 'F', 'X');sexe;genre
+prenom_etat_civil; text; identite; 1; prenom à l'état-civil (si différent);prenom_etat_civil
+civilite_etat_civil; text; identite; 1; sexe ('M', 'F', 'X') à l'état civil;civilite_etat_civil
date_naissance;text;identite; 1; date de naissance (jj/mm/aaaa)
lieu_naissance;text;identite; 1; lieu de naissance
nationalite; text; identite; 1; nationalite
From 8f844f5191fcfd1e2e5043e8ddc57bc25e74471d Mon Sep 17 00:00:00 2001
From: Emmanuel Viennet
Date: Wed, 19 Apr 2023 11:51:58 +0200
Subject: [PATCH 5/7] Apo: export BUT element annuel
---
app/comp/res_compat.py | 3 +-
app/scodoc/sco_apogee_csv.py | 232 +++++++++++++++++++++--------------
app/scodoc/sco_semset.py | 22 ++--
3 files changed, 156 insertions(+), 101 deletions(-)
diff --git a/app/comp/res_compat.py b/app/comp/res_compat.py
index 7b20ffd5..da7dffab 100644
--- a/app/comp/res_compat.py
+++ b/app/comp/res_compat.py
@@ -322,7 +322,8 @@ class NotesTableCompat(ResultatsSemestre):
def get_etud_decision_sem(self, etudid: int) -> dict:
"""Decision du jury semestre prise pour cet etudiant, ou None s'il n'y en pas eu.
{ 'code' : None|ATT|..., 'assidu' : 0|1, 'event_date' : , compense_formsemestre_id }
- Si état défaillant, force le code a DEF
+ Si état défaillant, force le code a DEF.
+ Toujours None en BUT.
"""
if self.get_etud_etat(etudid) == DEF:
return {
diff --git a/app/scodoc/sco_apogee_csv.py b/app/scodoc/sco_apogee_csv.py
index 64860522..42e8b1c8 100644
--- a/app/scodoc/sco_apogee_csv.py
+++ b/app/scodoc/sco_apogee_csv.py
@@ -265,7 +265,6 @@ class ApoEtud(dict):
"Vrai si BUT"
self.col_elts = {}
"{'V1RT': {'R': 'ADM', 'J': '', 'B': 20, 'N': '12.14'}}"
- self.new_cols = {} # { col_id : value to record in csv }
self.etud: Identite = None
"etudiant ScoDoc associé"
self.etat = None # ETUD_OK, ...
@@ -283,6 +282,17 @@ class ApoEtud(dict):
self.fmt_note = functools.partial(
_apo_fmt_note, fmt=ScoDocSiteConfig.get_code_apo("NOTES_FMT") or "3.2f"
)
+ # Initialisés par associate_sco:
+ self.autre_sem: dict = None
+ self.autre_res: NotesTableCompat = None
+ self.cur_sem: dict = None
+ self.cur_res: NotesTableCompat = None
+ self.new_cols = {}
+ "{ col_id : value to record in csv }"
+
+ # Pour le BUT:
+ self.validation_annee_but: ApcValidationAnnee = None
+ "validation de jury annuelle BUT, ou None"
def __repr__(self):
return f"""ApoEtud( nom='{self["nom"]}', nip='{self["nip"]}' )"""
@@ -336,18 +346,17 @@ class ApoEtud(dict):
sco_elts = {} # valeurs trouvées dans ScoDoc code : { N, B, J, R }
for col_id in apo_data.col_ids[4:]:
code = apo_data.cols[col_id]["Code"] # 'V1RT'
- el = sco_elts.get(
- code, None
- ) # {'R': ADM, 'J': '', 'B': 20, 'N': '12.14'}
- if el is None: # pas déjà trouvé
- cur_sem, autre_sem = self.etud_semestres_de_etape(apo_data)
+ elt = sco_elts.get(code, None)
+ # elt est {'R': ADM, 'J': '', 'B': 20, 'N': '12.14'}
+ if elt is None: # pas déjà trouvé
+ self.etud_set_semestres_de_etape(apo_data)
for sem in apo_data.sems_etape:
- el = self.search_elt_in_sem(code, sem, cur_sem, autre_sem)
- if el is not None:
- sco_elts[code] = el
+ elt = self.search_elt_in_sem(code, sem)
+ if elt is not None:
+ sco_elts[code] = elt
break
- self.col_elts[code] = el
- if el is None:
+ self.col_elts[code] = elt
+ if elt is None:
self.new_cols[col_id] = self.cols[col_id]
else:
try:
@@ -373,7 +382,7 @@ class ApoEtud(dict):
# codes = set([apo_data.cols[col_id].code for col_id in apo_data.col_ids])
# return codes - set(sco_elts)
- def search_elt_in_sem(self, code, sem, cur_sem, autre_sem) -> dict:
+ def search_elt_in_sem(self, code, sem) -> dict:
"""
VET code jury etape (en BUT, le code annuel)
ELP élément pédagogique: UE, module
@@ -387,20 +396,29 @@ class ApoEtud(dict):
code (str): code apo de l'element cherché
sem (dict): semestre dans lequel on cherche l'élément
cur_sem (dict): semestre "courant" pour résultats annuels (VET)
- autre_sem (dict): autre semestre utilisé pour calculé les résultats annuels (VET)
+ autre_sem (dict): autre semestre utilisé pour calculer les résultats annuels (VET)
Returns:
dict: with N, B, J, R keys, ou None si elt non trouvé
"""
etudid = self.etud["etudid"]
- formsemestre = FormSemestre.query.get_or_404(sem["formsemestre_id"])
- nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
+ if not self.cur_res:
+ log("search_elt_in_sem: no cur_res !")
+ return None
+ if sem["formsemestre_id"] == self.cur_res.formsemestre.id:
+ res = self.cur_res
+ elif (
+ self.autre_res and sem["formsemestre_id"] == self.autre_res.formsemestre.id
+ ):
+ res = self.autre_res
+ else:
+ formsemestre = FormSemestre.query.get_or_404(sem["formsemestre_id"])
+ res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
- if etudid not in nt.identdict:
+ if etudid not in res.identdict:
return None # etudiant non inscrit dans ce semestre
- decision = nt.get_etud_decision_sem(etudid)
- if not self.export_res_sdj and not decision:
+ if not self.export_res_sdj and not res.etud_has_decision(etudid):
# pas de decision de jury, on n'enregistre rien
# (meme si démissionnaire)
if not self.has_logged_no_decision:
@@ -408,43 +426,46 @@ class ApoEtud(dict):
self.has_logged_no_decision = True
return VOID_APO_RES
- if decision and decision["code"] == NAR:
- self.is_NAR = True
+ if res.is_apc: # export BUT
+ self._but_load_validation_annuelle()
+ else:
+ decision = res.get_etud_decision_sem(etudid)
+ if decision and decision["code"] == NAR:
+ self.is_NAR = True
+ # Element semestre: (non BUT donc)
+ if code in {x.strip() for x in sem["elt_sem_apo"].split(",")}:
+ if self.export_res_sem:
+ return self.comp_elt_semestre(res, decision, etudid)
+ else:
+ return VOID_APO_RES
# Element etape (annuel ou non):
if sco_formsemestre.sem_has_etape(sem, code) or (
code in {x.strip() for x in sem["elt_annee_apo"].split(",")}
):
export_res_etape = self.export_res_etape
- if (not export_res_etape) and cur_sem:
+ if (not export_res_etape) and self.cur_sem:
# exporte toujours le résultat de l'étape si l'étudiant est diplômé
Se = sco_cursus.get_situation_etud_cursus(
- self.etud, cur_sem["formsemestre_id"]
+ self.etud, self.cur_sem["formsemestre_id"]
)
export_res_etape = Se.all_other_validated()
if export_res_etape:
- return self.comp_elt_annuel(etudid, cur_sem, autre_sem)
+ return self.comp_elt_annuel(etudid)
else:
self.log.append("export étape désactivé")
return VOID_APO_RES
- # Element semestre:
- if code in {x.strip() for x in sem["elt_sem_apo"].split(",")}:
- if self.export_res_sem:
- return self.comp_elt_semestre(nt, decision, etudid)
- else:
- return VOID_APO_RES
-
# Elements UE
- decisions_ue = nt.get_etud_decisions_ue(etudid)
- for ue in nt.get_ues_stat_dict():
+ decisions_ue = res.get_etud_decisions_ue(etudid)
+ for ue in res.get_ues_stat_dict():
if ue["code_apogee"] and code in {
x.strip() for x in ue["code_apogee"].split(",")
}:
if self.export_res_ues:
if decisions_ue and ue["ue_id"] in decisions_ue:
- ue_status = nt.get_etud_ue_status(etudid, ue["ue_id"])
+ ue_status = res.get_etud_ue_status(etudid, ue["ue_id"])
code_decision_ue = decisions_ue[ue["ue_id"]]["code"]
return dict(
N=self.fmt_note(ue_status["moy"] if ue_status else ""),
@@ -459,14 +480,14 @@ class ApoEtud(dict):
return VOID_APO_RES
# Elements Modules
- modimpls = nt.get_modimpls_dict()
+ modimpls = res.get_modimpls_dict()
module_code_found = False
for modimpl in modimpls:
module = modimpl["module"]
if module["code_apogee"] and code in {
x.strip() for x in module["code_apogee"].split(",")
}:
- n = nt.get_etud_mod_moy(modimpl["moduleimpl_id"], etudid)
+ n = res.get_etud_mod_moy(modimpl["moduleimpl_id"], etudid)
if n != "NI" and self.export_res_modules:
return dict(N=self.fmt_note(n), B=20, J="", R="")
else:
@@ -480,8 +501,7 @@ class ApoEtud(dict):
"""Calcul résultat apo semestre.
Toujours vide pour en BUT/APC.
"""
- if self.is_apc:
- # pas de code semestre en APC !
+ if self.is_apc: # garde fou: pas de code semestre en APC !
return dict(N="", B=20, J="", R="", M="")
if decision is None:
etud = Identite.get_etud(etudid)
@@ -498,7 +518,7 @@ class ApoEtud(dict):
note_str = self.fmt_note(note)
return dict(N=note_str, B=20, J="", R=decision_apo, M="")
- def comp_elt_annuel(self, etudid, cur_sem, autre_sem):
+ def comp_elt_annuel(self, etudid):
"""Calcul resultat annuel (VET) à partir du semestre courant
et de l'autre (le suivant ou le précédent complétant l'année scolaire)
En BUT, c'est la décision de jury annuelle (ApcValidationAnnee).
@@ -516,18 +536,16 @@ class ApoEtud(dict):
# XXX cette règle est discutable, à valider
# log('comp_elt_annuel cur_sem=%s autre_sem=%s' % (cur_sem['formsemestre_id'], autre_sem['formsemestre_id']))
- if not cur_sem:
+ if not self.cur_sem:
# l'étudiant n'a pas de semestre courant ?!
self.log.append("pas de semestre courant")
log(f"comp_elt_annuel: etudid {etudid} has no cur_sem")
return VOID_APO_RES
- cur_formsemestre = FormSemestre.query.get_or_404(cur_sem["formsemestre_id"])
- cur_nt: NotesTableCompat = res_sem.load_formsemestre_results(cur_formsemestre)
if self.is_apc:
cur_decision = {} # comp_elt_semestre sera vide.
else:
- cur_decision = cur_nt.get_etud_decision_sem(etudid)
+ cur_decision = self.cur_res.get_etud_decision_sem(etudid)
if not cur_decision:
# pas de decision => pas de résultat annuel
return VOID_APO_RES
@@ -536,21 +554,17 @@ class ApoEtud(dict):
# ne touche pas aux RATs
return VOID_APO_RES
- if not autre_sem:
+ if not self.autre_sem:
# formations monosemestre, ou code VET semestriel,
# ou jury intermediaire et etudiant non redoublant...
- return self.comp_elt_semestre(cur_nt, cur_decision, etudid)
+ return self.comp_elt_semestre(self.cur_res, cur_decision, etudid)
- autre_formsemestre = FormSemestre.query.get_or_404(autre_sem["formsemestre_id"])
- autre_nt: NotesTableCompat = res_sem.load_formsemestre_results(
- autre_formsemestre
- )
# --- Traite le BUT à part:
if self.is_apc:
- return self.comp_elt_annuel_apc(cur_nt, autre_nt, etudid)
+ return self.comp_elt_annuel_apc()
# --- Formations classiques
decision_apo = ScoDocSiteConfig.get_code_apo(cur_decision["code"])
- autre_decision = autre_nt.get_etud_decision_sem(etudid)
+ autre_decision = self.autre_res.get_etud_decision_sem(etudid)
if not autre_decision:
# pas de decision dans l'autre => pas de résultat annuel
return VOID_APO_RES
@@ -566,8 +580,8 @@ class ApoEtud(dict):
):
note_str = "0,01" # note non nulle pour les démissionnaires
else:
- note = cur_nt.get_etud_moy_gen(etudid)
- autre_note = autre_nt.get_etud_moy_gen(etudid)
+ note = self.cur_res.get_etud_moy_gen(etudid)
+ autre_note = self.autre_res.get_etud_moy_gen(etudid)
# print 'note=%s autre_note=%s' % (note, autre_note)
try:
moy_annuelle = (note + autre_note) / 2
@@ -582,40 +596,46 @@ class ApoEtud(dict):
return dict(N=note_str, B=20, J="", R=decision_apo_annuelle, M="")
- def comp_elt_annuel_apc(
- self,
- cur_res: ResultatsSemestreBUT,
- autre_res: ResultatsSemestreBUT,
- etudid: int,
- ):
+ def comp_elt_annuel_apc(self):
"""L'élément Apo pour un résultat annuel BUT.
- cur_res : les résultats du semestre sur lequel a été appelé l'export.
+ self.cur_res == résultats du semestre sur lequel a été appelé l'export.
"""
+ if not self.validation_annee_but:
+ # pas de décision ou pas de sem. impair
+ return VOID_APO_RES
+
+ return dict(
+ N="",
+ B=20,
+ J="",
+ R=ScoDocSiteConfig.get_code_apo(self.validation_annee_but.code),
+ M="",
+ )
+
+ def _but_load_validation_annuelle(self):
+ "charge la validation de jury BUT annuelle"
# le semestre impair de l'année scolaire
- if cur_res.formsemestre.semestre_id % 2:
- formsemestre = cur_res.formsemestre
+ if self.cur_res.formsemestre.semestre_id % 2:
+ formsemestre = self.cur_res.formsemestre
elif (
- autre_res
- and autre_res.formsemestre.annee_scolaire()
- == cur_res.formsemestre.annee_scolaire()
+ self.autre_res
+ and self.autre_res.formsemestre.annee_scolaire()
+ == self.cur_res.formsemestre.annee_scolaire()
):
- formsemestre = autre_res.formsemestre
+ formsemestre = self.autre_res.formsemestre
assert formsemestre.semestre_id % 2
else:
# ne trouve pas de semestre impair
- return VOID_APO_RES
-
- validation: ApcValidationAnnee = ApcValidationAnnee.query.filter_by(
- formsemestre_id=formsemestre.id, etudid=etudid
- ).first()
- if validation is None:
- return VOID_APO_RES
- return dict(
- N="", B=20, J="", R=ScoDocSiteConfig.get_code_apo(validation.code), M=""
+ self.validation_annee_but = None
+ return
+ self.validation_annee_but: ApcValidationAnnee = (
+ ApcValidationAnnee.query.filter_by(
+ formsemestre_id=formsemestre.id, etudid=self.etud["etudid"]
+ ).first()
)
- def etud_semestres_de_etape(self, apo_data):
- """
+ def etud_set_semestres_de_etape(self, apo_data):
+ """Set .cur_sem and .autre_sem et charge les résultats.
Lorsqu'on a une formation semestrialisée mais avec un code étape annuel,
il faut considérer les deux semestres ((S1,S2) ou (S3,S4)) pour calculer
le code annuel (VET ou VRT1A (voir elt_annee_apo)).
@@ -623,7 +643,7 @@ class ApoEtud(dict):
Pour les jurys intermediaires (janvier, S1 ou S3): (S2 ou S4) de la même
étape lors d'une année précédente ?
- Renvoie le semestre "courant" et l'autre semestre, ou None s'il n'y en a pas.
+ Set cur_sem: le semestre "courant" et autre_sem, ou None s'il n'y en a pas.
"""
# Cherche le semestre "courant":
cur_sems = [
@@ -648,19 +668,29 @@ class ApoEtud(dict):
cur_sem = None
for sem in cur_sems:
formsemestre = FormSemestre.query.get_or_404(sem["formsemestre_id"])
- nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
- has_decision = nt.etud_has_decision(self.etud["etudid"])
+ res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
+ has_decision = res.etud_has_decision(self.etud["etudid"])
if has_decision:
cur_sem = sem
+ self.cur_res = res
break
if cur_sem is None:
cur_sem = cur_sems[0] # aucun avec décision, prend le plus recent
+ if res.formsemestre.id == cur_sem["formsemestre_id"]:
+ self.cur_res = res
+ else:
+ formsemestre = FormSemestre.query.get_or_404(
+ cur_sem["formsemestre_id"]
+ )
+ self.cur_res = res_sem.load_formsemestre_results(formsemestre)
+
+ self.cur_sem = cur_sem
if apo_data.cur_semestre_id <= 0:
- return (
- cur_sem,
- None,
- ) # "autre_sem" non pertinent pour sessions sans semestres
+ # "autre_sem" non pertinent pour sessions sans semestres:
+ self.autre_sem = None
+ self.autre_res = None
+ return
if apo_data.jury_intermediaire: # jury de janvier
# Le semestre suivant: exemple 2 si on est en jury de S1
@@ -678,7 +708,7 @@ class ApoEtud(dict):
courant_annee_debut = apo_data.annee_scolaire + 1
courant_mois_debut = 1 # ou 2 (fev-jul)
else:
- raise ValueError("invalid pediode value !") # bug ?
+ raise ValueError("invalid periode value !") # bug ?
courant_date_debut = "%d-%02d-01" % (
courant_annee_debut,
courant_mois_debut,
@@ -709,15 +739,24 @@ class ApoEtud(dict):
autre_sem = None
for sem in autres_sems:
formsemestre = FormSemestre.query.get_or_404(sem["formsemestre_id"])
- nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
- decision = nt.get_etud_decision_sem(self.etud["etudid"])
- if decision:
+ res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
+ if res.is_apc:
+ has_decision = res.etud_has_decision(self.etud["etudid"])
+ else:
+ has_decision = res.get_etud_decision_sem(self.etud["etudid"])
+ if has_decision:
autre_sem = sem
break
if autre_sem is None:
autre_sem = autres_sems[0] # aucun avec decision, prend le plus recent
- return cur_sem, autre_sem
+ self.autre_sem = autre_sem
+ # Charge les résultats:
+ if autre_sem:
+ formsemestre = FormSemestre.query.get_or_404(autre_sem["formsemestre_id"])
+ self.autre_res = res_sem.load_formsemestre_results(formsemestre)
+ else:
+ self.autre_res = None
class ApoData:
@@ -911,8 +950,12 @@ class ApoData:
return elts # { code apo : ApoElt }
def apo_read_etuds(self, f) -> list[ApoEtud]:
- """Lecture des etudiants (et resultats) du fichier CSV Apogée"""
- L = []
+ """Lecture des étudiants (et résultats) du fichier CSV Apogée.
+ Les lignes "étudiant" commencent toujours par
+ `12345678 NOM PRENOM 15/05/2003`
+ le premier code étant le NIP.
+ """
+ apo_etuds = []
while True:
line = f.readline()
if not line:
@@ -921,10 +964,15 @@ class ApoData:
continue # silently ignore blank lines
line = line.strip(APO_NEWLINE)
fields = line.split(APO_SEP)
+ if len(fields) < 4:
+ raise ScoValueError(
+ """Ligne étudiant invalide
+ (doit commencer par 'NIP NOM PRENOM dd/mm/yyyy')"""
+ )
cols = {} # { col_id : value }
for i, field in enumerate(fields):
cols[self.col_ids[i]] = field
- L.append(
+ apo_etuds.append(
ApoEtud(
nip=fields[0], # id etudiant
nom=fields[1],
@@ -940,7 +988,7 @@ class ApoData:
)
)
- return L
+ return apo_etuds
def get_etape_apogee(self):
"""Le code etape: 'V1RT', donné par le code de l'élément VET"""
diff --git a/app/scodoc/sco_semset.py b/app/scodoc/sco_semset.py
index e4cb5852..2a08d862 100644
--- a/app/scodoc/sco_semset.py
+++ b/app/scodoc/sco_semset.py
@@ -87,12 +87,12 @@ class SemSet(dict):
self.formsemestre_ids = []
cnx = ndb.GetDBConnexion()
if semset_id: # read existing set
- L = semset_list(cnx, args={"semset_id": semset_id})
- if not L:
+ semsets = semset_list(cnx, args={"semset_id": semset_id})
+ if not semsets:
raise ScoValueError(f"Ensemble inexistant ! (semset {semset_id})")
- self["title"] = L[0]["title"]
- self["annee_scolaire"] = L[0]["annee_scolaire"]
- self["sem_id"] = L[0]["sem_id"]
+ self["title"] = semsets[0]["title"]
+ self["annee_scolaire"] = semsets[0]["annee_scolaire"]
+ self["sem_id"] = semsets[0]["sem_id"]
r = ndb.SimpleDictFetch(
"SELECT formsemestre_id FROM notes_semset_formsemestre WHERE semset_id = %(semset_id)s",
{"semset_id": semset_id},
@@ -307,7 +307,7 @@ class SemSet(dict):
if self["sem_id"] == 1:
periode = "1re période (S1, S3)"
- elif self["sem_id"] == 1:
+ elif self["sem_id"] == 2:
periode = "2de période (S2, S4)"
else:
periode = "non semestrialisée (LP, ...). Incompatible avec BUT."
@@ -441,8 +441,14 @@ def do_semset_add_sem(semset_id, formsemestre_id):
if formsemestre_id == "":
raise ScoValueError("pas de semestre choisi !")
semset = SemSet(semset_id=semset_id)
- semset.add(formsemestre.id)
- return flask.redirect("apo_semset_maq_status?semset_id=%s" % semset_id)
+ semset.add(formsemestre_id)
+ return flask.redirect(
+ url_for(
+ "notes.apo_semset_maq_status",
+ scodoc_dept=g.scodoc_dept,
+ semset_id=semset_id,
+ )
+ )
def do_semset_remove_sem(semset_id, formsemestre_id):
From daa5b8ac53911cb283ee58ec01a75c5fd88f9fdd Mon Sep 17 00:00:00 2001
From: Emmanuel Viennet
Date: Thu, 11 May 2023 14:01:23 +0200
Subject: [PATCH 6/7] =?UTF-8?q?Fichiers=20Apog=C3=A9e:=20code=20refactorin?=
=?UTF-8?q?g=20+=20test=20unitaire?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/models/formsemestre.py | 7 +-
app/scodoc/sco_apogee_compare.py | 118 +++---
app/scodoc/sco_apogee_csv.py | 557 +++++---------------------
app/scodoc/sco_apogee_reader.py | 483 ++++++++++++++++++++++
app/scodoc/sco_etape_apogee.py | 14 +-
app/scodoc/sco_etape_apogee_view.py | 193 +++++----
app/scodoc/sco_etape_bilan.py | 4 +-
app/scodoc/sco_exceptions.py | 8 +-
app/scodoc/sco_semset.py | 39 +-
sco_version.py | 2 +-
tests/ressources/apogee/V1RET!117.txt | 93 +++++
tests/unit/test_apogee_csv.py | 67 ++++
12 files changed, 951 insertions(+), 634 deletions(-)
create mode 100644 app/scodoc/sco_apogee_reader.py
create mode 100644 tests/ressources/apogee/V1RET!117.txt
create mode 100644 tests/unit/test_apogee_csv.py
diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py
index 0ec4e6be..da858723 100644
--- a/app/models/formsemestre.py
+++ b/app/models/formsemestre.py
@@ -195,11 +195,14 @@ class FormSemestre(db.Model):
d["date_fin"] = d["date_fin_iso"] = ""
d["responsables"] = [u.id for u in self.responsables]
d["titre_formation"] = self.titre_formation()
- if convert_objects:
+ if convert_objects: # pour API
d["parcours"] = [p.to_dict() for p in self.get_parcours_apc()]
d["departement"] = self.departement.to_dict()
d["formation"] = self.formation.to_dict()
d["etape_apo"] = self.etapes_apo_str()
+ else:
+ # Converti les étapes Apogee sous forme d'ApoEtapeVDI (compat scodoc7)
+ d["etapes"] = [e.as_apovdi() for e in self.etapes]
return d
def to_dict_api(self):
@@ -937,7 +940,7 @@ class FormSemestreEtape(db.Model):
def __repr__(self):
return f""
- def as_apovdi(self):
+ def as_apovdi(self) -> ApoEtapeVDI:
return ApoEtapeVDI(self.etape_apo)
diff --git a/app/scodoc/sco_apogee_compare.py b/app/scodoc/sco_apogee_compare.py
index 0c93c665..3b82862d 100644
--- a/app/scodoc/sco_apogee_compare.py
+++ b/app/scodoc/sco_apogee_compare.py
@@ -46,13 +46,14 @@ Pour chaque étudiant commun:
from flask import g, url_for
from app import log
-from app.scodoc import sco_apogee_csv
+from app.scodoc import sco_apogee_csv, sco_apogee_reader
+from app.scodoc.sco_apogee_csv import ApoData
from app.scodoc.gen_tables import GenTable
from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc import html_sco_header
from app.scodoc import sco_preferences
-_help_txt = """
+_HELP_TXT = """
Outil de comparaison de fichiers (maquettes CSV) Apogée.
@@ -69,7 +70,7 @@ def apo_compare_csv_form():
""" Comparaison de fichiers Apogée
|