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
"""
import app . pe . pe_tools as pe_tools
from app . models import FormSemestre , Identite
from app . pe . pe_tools import pe_print
from app . scodoc import (
sco_etud ,
codes_cursus ,
sco_formsemestre ,
sco_formsemestre_inscriptions ,
sco_report ,
)
import datetime
class EtudiantsJuryPE :
""" Classe centralisant la gestion des étudiants à prendre en compte dans un jury de PE """
def __init__ ( self ) :
""" """
" Les identités des étudiants du jury "
self . identites = { } # ex. ETUDINFO_DICT
" Les cursus (semestres suivis, abandons, ...) "
self . cursus = { }
" Les etudids des étudiants à considérer au jury "
self . etudiants_jury_ids = { }
" Les etudids des étudiants dont il faut calculer les moyennes/classements "
self . etudiants_ids = { }
" Les formsemestres dont il faut calculer les moyennes "
self . formsemestres_jury_ids = { }
def find_etudiants ( self , annee_diplome : int , formation_id : int ) :
""" Liste des étudiants à prendre en compte dans le jury PE, en les recherchant
de manière automatique par rapport à leur année de diplomation ` ` annee_diplome ` `
dans la formation ` ` formation_id ` ` .
Les données obtenues sont stockées dans les attributs de EtudiantsJuryPE .
Args :
annee_diplome : L ' année de diplomation
formation_id : L ' identifiant de la formation
* Remarque * : ex : JuryPE . get_etudiants_in_jury ( )
"""
" Les cosemestres donnant lieu à même année de diplome "
cosemestres = pe_tools . get_cosemestres_diplomants (
annee_diplome , None # formation_id,
)
pe_tools . pe_print (
" 1) Recherche des coSemestres -> %d trouvés " % len ( cosemestres )
)
""" Les étudiants inscrits dans les co-semestres (ceux du jury mais aussi d ' autres ayant été réorientés ou ayant abandonnés) """
pe_tools . pe_print ( " 2) Liste des étudiants dans les différents co-semestres " )
self . etudiants_ids = get_etudiants_dans_semestres (
cosemestres
) # étudiants faisant partie de tous les cosemestres
pe_tools . pe_print ( " => %d étudiants trouvés " % len ( self . etudiants_ids ) )
# L'analyse des parcours étudiants pour déterminer leur année effective de diplome avec prise en compte des redoublements, des abandons, ....
pe_tools . pe_print ( " 3) Analyse des parcours individuels des étudiants " )
no_etud = 0
2024-01-20 16:34:38 +01:00
for no_etud , etudid in enumerate ( self . etudiants_ids ) :
2024-01-20 09:31:02 +01:00
self . add_etudid ( etudid , cosemestres )
if ( no_etud + 1 ) % 10 == 0 :
pe_tools . pe_print ( ( no_etud + 1 ) , " " , end = " " )
no_etud + = 1
pe_tools . pe_print ( )
""" Les étudiants à prendre dans le diplôme, étudiants ayant abandonnés non compris """
self . etudiants_jury_ids = self . get_etudids ( annee_diplome )
""" Les étudiants dont il faut calculer les moyennes """
self . etudiants_ids = { etudid for etudid in self . cursus }
""" Les formsemestres (des étudiants) dont il faut calculer les moyennes """
self . formsemestres_jury_ids = self . get_formsemestres_jury ( )
# Synthèse
2024-01-20 16:34:38 +01:00
pe_tools . pe_print (
f " => { len ( self . etudiants_jury_ids ) } étudiants à diplômer en { annee_diplome } "
)
2024-01-20 09:31:02 +01:00
nbre_abandons = len ( self . etudiants_ids ) - len ( self . etudiants_ids )
pe_tools . pe_print ( f " => { nbre_abandons } étudiants éliminer pour abandon " )
2024-01-20 16:34:38 +01:00
pe_tools . pe_print (
f " => quelques étudiants futurs diplômés : "
+ " , " . join ( [ str ( etudid ) for etudid in list ( self . etudiants_jury_ids ) [ : 10 ] ] )
)
pe_tools . pe_print (
f " => semestres dont il faut calculer les moyennes : "
+ " , " . join ( [ str ( fid ) for fid in list ( self . formsemestres_jury_ids ) ] )
)
2024-01-20 09:31:02 +01:00
2024-01-20 16:34:38 +01:00
def get_etudids ( self , annee_diplome : int = None , ordre = " aucun " ) - > list :
2024-01-20 09:31:02 +01:00
""" Liste des etudid des étudiants 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é .
2024-01-20 16:34:38 +01:00
Si l ' année de diplômation n ' est pas précisée ( None ) , inclus les étudiants réorientés
ou ayant abandonné .
2024-01-20 09:31:02 +01:00
Si l ' ``ordre`` est précisé, trie la liste par ordre alphabétique de etat_civil
Args :
annee_diplome : Année de diplomation visée pour le jury
ordre : Un ordre de tri
Returns :
Une liste contenant des ` ` etudids ` `
Note : ex JuryPE . get_etudids_du_jury ( )
"""
2024-01-20 16:34:38 +01:00
if annee_diplome :
etudids = [
etudid
for ( etudid , donnees ) in self . cursus . items ( )
if donnees [ " diplome " ] == annee_diplome and not donnees [ " abandon " ]
]
else :
etudids = [
etudid
for ( etudid , donnees ) in self . cursus . items ( )
]
2024-01-20 09:31:02 +01:00
if ordre == " alphabetique " : # Tri alphabétique
etudidsAvecNom = [
( etudid , etud [ " etat_civil " ] )
for ( etudid , etud ) in self . cursus . items ( )
if etudid in etudids
]
etudidsAvecNomTrie = sorted ( etudidsAvecNom , key = lambda col : col [ 1 ] )
etudids = [ etud [ 0 ] for etud in etudidsAvecNomTrie ]
return etudids
2024-01-20 16:34:38 +01:00
def get_etudiants ( self , annee_diplome : int = None ) - > dict [ Identite ] :
""" 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é .
Si l ' année de diplômation n ' est pas précisée ( None ) , inclus les étudiants réorientés
ou ayant abandonné .
Args :
annee_diplome : Année de diplomation visée pour le jury
Returns :
Un dictionnaire ` { etudid : Identite ( etudid ) } `
"""
etudids = self . get_etudids ( annee_diplome = annee_diplome )
etudiants = { etudid : self . identites [ etudids ] for etudid in etudids }
return etudiants
2024-01-20 09:31:02 +01:00
def add_etudid ( self , etudid : int , cosemestres ) :
""" Ajoute un étudiant à ceux qui devront être traités pendant le jury pouvant être :
* des étudiants sur lesquels le jury va statuer ( année de diplômation du jury considéré )
* des étudiants qui ne seront pas considérés dans le jury mais ont participé dans leur scolarité
à un ( ou plusieurs ) semestres communs aux étudiants du jury ( et impacteront les classements )
L ' ajout consiste :
* à insérer une entrée pour l ' étudiant en mémorisant son identité,
avec son nom , prénom , etc . . .
* à analyser son parcours , pour déterminer s ' il n ' a ( ou non ) abandonné l ' IUT en cours de
route ( cf . clé abandon )
* à chercher ses semestres valides ( formsemestre_id ) et ses années valides ( formannee_id ) ,
c ' est-à-dire ceux pour lesquels il faudra prendre en compte ses notes dans les calculs de
moyenne ( type 1 A = S1 + S2 / 2 )
Args :
etudid : L ' etudid d ' un étudiant , à ajouter à ceux traiter par le jury
cosemestres : Dictionnaire { fid : Formsemestre ( fid ) } donnant accès aux cosemestres de même année de diplomation
Note : ex JuryPE . add_etudid_to_jury ( )
"""
""" L ' identité de l ' étudiant """
identite = Identite . get_etud ( etudid )
self . identites [ etudid ] = identite
2024-01-20 16:34:38 +01:00
""" Le cursus global de l ' étudiant (restreint aux semestres APC) """
2024-01-20 09:31:02 +01:00
semestres_etudiant = {
2024-01-20 16:34:38 +01:00
frmsem . formsemestre_id : frmsem
for frmsem in identite . get_formsemestres ( )
if frmsem . formation . is_apc ( )
}
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
" diplome " : 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-20 09:31:02 +01:00
}
""" Est-il réorienté / démissionnaire ou a-t-il arrêté volontairement sa formation ? """
2024-01-20 16:34:38 +01:00
self . cursus [ etudid ] [ " abandon " ] = arret_de_formation ( identite , cosemestres )
2024-01-20 09:31:02 +01:00
""" Tri des semestres par n° de semestre """
for nom_sem in pe_tools . TOUS_LES_SEMESTRES :
2024-01-20 16:34:38 +01:00
i = int ( nom_sem [ 1 ] ) + 1 # le n° du semestre
semestres_i = {
fid : semestres_etudiant [ fid ]
for fid in semestres_etudiant
if semestres_etudiant [ fid ] . semestre_id == i
} # les semestres de n°i de l'étudiant
dernier_semestre_i = get_dernier_semestre ( semestres_i )
self . cursus [ etudid ] [ nom_sem ] = dernier_semestre_i
2024-01-20 09:31:02 +01:00
""" Tri des semestres par aggrégat """
for parcours in pe_tools . TOUS_LES_AGGREGATS :
""" L ' aggrégat considéré """
noms_semestre_de_aggregat = pe_tools . PARCOURS [ parcours ] [ " aggregat " ]
self . cursus [ etudid ] [ parcours ] = { }
for nom_sem in noms_semestre_de_aggregat :
2024-01-20 16:34:38 +01:00
self . cursus [ etudid ] [ parcours ] = (
self . cursus [ etudid ] [ parcours ] | self . cursus [ etudid ] [ nom_sem ]
)
2024-01-20 09:31:02 +01:00
if pe_tools . PE_DEBUG and pe_tools . PE_DEBUG > = 2 :
pe_tools . pe_print (
parcours + " = " + str ( self . cursus [ etudid ] [ parcours ] ) ,
end = " " ,
)
2024-01-20 16:34:38 +01:00
def get_formsemestres_jury ( self , semestres_recherches = None ) :
2024-01-20 09:31:02 +01:00
""" Ayant connaissance des étudiants dont il faut calculer les moyennes pour
2024-01-20 16:34:38 +01:00
le jury PE ( attribut ` self . etudiant_ids ) et de leur cursus ,
renvoie un dictionnaire ` { fid : FormSemestre ( fid ) } `
contenant l ' ensemble des formsemestres de leur cursus, dont il faudra calculer
la moyenne . Les formsemestres sont limités à ceux indiqués dans ` ` semestres_recherches ` ` .
Args :
semestres_recherches : Une liste ou une chaine de caractères parmi :
* None : pour obtenir tous les formsemestres du jury
* ' Si ' : pour obtenir les semestres de n ° i ( par ex . ' S1 ' )
* ' iA ' : pour obtenir les semestres de l ' année i (par ex. ' 1 A ' donne [ ' S1 , ' S2 ' ] )
* ' 3S ' , ' 4S ' : pour obtenir les combinaisons de semestres définies par les aggrégats
2024-01-20 09:31:02 +01:00
Returns :
2024-01-20 16:34:38 +01:00
Un dictionnaire de la forme { fid : FormSemestre ( fid ) }
Remarque :
Une liste de la forme ` [ ' Si ' , ' iA ' , . . . ] ` ( combinant les formats précédents ) est possible .
2024-01-20 09:31:02 +01:00
"""
2024-01-20 16:34:38 +01:00
if semestres_recherches is None :
""" Appel récursif pour obtenir tous les semestres (validants) """
semestres = self . get_formsemestres_jury ( pe_tools . AGGREGAT_DIPLOMANT )
return semestres
elif isinstance ( semestres_recherches , list ) :
""" Appel récursif sur tous les éléments de la liste """
semestres = { }
for elmt in semestres_recherches :
semestres_elmt = self . get_formsemestres_jury ( elmt )
semestres = semestres | semestres_elmt
return semestres
elif (
isinstance ( semestres_recherches , str )
and semestres_recherches in pe_tools . TOUS_LES_AGGREGATS
) :
""" Cas d ' un aggrégat avec appel récursif sur toutes les entrées de l ' aggrégat """
semestres = self . get_formsemestres_jury (
pe_tools . PARCOURS [ semestres_recherches ] [ " aggregat " ]
)
return semestres
elif (
isinstance ( semestres_recherches , str )
and semestres_recherches in pe_tools . TOUS_LES_SEMESTRES
) :
""" semestres_recherches est un nom de semestre de type S1,
pour une recherche parmi les étudiants à prendre en compte
dans le jury ( diplômé et redoublants non diplômé )
"""
nom_sem = semestres_recherches
semestres = { }
for etudid in self . etudiants_ids :
semestres = semestres | self . cursus [ etudid ] [ nom_sem ]
return semestres
else :
raise ValueError (
" Probleme de paramètres d ' appel dans get_formsemestreids_du_jury "
)
2024-01-20 09:31:02 +01:00
def get_etudiants_dans_semestres ( semestres : dict [ FormSemestre ] ) - > set :
""" Ensemble d ' identifiants des étudiants (identifiés via leur ``etudid``)
inscrits à l ' un des semestres de la liste de ``semestres``.
Remarque : Les ` ` cosemestres ` ` sont généralement obtenus avec ` ` sco_formsemestre . do_formsemestre_list ( ) ` `
Args :
semestres : Un dictionnaire { fid : Formsemestre ( fid ) } donnant un
ensemble d ' identifiant de semestres
Returns :
Un ensemble d ` ` etudid ` `
"""
etudiants_ids = set ( )
2024-01-20 16:34:38 +01:00
for fid , sem in semestres . items ( ) : # 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 }
pe_print ( f " --> { sem } : { len ( etudiants_du_sem ) } etudiants " )
etudiants_ids = (
etudiants_ids | etudiants_du_sem
) # incluant la suppression des doublons
return etudiants_ids
def annee_diplome ( identite : Identite ) - > int :
""" 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 :
L ' année prévue de sa diplômation
NOTE : Pourrait être déplacé dans app . models . etudiants . Identite
"""
formsemestres = identite . get_formsemestres ( )
if formsemestres :
return max (
[
pe_tools . get_annee_diplome_semestre ( sem_base )
for sem_base in formsemestres
]
)
else :
return None
def semestres_etudiant ( etudid : int , semestre_id = None ) :
""" La liste des semestres BUT d ' un étudiant
pour un semestre_id ( parmi 1 , 2 , 3 , 4 , 5 , 6 ) donné
en fonction de ses infos d ' etud (cf. sco_etud.get_etud_info(etudid=etudid, filled=True)[0]),
les semestres étant triés par ordre décroissant .
Si semestre_id == None renvoie tous les semestres
NOTE : : ex : : JuryPE . get_semestresBUT_d_un_etudiant ( )
TODO : : A revoir """
etud = sco_etud . get_etud_info ( etudid = etudid , filled = True ) [ 0 ]
nbre_semestres = int ( pe_tools . AGGREGAT_DIPLOMANT [ 0 ] ) # 6
if semestre_id == None :
sesSems = [
sem for sem in etud [ " sems " ] if 1 < = sem [ " semestre_id " ] < = nbre_semestres
]
else :
sesSems = [ sem for sem in etud [ " sems " ] if sem [ " semestre_id " ] == semestre_id ]
return sesSems
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 ' 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 ' 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 .
Args :
identite : L ' identité d ' un étudiant
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 ?
TODO : : A reprendre avec l ' accès aux résultats des jury prévu par Scodoc9
"""
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 ?
dernier_formsemestre = identite . get_formsemestres ( ) [ 0 ]
# 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
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 )
if len ( formsestres_superieurs_possibles ) > 0 :
return True
return False
2024-01-20 16:34:38 +01:00
def get_dernier_semestre ( semestres : dict [ FormSemestre ] ) :
""" Renvoie le dernier semestre en date d ' un dictionnaire
de semestres de la forme { fid : FormSemestre ( fid )
Args :
semestres : Un dictionnaire de semestres
Return :
Un dictionnaire { fid : FormSemestre ( fid ) } contenant le semestre le plus récent
"""
if semestres :
fid_dernier_semestre = list ( semestres . keys ( ) ) [ 0 ]
dernier_semestre = { fid_dernier_semestre : semestres [ fid_dernier_semestre ] }
for fid in semestres :
if (
semestres [ fid ] . date_fin
> dernier_semestre [ fid_dernier_semestre ] . date_fin
) :
dernier_semestre = { fid : semestres [ fid ] }
fid_dernier_semestre = fid
return dernier_semestre
else :
return { }