Améliore code gestion civilité. Tests unitaires étudiants et import excel. Diverses corrections.

This commit is contained in:
Emmanuel Viennet 2023-10-17 22:12:28 +02:00
parent 194e58419e
commit e87bb9a8db
10 changed files with 343 additions and 78 deletions

View File

@ -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":

View File

@ -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:

View File

@ -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":

View File

@ -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])

View File

@ -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",
},

View File

@ -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")

Binary file not shown.

View File

@ -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,
)

View File

@ -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())

View File

@ -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;
#