2024-01-20 09:31:02 +01:00
# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Emmanuel Viennet emmanuel.viennet@viennet.net
#
##############################################################################
##############################################################################
# Module "Avis de poursuite d'étude"
# conçu et développé par Cléo Baras (IUT de Grenoble)
##############################################################################
"""
Created on 17 / 01 / 2024
@author : barasc
"""
2024-01-27 10:13:04 +01:00
import pandas as pd
2024-02-14 14:34:22 +01:00
from app import ScoValueError
2024-02-02 11:49:24 +01:00
from app . models import FormSemestre , Identite , Formation
2024-01-27 10:13:04 +01:00
from app . pe import pe_comp , pe_affichage
2024-02-02 11:49:24 +01:00
from app . scodoc import codes_cursus
2024-02-06 18:57:36 +01:00
from app . scodoc import sco_utils as scu
from app . comp . res_sem import load_formsemestre_results
2024-01-20 09:31:02 +01:00
2024-02-08 22:09:11 +01:00
2024-01-20 09:31:02 +01:00
class EtudiantsJuryPE :
2024-01-30 11:02:28 +01:00
""" Classe centralisant la gestion des étudiants à prendre en compte dans un jury de PE """
2024-01-20 09:31:02 +01:00
2024-01-23 18:44:44 +01:00
def __init__ ( self , annee_diplome : int ) :
"""
Args :
annee_diplome : L ' année de diplomation
"""
self . annee_diplome = annee_diplome
2024-01-27 10:13:04 +01:00
""" L ' année du diplôme """
2024-01-20 09:31:02 +01:00
2024-01-30 11:02:28 +01:00
self . identites : dict [ int , Identite ] = { } # ex. ETUDINFO_DICT
2024-01-27 10:13:04 +01:00
" Les identités des étudiants traités pour le jury "
2024-01-30 11:02:28 +01:00
self . cursus : dict [ int , dict ] = { }
2024-01-27 10:13:04 +01:00
" Les cursus (semestres suivis, abandons) des étudiants "
2024-01-24 15:37:50 +01:00
self . trajectoires = { }
2024-01-27 10:13:04 +01:00
""" Les trajectoires/chemins de semestres suivis par les étudiants
pour atteindre un aggrégat donné
( par ex : 3 S = S1 + S2 + S3 à prendre en compte avec d ' éventuels redoublements) " " "
2024-01-21 18:55:21 +01:00
2024-01-24 19:37:45 +01:00
self . etudiants_diplomes = { }
2024-01-30 11:02:28 +01:00
""" Les identités des étudiants à considérer au jury (ceux qui seront effectivement
diplômés ) """
2024-01-27 10:13:04 +01:00
2024-01-23 09:05:52 +01:00
self . diplomes_ids = { }
2024-01-27 10:13:04 +01:00
""" Les etudids des étudiants diplômés """
2024-01-24 19:37:45 +01:00
2024-01-20 09:31:02 +01:00
self . etudiants_ids = { }
2024-01-30 11:02:28 +01:00
""" Les etudids des étudiants dont il faut calculer les moyennes/classements
( même si d ' éventuels abandons).
Il s ' agit des étudiants inscrits dans les co-semestres (ceux du jury mais aussi
d ' autres ayant été réorientés ou ayant abandonnés) " " "
2024-01-25 21:54:22 +01:00
self . cosemestres : dict [ int , FormSemestre ] = None
" Les cosemestres donnant lieu à même année de diplome "
2024-01-21 18:55:21 +01:00
2024-01-27 10:13:04 +01:00
self . abandons = { }
""" Les étudiants qui ne seront pas diplômés à ce jury (redoublants/réorientés) """
self . abandons_ids = { }
""" Les etudids des étudiants redoublants/réorientés """
2024-02-14 14:34:22 +01:00
def find_etudiants ( self , formsemestre_base : FormSemestre ) :
2024-01-20 09:31:02 +01:00
""" Liste des étudiants à prendre en compte dans le jury PE, en les recherchant
2024-02-02 13:48:07 +01:00
de manière automatique par rapport à leur année de diplomation ` ` annee_diplome ` ` .
2024-01-20 09:31:02 +01:00
Les données obtenues sont stockées dans les attributs de EtudiantsJuryPE .
* Remarque * : ex : JuryPE . get_etudiants_in_jury ( )
"""
2024-02-06 17:52:52 +01:00
cosemestres = pe_comp . get_cosemestres_diplomants ( self . annee_diplome )
2024-01-23 09:54:30 +01:00
self . cosemestres = cosemestres
2024-01-20 09:31:02 +01:00
2024-01-27 10:13:04 +01:00
pe_affichage . pe_print (
f " 1) Recherche des coSemestres -> { len ( cosemestres ) } trouvés "
)
2024-01-25 21:54:22 +01:00
2024-01-27 08:22:36 +01:00
pe_affichage . pe_print ( " 2) Liste des étudiants dans les différents co-semestres " )
2024-01-21 18:55:21 +01:00
self . etudiants_ids = get_etudiants_dans_semestres ( cosemestres )
2024-01-27 08:22:36 +01:00
pe_affichage . pe_print (
2024-01-30 11:02:28 +01:00
f " => { len ( self . etudiants_ids ) } étudiants trouvés dans les cosemestres "
2024-01-21 18:55:21 +01:00
)
2024-01-20 09:31:02 +01:00
2024-01-26 09:29:36 +01:00
# Analyse des parcours étudiants pour déterminer leur année effective de diplome
# avec prise en compte des redoublements, des abandons, ....
2024-01-27 08:22:36 +01:00
pe_affichage . pe_print ( " 3) Analyse des parcours individuels des étudiants " )
2024-01-20 09:31:02 +01:00
2024-01-30 11:02:28 +01:00
for etudid in self . etudiants_ids :
self . identites [ etudid ] = Identite . get_etud ( etudid )
2024-01-21 18:55:21 +01:00
2024-01-26 09:29:36 +01:00
# Analyse son cursus
2024-02-14 14:34:22 +01:00
self . analyse_etat_etudiant ( etudid , cosemestres , formsemestre_base )
2024-01-21 18:55:21 +01:00
2024-01-26 09:29:36 +01:00
# Analyse son parcours pour atteindre chaque semestre de la formation
2024-01-24 15:37:50 +01:00
self . structure_cursus_etudiant ( etudid )
2024-01-21 18:55:21 +01:00
2024-01-26 09:29:36 +01:00
# Les étudiants à prendre dans le diplôme, étudiants ayant abandonnés non compris
2024-01-24 19:37:45 +01:00
self . etudiants_diplomes = self . get_etudiants_diplomes ( )
self . diplomes_ids = set ( self . etudiants_diplomes . keys ( ) )
2024-01-27 10:13:04 +01:00
self . etudiants_ids = set ( self . identites . keys ( ) )
2024-01-20 09:31:02 +01:00
2024-01-27 10:13:04 +01:00
# Les abandons (pour debug)
self . abandons = self . get_etudiants_redoublants_ou_reorientes ( )
2024-02-08 22:09:11 +01:00
# Les identités des étudiants ayant redoublés ou ayant abandonnés
2024-02-05 12:58:09 +01:00
2024-01-27 10:13:04 +01:00
self . abandons_ids = set ( self . abandons )
2024-02-08 22:09:11 +01:00
# Les identifiants des étudiants ayant redoublés ou ayant abandonnés
2024-01-27 10:13:04 +01:00
2024-01-20 09:31:02 +01:00
# Synthèse
2024-01-27 08:22:36 +01:00
pe_affichage . pe_print (
2024-01-24 19:37:45 +01:00
f " => { len ( self . etudiants_diplomes ) } étudiants à diplômer en { self . annee_diplome } "
2024-01-20 16:34:38 +01:00
)
2024-01-24 19:37:45 +01:00
nbre_abandons = len ( self . etudiants_ids ) - len ( self . etudiants_diplomes )
2024-01-27 10:13:04 +01:00
assert nbre_abandons == len ( self . abandons_ids )
pe_affichage . pe_print (
f " => { nbre_abandons } étudiants non considérés (redoublement, réorientation, abandon "
)
2024-01-27 08:22:36 +01:00
# pe_affichage.pe_print(
# " => quelques étudiants futurs diplômés : "
# + ", ".join([str(etudid) for etudid in list(self.etudiants_diplomes)[:10]])
# )
# pe_affichage.pe_print(
# " => semestres dont il faut calculer les moyennes : "
# + ", ".join([str(fid) for fid in list(self.formsemestres_jury_ids)])
# )
2024-01-27 10:13:04 +01:00
2024-01-23 18:44:44 +01:00
def get_etudiants_diplomes ( self ) - > dict [ int , Identite ] :
2024-01-20 16:34:38 +01:00
""" Identités des étudiants (sous forme d ' un dictionnaire ` { etudid: Identite(etudid)}`
qui vont être à traiter au jury PE pour
l ' année de diplômation donnée et n ' ayant ni été réorienté , ni abandonné .
Returns :
Un dictionnaire ` { etudid : Identite ( etudid ) } `
"""
2024-01-21 18:55:21 +01:00
etudids = [
etudid
2024-01-30 11:02:28 +01:00
for etudid , cursus_etud in self . cursus . items ( )
if cursus_etud [ " diplome " ] == self . annee_diplome
and cursus_etud [ " abandon " ] is False
2024-01-21 18:55:21 +01:00
]
etudiants = { etudid : self . identites [ etudid ] for etudid in etudids }
2024-01-20 16:34:38 +01:00
return etudiants
2024-01-27 10:13:04 +01:00
def get_etudiants_redoublants_ou_reorientes ( self ) - > dict [ int , Identite ] :
""" Identités des étudiants (sous forme d ' un dictionnaire ` { etudid: Identite(etudid)}`
dont les notes seront prises en compte ( pour les classements ) mais qui n ' apparaitront
pas dans le jury car diplômé une autre année ( redoublants ) ou réorienté ou démissionnaire .
Returns :
Un dictionnaire ` { etudid : Identite ( etudid ) } `
"""
etudids = [
etudid
2024-01-30 11:02:28 +01:00
for etudid , cursus_etud in self . cursus . items ( )
if cursus_etud [ " diplome " ] != self . annee_diplome
or cursus_etud [ " abandon " ] is True
2024-01-27 10:13:04 +01:00
]
etudiants = { etudid : self . identites [ etudid ] for etudid in etudids }
return etudiants
2024-02-14 14:34:22 +01:00
def analyse_etat_etudiant (
self ,
etudid : int ,
cosemestres : dict [ int , FormSemestre ] ,
formsemestre_base : FormSemestre ,
) :
2024-01-21 18:55:21 +01:00
""" 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é)
* un étudiant qui ne sera pas considéré dans le jury mais qui a participé dans sa scolarité
à un ( ou plusieurs ) semestres communs aux étudiants du jury ( et impactera les classements )
2024-01-20 09:31:02 +01:00
2024-01-21 18:55:21 +01:00
L ' analyse consiste :
2024-01-20 09:31:02 +01:00
2024-01-21 18:55:21 +01:00
* à insérer une entrée dans ` ` self . cursus ` ` pour mémoriser son identité ,
2024-01-20 09:31:02 +01:00
avec son nom , prénom , etc . . .
2024-02-14 14:34:22 +01:00
* à analyser son parcours , pour déterminer s ' il a démissionné, redoublé (autre année de diplôme)
ou a abandonné l ' IUT en cours de route (cf. clé abandon). Un étudiant est considéré en abandon s ' il n ' est
inscrit à aucun cosemestres de rang supérieur ou égal ( et donc de dates )
à celui ayant servi à lancer le jury ( ` formsemestre_base ` )
2024-01-20 09:31:02 +01:00
Args :
etudid : L ' etudid d ' un étudiant , à ajouter à ceux traiter par le jury
2024-01-21 18:55:21 +01:00
cosemestres : Dictionnaire { fid : Formsemestre ( fid ) } donnant accès aux cosemestres
de même année de diplomation
2024-01-20 09:31:02 +01:00
"""
identite = Identite . get_etud ( etudid )
2024-01-26 09:42:41 +01:00
# Le cursus global de l'étudiant (restreint aux semestres APC)
2024-01-25 17:17:01 +01:00
formsemestres = identite . get_formsemestres ( )
2024-01-20 09:31:02 +01:00
semestres_etudiant = {
2024-01-30 11:02:28 +01:00
formsemestre . formsemestre_id : formsemestre
for formsemestre in formsemestres
if formsemestre . formation . is_apc ( )
2024-01-20 16:34:38 +01:00
}
2024-01-20 09:31:02 +01:00
self . cursus [ etudid ] = {
" etudid " : etudid , # les infos sur l'étudiant
" etat_civil " : identite . etat_civil , # Ajout à la table jury
2024-01-23 09:54:30 +01:00
" nom " : identite . nom ,
2024-01-25 21:54:22 +01:00
" entree " : formsemestres [ - 1 ] . date_debut . year , # La date d'entrée à l'IUT
2024-01-26 09:29:36 +01:00
" diplome " : get_annee_diplome (
identite
) , # Le date prévisionnelle de son diplôme
2024-01-20 16:34:38 +01:00
" formsemestres " : semestres_etudiant , # les semestres de l'étudiant
2024-01-25 21:54:22 +01:00
" nb_semestres " : len (
semestres_etudiant
) , # le nombre de semestres de l'étudiant
2024-01-23 09:54:30 +01:00
" abandon " : False , # va être traité en dessous
2024-01-20 09:31:02 +01:00
}
2024-02-14 14:34:22 +01:00
# Si l'étudiant est succeptible d'être diplomé
if self . cursus [ etudid ] [ " diplome " ] == self . annee_diplome :
# Est-il démissionnaire : charge son dernier semestre pour connaitre son état ?
dernier_semes_etudiant = formsemestres [ 0 ]
res = load_formsemestre_results ( dernier_semes_etudiant )
etud_etat = res . get_etud_etat ( etudid )
if etud_etat == scu . DEMISSION :
self . cursus [ etudid ] [ " abandon " ] = True
else :
# Est-il réorienté ou a-t-il arrêté (volontairement) sa formation ?
self . cursus [ etudid ] [ " abandon " ] = arret_de_formation (
identite , cosemestres , formsemestre_base
)
2024-01-20 09:31:02 +01:00
2024-01-24 15:37:50 +01:00
def get_semestres_significatifs ( self , etudid : int ) :
""" Ensemble des semestres d ' un étudiant, qui l ' auraient amené à être diplomé
l ' année visée (supprime les semestres qui conduisent à une diplomation
postérieure à celle du jury visé )
2024-01-21 18:55:21 +01:00
2024-01-24 15:37:50 +01:00
Args :
etudid : L ' identifiant d ' un étudiant
2024-01-21 18:55:21 +01:00
2024-01-24 15:37:50 +01:00
Returns :
Un dictionnaire ` ` { fid : FormSemestre ( fid ) ` ` dans lequel les semestres
amènent à une diplomation avant l ' annee de diplomation du jury
2024-01-21 18:55:21 +01:00
"""
2024-01-24 15:37:50 +01:00
semestres_etudiant = self . cursus [ etudid ] [ " formsemestres " ]
2024-01-23 18:44:44 +01:00
semestres_significatifs = { }
for fid in semestres_etudiant :
semestre = semestres_etudiant [ fid ]
2024-01-25 17:17:01 +01:00
if pe_comp . get_annee_diplome_semestre ( semestre ) < = self . annee_diplome :
2024-01-23 18:44:44 +01:00
semestres_significatifs [ fid ] = semestre
2024-01-24 15:37:50 +01:00
return semestres_significatifs
2024-01-23 18:44:44 +01:00
2024-01-24 15:37:50 +01:00
def structure_cursus_etudiant ( self , etudid : int ) :
""" 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 .
Cette structuration s ' appuie sur les numéros de semestre: pour chaque Si, stocke :
2024-01-26 09:29:36 +01:00
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 .
2024-01-24 15:37:50 +01:00
"""
semestres_significatifs = self . get_semestres_significatifs ( etudid )
2024-01-23 09:54:30 +01:00
2024-01-26 09:29:36 +01:00
# Tri des semestres par numéro de semestre
2024-02-08 22:09:11 +01:00
for i in range ( 1 , pe_comp . NBRE_SEMESTRES_DIPLOMANT + 1 ) :
2024-01-30 11:02:28 +01:00
# les semestres de n°i de l'étudiant:
2024-01-20 16:34:38 +01:00
semestres_i = {
2024-01-30 11:02:28 +01:00
fid : sem_sig
for fid , sem_sig in semestres_significatifs . items ( )
if sem_sig . semestre_id == i
}
2024-02-05 12:58:09 +01:00
self . cursus [ etudid ] [ f " S { i } " ] = semestres_i
2024-01-24 15:37:50 +01:00
2024-02-08 22:09:11 +01:00
def get_formsemestres_terminaux_aggregat (
self , aggregat : str
) - > dict [ int , FormSemestre ] :
2024-01-23 18:44:44 +01:00
""" Pour un aggrégat donné, ensemble des formsemestres terminaux possibles pour l ' aggrégat
( pour l ' aggrégat ' 3 S ' incluant S1+S2+S3, a pour semestre terminal S3).
Ces formsemestres traduisent :
2024-01-21 18:55:21 +01:00
2024-02-08 22:09:11 +01:00
* les différents parcours des étudiants liés par exemple au choix de modalité
( par ex : S1 FI + S2 FI + S3 FI ou S1 FI + S2 FI + S3 UFA ) , en renvoyant les
formsemestre_id du S3 FI et du S3 UFA .
* les éventuelles situations de redoublement ( par ex pour 1 étudiant ayant
redoublé sa 2 ème année :
S1 + S2 + S3 ( 1 ère session ) et S1 + S2 + S3 + S4 + S3 ( 2 ème session ) , en
renvoyant les formsemestre_id du S3 ( 1 ère session ) et du S3 ( 2 ème session )
2024-01-21 18:55:21 +01:00
Args :
aggregat : L ' aggrégat
Returns :
2024-01-24 15:37:50 +01:00
Un dictionnaire ` ` { fid : FormSemestre ( fid ) } ` `
2024-01-21 18:55:21 +01:00
"""
formsemestres_terminaux = { }
2024-01-26 09:29:36 +01:00
for trajectoire_aggr in self . trajectoires . values ( ) :
trajectoire = trajectoire_aggr [ aggregat ]
if trajectoire :
# Le semestre terminal de l'étudiant de l'aggrégat
2024-02-03 10:46:14 +01:00
fid = trajectoire . formsemestre_final . formsemestre_id
formsemestres_terminaux [ fid ] = trajectoire . formsemestre_final
2024-01-21 18:55:21 +01:00
return formsemestres_terminaux
2024-01-27 10:13:04 +01:00
def nbre_etapes_max_diplomes ( self , etudids : list [ int ] ) - > int :
""" Partant d ' un ensemble d ' étudiants,
2024-01-25 17:17:01 +01:00
nombre de semestres ( étapes ) maximum suivis par les étudiants du jury .
2024-01-27 10:13:04 +01:00
Args :
etudids : Liste d ' étudid d ' étudiants
2024-01-25 17:17:01 +01:00
"""
nbres_semestres = [ ]
2024-01-27 10:13:04 +01:00
for etudid in etudids :
2024-01-25 21:54:22 +01:00
nbres_semestres . append ( self . cursus [ etudid ] [ " nb_semestres " ] )
2024-01-27 10:13:04 +01:00
if not nbres_semestres :
return 0
2024-01-30 11:02:28 +01:00
return max ( nbres_semestres )
2024-01-27 10:13:04 +01:00
def df_administratif ( self , etudids : list [ int ] ) - > pd . DataFrame :
""" Synthétise toutes les données administratives d ' un groupe
d ' étudiants fournis par les etudid dans un dataFrame
Args :
etudids : La liste des étudiants à prendre en compte
"""
etudids = list ( etudids )
# Récupération des données des étudiants
administratif = { }
nbre_semestres_max = self . nbre_etapes_max_diplomes ( etudids )
for etudid in etudids :
etudiant = self . identites [ etudid ]
cursus = self . cursus [ etudid ]
formsemestres = cursus [ " formsemestres " ]
if cursus [ " diplome " ] :
diplome = cursus [ " diplome " ]
else :
diplome = " indéterminé "
administratif [ etudid ] = {
2024-01-30 11:02:28 +01:00
" etudid " : etudiant . id ,
" INE " : etudiant . code_ine or " " ,
" NIP " : etudiant . code_nip or " " ,
2024-01-27 10:13:04 +01:00
" Nom " : etudiant . nom ,
" Prenom " : etudiant . prenom ,
" Civilite " : etudiant . civilite_str ,
" Age " : pe_comp . calcul_age ( etudiant . date_naissance ) ,
2024-01-30 11:02:28 +01:00
" Date entree " : cursus [ " entree " ] ,
" Date diplome " : diplome ,
" Nb semestres " : len ( formsemestres ) ,
2024-01-27 10:13:04 +01:00
}
# Ajout des noms de semestres parcourus
2024-02-02 11:49:24 +01:00
etapes = etapes_du_cursus ( formsemestres , nbre_semestres_max )
2024-01-27 10:13:04 +01:00
administratif [ etudid ] | = etapes
# Construction du dataframe
df = pd . DataFrame . from_dict ( administratif , orient = " index " )
# Tri par nom/prénom
df . sort_values ( by = [ " Nom " , " Prenom " ] , inplace = True )
return df
2024-01-25 17:17:01 +01:00
2024-01-20 09:31:02 +01:00
2024-01-24 15:37:50 +01:00
def get_etudiants_dans_semestres ( semestres : dict [ int , FormSemestre ] ) - > set :
2024-01-20 09:31:02 +01:00
""" Ensemble d ' identifiants des étudiants (identifiés via leur ``etudid``)
inscrits à l ' un des semestres de la liste de ``semestres``.
2024-01-26 09:29:36 +01:00
Remarque : Les ` ` cosemestres ` ` sont généralement obtenus avec
` ` sco_formsemestre . do_formsemestre_list ( ) ` `
2024-01-20 09:31:02 +01:00
Args :
2024-01-24 15:37:50 +01:00
semestres : Un dictionnaire ` ` { fid : Formsemestre ( fid ) } ` ` donnant un
2024-01-20 09:31:02 +01:00
ensemble d ' identifiant de semestres
Returns :
Un ensemble d ` ` etudid ` `
"""
etudiants_ids = set ( )
2024-01-26 09:29:36 +01:00
for sem in semestres . values ( ) : # pour chacun des semestres de la liste
2024-01-20 09:31:02 +01:00
etudiants_du_sem = { ins . etudid for ins in sem . inscriptions }
2024-01-27 08:22:36 +01:00
pe_affichage . pe_print ( f " --> { sem } : { len ( etudiants_du_sem ) } etudiants " )
2024-01-20 09:31:02 +01:00
etudiants_ids = (
etudiants_ids | etudiants_du_sem
) # incluant la suppression des doublons
return etudiants_ids
2024-02-08 22:09:11 +01:00
def get_annee_diplome ( etud : Identite ) - > int | None :
2024-01-20 09:31:02 +01:00
""" L ' année de diplôme prévue d ' un étudiant en fonction de ses semestres
d ' inscription (pour un BUT).
Args :
identite : L ' identité d ' un étudiant
Returns :
2024-02-08 22:09:11 +01:00
L ' année prévue de sa diplômation, ou None si aucun semestre
2024-01-20 09:31:02 +01:00
"""
2024-01-26 15:26:11 +01:00
formsemestres_apc = get_semestres_apc ( etud )
2024-01-26 07:13:09 +01:00
2024-01-26 09:42:41 +01:00
if formsemestres_apc :
2024-01-26 09:53:32 +01:00
dates_possibles_diplome = [ ]
2024-02-08 22:09:11 +01:00
# Années de diplômation prédites en fonction des semestres
# (d'une formation APC) d'un étudiant
2024-01-26 09:42:41 +01:00
for sem_base in formsemestres_apc :
2024-01-26 07:13:09 +01:00
annee = pe_comp . get_annee_diplome_semestre ( sem_base )
if annee :
2024-01-26 09:42:41 +01:00
dates_possibles_diplome . append ( annee )
2024-01-26 07:13:09 +01:00
if dates_possibles_diplome :
return max ( dates_possibles_diplome )
2024-01-26 09:42:41 +01:00
return None
def get_semestres_apc ( identite : Identite ) - > list :
""" Liste des semestres d ' un étudiant qui corresponde à une formation APC.
Args :
identite : L ' identité d ' un étudiant
Returns :
Liste de ` ` FormSemestre ` ` correspondant à une formation APC
"""
semestres = identite . get_formsemestres ( )
semestres_apc = [ ]
for sem in semestres :
if sem . formation . is_apc ( ) :
semestres_apc . append ( sem )
return semestres_apc
2024-01-20 09:31:02 +01:00
2024-02-14 14:34:22 +01:00
def arret_de_formation (
etud : Identite , cosemestres : dict [ int , FormSemestre ] , formsemestre_base : FormSemestre
) - > bool :
""" Détermine si un étudiant a arrêté sa formation (volontairement ou non). Il peut s ' agir :
2024-01-23 09:05:52 +01:00
2024-02-08 22:09:11 +01:00
* 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 )
2024-01-23 09:05:52 +01:00
2024-02-08 22:09:11 +01:00
* d ' un arrêt volontaire : l ' étudiant disparait des listes d ' inscrits (sans pour
autant avoir été indiqué NAR ou DEM ) .
2024-01-23 09:05:52 +01:00
2024-02-08 22:09:11 +01:00
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)
2024-02-14 14:34:22 +01:00
connu dans Scodoc . Par " derniers " cosemestres , est fait le choix d ' analyser tous les cosemestres
de rang / semestre_id supérieur ou égal ( et donc de dates ) à celui du ` ` formsemestre_base ` ` ayant servi à lancer
le jury PE .
2024-01-20 09:31:02 +01:00
2024-02-08 22:09:11 +01:00
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 là, pas de notes.
2024-01-23 09:05:52 +01:00
TODO : : Cas de l ' étranger, à coder/tester
2024-01-20 09:31:02 +01:00
2024-02-08 22:09:11 +01:00
* * 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 )
2024-01-23 09:05:52 +01:00
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 .
2024-01-20 09:31:02 +01:00
Args :
2024-01-26 09:53:32 +01:00
etud : L ' identité d ' un étudiant
2024-01-20 09:31:02 +01:00
cosemestres : Les semestres donnant lieu à diplômation ( sans redoublement ) en date du jury
Returns :
Est - il réorienté , démissionnaire ou a - t - il arrêté de son propre chef sa formation ?
2024-01-23 09:05:52 +01:00
TODO : : A reprendre pour le cas des étudiants à l ' étranger
2024-01-23 09:54:30 +01:00
TODO : : A reprendre si BUT avec semestres décalés
2024-01-20 09:31:02 +01:00
"""
2024-01-26 09:42:41 +01:00
# Les semestres APC de l'étudiant
2024-01-26 09:53:32 +01:00
semestres = get_semestres_apc ( etud )
2024-01-26 09:42:41 +01:00
semestres_apc = { sem . semestre_id : sem for sem in semestres }
if not semestres_apc :
return True
2024-02-14 14:34:22 +01:00
# Les cosemestres de rang supérieur ou égal à celui de formsemestre, triés par rang,
# sous la forme ``{semestre_id: [liste des comestres associé à ce semestre_id]}``
cosemestres_tries_par_rang = pe_comp . tri_semestres_par_rang ( cosemestres )
cosemestres_superieurs = { }
for rang in cosemestres_tries_par_rang :
if rang > = formsemestre_base . semestre_id :
cosemestres_superieurs [ rang ] = cosemestres_tries_par_rang [ rang ]
# Si pas d'autres cosemestres postérieurs
if not cosemestres_superieurs :
return False
# Pour chaque rang de (co)semestres, y-a-il un dans lequel il est inscrit ?
etat_inscriptions = { rang : False for rang in cosemestres_superieurs }
for rang in etat_inscriptions :
for sem in cosemestres_superieurs [ rang ] :
etudiants_du_sem = { ins . etudid for ins in sem . inscriptions }
if etud . etudid in etudiants_du_sem :
etat_inscriptions [ rang ] = True
# Vérifie qu'il n'y a pas de "trous" dans les rangs des cosemestres
rangs = etat_inscriptions . keys ( )
if list ( rangs ) != list ( range ( min ( rangs ) , max ( rangs ) + 1 ) ) :
difference = set ( range ( min ( rangs ) , max ( rangs ) + 1 ) ) - set ( rangs )
affichage = " , " . join ( [ f " S { val } " for val in difference ] )
raise ScoValueError ( f " Il manque le(s) semestre(s) { affichage } au cursus de vos étudiants. " )
# Est-il inscrit à tous les semestres de rang supérieur ? Si non, est démissionnaire
est_demissionnaire = sum ( etat_inscriptions . values ( ) ) != len ( rangs )
if est_demissionnaire :
non_inscrit_a = [ rang for rang in etat_inscriptions if not etat_inscriptions [ rang ] ]
affichage = " , " . join ( [ f " S { val } " for val in non_inscrit_a ] )
pe_affichage . pe_print ( f " { etud . etat_civil } ( { etud . etudid } considéré en abandon car non inscrit dans un (ou des) semestre(s) { affichage } amenant à diplômation " )
return est_demissionnaire
# # Son dernier semestre APC en date
# dernier_formsemestre = get_dernier_semestre_en_date(semestres_apc)
# numero_dernier_formsemestre = dernier_formsemestre.semestre_id
#
# # 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_comp.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_comp.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 (
# fid != dernier_formsemestre.formsemestre_id
# and sem.semestre_id in numeros_possibles
# and sem.date_debut.year >= dernier_formsemestre.date_debut.year
# ):
# # 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)
#
# if len(formsestres_superieurs_possibles) > 0:
# return True
#
# return False
2024-01-20 16:34:38 +01:00
2024-01-25 17:17:01 +01:00
def get_dernier_semestre_en_date ( semestres : dict [ int , FormSemestre ] ) - > FormSemestre :
2024-01-24 15:37:50 +01:00
""" Renvoie le dernier semestre en **date de fin** d ' un dictionnaire
2024-01-25 17:17:01 +01:00
de semestres ( potentiellement non trié ) de la forme ` ` { fid : FormSemestre ( fid ) } ` ` .
2024-01-20 16:34:38 +01:00
Args :
semestres : Un dictionnaire de semestres
Return :
2024-01-24 15:37:50 +01:00
Le FormSemestre du semestre le plus récent
2024-01-20 16:34:38 +01:00
"""
if semestres :
fid_dernier_semestre = list ( semestres . keys ( ) ) [ 0 ]
2024-01-24 15:37:50 +01:00
dernier_semestre : FormSemestre = semestres [ fid_dernier_semestre ]
2024-01-20 16:34:38 +01:00
for fid in semestres :
2024-01-24 15:37:50 +01:00
if semestres [ fid ] . date_fin > dernier_semestre . date_fin :
dernier_semestre = semestres [ fid ]
2024-01-20 16:34:38 +01:00
return dernier_semestre
2024-01-26 09:29:36 +01:00
return None
2024-02-02 11:49:24 +01:00
def etapes_du_cursus (
semestres : dict [ int , FormSemestre ] , nbre_etapes_max : int
) - > list [ str ] :
""" Partant d ' un dictionnaire de semestres (qui retrace
la scolarité d ' un étudiant), liste les noms des
semestres ( en version abbrégée )
qu ' un étudiant a suivi au cours de sa scolarité à l ' IUT .
Les noms des semestres sont renvoyés dans un dictionnaire
` ` { " etape i " : nom_semestre_a_etape_i } ` `
avec i variant jusqu ' à nbre_semestres_max. (S ' il n ' y a pas de semestre à l ' étape i ,
le nom affiché est vide .
La fonction suppose la liste des semestres triées par ordre
décroissant de date .
Args :
semestres : une liste de ` ` FormSemestre ` `
nbre_etapes_max : le nombre d ' étapes max prise en compte
Returns :
Une liste de nom de semestre ( dans le même ordre que les ` ` semestres ` ` )
See also :
app . pe . pe_affichage . nom_semestre_etape
"""
assert len ( semestres ) < = nbre_etapes_max
noms = [ nom_semestre_etape ( sem , avec_fid = False ) for ( fid , sem ) in semestres . items ( ) ]
noms = noms [ : : - 1 ] # trie par ordre croissant
dico = { f " Etape { i + 1 } " : " " for i in range ( nbre_etapes_max ) }
for i , nom in enumerate ( noms ) : # Charge les noms de semestres
dico [ f " Etape { i + 1 } " ] = nom
return dico
def nom_semestre_etape ( semestre : FormSemestre , avec_fid = False ) - > str :
""" Nom d ' un semestre à afficher dans le descriptif des étapes de la scolarité
d ' un étudiant.
Par ex : Pour un S2 , affiche ` ` " Semestre 2 FI S014-2015 (129) " ` ` avec :
* 2 le numéro du semestre ,
* FI la modalité ,
* 2014 - 2015 les dates
Args :
semestre : Un ` ` FormSemestre ` `
avec_fid : Ajoute le n ° du semestre à la description
Returns :
La chaine de caractères décrivant succintement le semestre
"""
formation : Formation = semestre . formation
parcours = codes_cursus . get_cursus_from_code ( formation . type_parcours )
description = [
parcours . SESSION_NAME . capitalize ( ) ,
str ( semestre . semestre_id ) ,
semestre . modalite , # eg FI ou FC
f " { semestre . date_debut . year } - { semestre . date_fin . year } " ,
]
if avec_fid :
description . append ( f " ( { semestre . formsemestre_id } ) " )
return " " . join ( description )