Compare commits

...

2 Commits

Author SHA1 Message Date
Cléo Baras 8b3efe9dad Améliore détection étudiants 2024-01-23 09:54:30 +01:00
Cléo Baras 02976c9996 Etat temporaire n°2 2024-01-23 09:05:52 +01:00
2 changed files with 100 additions and 67 deletions

View File

@ -57,11 +57,13 @@ class EtudiantsJuryPE:
"Les identités des étudiants traités pour le jury"
self.identites = {} # ex. ETUDINFO_DICT
"Les cursus (semestres suivis, abandons, ...) des étudiants"
"Les cursus (semestres suivis, abandons, dernier S1, S2, ...) des étudiants"
self.cursus = {}
"""Les aggrégats des semestres suivis (par ex: 3S=S1+S2+S3 à prendre en compte avec d'éventuels redoublements) des étudiants"""
self.aggregats = {}
"Les etudids des étudiants à considérer au jury (ceux qui seront effectivement diplômés)"
self.etudiants_jury_ids = {}
self.diplomes_ids = {}
"Les etudids des étudiants dont il faut calculer les moyennes/classements (même si d'éventuels abandons)"
self.etudiants_ids = {}
@ -83,6 +85,7 @@ class EtudiantsJuryPE:
"""
"Les cosemestres donnant lieu à même année de diplome"
cosemestres = pe_tools.get_cosemestres_diplomants(annee_diplome, None)
self.cosemestres = cosemestres
pe_tools.pe_print(
"1) Recherche des coSemestres -> %d trouvés" % len(cosemestres)
)
@ -116,23 +119,24 @@ class EtudiantsJuryPE:
pe_tools.pe_print()
"""Les étudiants à prendre dans le diplôme, étudiants ayant abandonnés non compris"""
self.etudiants_jury_ids = self.get_etudiants(annee_diplome)
self.diplomes_ids = self.get_etudiants(annee_diplome)
"""Les étudiants dont il faut calculer les moyennes"""
self.etudiants_ids = {etudid for etudid in self.cursus}
self.etudiants_ids = {etudid for etudid in self.identites}
"""Les formsemestres (des étudiants) dont il faut calculer les moyennes"""
self.formsemestres_jury_ids = self.get_formsemestres_jury()
# Synthèse
pe_tools.pe_print(
f" => {len(self.etudiants_jury_ids)} étudiants à diplômer en {annee_diplome}"
f" => {len(self.diplomes_ids)} étudiants à diplômer en {annee_diplome}"
)
nbre_abandons = len(self.etudiants_ids) - len(self.etudiants_ids)
nbre_abandons = len(self.etudiants_ids) - len(self.diplomes_ids)
pe_tools.pe_print(f" => {nbre_abandons} étudiants éliminer pour abandon")
pe_tools.pe_print(f" => {len(self.formsemestres_jury_ids)} semestres dont il faut calculer la moyenne")
pe_tools.pe_print(
f" => quelques étudiants futurs diplômés : "
+ ", ".join([str(etudid) for etudid in list(self.etudiants_jury_ids)[:10]])
+ ", ".join([str(etudid) for etudid in list(self.diplomes_ids)[:10]])
)
pe_tools.pe_print(
f" => semestres dont il faut calculer les moyennes : "
@ -153,15 +157,13 @@ class EtudiantsJuryPE:
etudids = [
etudid
for etudid in self.cursus
if self.cursus[etudid]["diplome"] == annee_diplome and self.cursus[etudid]["abandon"]
if self.cursus[etudid]["diplome"] == annee_diplome
and self.cursus[etudid]["abandon"] == False
]
etudiants = {etudid: self.identites[etudid] for etudid in etudids}
return etudiants
def analyse_etat_etudiant(
self, etudid: int, cosemestres: dict[int, FormSemestre]
):
def analyse_etat_etudiant(self, etudid: int, cosemestres: dict[int, FormSemestre]):
"""Analyse le cursus d'un étudiant pouvant être :
* l'un de ceux sur lesquels le jury va statuer (année de diplômation du jury considéré)
@ -192,33 +194,38 @@ class EtudiantsJuryPE:
self.cursus[etudid] = {
"etudid": etudid, # les infos sur l'étudiant
"etat_civil": identite.etat_civil, # Ajout à la table jury
"nom": identite.nom,
"diplome": annee_diplome(identite), # Le date prévisionnelle de son diplôme
"formsemestres": semestres_etudiant, # les semestres de l'étudiant
"semestres": {},
"aggregats": {},
"abandon": False, # va être traité en dessous
}
""" Est-il réorienté / démissionnaire ou a-t-il arrêté volontairement sa formation ?"""
self.cursus[etudid]["abandon"] = arret_de_formation(identite, cosemestres)
def analyse_parcours_etudiant_dans_semestres(self, etudid):
"""Structure les informations sur les semestres suivis par un
étudiant, pour identifier les semestres qui seront pris en compte lors de ses calculs
de moyennes PE.
La structure s'appuie sur les numéros de semestre: pour chaque Si, stocke :
* le (ou les) formsemestres de numéro i qu'a suivi un étudiant (2 si redoublant)
* le dernier semestre de numéro i qu'il a suivi (1 ou 0 si pas encore suivi)
Cette structuration (cf. attribut self.cursus) s'appuie sur les numéros de semestre: pour chaque Si, stocke :
le dernier semestre (en date) de numéro i qu'il a suivi (1 ou 0 si pas encore suivi). Ce semestre influera les
interclassement par semestre dans la promo.
Elle s'appuie également sur les aggrégats: pour chaque aggrégat (par ex, 3A=S1+S2+S3),
identifie les semestres que l'étudiant a suivi pour l'amener jusqu'au semestre terminal
de l'aggrégat. Ce parcours peut être :
* S1+S2+S1+S2+S3 si redoublement de la 1ère année
* S1+S2+(année de césure)+S3 si césure, ...
Elle calcule également sur les aggrégats (cf. attribut self.aggregats):
* pour un semestre de type Si, elle stocke le (ou les) formsemestres de numéro i qu'a suivi un étudiant
(2 si redoublant) dans self.aggregats
* pour des aggrégats de type iS ou iA (par ex, 3A=S1+S2+S3), elle identifie les semestres que l'étudiant
a suivi pour l'amener jusqu'au semestre terminal de l'aggrégat. Ce parcours peut être :
** S1+S2+S1+S2+S3 si redoublement de la 1ère année
** S1+S2+(année de césure)+S3 si césure, ...
"""
semestres_etudiant = self.cursus[etudid]["formsemestres"]
self.aggregats[etudid] = {}
"""Tri des semestres par numéro de semestre"""
for nom_sem in pe_tools.TOUS_LES_SEMESTRES:
i = int(nom_sem[1]) # le n° du semestre
semestres_i = {
@ -226,28 +233,28 @@ class EtudiantsJuryPE:
for fid in semestres_etudiant
if semestres_etudiant[fid].semestre_id == i
} # les semestres de n°i de l'étudiant
self.cursus[etudid]["aggregats"][nom_sem] = semestres_i
self.cursus[etudid]["semestres"][nom_sem] = get_dernier_semestre(semestres_i)
self.aggregats[etudid][nom_sem] = semestres_i
self.cursus[etudid][nom_sem] = get_dernier_semestre(semestres_i)
"""Tri des semestres par aggrégat et par semestre terminal"""
for aggregat in pe_tools.TOUS_LES_AGGREGATS:
self.cursus[etudid][aggregat] = {}
self.aggregats[etudid][aggregat] = {}
"""L'aggrégat considéré (par ex: 3S), son nom de son semestre terminal (par ex: S3) et son numéro (par ex: 3)"""
noms_semestre_de_aggregat = pe_tools.PARCOURS[aggregat]["aggregat"]
nom_semestre_terminal = noms_semestre_de_aggregat[-1]
numero_semestre_terminal = int(nom_semestre_terminal[-1])
"""Les semestres terminaux de l'aggrégat"""
# formsemestres_terminal = self.cursus[etudid]["aggregats"][nom_semestre_terminal]
# dernier_formsemestre_terminal = get_dernier_semestre(formsemestres_terminal) # le dernier en date
dernier_formsemestre_terminal = self.cursus[etudid]["semestres"][nom_semestre_terminal]
"""Le formsemestre terminal de l'aggrégat (par ex: son dernier S3 en date)"""
dernier_formsemestre_terminal = self.cursus[etudid][nom_semestre_terminal]
# for formsem_id_term in formsemestres_terminal:
if dernier_formsemestre_terminal: # ne considérant que le dernier
if dernier_formsemestre_terminal: # ne considérant que le dernier
formsem_id_term = list(dernier_formsemestre_terminal.keys())[0]
formsemestre_terminal = self.cursus[etudid]["formsemestres"][formsem_id_term]
formsemestre_terminal = self.cursus[etudid]["formsemestres"][
formsem_id_term
]
"""Semestres de n° inférieur (pax ex: des S1, S2, S3 pour un S3 terminal) et qui lui sont antérieurs"""
semestres_aggreges = {}
@ -259,7 +266,11 @@ class EtudiantsJuryPE:
):
semestres_aggreges[fid] = semestre
self.cursus[etudid][aggregat][formsem_id_term] = semestres_aggreges
self.aggregats[etudid][aggregat][formsem_id_term] = semestres_aggreges
"""Vérifications"""
dernier_semestre_aggregat = get_dernier_semestre(semestres_aggreges)
assert dernier_semestre_aggregat == dernier_formsemestre_terminal
def get_formsemestres_terminaux_aggregat(self, aggregat: str):
"""Pour un aggrégat donné, ensemble des formsemestres terminaux possibles pour l'aggrégat (pour l'aggrégat '3S'
@ -362,7 +373,7 @@ class EtudiantsJuryPE:
nom_sem = semestres_recherches
semestres = {}
for etudid in self.etudiants_ids:
semestres = semestres | self.cursus[etudid]["aggregats"][nom_sem]
semestres = semestres | self.aggregats[etudid][nom_sem]
return semestres
else:
raise ValueError(
@ -420,19 +431,33 @@ def annee_diplome(identite: Identite) -> int:
return None
def arret_de_formation(identite: Identite, cosemestres: list[FormSemestre]) -> bool:
"""Détermine si un étudiant a arrêté sa formation. Il peut s'agir :
"""Détermine si un étudiant a arrêté sa formation. Il peut s'agir :
* d'une réorientation à l'initiative du jury de semestre ou d'une démission ; dans ce cas, utilise les
décisions prises dans les jury de semestres (code NAR pour réorienté & DEM pour démissionnaire)
* d'une réorientation à l'initiative du jury de semestre ou d'une démission (on pourrait
utiliser les code NAR pour réorienté & DEM pour démissionnaire des résultats du jury renseigné dans la BDD,
mais pas nécessaire ici)
* d'un arrêt volontaire : l'étudiant disparait des listes d'inscrit, sans
pour autant avoir été indiqué NAR ou DEM. Dans ce cas, recherche son dernier semestre validé et
regarde s'il n'existe pas parmi les semestres existants dans Scodoc un semestre postérieur
(en terme de date de début)
de n° au moins égal à celui de son dernier semestre valide dans lequel il aurait pu
s'inscrire mais ne l'a pas fait.
* d'un arrêt volontaire : l'étudiant disparait des listes d'inscrits (sans pour autant avoir été indiqué NAR ou DEM).
Dans les cas, on considérera que l'étudiant a arrêté sa formation s'il n'est pas dans l'un des "derniers" cosemestres
(semestres conduisant à la même année de diplômation) connu dans Scodoc.
Par ex: au moment du jury PE en fin de S5 (pas de S6 renseigné dans Scodoc), l'étudiant doit appartenir à une
instance des S5 qui conduisent à la diplomation dans l'année visée. S'il n'est que dans un S4, il a sans doute
arrêté. A moins qu'il ne soit parti à l'étranger et , pas de notes.
TODO:: Cas de l'étranger, à coder/tester
**Attention** : Cela suppose que toutes les instances d'un semestre donné (par ex: toutes les instances de S6
accueillant un étudiant soient créées ; sinon les étudiants non inscrits dans un S6 seront considérés comme
ayant abandonnés)
TODO:: Peut-être à mettre en regard avec les propositions d'inscriptions d'étudiants dans un nouveau semestre
Pour chaque étudiant, recherche son dernier semestre en date (validé ou non) et
regarde s'il n'existe pas parmi les semestres existants dans Scodoc un semestre :
* dont les dates sont postérieures (en terme de date de début)
* de n° au moins égal à celui de son dernier semestre valide (S5 -> S5 ou S5 -> S6)
dans lequel il aurait pu s'inscrire mais ne l'a pas fait.
Args:
identite: L'identité d'un étudiant
@ -441,36 +466,37 @@ def arret_de_formation(identite: Identite, cosemestres: list[FormSemestre]) -> b
Returns:
Est-il réorienté, démissionnaire ou a-t-il arrêté de son propre chef sa formation ?
TODO:: A reprendre avec l'accès aux résultats des jury prévu par Scodoc9
TODO:: A reprendre pour le cas des étudiants à l'étranger
TODO:: A reprendre si BUT avec semestres décalés
"""
etudid = identite.etudid
reponse = False
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
(code, parcours) = sco_report.get_code_cursus_etud(etud)
# Est-il réorienté ou démissionnaire ?
if (
len(codes_cursus.CODES_SEM_REO & set(parcours.values())) > 0
): # Eliminé car NAR apparait dans le parcours
if pe_tools.PE_DEBUG and pe_tools.PE_DEBUG >= 2:
pe_tools.pe_print(" -> à éliminer car réorienté (NAR)")
return True
if "DEM" in list(parcours.values()): # Eliminé car DEM
if pe_tools.PE_DEBUG and pe_tools.PE_DEBUG >= 2:
pe_tools.pe_print(" -> à éliminer car DEM")
return True
# A-t-il arrêté volontairement sa formation ?
"""Son dernier semestre en date"""
dernier_formsemestre = identite.get_formsemestres()[0]
numero_dernier_formsemestre = dernier_formsemestre.semestre_id
# Y-a-t-il des cosemestres dans lesquels il aurait pu s'incrire ?
"""Les numéro de semestres possible dans lesquels il pourrait s'incrire"""
# semestre impair => passage de droit en semestre pair suivant (effet de l'annualisation)
if numero_dernier_formsemestre % 2 == 1:
numeros_possibles = list(
range(numero_dernier_formsemestre + 1, pe_tools.NBRE_SEMESTRES_DIPLOMANT)
)
# semestre pair => passage en année supérieure ou redoublement
else: #
numeros_possibles = list(
range(
max(numero_dernier_formsemestre - 1, 1),
pe_tools.NBRE_SEMESTRES_DIPLOMANT,
)
)
"""Y-a-t-il des cosemestres dans lesquels il aurait pu s'incrire ?"""
formsestres_superieurs_possibles = []
for fid, sem in cosemestres.items(): # Les semestres ayant des inscrits
if (
sem.formsemestre_id != dernier_formsemestre.formsemestre_id
fid != dernier_formsemestre.formsemestre_id
and sem.semestre_id in numeros_possibles
and sem.date_debut.year >= dernier_formsemestre.date_debut.year
and sem.semestre_id > dernier_formsemestre.semestre_id
): # date de debut des semestres possibles postérieur au dernier semestre de l'étudiant et de niveau plus élevé que le dernier semestre valide de l'étudiant
formsestres_superieurs_possibles.append(fid)
@ -481,7 +507,7 @@ def arret_de_formation(identite: Identite, cosemestres: list[FormSemestre]) -> b
def get_dernier_semestre(semestres: dict[int, FormSemestre]):
"""Renvoie le dernier semestre en date d'un dictionnaire
"""Renvoie le dernier semestre en date (de fin) d'un dictionnaire
de semestres de la forme {fid: FormSemestre(fid)}.
La date prise en compte est celle marquant la **fin** des semestres.

View File

@ -76,6 +76,12 @@ PE_LOCAL_FOOTER_TMPL = REP_LOCAL_AVIS + "local/modeles/un_footer.tex"
# ----------------------------------------------------------------------------------------
"""
Descriptif d'un parcours classique BUT
TODO:: A améliorer si BUT en moins de 6 semestres
"""
PARCOURS = {
"S1": {
"aggregat": ["S1"],
@ -156,6 +162,7 @@ PARCOURS = {
"affichage_long": "BUT (tout semestre inclus)",
},
}
NBRE_SEMESTRES_DIPLOMANT = 6
AGGREGAT_DIPLOMANT = (
"6S" # aggrégat correspondant à la totalité des notes pour le diplôme
)