1
0
forked from ScoDoc/ScoDoc

Réactive la détection des étudiants à prendre en compte dans un jury BUT + Désactive les avis LaTeX

This commit is contained in:
Cléo Baras 2024-01-16 06:19:49 +01:00
parent e28bfa34be
commit 898270d2f0
3 changed files with 183 additions and 125 deletions

View File

@ -78,20 +78,24 @@ def comp_nom_semestre_dans_parcours(sem):
# ----------------------------------------------------------------------------------------
class JuryPE(object):
"""Classe memorisant toutes les informations necessaires pour etablir un jury de PE. Modele
base sur NotesTable
"""Classe mémorisant toutes les informations nécessaires pour établir un jury de PE.
Modèle basé sur NotesTable.
Attributs : - diplome : l'annee d'obtention du diplome DUT et du jury de PE (generalement fevrier XXXX)
- juryEtudDict : dictionnaire récapitulant les étudiants participant au jury PE (données administratives +
Attributs :
* diplome : l'annee d'obtention du diplome BUT et du jury de PE (generalement fevrier XXXX)
* juryEtudDict : dictionnaire récapitulant les étudiants participant au jury PE (données administratives +
celles des semestres valides à prendre en compte permettant le calcul des moyennes ...
{'etudid : { 'nom', 'prenom', 'civilite', 'diplome', '', }}
``{'etudid : { 'nom', 'prenom', 'civilite', 'diplome', '', }}``
Rq: il contient à la fois les étudiants qui vont être diplomés à la date prévue
et ceux qui sont éliminés (abandon, redoublement, ...) pour affichage alternatif
"""
# Variables de classe décrivant les aggrégats, leur ordre d'apparition temporelle et
# leur affichage dans les avis latex
NBRE_SEMESTRES_PARCOURS = 6
PARCOURS = {
"S1": {
"aggregat": ["S1"],
@ -105,6 +109,12 @@ class JuryPE(object):
"affichage_court": "S2",
"affichage_long": "Semestre 2",
},
"1A": {
"aggregat": ["S1", "S2"],
"ordre": 3,
"affichage_court": "1A",
"affichage_long": "1ère année",
},
"S3": {
"aggregat": ["S3"],
"ordre": 4,
@ -117,12 +127,6 @@ class JuryPE(object):
"affichage_court": "S4",
"affichage_long": "Semestre 4",
},
"1A": {
"aggregat": ["S1", "S2"],
"ordre": 3,
"affichage_court": "1A",
"affichage_long": "1ère année",
},
"2A": {
"aggregat": ["S3", "S4"],
"ordre": 6,
@ -133,16 +137,52 @@ class JuryPE(object):
"aggregat": ["S1", "S2", "S3"],
"ordre": 7,
"affichage_court": "S1+S2+S3",
"affichage_long": "DUT du semestre 1 au semestre 3",
"affichage_long": "BUT du semestre 1 au semestre 3",
},
"4S": {
"aggregat": ["S1", "S2", "S3", "S4"],
"ordre": 8,
"affichage_court": "DUT",
"affichage_long": "DUT (tout semestre inclus)",
"affichage_court": "BUT",
"affichage_long": "BUT du semestre 1 au semestre 4",
},
"S5": {
"aggregat": ["S5"],
"ordre": 9,
"affichage_court": "S5",
"affichage_long": "Semestre 5",
},
"S6": {
"aggregat": ["S6"],
"ordre": 10,
"affichage_court": "S6",
"affichage_long": "Semestre 6",
},
"3A": {
"aggregat": ["S5", "S6"],
"ordre": 11,
"affichage_court": "3A",
"affichage_long": "3ème année",
},
"5S": {
"aggregat": ["S1", "S2", "S3", "S4", "S5"],
"ordre": 12,
"affichage_court": "S1+S2+S3+S4+S5",
"affichage_long": "BUT du semestre 1 au semestre 5",
},
"6S": {
"aggregat": ["S1", "S2", "S3", "S4", "S5", "S6"],
"ordre": 13,
"affichage_court": "BUT",
"affichage_long": "BUT (tout semestre inclus)",
},
}
AGGREGAT_DIPLOMANT = "6S" # aggrégat correspondant à la totalité des notes pour le diplôme
TOUS_LES_SEMESTRES = PARCOURS["6S"]["aggregat"]
TOUS_LES_AGGREGATS = [cle for cle in PARCOURS.keys() if not cle.startswith("S")]
TOUS_LES_PARCOURS = list(PARCOURS.keys())
# ------------------------------------------------------------------------------------------------------------------
def __init__(self, semBase):
"""
@ -325,7 +365,7 @@ class JuryPE(object):
for sem in semsListe: # pour chacun des semestres de la liste
nt = self.get_cache_notes_d_un_semestre(sem["formsemestre_id"])
etudiantsDuSemestre = (
nt.get_etudids()
[ins.etudid for ins in nt.formsemestre.inscriptions] # nt.get_etudids()
) # identification des etudiants du semestre
if pe_tools.PE_DEBUG:
@ -341,7 +381,7 @@ class JuryPE(object):
def get_etudids_du_jury(self,
ordre="aucun"):
"""Renvoie la liste de tous les étudiants (concrètement leur etudid)
participant au jury c'est à dire, ceux dont la date du 'jury' est self.diplome
participant au jury c'est-à-dire, ceux dont la date du 'jury' est self.diplome
et n'ayant pas abandonné.
Si l'ordre est précisé, donne une liste etudid dont le nom, prenom trié par ordre alphabétique
"""
@ -365,13 +405,18 @@ class JuryPE(object):
# ------------------------------------------------------------------------------------------------------------------
def add_etudiants(self,
etudid):
"""Ajoute un étudiant (via son etudid) au dictionnaire de synthèse jurydict.
"""Ajoute un étudiant connaissant son etudid au dictionnaire de synthèse jurydict.
L'ajout consiste à :
> insérer une entrée pour l'étudiant en mémorisant ses infos (get_etudInfo),
* insérer une entrée pour l'étudiant en mémorisant ses infos (get_etudInfo),
avec son nom, prénom, etc...
> à analyser son parcours, pour vérifier s'il n'a pas abandonné l'IUT en cours de route => 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 1A=S1+S2/2)
* à analyser son parcours, pour vérifier s'il n'a pas 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 1A=S1+S2/2)
Args:
etudid: L'etudid d'un étudiant, à ajouter au jury s'il respecte les critères précédents
"""
if etudid not in self.PARCOURSINFO_DICT:
@ -391,7 +436,7 @@ class JuryPE(object):
# Sa date prévisionnelle de diplome
self.PARCOURSINFO_DICT[etudid][
"diplome"
] = self.calcul_anneePromoDUT_d_un_etudiant(etudid)
] = self.calcul_anneePromoBUT_d_un_etudiant(etudid)
if pe_tools.PE_DEBUG and pe_tools.PE_DEBUG >= 2:
pe_tools.pe_print(
"promo=" + str(self.PARCOURSINFO_DICT[etudid]["diplome"]), end=""
@ -412,11 +457,10 @@ class JuryPE(object):
# et s'ils existent quelles sont ses notes utiles ?
sesFormsemestre_idValidants = [
self.get_Fid_d_un_Si_valide_d_un_etudiant(etudid, nom_sem)
for nom_sem in JuryPE.PARCOURS["4S"][
"aggregat"
] # Recherche du formsemestre_id de son Si valide (ou a défaut en cours)
for nom_sem in JuryPE.TOUS_LES_SEMESTRES
# Recherche du formsemestre_id de son Si valide (ou a défaut en cours)
]
for i, nom_sem in enumerate(JuryPE.PARCOURS["4S"]["aggregat"]):
for i, nom_sem in enumerate(JuryPE.TOUS_LES_SEMESTRES):
fid = sesFormsemestre_idValidants[i]
self.PARCOURSINFO_DICT[etudid][nom_sem] = fid # ['formsemestre_id']
if fid != None and pe_tools.PE_DEBUG and pe_tools.PE_DEBUG >= 2:
@ -424,13 +468,11 @@ class JuryPE(object):
# self.get_moyennesEtClassements_par_semestre_d_un_etudiant( etudid, fid )
# Quelles sont ses années validantes ('1A', '2A') et ses parcours (3S, 4S) validants ?
for parcours in ["1A", "2A", "3S", "4S"]:
lesSemsDuParcours = JuryPE.PARCOURS[parcours][
"aggregat"
] # les semestres du parcours : par ex. ['S1', 'S2', 'S3']
for parcours in JuryPE.TOUS_LES_AGGREGATS:
lesSemsDuParcours = JuryPE.PARCOURS[parcours]["aggregat"] # les semestres du parcours : par ex. ['S1', 'S2', 'S3']
lesFidsValidantDuParcours = [
sesFormsemestre_idValidants[
JuryPE.PARCOURS["4S"]["aggregat"].index(nom_sem)
JuryPE.TOUS_LES_SEMESTRES.index(nom_sem)
]
for nom_sem in lesSemsDuParcours # par ex. ['SEM4532', 'SEM567', ...]
]
@ -482,7 +524,7 @@ class JuryPE(object):
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."""
sessems = self.get_semestresDUT_d_un_etudiant(
sessems = self.get_semestresBUT_d_un_etudiant(
etudid
) # les semestres de l'étudiant
sonDernierSidValide = self.get_dernier_semestre_id_valide_d_un_etudiant(etudid)
@ -556,8 +598,8 @@ class JuryPE(object):
"""Récupère le formsemestre_id valide d'un étudiant fourni son etudid à un semestre DUT de n° semestre_id
donné. Si le semestre est en cours (pas encore de jury), renvoie le formsemestre_id actuel.
"""
semestre_id = JuryPE.PARCOURS["4S"]["aggregat"].index(nom_semestre) + 1
sesSi = self.get_semestresDUT_d_un_etudiant(
semestre_id = JuryPE.TOUS_LES_SEMESTRES.index(nom_semestre) + 1
sesSi = self.get_semestresBUT_d_un_etudiant(
etudid, semestre_id
) # extrait uniquement les Si par ordre temporel décroissant
@ -590,7 +632,8 @@ class JuryPE(object):
Calcule les moyennes et les classements de chaque semestre par tag et les statistiques de ces semestres.
"""
lesFids = self.get_formsemestreids_du_jury(
self.get_etudids_du_jury(), liste_semestres=["S1", "S2", "S3", "S4"]
self.get_etudids_du_jury(),
liste_semestres=JuryPE.TOUS_LES_SEMESTRES
)
for i, fid in enumerate(lesFids):
if pe_tools.PE_DEBUG:
@ -634,7 +677,7 @@ class JuryPE(object):
" - %d étudiants classés " % (nbinscrit)
+ ": "
+ ",".join(
[etudid for etudid in self.semTagDict[fid].get_etudids()]
[str(etudid) for etudid in self.semTagDict[fid].get_etudids()]
)
)
if lesEtudidsManquants:
@ -642,7 +685,7 @@ class JuryPE(object):
" - dont %d étudiants manquants ajoutés aux données du jury"
% (len(lesEtudidsManquants))
+ ": "
+ ", ".join(lesEtudidsManquants)
+ ", ".join([str(etudid) for etudid in lesEtudidsManquants])
)
pe_tools.pe_print(" - Export csv")
filename = self.NOM_EXPORT_ZIP + self.semTagDict[fid].nom + ".csv"
@ -651,7 +694,7 @@ class JuryPE(object):
# ----------------------------------------------------------------------------------------------------------------
def get_formsemestreids_du_jury(self,
etudids,
liste_semestres="4S"):
liste_semestres="6S"):
"""Renvoie la liste des formsemestre_id validants des étudiants en parcourant les semestres valides des étudiants mémorisés dans
self.PARCOURSINFO_DICT.
Les étudiants sont identifiés par leur etudic donnés dans la liste etudids (généralement self.get_etudids_in_jury() ).
@ -736,8 +779,8 @@ class JuryPE(object):
# '3S' : ['S1', 'S2', 'S3'], '4S' : ['S1', 'S2', 'S3', 'S4'] }
# ---> sur 2 parcours DUT (cas S3 fini, cas S4 fini)
combinaisons = ["1A", "2A", "3S", "4S"]
for i, nom in enumerate(combinaisons):
for i, nom in enumerate(JuryPE.TOUS_LES_AGGREGATS):
parcours = JuryPE.PARCOURS[nom][
"aggregat"
] # La liste des noms de semestres (S1, S2, ...) impliqués dans l'aggrégat
@ -806,7 +849,7 @@ class JuryPE(object):
pe_tools.pe_print(
"%d) %s avec interclassement sur la promo" % (i + 1, nom)
)
if nom in ["S1", "S2", "S3", "S4"]:
if nom in JuryPE.TOUS_LES_SEMESTRES:
settag.set_SetTagDict(self.semTagDict)
else: # cas des aggrégats
settag.set_SetTagDict(self.setTagDict[nom])
@ -859,7 +902,7 @@ class JuryPE(object):
if (
self.PARCOURSINFO_DICT[etudid][nom] != None
): # Un parcours valide existe
if nom in ["S1", "S2", "S3", "S4"]:
if nom in JuryPE.TOUS_LES_SEMESTRES:
tagtable = self.semTagDict[self.PARCOURSINFO_DICT[etudid][nom]]
else:
tagtable = self.setTagDict[nom][
@ -883,7 +926,7 @@ class JuryPE(object):
etudid):
"""Renvoie l'année d'entrée de l'étudiant à l'IUT"""
# etudinfo = self.ETUDINFO_DICT[etudid]
semestres = self.get_semestresDUT_d_un_etudiant(etudid)
semestres = self.get_semestresBUT_d_un_etudiant(etudid)
if semestres:
# le 1er sem à l'IUT
return semestres[0]["annee_debut"]
@ -894,7 +937,7 @@ class JuryPE(object):
etudid):
"""Renvoie une liste d'infos sur les semestres du parcours d'un étudiant"""
# etudinfo = self.ETUDINFO_DICT[etudid]
sems = self.get_semestresDUT_d_un_etudiant(etudid)
sems = self.get_semestresBUT_d_un_etudiant(etudid)
infos = []
for sem in sems:
@ -915,7 +958,7 @@ class JuryPE(object):
delim=";"):
# En tete:
entete = ["Id", "Nom", "Abandon", "Diplome"]
for nom_sem in ["S1", "S2", "S3", "S4", "1A", "2A", "3S", "4S"]:
for nom_sem in JuryPE.TOUS_LES_PARCOURS:
entete += [nom_sem, "descr"]
chaine = delim.join(entete) + "\n"
@ -930,8 +973,8 @@ class JuryPE(object):
str(donnees["diplome"]),
]
# les semestres
for nom_sem in ["S1", "S2", "S3", "S4", "1A", "2A", "3S", "4S"]:
# les semestres et les aggrégats
for nom_sem in JuryPE.TOUS_LES_PARCOURS:
table = (
self.semTagDict[donnees[nom_sem]].nom
if donnees[nom_sem] in self.semTagDict
@ -968,9 +1011,10 @@ class JuryPE(object):
return list(taglist)
def get_allTagInSyntheseJury(self):
"""Extrait tous les tags du dictionnaire syntheseJury trié par ordre alphabétique. [] si aucun tag"""
"""Extrait tous les tags du dictionnaire syntheseJury trié par
ordre alphabétique. [] si aucun tag"""
allTags = set()
for nom in JuryPE.PARCOURS.keys():
for nom in JuryPE.TOUS_LES_PARCOURS:
allTags = allTags.union(set(self.get_allTagForAggregat(nom)))
return sorted(list(allTags)) if len(allTags) > 0 else []
@ -1160,7 +1204,7 @@ class JuryPE(object):
# ------------------------------------------------------------------------------------------------------------------
# ------------------------------------------------------------------------------------------------------------------
def get_semestresDUT_d_un_etudiant(self,
def get_semestresBUT_d_un_etudiant(self,
etudid,
semestre_id=None):
"""Renvoie la liste des semestres DUT d'un étudiant
@ -1169,18 +1213,19 @@ class JuryPE(object):
les semestres étant triés par ordre décroissant.
Si semestre_id == None renvoie tous les semestres"""
etud = self.get_cache_etudInfo_d_un_etudiant(etudid)
nbre_semestres = int(JuryPE.AGGREGAT_DIPLOMANT[0]) # 6
if semestre_id == None:
sesSems = [sem for sem in etud["sems"] if 1 <= sem["semestre_id"] <= 4]
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 calcul_anneePromoDUT_d_un_etudiant(self,
def calcul_anneePromoBUT_d_un_etudiant(self,
etudid) -> int:
"""Calcule et renvoie la date de diplome prévue pour un étudiant fourni avec son etudid
en fonction de ses semestres de scolarisation"""
semestres = self.get_semestresDUT_d_un_etudiant(etudid)
semestres = self.get_semestresBUT_d_un_etudiant(etudid)
if semestres:
return max([get_annee_diplome_semestre(sem) for sem in semestres])
else:
@ -1191,7 +1236,7 @@ class JuryPE(object):
def get_resultat_d_un_etudiant(self,
etudid):
chaine = ""
for nom_sem in ["S1", "S2", "S3", "S4"]:
for nom_sem in JuryPE.TOUS_LES_SEMESTRES:
semtagid = self.PARCOURSINFO_DICT[etudid][
nom_sem
] # le formsemestre_id du semestre taggué de l'étudiant
@ -1230,27 +1275,37 @@ class JuryPE(object):
# ----------------------------------------------------------------------------------------
def get_annee_diplome_semestre(sem) -> int:
"""Pour un semestre donne, décrit par le biais du dictionnaire sem usuel :
sem = {'formestre_id': ..., 'semestre_id': ..., 'annee_debut': ...},
à condition qu'il soit un semestre de formation DUT,
predit l'annee à laquelle sera remis le diplome DUT des etudiants scolarisés dans le semestre
sem = {'formestre_id': ..., 'semestre_id': ..., 'annee_debut': ...}
à condition qu'il soit un semestre de formation BUT,
predit l'annee à laquelle sera remis le diplome BUT des etudiants scolarisés dans le semestre
(en supposant qu'il n'y ait plus de redoublement) et la renvoie sous la forme d'un int.
Hypothese : les semestres de 1ere partie d'annee universitaire (comme des S1 ou des S3) s'etalent
sur deux annees civiles - contrairement au semestre de seconde partie d'annee universitaire (comme
des S2 ou des S4).
Les semestres de 1ère partie d'année (S1, S3, S5 ou S4, S6 pour des semestres décalés)
s'étalent sur deux années civiles ; contrairement au semestre de seconde partie d'annee universitaire.
Par exemple :
> S4 debutant en 2016 finissant en 2016 => diplome en 2016
> S3 debutant en 2015 et finissant en 2016 => diplome en 2016
> S3 (decale) debutant en 2015 et finissant en 2015 => diplome en 2016
La regle de calcul utilise l'annee_fin du semestre sur le principe suivant :
nbreSemRestant = nombre de semestres restant avant diplome
nbreAnneeRestant = nombre d'annees restant avant diplome
1 - delta = 0 si semestre de 1ere partie d'annee / 1 sinon
decalage = active ou desactive un increment a prendre en compte en cas de semestre decale
* S5 débutant en 2025 finissant en 2026 => diplome en 2026
* S3 debutant en 2025 et finissant en 2026 => diplome en 2027
* S5 décalé débutant en 2025 et finissant en 2025 => diplome en 2026
* S3 decale débutant en 2025 et finissant en 2025 => diplome en 2027
La règle de calcul utilise l'``annee_fin`` du semestre sur le principe suivant :
* nbreSemRestant = nombre de semestres restant avant diplome
* nbreAnneeRestant = nombre d'annees restant avant diplome
* 1 - delta = 0 si semestre de 1ere partie d'annee / 1 sinon
* decalage = active ou désactive un increment à prendre en compte en cas de semestre decale
Args:
sem: Le semestre
"""
nbre_semestres = int(JuryPE.AGGREGAT_DIPLOMANT[0]) # 6
if (
1 <= sem["semestre_id"] <= 4
): # Si le semestre est un semestre DUT => problème si formation DUT en 1 an ??
nbreSemRestant = 4 - sem["semestre_id"]
1 <= sem["semestre_id"] <= nbre_semestres
): # Si le semestre est un semestre BUT => problème si formation BUT en 1 an ??
nbreSemRestant = nbre_semestres - sem["semestre_id"]
nbreAnRestant = nbreSemRestant // 2
delta = int(sem["annee_fin"]) - int(sem["annee_debut"])
decalage = nbreSemRestant % 2 # 0 si S4, 1 si S3, 0 si S2, 1 si S1

View File

@ -291,7 +291,7 @@ class TableTag(object):
for etudid in self.identdict:
descr = delim.join(
[
etudid,
str(etudid),
self.identdict[etudid]["nom"],
self.identdict[etudid]["prenom"],
]

View File

@ -106,6 +106,7 @@ def pe_view_sem_recap(
# (chaines unicodes, html non quoté)
template_latex = ""
# template fourni via le formulaire Web
if False:
if avis_tmpl_file:
try:
template_latex = avis_tmpl_file.read().decode("utf-8")
@ -151,6 +152,7 @@ def pe_view_sem_recap(
jury.NOM_EXPORT_ZIP + "_annotationsPE" + scu.XLSX_SUFFIX, sT.excel()
)
if False:
latex_pages = {} # Dictionnaire de la forme nom_fichier => contenu_latex
for etudid in etudids:
[nom_fichier, contenu_latex] = pe_avislatex.get_avis_poursuite_par_etudiant(
@ -172,6 +174,7 @@ def pe_view_sem_recap(
# Ajoute image, LaTeX class file(s) and modeles
pe_tools.add_pe_stuff_to_zip(jury.zipfile, jury.NOM_EXPORT_ZIP)
data = jury.get_zipped_data()
return send_file(