diff --git a/app/models/etudiants.py b/app/models/etudiants.py index 764dbcb9c..95f30d184 100644 --- a/app/models/etudiants.py +++ b/app/models/etudiants.py @@ -91,8 +91,8 @@ class Identite(db.Model, models.ScoDocModel): unique=True, postgresql_where=(code_ine.isnot(None)), ), - db.CheckConstraint("civilite IN ('M', 'F', 'X')"), - db.CheckConstraint("civilite_etat_civil IN ('M', 'F', 'X')"), + db.CheckConstraint("civilite IN ('M', 'F', 'X')"), # non nullable + db.CheckConstraint("civilite_etat_civil IN ('M', 'F', 'X')"), # nullable ) # ----- Relations adresses = db.relationship( @@ -213,33 +213,40 @@ class Identite(db.Model, models.ScoDocModel): return etud @property - def civilite_str(self): - """returns 'M.' ou 'Mme' ou '' (pour le genre neutre, + def civilite_str(self) -> str: + """returns civilité usuelle: 'M.' ou 'Mme' ou '' (pour le genre neutre, personnes ne souhaitant pas d'affichage). """ return {"M": "M.", "F": "Mme", "X": ""}[self.civilite] @property - def civilite_etat_civil_str(self): - """returns 'M.' ou 'Mme' ou '' (pour le genre neutre, - personnes ne souhaitant pas d'affichage). + def civilite_etat_civil_str(self) -> str: + """returns 'M.' ou 'Mme', selon état civil officiel. + La France ne reconnait pas le genre neutre dans l'état civil: + si cette donnée état civil est précisée, elle est utilisée, + sinon on renvoie la civilité usuelle. """ - return {"M": "M.", "F": "Mme", "X": ""}[self.civilite_etat_civil] + return ( + {"M": "M.", "F": "Mme"}.get(self.civilite_etat_civil, "") + if self.civilite_etat_civil + else self.civilite_str + ) def sex_nom(self, no_accents=False) -> str: - "'M. DUPONTÉ', ou si no_accents, 'M. DUPONTE'" + "'M. DUPONTÉ', ou si no_accents, 'M. DUPONTE'. Civilité usuelle." s = f"{self.civilite_str} {(self.nom_usuel or self.nom).upper()}" if no_accents: return scu.suppress_accents(s) return s @property - def e(self): - "terminaison en français: 'ne', '', 'ou '(e)'" + def e(self) -> str: + "terminaison en français: 'ne', '', 'ou '(e)', selon la civilité usuelle" return {"M": "", "F": "e"}.get(self.civilite, "(e)") def nom_disp(self) -> str: - "Nom à afficher" + """Nom à afficher. + Note: le nom est stocké en base en majuscules.""" if self.nom_usuel: return ( (self.nom_usuel + " (" + self.nom + ")") if self.nom else self.nom_usuel @@ -273,7 +280,8 @@ class Identite(db.Model, models.ScoDocModel): return " ".join(r) @property - def etat_civil(self): + def etat_civil(self) -> str: + "M. Prénom NOM, utilisant les données état civil si présentes, usuelles sinon." if self.prenom_etat_civil: civ = {"M": "M.", "F": "Mme", "X": ""}[self.civilite_etat_civil] return f"{civ} {self.prenom_etat_civil} {self.nom}" @@ -343,7 +351,7 @@ class Identite(db.Model, models.ScoDocModel): if key == "civilite": # requis value = input_civilite(value) elif key == "civilite_etat_civil": - value = input_civilite(value) if value else None + value = input_civilite_etat_civil(value) elif key == "boursier": value = bool(value) elif key == "date_naissance": @@ -375,7 +383,7 @@ class Identite(db.Model, models.ScoDocModel): e_dict.pop("_sa_instance_state", None) # ScoDoc7 output_formators: (backward compat) e_dict["etudid"] = self.id - e_dict["date_naissance"] = ndb.DateISOtoDMY(e_dict["date_naissance"]) + e_dict["date_naissance"] = ndb.DateISOtoDMY(e_dict.get("date_naissance", "")) e_dict["ne"] = self.e e_dict["nomprenom"] = self.nomprenom adresse = self.adresses.first() @@ -711,6 +719,11 @@ def input_civilite(s: str) -> str: raise ScoValueError(f"valeur invalide pour la civilité: {s}") +def input_civilite_etat_civil(s: str) -> str | None: + """Same as input_civilite, but empty gives None (null)""" + return input_civilite(s) if s and s.strip() else None + + PIVOT_YEAR = 70 @@ -786,9 +799,9 @@ class Admission(db.Model, models.ScoDocModel): specialite = db.Column(db.Text) annee_bac = db.Column(db.Integer) math = db.Column(db.Text) - physique = db.Column(db.Float) - anglais = db.Column(db.Float) - francais = db.Column(db.Float) + physique = db.Column(db.Text) + anglais = db.Column(db.Text) + francais = db.Column(db.Text) # Rang dans les voeux du candidat (inconnu avec APB et PS) rang = db.Column(db.Integer) # Qualité et décision du jury d'admission (ou de l'examinateur) @@ -852,8 +865,6 @@ class Admission(db.Model, models.ScoDocModel): value = None if key in fs_uppercase and value: value = value.upper() - if key == "civilite" or key == "civilite_etat_civil": - value = input_civilite(value) elif key == "annee" or key == "annee_bac": value = pivot_year(value) elif key == "classement" or key == "apb_classement_gr": diff --git a/app/scodoc/sco_etud.py b/app/scodoc/sco_etud.py index efa00eee5..a90b54d3b 100644 --- a/app/scodoc/sco_etud.py +++ b/app/scodoc/sco_etud.py @@ -38,7 +38,12 @@ from flask import url_for, g from app import db, email from app import log from app.models import Admission, Identite -from app.models.etudiants import input_civilite, make_etud_args, pivot_year +from app.models.etudiants import ( + input_civilite, + input_civilite_etat_civil, + make_etud_args, + pivot_year, +) import app.scodoc.sco_utils as scu import app.scodoc.notesdb as ndb from app.scodoc.sco_exceptions import ScoGenError, ScoValueError @@ -47,9 +52,13 @@ from app.scodoc import sco_preferences from app.scodoc.scolog import logdb -def format_etud_ident(etud): +def format_etud_ident(etud: dict): """Format identite de l'étudiant (modifié en place) - nom, prénom et formes associees + nom, prénom et formes associees. + + Note: par rapport à Identite.to_dict_bul(), + ajoute les champs: + 'email_default', 'nom_disp', 'nom_usuel', 'civilite_etat_civil_str', 'ne', 'civilite_str' """ etud["nom"] = format_nom(etud["nom"]) if "nom_usuel" in etud: @@ -65,7 +74,7 @@ def format_etud_ident(etud): etud["civilite_etat_civil_str"] = ( format_civilite(etud["civilite_etat_civil"]) if etud["civilite_etat_civil"] - else "" + else etud["civilite_str"] ) # Nom à afficher: if etud["nom_usuel"]: @@ -76,7 +85,7 @@ def format_etud_ident(etud): etud["nom_disp"] = etud["nom"] etud["nomprenom"] = format_nomprenom(etud) # M. Pierre DUPONT - etud["etat_civil"] = format_etat_civil(etud) + etud["etat_civil"] = _format_etat_civil(etud) if etud["civilite"] == "M": etud["ne"] = "" elif etud["civilite"] == "F": @@ -147,12 +156,13 @@ def format_civilite(civilite): raise ScoValueError(f"valeur invalide pour la civilité: {civilite}") from exc -def format_etat_civil(etud: dict): - if etud["prenom_etat_civil"]: - civ = {"M": "M.", "F": "Mme", "X": ""}[etud.get("civilite_etat_civil", "X")] - return f'{civ} {etud["prenom_etat_civil"]} {etud["nom"]}' - else: - return etud["nomprenom"] +def _format_etat_civil(etud: dict) -> str: + "Mme Béatrice DUPONT, en utilisant les données d'état civil si indiquées." + if etud["prenom_etat_civil"] or etud["civilite_etat_civil"]: + return f"""{etud["civilite_etat_civil_str"]} { + etud["prenom_etat_civil"] or etud["prenom"] + } {etud["nom"]}""" + return etud["nomprenom"] def format_lycee(nomlycee): @@ -237,7 +247,7 @@ _identiteEditor = ndb.EditableTable( "prenom": force_uppercase, "prenom_etat_civil": force_uppercase, "civilite": input_civilite, - "civilite_etat_civil": input_civilite, + "civilite_etat_civil": input_civilite_etat_civil, "date_naissance": ndb.DateDMYtoISO, "boursier": bool, }, @@ -300,7 +310,9 @@ def check_nom_prenom_homonyms( def _check_duplicate_code(cnx, args, code_name, disable_notify=False, edit=True): - """Vérifie que le code n'est pas dupliqué""" + """Vérifie que le code n'est pas dupliqué. + Raises ScoGenError si problème. + """ etudid = args.get("etudid", None) if args.get(code_name, None): etuds = identite_list(cnx, {code_name: str(args[code_name])}) @@ -355,11 +367,6 @@ def _check_duplicate_code(cnx, args, code_name, disable_notify=False, edit=True) raise ScoGenError(err_page) -def _check_civilite(args): - civilite = args.get("civilite", "X") or "X" - args["civilite"] = input_civilite(civilite) # TODO: A faire valider - - def identite_edit(cnx, args, disable_notify=False): """Modifie l'identite d'un étudiant. Si pref notification et difference, envoie message notification, sauf si disable_notify @@ -400,7 +407,6 @@ def identite_create(cnx, args): "check unique etudid, then create" _check_duplicate_code(cnx, args, "code_nip", edit=False) _check_duplicate_code(cnx, args, "code_ine", edit=False) - _check_civilite(args) if "etudid" in args: etudid = args["etudid"] @@ -915,12 +921,12 @@ def etud_inscriptions_infos(etudid: int, ne="") -> dict: from app.scodoc import sco_formsemestre from app.scodoc import sco_formsemestre_inscriptions - etud = {} + infos = {} # Semestres dans lesquel il est inscrit ins = sco_formsemestre_inscriptions.do_formsemestre_inscription_list( {"etudid": etudid} ) - etud["ins"] = ins + infos["ins"] = ins sems = [] cursem = None # semestre "courant" ou il est inscrit for i in ins: @@ -933,29 +939,31 @@ def etud_inscriptions_infos(etudid: int, ne="") -> dict: # trie les semestres par date de debut, le plus recent d'abord # (important, ne pas changer (suivi cohortes)) sems.sort(key=itemgetter("dateord"), reverse=True) - etud["sems"] = sems - etud["cursem"] = cursem + infos["sems"] = sems + infos["cursem"] = cursem if cursem: - etud["inscription"] = cursem["titremois"] - etud["inscriptionstr"] = "Inscrit en " + cursem["titremois"] - etud["inscription_formsemestre_id"] = cursem["formsemestre_id"] - etud["etatincursem"] = curi["etat"] - etud["situation"] = descr_situation_etud(etudid, ne) + infos["inscription"] = cursem["titremois"] + infos["inscriptionstr"] = "Inscrit en " + cursem["titremois"] + infos["inscription_formsemestre_id"] = cursem["formsemestre_id"] + infos["etatincursem"] = curi["etat"] + infos["situation"] = descr_situation_etud(etudid, ne) else: - if etud["sems"]: - if etud["sems"][0]["dateord"] > time.strftime("%Y-%m-%d", time.localtime()): - etud["inscription"] = "futur" - etud["situation"] = "futur élève" + if infos["sems"]: + if infos["sems"][0]["dateord"] > time.strftime( + "%Y-%m-%d", time.localtime() + ): + infos["inscription"] = "futur" + infos["situation"] = "futur élève" else: - etud["inscription"] = "ancien" - etud["situation"] = "ancien élève" + infos["inscription"] = "ancien" + infos["situation"] = "ancien élève" else: - etud["inscription"] = "non inscrit" - etud["situation"] = etud["inscription"] - etud["inscriptionstr"] = etud["inscription"] - etud["inscription_formsemestre_id"] = None - etud["etatincursem"] = "?" - return etud + infos["inscription"] = "non inscrit" + infos["situation"] = infos["inscription"] + infos["inscriptionstr"] = infos["inscription"] + infos["inscription_formsemestre_id"] = None + infos["etatincursem"] = "?" + return infos def descr_situation_etud(etudid: int, ne="") -> str: diff --git a/app/scodoc/sco_import_etuds.py b/app/scodoc/sco_import_etuds.py index fdd25af7e..05e58ecc3 100644 --- a/app/scodoc/sco_import_etuds.py +++ b/app/scodoc/sco_import_etuds.py @@ -38,18 +38,14 @@ from flask import g, url_for from app import db, log from app.models import Identite, GroupDescr, ScolarNews -from app.models.etudiants import input_civilite +from app.models.etudiants import input_civilite, input_civilite_etat_civil from app.scodoc.gen_tables import GenTable from app.scodoc.sco_excel import COLORS from app.scodoc.sco_exceptions import ( - AccessDenied, ScoFormatError, ScoException, ScoValueError, - ScoInvalidDateError, - ScoLockedFormError, - ScoGenError, ) from app.scodoc import html_sco_header from app.scodoc import sco_cache @@ -375,6 +371,15 @@ def scolars_import_excel_file( (doit etre 'M', 'F', ou 'MME', 'H', 'X' mais pas '{ val}') ligne {linenum}, colonne {titleslist[i]}""" ) from exc + if titleslist[i].lower() == "civilite_etat_civil": + try: + val = input_civilite_etat_civil(val) + except ScoValueError as exc: + raise ScoValueError( + f"""valeur invalide pour 'civilite' + (doit etre 'M', 'F', vide ou 'MME', 'H', 'X' mais pas '{ + val}') ligne {linenum}, colonne {titleslist[i]}""" + ) from exc # Excel date conversion: if titleslist[i].lower() == "date_naissance": diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py index c567b8d3c..bbf785a7c 100644 --- a/app/scodoc/sco_utils.py +++ b/app/scodoc/sco_utils.py @@ -79,6 +79,12 @@ STATIC_DIR = ( # Attention: suppose que la timezone utilisée par postgresql soit la même ! TIME_ZONE = timezone("/".join(os.path.realpath("/etc/localtime").split("/")[-2:])) +# ----- CIVILITE ETUDIANTS +CIVILITES = {"M": "M.", "F": "Mme", "X": ""} +CIVILITES_ETAT_CIVIL = {"M": "M.", "F": "Mme"} +# Si l'état civil reconnait le genre neutre (X),: +# CIVILITES_ETAT_CIVIL = CIVILITES + # ----- CALCUL ET PRESENTATION DES NOTES NOTES_PRECISION = 1e-4 # evite eventuelles erreurs d'arrondis NOTES_MIN = 0.0 # valeur minimale admise pour une note (sauf malus, dans [-20, 20]) diff --git a/app/views/scolar.py b/app/views/scolar.py index c136252ae..7ea499124 100644 --- a/app/views/scolar.py +++ b/app/views/scolar.py @@ -1542,7 +1542,6 @@ def _etudident_create_or_edit_form(edit): "math", { "size": 3, - "type": "float", "title": "Note de mathématiques", "explanation": "note sur 20 en terminale", }, @@ -1551,7 +1550,6 @@ def _etudident_create_or_edit_form(edit): "physique", { "size": 3, - "type": "float", "title": "Note de physique", "explanation": "note sur 20 en terminale", }, @@ -1560,7 +1558,6 @@ def _etudident_create_or_edit_form(edit): "anglais", { "size": 3, - "type": "float", "title": "Note d'anglais", "explanation": "note sur 20 en terminale", }, @@ -1569,7 +1566,6 @@ def _etudident_create_or_edit_form(edit): "francais", { "size": 3, - "type": "float", "title": "Note de français", "explanation": "note sur 20 obtenue au bac", }, diff --git a/migrations/versions/497ba81343f7_identite_admission.py b/migrations/versions/497ba81343f7_identite_admission.py index aa275ae1a..c60ac3def 100644 --- a/migrations/versions/497ba81343f7_identite_admission.py +++ b/migrations/versions/497ba81343f7_identite_admission.py @@ -122,6 +122,24 @@ def upgrade(): with op.batch_alter_table("admissions", schema=None) as batch_op: batch_op.drop_constraint("admissions_etudid_fkey", type_="foreignkey") + batch_op.alter_column( + "physique", + existing_type=sa.DOUBLE_PRECISION(precision=53), + type_=sa.Text(), + existing_nullable=True, + ) + batch_op.alter_column( + "anglais", + existing_type=sa.DOUBLE_PRECISION(precision=53), + type_=sa.Text(), + existing_nullable=True, + ) + batch_op.alter_column( + "francais", + existing_type=sa.DOUBLE_PRECISION(precision=53), + type_=sa.Text(), + existing_nullable=True, + ) # laisse l'ancienne colonne pour downgrade (tests) # batch_op.drop_column('etudid') @@ -160,6 +178,24 @@ def downgrade(): batch_op.create_foreign_key( "admissions_etudid_fkey", "identite", ["etudid"], ["id"], ondelete="CASCADE" ) + batch_op.alter_column( + "francais", + existing_type=sa.Text(), + type_=sa.DOUBLE_PRECISION(precision=53), + existing_nullable=True, + ) + batch_op.alter_column( + "anglais", + existing_type=sa.Text(), + type_=sa.DOUBLE_PRECISION(precision=53), + existing_nullable=True, + ) + batch_op.alter_column( + "physique", + existing_type=sa.Text(), + type_=sa.DOUBLE_PRECISION(precision=53), + existing_nullable=True, + ) with op.batch_alter_table("adresse", schema=None) as batch_op: batch_op.drop_constraint("adresse_etudid_fkey", type_="foreignkey") diff --git a/tests/ressources/misc/ImportEtudiants.xlsx b/tests/ressources/misc/ImportEtudiants.xlsx new file mode 100644 index 000000000..4aed71163 Binary files /dev/null and b/tests/ressources/misc/ImportEtudiants.xlsx differ diff --git a/tests/unit/setup.py b/tests/unit/setup.py index a3756d9f9..776210358 100644 --- a/tests/unit/setup.py +++ b/tests/unit/setup.py @@ -4,6 +4,7 @@ Quelques fonctions d'initialisation pour tests unitaires import datetime from app import db, models +from app.models import Formation import app.scodoc.sco_utils as scu from app.scodoc import codes_cursus @@ -16,16 +17,34 @@ from tests.unit import sco_fake_gen def build_formation_test( - nb_mods=1, parcours=codes_cursus.CursusBUT, with_ue_sport=False -): + nb_mods=1, + parcours=codes_cursus.CursusBUT, + with_ue_sport=False, + acronyme="F3", + titre="Formation 2", +) -> tuple[sco_fake_gen.ScoFake, int, list[int], list[int]]: """Crée une formation simple pour les tests. Création à partir de zéro (n'importe pas un fichier xml). Avec 3 UEs en S2 et une UE en S4. """ G = sco_fake_gen.ScoFake(verbose=False) + # If already exists, just use it + formation = Formation.query.filter_by( + dept_id=G.dept.id, + acronyme=acronyme, + titre=titre, + version=1, + ).first() + if formation is not None: + return ( + G, + formation.id, + [ue.id for ue in formation.ues], + [m.id for m in formation.modules], + ) formation_id = G.create_formation( - acronyme="F3", - titre="Formation 2", + acronyme=acronyme, + titre=titre, titre_officiel="Titre officiel 2", type_parcours=parcours.TYPE_CURSUS, ) diff --git a/tests/unit/test_etudiants.py b/tests/unit/test_etudiants.py new file mode 100644 index 000000000..8344c1435 --- /dev/null +++ b/tests/unit/test_etudiants.py @@ -0,0 +1,187 @@ +# -*- coding: utf-8 -*- + +"""Test modèle étudiant (identite) + + +Utiliser comme: + pytest tests/unit/test_etudiants.py + +""" +import datetime +from pathlib import Path + +from flask import current_app + +from app import db +from app.models import Admission, Adresse, Departement, FormSemestre, Identite +from app.scodoc import sco_etud +from app.scodoc import sco_import_etuds + +from tests.unit import setup + + +def test_identite(test_client): + "Test de base du modèle identité: création, clonage, ..." + args = { + "civilite": "X", + "code_ine": "code_ine", + "code_nip": "code_nip", + "date_naissance": datetime.date(2000, 1, 2), + "dept_id": 1, + "dept_naissance": "dept_naissance", + "lieu_naissance": "lieu_naissance", + "nationalite": "nationalite", + "nom_usuel": "nom_usuel", + "nom": "nom", + "prenom_etat_civil": "prenom_etat_civil", + "prenom": "prenom", + } + e = Identite.create_etud(**args) + db.session.add(e) + db.session.flush() + admission_id = e.admission_id + admission = db.session.get(Admission, admission_id) + assert admission is not None + assert e.boursier is False + assert e.adresses.count() == 1 + adresses_ids = [a.id for a in e.adresses] + # --- Teste cascade admission: + db.session.delete(e) + db.session.flush() + assert db.session.get(Admission, admission_id) is None + assert db.session.get(Departement, 1) is not None + # --- Teste cascade adresses + for adresse_id in adresses_ids: + assert db.session.get(Adresse, adresse_id) is None + # --- Test cascade département + dept = Departement(acronym="test_identite") + db.session.add(dept) + db.session.flush() + args1 = args | {"dept_id": dept.id} + e = Identite.create_etud(**args1) + db.session.add(e) + db.session.flush() + etudid = e.id + db.session.delete(dept) + db.session.flush() + assert db.session.get(Identite, etudid) is None + + +def test_etat_civil(test_client): + "Test des attributs état civil" + dept = Departement.query.first() + args = {"nom": "nom", "prenom": "prénom", "civilite": "M", "dept_id": dept.id} + # Homme + e = Identite(**args) + db.session.add(e) + db.session.flush() + assert e.civilite_etat_civil_str == "M." + assert e.e == "" + # Femme + e = Identite(**args | {"civilite": "F"}) + db.session.add(e) + db.session.flush() + assert e.civilite_etat_civil_str == "Mme" + assert e.e == "e" + # Homme devenu femme + e = Identite(**(args | {"civilite_etat_civil": "F"})) + db.session.add(e) + db.session.flush() + assert e.civilite_etat_civil_str == "Mme" + assert e.civilite_str == "M." + assert e.e == "" + # Femme devenue neutre + e = Identite(**(args | {"civilite": "X", "civilite_etat_civil": "F"})) + db.session.add(e) + db.session.flush() + assert e.civilite_etat_civil_str == "Mme" + assert e.civilite_str == "" + assert e.e == "(e)" + assert e.prenom_etat_civil is None + # La version dict + e_d = e.to_dict_scodoc7() + assert e_d["civilite"] == "X" + assert e_d["civilite_etat_civil"] == "F" + assert e_d["ne"] == "(e)" + + +def test_etud_legacy(test_client): + "Test certaines fonctions scodoc7 (sco_etud)" + dept = Departement.query.first() + args = {"nom": "nom", "prenom": "prénom", "civilite": "M", "dept_id": dept.id} + # Prénom état civil + e = Identite(**(args)) + db.session.add(e) + db.session.flush() + e_dict = e.to_dict_bul() + sco_etud.format_etud_ident(e_dict) + assert e_dict["nom_disp"] == "NOM" + assert e_dict["prenom_etat_civil"] == "" + + +def test_import_etuds_xlsx(test_client): + "test import étudiant depuis xlsx" + G, formation_id, (ue1_id, ue2_id, ue3_id), module_ids = setup.build_formation_test( + acronyme="IMPXLSX" + ) + formsemestre_id = G.create_formsemestre( + formation_id=formation_id, + semestre_id=1, + date_debut="01/01/2021", + date_fin="30/06/2021", + ) + filename = ( + Path(current_app.config["SCODOC_DIR"]) + / "tests/ressources/misc/ImportEtudiants.xlsx" + ) + with open(filename, mode="rb") as f: + sco_import_etuds.scolars_import_excel_file( + f, formsemestre_id=formsemestre_id, exclude_cols=["photo_filename"] + ) + formsemestre = db.session.get(FormSemestre, formsemestre_id) + # Vérifie tous les champs du premier étudiant + etud = formsemestre.etuds.first() + assert etud.code_nip == "nip1" + assert etud.code_ine == "ine1" + assert etud.nom == "NOM1" + assert etud.nom_usuel == "nom_usuel1" + assert etud.prenom == "PRÉNOM1" + assert etud.civilite == "M" + assert etud.prenom_etat_civil == "PRÉNOM_CIVIL1" + assert etud.civilite_etat_civil == "M" + assert etud.date_naissance == datetime.date(2001, 5, 1) + assert etud.lieu_naissance == "Paris" + assert etud.nationalite == "Belge" + assert etud.boursier is True + # Admission + adm = etud.admission + assert adm.bac == "C" + assert adm.specialite == "SPÉ" + assert adm.annee_bac == 2023 + assert adm.math == "11.0" # deprecated field + assert adm.physique == "12.0" # deprecated field + assert adm.anglais == "13.0" # deprecated field + assert adm.francais == "14.0" # deprecated field + assert adm.boursier_prec is False + assert adm.qualite == 10 + assert adm.rapporteur == "xx" + assert adm.score == 5 + assert adm.classement == 111 + assert adm.nomlycee == "nomlycée" + assert adm.codepostallycee == "75005" + # Adresse + adresse: Adresse = etud.adresses.first() + assert adresse.email == "etud1@etud.no" + assert adresse.emailperso == "etud1@perso.no" + assert adresse.domicile == "1 rue A" + assert adresse.codepostaldomicile == "12345" + assert adresse.villedomicile == "Lima" + assert adresse.paysdomicile == "Pérou" + assert adresse.telephone == "102030405" + assert adresse.telephonemobile == "605040302" + # + + +# mapp.set_sco_dept("TEST_") +# ctx.push() +# login_user(User.query.filter_by(user_name="admin").first()) diff --git a/tools/format_import_etudiants.txt b/tools/format_import_etudiants.txt index 7d4481b82..ae7ebe3bc 100644 --- a/tools/format_import_etudiants.txt +++ b/tools/format_import_etudiants.txt @@ -10,7 +10,7 @@ nom_usuel; text; identite; 1; nom usuel (si different); prenom; text; identite; 0; prénom de l'etudiant civilite; text; identite; 0; sexe ('M', 'F', 'X');sexe;genre prenom_etat_civil; text; identite; 1; prénom à 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 +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 @@ -52,7 +52,4 @@ villedomicile; text; adresse; 1; ville domicile paysdomicile; text; adresse; 1; pays domicile telephone; text; adresse; 1; num. telephone (fixe) telephonemobile; text; adresse; 1; num. telephone (mobile) -# -# Pas tout à fait admission: -debouche;text; admissions;1;(OBSOLETE, ne plus utiliser) situation APRES être passé par chez nous; - +# \ No newline at end of file