forked from ScoDoc/ScoDoc
1333 lines
60 KiB
Python
1333 lines
60 KiB
Python
# -*- 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 Fri Sep 9 09:15:05 2016
|
|
|
|
@author: barasc
|
|
"""
|
|
|
|
# ----------------------------------------------------------
|
|
# Ensemble des fonctions et des classes
|
|
# permettant les calculs preliminaires (hors affichage)
|
|
# a l'edition d'un jury de poursuites d'etudes
|
|
# ----------------------------------------------------------
|
|
|
|
import io
|
|
import os
|
|
from zipfile import ZipFile
|
|
|
|
from app.comp import res_sem
|
|
from app.comp.res_compat import NotesTableCompat
|
|
from app.models import Formation, FormSemestre
|
|
|
|
from app.scodoc.gen_tables import GenTable, SeqGenTable
|
|
import app.scodoc.sco_utils as scu
|
|
from app.scodoc import codes_cursus # codes_cursus.NEXT -> sem suivant
|
|
from app.scodoc import sco_etud
|
|
from app.scodoc import sco_formsemestre
|
|
from app.pe import pe_tagtable
|
|
from app.pe import pe_tools
|
|
from app.pe import pe_semestretag
|
|
from app.pe import pe_settag
|
|
|
|
|
|
# ----------------------------------------------------------------------------------------
|
|
def comp_nom_semestre_dans_parcours(sem):
|
|
"""Le nom a afficher pour titrer un semestre
|
|
par exemple: "semestre 2 FI 2015"
|
|
"""
|
|
formation: Formation = Formation.query.get_or_404(sem["formation_id"])
|
|
parcours = codes_cursus.get_cursus_from_code(formation.type_parcours)
|
|
return "%s %s %s %s" % (
|
|
parcours.SESSION_NAME, # eg "semestre"
|
|
sem["semestre_id"], # eg 2
|
|
sem.get("modalite", ""), # eg FI ou FC
|
|
sem["annee_debut"], # eg 2015
|
|
)
|
|
|
|
|
|
# ----------------------------------------------------------------------------------------
|
|
class JuryPE(object):
|
|
"""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 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', '', }}``
|
|
|
|
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"],
|
|
"ordre": 1,
|
|
"affichage_court": "S1",
|
|
"affichage_long": "Semestre 1",
|
|
},
|
|
"S2": {
|
|
"aggregat": ["S2"],
|
|
"ordre": 2,
|
|
"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,
|
|
"affichage_court": "S3",
|
|
"affichage_long": "Semestre 3",
|
|
},
|
|
"S4": {
|
|
"aggregat": ["S4"],
|
|
"ordre": 5,
|
|
"affichage_court": "S4",
|
|
"affichage_long": "Semestre 4",
|
|
},
|
|
"2A": {
|
|
"aggregat": ["S3", "S4"],
|
|
"ordre": 6,
|
|
"affichage_court": "2A",
|
|
"affichage_long": "2ème année",
|
|
},
|
|
"3S": {
|
|
"aggregat": ["S1", "S2", "S3"],
|
|
"ordre": 7,
|
|
"affichage_court": "S1+S2+S3",
|
|
"affichage_long": "BUT du semestre 1 au semestre 3",
|
|
},
|
|
"4S": {
|
|
"aggregat": ["S1", "S2", "S3", "S4"],
|
|
"ordre": 8,
|
|
"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, sem_base: FormSemestre, semBase): # CB: à supprimer à long terme
|
|
"""
|
|
Création d'une table PE sur la base d'un semestre selectionné. De ce semestre est déduit :
|
|
1. l'année d'obtention du DUT,
|
|
2. tous les étudiants susceptibles à ce stade (au regard de leur parcours) d'être diplomés.
|
|
|
|
Args:
|
|
sem_base: le FormSemestre donnant le semestre à la base du jury PE
|
|
semBase: le dictionnaire sem donnant la base du jury (CB: TODO: A supprimer à long term)
|
|
meme_programme: si True, impose un même programme pour tous les étudiants participant au jury,
|
|
si False, permet des programmes differents
|
|
"""
|
|
self.semTagDict = (
|
|
{}
|
|
) # Les semestres taggués à la base des calculs de moyenne par tag
|
|
self.setTagDict = (
|
|
{}
|
|
) # dictionnaire récapitulant les semTag impliqués dans le jury de la forme { 'formsemestre_id' : object Semestre_tag
|
|
self.promoTagDict = {}
|
|
|
|
# L'année du diplome
|
|
self.diplome = get_annee_diplome_semestre(semBase)
|
|
|
|
# Un zip où ranger les fichiers générés:
|
|
self.NOM_EXPORT_ZIP = "Jury_PE_%s" % self.diplome
|
|
self.zipdata = io.BytesIO()
|
|
self.zipfile = ZipFile(self.zipdata, "w")
|
|
|
|
#
|
|
self.ETUDINFO_DICT = {} # Les infos sur les étudiants
|
|
self.PARCOURSINFO_DICT = {} # Les parcours des étudiants
|
|
self.syntheseJury = {} # Le jury de synthèse
|
|
|
|
self.semestresDeScoDoc = sco_formsemestre.do_formsemestre_list()
|
|
|
|
# Calcul du jury PE
|
|
self.exe_calculs_juryPE(semBase)
|
|
self.synthetise_juryPE()
|
|
|
|
# Export des données => mode 1 seule feuille -> supprimé
|
|
# filename = self.NOM_EXPORT_ZIP + "jurySyntheseDict_" + str(self.diplome) + '.xls'
|
|
# self.xls = self.table_syntheseJury(mode="singlesheet")
|
|
# self.add_file_to_zip(filename, self.xls.excel())
|
|
|
|
# Fabrique 1 fichier excel résultat avec 1 seule feuille => trop gros
|
|
filename = self.NOM_EXPORT_ZIP + "_jurySyntheseDict" + scu.XLSX_SUFFIX
|
|
self.xlsV2 = self.table_syntheseJury(mode="multiplesheet")
|
|
if self.xlsV2:
|
|
self.add_file_to_zip(filename, self.xlsV2.excel())
|
|
|
|
# Pour debug
|
|
# self.syntheseJury = pe_tools.JURY_SYNTHESE_POUR_DEBUG #Un dictionnaire fictif pour debug
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
def add_file_to_zip(self, filename, data, path=""):
|
|
"""Add a file to our zip
|
|
All files under NOM_EXPORT_ZIP/
|
|
path may specify a subdirectory
|
|
"""
|
|
path_in_zip = os.path.join(self.NOM_EXPORT_ZIP, path, filename)
|
|
self.zipfile.writestr(path_in_zip, data)
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
def get_zipped_data(self):
|
|
"""returns file-like data with a zip of all generated (CSV) files.
|
|
Reset file cursor at the beginning !
|
|
"""
|
|
if self.zipfile:
|
|
self.zipfile.close()
|
|
self.zipfile = None
|
|
self.zipdata.seek(0)
|
|
return self.zipdata
|
|
|
|
# **************************************************************************************************************** #
|
|
# Lancement des différentes actions permettant le calcul du jury PE
|
|
# **************************************************************************************************************** #
|
|
def exe_calculs_juryPE(self, semBase):
|
|
# Liste des étudiants à traiter pour identifier ceux qui seront diplômés
|
|
if pe_tools.PE_DEBUG:
|
|
pe_tools.pe_print(
|
|
"*** Recherche et chargement des étudiants diplômés en %d"
|
|
% (self.diplome)
|
|
)
|
|
self.get_etudiants_in_jury(
|
|
semBase, avec_meme_formation=False
|
|
) # calcul des coSemestres
|
|
|
|
# Les semestres impliqués (ceux valides pour les étudiants à traiter)
|
|
# -------------------------------------------------------------------
|
|
if pe_tools.PE_DEBUG:
|
|
pe_tools.pe_print("*** Création des semestres taggués")
|
|
self.get_semtags_in_jury()
|
|
if pe_tools.PE_DEBUG:
|
|
for semtag in self.semTagDict.values(): # Export
|
|
filename = self.NOM_EXPORT_ZIP + semtag.nom + ".csv"
|
|
self.zipfile.writestr(filename, semtag.str_tagtable())
|
|
# self.export_juryPEDict()
|
|
|
|
# Les moyennes sur toute la scolarité
|
|
# -----------------------------------
|
|
if pe_tools.PE_DEBUG:
|
|
pe_tools.pe_print(
|
|
"*** Création des moyennes sur différentes combinaisons de semestres et différents groupes d'étudiant"
|
|
)
|
|
self.get_settags_in_jury()
|
|
if pe_tools.PE_DEBUG:
|
|
for settagdict in self.setTagDict.values(): # Export
|
|
for settag in settagdict.values():
|
|
filename = self.NOM_EXPORT_ZIP + semtag.nom + ".csv"
|
|
self.zipfile.writestr(filename, semtag.str_tagtable())
|
|
# self.export_juryPEDict()
|
|
|
|
# Les interclassements
|
|
# --------------------
|
|
if pe_tools.PE_DEBUG:
|
|
pe_tools.pe_print(
|
|
"*** Création des interclassements au sein de la promo sur différentes combinaisons de semestres"
|
|
)
|
|
self.get_promotags_in_jury()
|
|
|
|
# **************************************************************************************************************** #
|
|
# Fonctions relatives à la liste des étudiants à prendre en compte dans le jury
|
|
# **************************************************************************************************************** #
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
def get_etudiants_in_jury(self, semBase, avec_meme_formation=False):
|
|
"""
|
|
Calcule la liste des étudiants à prendre en compte dans le jury et la renvoie sous la forme
|
|
"""
|
|
# Les cosemestres donnant lieu à meme année de diplome
|
|
coSems = get_cosemestres_diplomants(
|
|
semBase, avec_meme_formation=avec_meme_formation
|
|
) # calcul des coSemestres
|
|
if pe_tools.PE_DEBUG:
|
|
pe_tools.pe_print(
|
|
"1) Recherche des coSemestres -> %d trouvés" % len(coSems)
|
|
)
|
|
|
|
# Les étudiants inscrits dans les cosemestres
|
|
if pe_tools.PE_DEBUG:
|
|
pe_tools.pe_print("2) Liste des étudiants dans les différents co-semestres")
|
|
listEtudId = self.get_etudiants_dans_semestres(
|
|
coSems
|
|
) # étudiants faisant parti des cosemestres
|
|
if pe_tools.PE_DEBUG:
|
|
pe_tools.pe_print(" => %d étudiants trouvés" % len(listEtudId))
|
|
|
|
# L'analyse des parcours étudiants pour déterminer leur année effective de diplome avec prise en compte des redoublements, des abandons, ....
|
|
if pe_tools.PE_DEBUG:
|
|
pe_tools.pe_print("3) Analyse des parcours individuels des étudiants")
|
|
|
|
for no_etud, etudid in enumerate(listEtudId):
|
|
self.add_etudiants(etudid)
|
|
if pe_tools.PE_DEBUG:
|
|
if (no_etud + 1) % 10 == 0:
|
|
pe_tools.pe_print((no_etud + 1), " ", end="")
|
|
pe_tools.pe_print()
|
|
|
|
if pe_tools.PE_DEBUG:
|
|
pe_tools.pe_print(
|
|
" => %d étudiants à diplômer en %d"
|
|
% (len(self.get_etudids_du_jury()), self.diplome)
|
|
)
|
|
pe_tools.pe_print(
|
|
" => %d étudiants éliminer pour abandon"
|
|
% (len(listEtudId) - len(self.get_etudids_du_jury()))
|
|
)
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
def get_etudiants_dans_semestres(self, semsListe):
|
|
"""Renvoie la liste des etudid des etudiants inscrits à l'un des semestres de la liste fournie en paramètre
|
|
en supprimant les doublons (i.e. un même étudiant qui apparaîtra 2 fois)"""
|
|
|
|
etudiants = []
|
|
for sem in semsListe: # pour chacun des semestres de la liste
|
|
nt = self.get_cache_notes_d_un_semestre(sem["formsemestre_id"])
|
|
etudiantsDuSemestre = [
|
|
ins.etudid for ins in nt.formsemestre.inscriptions
|
|
] # nt.get_etudids() # identification des etudiants du semestre
|
|
|
|
if pe_tools.PE_DEBUG:
|
|
pe_tools.pe_print(
|
|
" --> chargement du semestre %s : %d etudiants "
|
|
% (sem["formsemestre_id"], len(etudiantsDuSemestre))
|
|
)
|
|
etudiants.extend(etudiantsDuSemestre)
|
|
|
|
return list(set(etudiants)) # suppression des doublons
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
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
|
|
et n'ayant pas abandonné.
|
|
Si l'ordre est précisé, donne une liste etudid dont le nom, prenom trié par ordre alphabétique
|
|
"""
|
|
etudids = [
|
|
etudid
|
|
for (etudid, donnees) in self.PARCOURSINFO_DICT.items()
|
|
if donnees["diplome"] == self.diplome and donnees["abandon"] == False
|
|
]
|
|
if ordre == "alphabetique": # Tri alphabétique
|
|
etudidsAvecNom = [
|
|
(etudid, etud["nom"] + "/" + etud["prenom"])
|
|
for (etudid, etud) in self.PARCOURSINFO_DICT.items()
|
|
if etudid in etudids
|
|
]
|
|
etudidsAvecNomTrie = sorted(etudidsAvecNom, key=lambda col: col[1])
|
|
etudids = [etud[0] for etud in etudidsAvecNomTrie]
|
|
return etudids
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
def add_etudiants(self, etudid):
|
|
"""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),
|
|
avec son nom, prénom, etc...
|
|
* à 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:
|
|
etud = self.get_cache_etudInfo_d_un_etudiant(
|
|
etudid
|
|
) # On charge les données de l'étudiant
|
|
if pe_tools.PE_DEBUG and pe_tools.PE_DEBUG >= 2:
|
|
pe_tools.pe_print(etud["nom"] + " " + etud["prenom"], end="")
|
|
|
|
self.PARCOURSINFO_DICT[etudid] = {
|
|
"etudid": etudid, # les infos sur l'étudiant
|
|
"nom": etud["nom"], # Ajout à la table jury
|
|
}
|
|
|
|
# Analyse du parcours de l'étudiant
|
|
|
|
# Sa date prévisionnelle de diplome
|
|
self.PARCOURSINFO_DICT[etudid][
|
|
"diplome"
|
|
] = 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=""
|
|
)
|
|
|
|
# Est-il réorienté ou démissionnaire ?
|
|
self.PARCOURSINFO_DICT[etudid][
|
|
"abandon"
|
|
] = self.est_un_etudiant_reoriente_ou_demissionnaire(etudid)
|
|
|
|
# A-t-il arrêté de lui-même sa formation avant la fin ?
|
|
etatD = self.est_un_etudiant_disparu(etudid)
|
|
if etatD == True:
|
|
self.PARCOURSINFO_DICT[etudid]["abandon"] = True
|
|
# dans le jury ne seront traités que les étudiants ayant la date attendue de diplome et n'ayant pas abandonné
|
|
|
|
# Quels sont ses semestres validant (i.e ceux dont les notes doivent être prises en compte pour le jury)
|
|
# 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.TOUS_LES_SEMESTRES
|
|
# Recherche du formsemestre_id de son Si valide (ou a défaut en cours)
|
|
]
|
|
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:
|
|
pe_tools.pe_print(nom_sem + "=" + str(fid), end="")
|
|
# 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 JuryPE.TOUS_LES_AGGREGATS:
|
|
lesSemsDuParcours = JuryPE.PARCOURS[parcours][
|
|
"aggregat"
|
|
] # les semestres du parcours : par ex. ['S1', 'S2', 'S3']
|
|
lesFidsValidantDuParcours = [
|
|
sesFormsemestre_idValidants[
|
|
JuryPE.TOUS_LES_SEMESTRES.index(nom_sem)
|
|
]
|
|
for nom_sem in lesSemsDuParcours # par ex. ['SEM4532', 'SEM567', ...]
|
|
]
|
|
parcours_incomplet = (
|
|
sum([fid == None for fid in lesFidsValidantDuParcours]) > 0
|
|
)
|
|
|
|
if not parcours_incomplet:
|
|
self.PARCOURSINFO_DICT[etudid][
|
|
parcours
|
|
] = lesFidsValidantDuParcours[-1]
|
|
else:
|
|
self.PARCOURSINFO_DICT[etudid][parcours] = None
|
|
if pe_tools.PE_DEBUG and pe_tools.PE_DEBUG >= 2:
|
|
pe_tools.pe_print(
|
|
parcours + "=" + str(self.PARCOURSINFO_DICT[etudid][parcours]),
|
|
end="",
|
|
)
|
|
|
|
# if pe_tools.PE_DEBUG and pe_tools.PE_DEBUG >= 2:
|
|
# print
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
def est_un_etudiant_reoriente_ou_demissionnaire(self, etudid):
|
|
"""Renvoie True si l'étudiant est réorienté (NAR) ou démissionnaire (DEM)"""
|
|
from app.scodoc import sco_report
|
|
|
|
reponse = False
|
|
etud = self.get_cache_etudInfo_d_un_etudiant(etudid)
|
|
(_, parcours) = sco_report.get_code_cursus_etud(etud)
|
|
if (
|
|
len(codes_cursus.CODES_SEM_REO & set(parcours.values())) > 0
|
|
): # Eliminé car NAR apparait dans le parcours
|
|
reponse = True
|
|
if pe_tools.PE_DEBUG and pe_tools.PE_DEBUG >= 2:
|
|
pe_tools.pe_print(" -> à éliminer car réorienté (NAR)")
|
|
if "DEM" in list(parcours.values()): # Eliminé car DEM
|
|
reponse = True
|
|
if pe_tools.PE_DEBUG and pe_tools.PE_DEBUG >= 2:
|
|
pe_tools.pe_print(" -> à éliminer car DEM")
|
|
return reponse
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
def est_un_etudiant_disparu(self, etudid):
|
|
"""Renvoie True si l'étudiant n'a pas achevé la formation à l'IUT et a disparu des listes, sans
|
|
pour autant avoir été indiqué NAR ou DEM ; 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."""
|
|
sessems = self.get_semestresBUT_d_un_etudiant(
|
|
etudid
|
|
) # les semestres de l'étudiant
|
|
sonDernierSidValide = self.get_dernier_semestre_id_valide_d_un_etudiant(etudid)
|
|
|
|
sesdates = [
|
|
pe_tagtable.conversionDate_StrToDate(sem["date_fin"]) for sem in sessems
|
|
] # association 1 date -> 1 semestrePE pour les semestres de l'étudiant
|
|
if sesdates:
|
|
lastdate = max(sesdates) # date de fin de l'inscription la plus récente
|
|
else:
|
|
return False
|
|
|
|
# if PETable.AFFICHAGE_DEBUG_PE == True : pe_tools.pe_print(" derniere inscription = ", lastDateSem)
|
|
|
|
if sonDernierSidValide is None:
|
|
# si l'étudiant n'a validé aucun semestre, les prend tous ? (à vérifier)
|
|
semestresSuperieurs = self.semestresDeScoDoc
|
|
else:
|
|
semestresSuperieurs = [
|
|
sem
|
|
for sem in self.semestresDeScoDoc
|
|
if sem["semestre_id"] > sonDernierSidValide
|
|
] # Semestre de rang plus élevé que son dernier sem valide
|
|
datesDesSemestresSuperieurs = [
|
|
pe_tagtable.conversionDate_StrToDate(sem["date_debut"])
|
|
for sem in semestresSuperieurs
|
|
]
|
|
datesDesSemestresPossibles = [
|
|
date_deb for date_deb in datesDesSemestresSuperieurs if date_deb >= lastdate
|
|
] # 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
|
|
if (
|
|
len(datesDesSemestresPossibles) > 0
|
|
): # etudiant ayant disparu de la circulation
|
|
# if PETable.AFFICHAGE_DEBUG_PE == True :
|
|
# pe_tools.pe_print(" -> à éliminer car des semestres où il aurait pu s'inscrire existent ")
|
|
# pe_tools.pe_print(pe_tools.print_semestres_description( datesDesSemestresPossibles.values() ))
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
def get_dernier_semestre_id_valide_d_un_etudiant(self, etudid):
|
|
"""Renvoie le n° (semestre_id) du dernier semestre validé par un étudiant fourni par son etudid
|
|
et None si aucun semestre n'a été validé
|
|
"""
|
|
from app.scodoc import sco_report
|
|
|
|
etud = self.get_cache_etudInfo_d_un_etudiant(etudid)
|
|
(code, parcours) = sco_report.get_code_cursus_etud(
|
|
etud
|
|
) # description = '1234:A', parcours = {1:ADM, 2:NAR, ...}
|
|
sonDernierSemestreValide = max(
|
|
[
|
|
int(cle)
|
|
for (cle, code) in parcours.items()
|
|
if code in codes_cursus.CODES_SEM_VALIDES
|
|
]
|
|
+ [0]
|
|
) # n° du dernier semestre valide, 0 sinon
|
|
return sonDernierSemestreValide if sonDernierSemestreValide > 0 else None
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
def get_Fid_d_un_Si_valide_d_un_etudiant(self, etudid, nom_semestre):
|
|
"""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.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
|
|
|
|
if len(sesSi) > 0: # S'il a obtenu au moins une note
|
|
# mT = sesMoyennes[0]
|
|
leFid = sesSi[0]["formsemestre_id"]
|
|
for i, sem in enumerate(
|
|
sesSi
|
|
): # Parcours des éventuels semestres précédents
|
|
nt = self.get_cache_notes_d_un_semestre(sem["formsemestre_id"])
|
|
dec = nt.get_etud_decision_sem(
|
|
etudid
|
|
) # quelle est la décision du jury ?
|
|
if dec and (dec["code"] in codes_cursus.CODES_SEM_VALIDES):
|
|
# isinstance( sesMoyennes[i+1], float) and
|
|
# mT = sesMoyennes[i+1] # substitue la moyenne si le semestre suivant est "valide"
|
|
leFid = sem["formsemestre_id"]
|
|
else:
|
|
leFid = None
|
|
return leFid
|
|
|
|
# **************************************************************************************************************** #
|
|
# Traitements des semestres impliqués dans le jury
|
|
# **************************************************************************************************************** #
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
def get_semtags_in_jury(self):
|
|
"""
|
|
Créé les semestres tagués relatifs aux résultats des étudiants à prendre en compte dans le jury.
|
|
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=JuryPE.TOUS_LES_SEMESTRES
|
|
)
|
|
for i, fid in enumerate(lesFids):
|
|
if pe_tools.PE_DEBUG:
|
|
pe_tools.pe_print(
|
|
"%d) Semestre taggué %s (avec classement dans groupe)"
|
|
% (i + 1, fid)
|
|
)
|
|
self.add_semtags_in_jury(fid)
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
def add_semtags_in_jury(self, fid):
|
|
"""Crée si nécessaire un semtag et le mémorise dans self.semTag ;
|
|
charge également les données des nouveaux étudiants qui en font partis.
|
|
"""
|
|
# Semestre taggué avec classement dans le groupe
|
|
if fid not in self.semTagDict:
|
|
nt = self.get_cache_notes_d_un_semestre(fid)
|
|
|
|
# Création du semestres
|
|
self.semTagDict[fid] = pe_semestretag.SemestreTag(
|
|
nt, nt.sem
|
|
) # Création du pesemestre associé
|
|
self.semTagDict[fid].comp_data_semtag()
|
|
lesEtudids = self.semTagDict[fid].get_etudids()
|
|
|
|
lesEtudidsManquants = []
|
|
for etudid in lesEtudids:
|
|
if (
|
|
etudid not in self.PARCOURSINFO_DICT
|
|
): # Si l'étudiant n'a pas été pris en compte dans le jury car déjà diplômé ou redoublant
|
|
lesEtudidsManquants.append(etudid)
|
|
# self.get_cache_etudInfo_d_un_etudiant(etudid)
|
|
self.add_etudiants(
|
|
etudid
|
|
) # Ajoute les élements de parcours de l'étudiant
|
|
|
|
nbinscrit = self.semTagDict[fid].get_nbinscrits()
|
|
if pe_tools.PE_DEBUG:
|
|
pe_tools.pe_print(
|
|
" - %d étudiants classés " % (nbinscrit)
|
|
+ ": "
|
|
+ ",".join(
|
|
[str(etudid) for etudid in self.semTagDict[fid].get_etudids()]
|
|
)
|
|
)
|
|
if lesEtudidsManquants:
|
|
pe_tools.pe_print(
|
|
" - dont %d étudiants manquants ajoutés aux données du jury"
|
|
% (len(lesEtudidsManquants))
|
|
+ ": "
|
|
+ ", ".join([str(etudid) for etudid in lesEtudidsManquants])
|
|
)
|
|
pe_tools.pe_print(" - Export csv")
|
|
filename = self.NOM_EXPORT_ZIP + self.semTagDict[fid].nom + ".csv"
|
|
self.zipfile.writestr(filename, self.semTagDict[fid].str_tagtable())
|
|
|
|
# ----------------------------------------------------------------------------------------------------------------
|
|
def get_formsemestreids_du_jury(self, etudids, 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() ).
|
|
La liste_semestres peut être une liste ou une chaine de caractères parmi :
|
|
* None => tous les Fids validant
|
|
* 'Si' => le ième 1 semestre
|
|
* 'iA' => l'année i = ['S1, 'S2'] ou ['S3', 'S4']
|
|
* '3S', '4S' => fusion des semestres
|
|
* [ 'Si', 'iA' , ... ] => une liste combinant les formats précédents
|
|
"""
|
|
champs_possibles = list(JuryPE.PARCOURS.keys())
|
|
if (
|
|
not isinstance(liste_semestres, list)
|
|
and not isinstance(liste_semestres, str)
|
|
and liste_semestres not in champs_possibles
|
|
):
|
|
raise ValueError(
|
|
"Probleme de paramètres d'appel dans pe_jurype.JuryPE.get_formsemestreids_du_jury"
|
|
)
|
|
|
|
if isinstance(liste_semestres, list):
|
|
res = []
|
|
for elmt in liste_semestres:
|
|
res.extend(self.get_formsemestreids_du_jury(etudids, elmt))
|
|
return list(set(res))
|
|
|
|
# si liste_sem est un nom de parcours
|
|
nom_sem = liste_semestres
|
|
# if nom_sem in ['1A', '2A', '3S', '4S'] :
|
|
# return self.get_formsemestreids_du_jury(etudids, JuryPE.PARCOURS[nom_sem] )
|
|
# else :
|
|
fids = {
|
|
self.PARCOURSINFO_DICT[etudid][nom_sem]
|
|
for etudid in etudids
|
|
if self.PARCOURSINFO_DICT[etudid][nom_sem] != None
|
|
}
|
|
|
|
return list(fids)
|
|
|
|
# **************************************************************************************************************** #
|
|
# Traitements des parcours impliquées dans le jury
|
|
# **************************************************************************************************************** #
|
|
|
|
# # ----------------------------------------------------------------------------------------------------------------
|
|
# def get_antags_in_jury(self, avec_affichage_debug=True ):
|
|
# """Construit les settag associés aux années 1A et 2A du jury"""
|
|
# lesAnnees = {'1A' : ['S1', 'S2'], '2A' : ['S3', 'S4'] }
|
|
# for nom_annee in lesAnnees:
|
|
# lesAidDesAnnees = self.get_anneeids_du_jury(annee= nom_annee) # les annee_ids des étudiants du jury
|
|
# for aid in lesAidDesAnnees:
|
|
# fidSemTagFinal = JuryPE.convert_aid_en_fid( aid )
|
|
# lesEtudisDelAnnee = self.semTagDict[ fidSemTagFinal ].get_etudids() # les etudiants sont ceux inscrits dans le semestre final de l'année
|
|
# parcoursDesEtudiants = { etudid : self.PARCOURSINFO_DICT[etudid] for etudid in lesEtudisDelAnnee } # les parcours des etudid aka quels semestres sont à prendre en compte
|
|
#
|
|
# lesFidsDesEtudiants = self.get_formsemestreids_du_jury(lesEtudisDelAnnee, nom_annee) # les formsemestres_id à prendre en compte pour les moyennes
|
|
# # Manque-t-il des semtag associés ; si oui, les créé
|
|
# pe_tools.pe_print(aid, lesFidsDesEtudiants)
|
|
# for fid in lesFidsDesEtudiants:
|
|
# self.add_semtags_in_jury(fid, avec_affichage_debug=avec_affichage_debug)
|
|
# lesSemTagDesEtudiants = { fid: self.semTagDict[fid] for fid in lesFidsDesEtudiants }
|
|
#
|
|
# # Tous les semtag nécessaires pour ses étudiants avec ajout éventuel s'ils n'ont pas été chargés
|
|
# pe_tools.pe_print(" -> Création de l'année tagguée " + str( aid ))
|
|
# #settag_id, short_name, listeEtudId, groupe, listeSemAAggreger, ParcoursEtudDict, SemTagDict, with_comp_moy=True)
|
|
# self.anTagDict[ aid ] = pe_settag.SetTag( aid, "Annee " + self.semTagDict[fidSemTagFinal].short_name, \
|
|
# lesEtudisDelAnnee, 'groupe', lesAnnees[ nom_annee ], parcoursDesEtudiants, lesSemTagDesEtudiants )
|
|
# self.anTagDict[ aid ].comp_data_settag() # calcul les moyennes
|
|
|
|
# **************************************************************************************************************** #
|
|
# Traitements des moyennes sur différentes combinaisons de parcours 1A, 2A, 3S et 4S,
|
|
# impliquées dans le jury
|
|
# **************************************************************************************************************** #
|
|
|
|
def get_settags_in_jury(self):
|
|
"""Calcule les moyennes sur la totalité du parcours (S1 jusqu'à S3 ou S4)
|
|
en classant les étudiants au sein du semestre final du parcours (même S3, même S4, ...)
|
|
"""
|
|
|
|
# Par groupe :
|
|
# combinaisons = { 'S1' : ['S1'], 'S2' : ['S2'], 'S3' : ['S3'], 'S4' : ['S4'], \
|
|
# '1A' : ['S1', 'S2'], '2A' : ['S3', 'S4'],
|
|
# '3S' : ['S1', 'S2', 'S3'], '4S' : ['S1', 'S2', 'S3', 'S4'] }
|
|
|
|
# ---> sur 2 parcours DUT (cas S3 fini, cas S4 fini)
|
|
|
|
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
|
|
|
|
# Recherche des parcours possibles par le biais de leur Fid final
|
|
fids_finaux = self.get_formsemestreids_du_jury(
|
|
self.get_etudids_du_jury(), nom
|
|
) # les formsemestre_ids validant finaux des étudiants du jury
|
|
|
|
if len(fids_finaux) > 0: # S'il existe des parcours validant
|
|
if pe_tools.PE_DEBUG and pe_tools.PE_DEBUG >= 1:
|
|
pe_tools.pe_print("%d) Fusion %s avec" % (i + 1, nom))
|
|
|
|
if nom not in self.setTagDict:
|
|
self.setTagDict[nom] = {}
|
|
|
|
for fid in fids_finaux:
|
|
if pe_tools.PE_DEBUG and pe_tools.PE_DEBUG >= 1:
|
|
pe_tools.pe_print(" - semestre final %s" % (fid))
|
|
settag = pe_settag.SetTag(
|
|
nom, parcours=parcours
|
|
) # Le set tag fusionnant les données
|
|
etudiants = self.semTagDict[
|
|
fid
|
|
].get_etudids() # Les étudiants du sem final
|
|
|
|
# ajoute les étudiants au semestre
|
|
settag.set_Etudiants(
|
|
etudiants,
|
|
self.PARCOURSINFO_DICT,
|
|
self.ETUDINFO_DICT,
|
|
nom_sem_final=self.semTagDict[fid].nom,
|
|
)
|
|
|
|
# manque-t-il des semestres ? Si oui, les ajoute au jurype puis au settag
|
|
for ffid in settag.get_Fids_in_settag():
|
|
if pe_tools.PE_DEBUG and pe_tools.PE_DEBUG >= 1:
|
|
pe_tools.pe_print(
|
|
" -> ajout du semestre tagué %s" % (ffid)
|
|
)
|
|
self.add_semtags_in_jury(ffid)
|
|
settag.set_SemTagDict(
|
|
self.semTagDict
|
|
) # ajoute les semestres au settag
|
|
|
|
settag.comp_data_settag() # Calcul les moyennes, les rangs, ..
|
|
|
|
self.setTagDict[nom][fid] = settag # Mémorise le résultat
|
|
|
|
else:
|
|
if pe_tools.PE_DEBUG and pe_tools.PE_DEBUG >= 1:
|
|
pe_tools.pe_print("%d) Pas de fusion %s possible" % (i + 1, nom))
|
|
|
|
def get_promotags_in_jury(self):
|
|
"""Calcule les aggrégats en interclassant les étudiants du jury (les moyennes ont déjà été calculées en amont)"""
|
|
|
|
lesEtudids = self.get_etudids_du_jury()
|
|
|
|
for i, nom in enumerate(JuryPE.PARCOURS.keys()):
|
|
settag = pe_settag.SetTagInterClasse(nom, diplome=self.diplome)
|
|
nbreEtudInscrits = settag.set_Etudiants(
|
|
lesEtudids, self.PARCOURSINFO_DICT, self.ETUDINFO_DICT
|
|
)
|
|
if nbreEtudInscrits > 0:
|
|
if pe_tools.PE_DEBUG:
|
|
pe_tools.pe_print(
|
|
"%d) %s avec interclassement sur la promo" % (i + 1, nom)
|
|
)
|
|
if nom in JuryPE.TOUS_LES_SEMESTRES:
|
|
settag.set_SetTagDict(self.semTagDict)
|
|
else: # cas des aggrégats
|
|
settag.set_SetTagDict(self.setTagDict[nom])
|
|
settag.comp_data_settag()
|
|
self.promoTagDict[nom] = settag
|
|
else:
|
|
if pe_tools.PE_DEBUG:
|
|
pe_tools.pe_print(
|
|
"%d) Pas d'interclassement %s sur la promo faute de notes"
|
|
% (i + 1, nom)
|
|
)
|
|
|
|
# **************************************************************************************************************** #
|
|
# Méthodes pour la synthèse du juryPE
|
|
# *****************************************************************************************************************
|
|
def synthetise_juryPE(self):
|
|
"""Synthétise tous les résultats du jury PE dans un dictionnaire"""
|
|
self.syntheseJury = {}
|
|
for etudid in self.get_etudids_du_jury():
|
|
etudinfo = self.ETUDINFO_DICT[etudid]
|
|
self.syntheseJury[etudid] = {
|
|
"nom": etudinfo["nom"],
|
|
"prenom": etudinfo["prenom"],
|
|
"civilite": etudinfo["civilite"],
|
|
"civilite_str": etudinfo["civilite_str"],
|
|
"age": str(pe_tools.calcul_age(etudinfo["date_naissance"])),
|
|
"lycee": etudinfo["nomlycee"]
|
|
+ (
|
|
" (" + etudinfo["villelycee"] + ")"
|
|
if etudinfo["villelycee"] != ""
|
|
else ""
|
|
),
|
|
"bac": etudinfo["bac"],
|
|
"code_nip": etudinfo["code_nip"], # pour la photo
|
|
"entree": self.get_dateEntree(etudid),
|
|
"promo": self.diplome,
|
|
}
|
|
# Le parcours
|
|
self.syntheseJury[etudid]["parcours"] = self.get_parcoursIUT(
|
|
etudid
|
|
) # liste des semestres
|
|
self.syntheseJury[etudid]["nbSemestres"] = len(
|
|
self.syntheseJury[etudid]["parcours"]
|
|
) # nombre de semestres
|
|
|
|
# Ses résultats
|
|
for nom in JuryPE.PARCOURS: # S1, puis S2, puis 1A
|
|
# dans le groupe : la table tagguée dans les semtag ou les settag si aggrégat
|
|
self.syntheseJury[etudid][nom] = {"groupe": {}, "promo": {}}
|
|
if (
|
|
self.PARCOURSINFO_DICT[etudid][nom] != None
|
|
): # Un parcours valide existe
|
|
if nom in JuryPE.TOUS_LES_SEMESTRES:
|
|
tagtable = self.semTagDict[self.PARCOURSINFO_DICT[etudid][nom]]
|
|
else:
|
|
tagtable = self.setTagDict[nom][
|
|
self.PARCOURSINFO_DICT[etudid][nom]
|
|
]
|
|
for tag in tagtable.get_all_tags():
|
|
self.syntheseJury[etudid][nom]["groupe"][
|
|
tag
|
|
] = tagtable.get_resultatsEtud(
|
|
tag, etudid
|
|
) # Le tuple des résultats
|
|
|
|
# interclassé dans la promo
|
|
tagtable = self.promoTagDict[nom]
|
|
for tag in tagtable.get_all_tags():
|
|
self.syntheseJury[etudid][nom]["promo"][
|
|
tag
|
|
] = tagtable.get_resultatsEtud(tag, etudid)
|
|
|
|
def get_dateEntree(self, etudid):
|
|
"""Renvoie l'année d'entrée de l'étudiant à l'IUT"""
|
|
# etudinfo = self.ETUDINFO_DICT[etudid]
|
|
semestres = self.get_semestresBUT_d_un_etudiant(etudid)
|
|
if semestres:
|
|
# le 1er sem à l'IUT
|
|
return semestres[0]["annee_debut"]
|
|
else:
|
|
return ""
|
|
|
|
def get_parcoursIUT(self, etudid):
|
|
"""Renvoie une liste d'infos sur les semestres du parcours d'un étudiant"""
|
|
# etudinfo = self.ETUDINFO_DICT[etudid]
|
|
sems = self.get_semestresBUT_d_un_etudiant(etudid)
|
|
|
|
infos = []
|
|
for sem in sems:
|
|
nomsem = comp_nom_semestre_dans_parcours(sem)
|
|
infos.append(
|
|
{
|
|
"nom_semestre_dans_parcours": nomsem,
|
|
"titreannee": sem["titreannee"],
|
|
"formsemestre_id": sem["formsemestre_id"], # utile dans le futur ?
|
|
}
|
|
)
|
|
return infos
|
|
|
|
# **************************************************************************************************************** #
|
|
# Méthodes d'affichage pour debug
|
|
# **************************************************************************************************************** #
|
|
def str_etudiants_in_jury(self, delim=";"):
|
|
# En tete:
|
|
entete = ["Id", "Nom", "Abandon", "Diplome"]
|
|
for nom_sem in JuryPE.TOUS_LES_PARCOURS:
|
|
entete += [nom_sem, "descr"]
|
|
chaine = delim.join(entete) + "\n"
|
|
|
|
for etudid in self.PARCOURSINFO_DICT:
|
|
donnees = self.PARCOURSINFO_DICT[etudid]
|
|
# pe_tools.pe_print(etudid, donnees)
|
|
# les infos générales
|
|
descr = [
|
|
etudid,
|
|
donnees["nom"],
|
|
str(donnees["abandon"]),
|
|
str(donnees["diplome"]),
|
|
]
|
|
|
|
# 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
|
|
else "manquant"
|
|
)
|
|
descr += [
|
|
donnees[nom_sem] if donnees[nom_sem] != None else "manquant",
|
|
table,
|
|
]
|
|
|
|
chaine += delim.join(descr) + "\n"
|
|
return chaine
|
|
|
|
#
|
|
def export_juryPEDict(self):
|
|
"""Export csv de self.PARCOURSINFO_DICT"""
|
|
fichier = "juryParcoursDict_" + str(self.diplome)
|
|
pe_tools.pe_print(" -> Export de " + fichier)
|
|
filename = self.NOM_EXPORT_ZIP + fichier + ".csv"
|
|
self.zipfile.writestr(filename, self.str_etudiants_in_jury())
|
|
|
|
def get_allTagForAggregat(self, nom_aggregat):
|
|
"""Extrait du dictionnaire syntheseJury la liste des tags d'un semestre ou
|
|
d'un aggrégat donné par son nom (S1, S2, S3 ou S4, 1A, ...). Renvoie [] si aucun tag.
|
|
"""
|
|
taglist = set()
|
|
for etudid in self.get_etudids_du_jury():
|
|
taglist = taglist.union(
|
|
set(self.syntheseJury[etudid][nom_aggregat]["groupe"].keys())
|
|
)
|
|
taglist = taglist.union(
|
|
set(self.syntheseJury[etudid][nom_aggregat]["promo"].keys())
|
|
)
|
|
return list(taglist)
|
|
|
|
def get_allTagInSyntheseJury(self):
|
|
"""Extrait tous les tags du dictionnaire syntheseJury trié par
|
|
ordre alphabétique. [] si aucun tag"""
|
|
allTags = set()
|
|
for nom in JuryPE.TOUS_LES_PARCOURS:
|
|
allTags = allTags.union(set(self.get_allTagForAggregat(nom)))
|
|
return sorted(list(allTags)) if len(allTags) > 0 else []
|
|
|
|
def table_syntheseJury(self, mode="singlesheet"): # was str_syntheseJury
|
|
"""Table(s) du jury
|
|
mode: singlesheet ou multiplesheet pour export excel
|
|
"""
|
|
sT = SeqGenTable() # le fichier excel à générer
|
|
|
|
# Les etudids des étudiants à afficher, triés par ordre alphabétiques de nom+prénom
|
|
donnees_tries = sorted(
|
|
[
|
|
(
|
|
etudid,
|
|
self.syntheseJury[etudid]["nom"]
|
|
+ " "
|
|
+ self.syntheseJury[etudid]["prenom"],
|
|
)
|
|
for etudid in self.syntheseJury.keys()
|
|
],
|
|
key=lambda c: c[1],
|
|
)
|
|
etudids = [e[0] for e in donnees_tries]
|
|
if not etudids: # Si pas d'étudiants
|
|
T = GenTable(
|
|
columns_ids=["pas d'étudiants"],
|
|
rows=[],
|
|
titles={"pas d'étudiants": "pas d'étudiants"},
|
|
html_sortable=True,
|
|
xls_sheet_name="but",
|
|
)
|
|
sT.add_genTable("but", T)
|
|
return sT
|
|
|
|
# Si des étudiants
|
|
maxParcours = max(
|
|
[self.syntheseJury[etudid]["nbSemestres"] for etudid in etudids]
|
|
)
|
|
|
|
infos = ["civilite", "nom", "prenom", "age", "nbSemestres"]
|
|
entete = ["etudid"]
|
|
entete.extend(infos)
|
|
entete.extend(["P%d" % i for i in range(1, maxParcours + 1)])
|
|
champs = [
|
|
"note",
|
|
"class groupe",
|
|
"class promo",
|
|
"min/moy/max groupe",
|
|
"min/moy/max promo",
|
|
]
|
|
|
|
# Les aggrégats à afficher par ordre tel que indiqué dans le dictionnaire parcours
|
|
aggregats = list(JuryPE.PARCOURS.keys()) # ['S1', 'S2', ..., '1A', '4S']
|
|
# aggregats = sorted(
|
|
# aggregats, key=lambda t: JuryPE.PARCOURS[t]["ordre"]
|
|
# ) # Tri des aggrégats
|
|
|
|
if mode == "multiplesheet":
|
|
allSheets = (
|
|
self.get_allTagInSyntheseJury()
|
|
) # tous les tags de syntheseJuryDict
|
|
allSheets = sorted(allSheets) # Tri des tags par ordre alphabétique
|
|
for sem in JuryPE.TOUS_LES_PARCOURS:
|
|
entete.extend(["%s %s" % (sem, champ) for champ in champs])
|
|
else: # "singlesheet"
|
|
allSheets = ["singlesheet"]
|
|
for (
|
|
sem
|
|
) in (
|
|
JuryPE.TOUS_LES_PARCOURS
|
|
): # JuryPE.PARCOURS.keys() -> ['S1', 'S2', ..., '1A', '4S']
|
|
tags = self.get_allTagForAggregat(sem)
|
|
entete.extend(
|
|
["%s %s %s" % (sem, tag, champ) for tag in tags for champ in champs]
|
|
)
|
|
|
|
columns_ids = entete # les id et les titres de colonnes sont ici identiques
|
|
titles = {i: i for i in columns_ids}
|
|
|
|
for (
|
|
sheet
|
|
) in (
|
|
allSheets
|
|
): # Pour tous les sheets à générer (1 si singlesheet, autant que de tags si multiplesheet)
|
|
rows = []
|
|
for etudid in etudids:
|
|
e = self.syntheseJury[etudid]
|
|
# Les info générales:
|
|
row = {
|
|
"etudid": etudid,
|
|
"civilite": e["civilite"],
|
|
"nom": e["nom"],
|
|
"prenom": e["prenom"],
|
|
"age": e["age"],
|
|
"nbSemestres": e["nbSemestres"],
|
|
}
|
|
# Les parcours: P1, P2, ...
|
|
n = 1
|
|
for p in e["parcours"]:
|
|
row["P%d" % n] = p["titreannee"]
|
|
n += 1
|
|
# if self.syntheseJury[etudid]['nbSemestres'] < maxParcours:
|
|
# descr += delim.join( ['']*( maxParcours -self.syntheseJury[etudid]['nbSemestres']) ) + delim
|
|
for sem in aggregats: # JuryPE.PARCOURS.keys():
|
|
listeTags = (
|
|
self.get_allTagForAggregat(sem)
|
|
if mode == "singlesheet"
|
|
else [sheet]
|
|
)
|
|
for tag in listeTags:
|
|
if tag in self.syntheseJury[etudid][sem]["groupe"]:
|
|
resgroupe = self.syntheseJury[etudid][sem]["groupe"][
|
|
tag
|
|
] # tuple
|
|
else:
|
|
resgroupe = (None, None, None, None, None, None, None)
|
|
if tag in self.syntheseJury[etudid][sem]["promo"]:
|
|
respromo = self.syntheseJury[etudid][sem]["promo"][tag]
|
|
else:
|
|
respromo = (None, None, None, None, None, None, None)
|
|
|
|
# note = "%2.2f" % resgroupe[0] if isinstance(resgroupe[0], float) else str(resgroupe[0])
|
|
champ = (
|
|
"%s %s " % (sem, tag)
|
|
if mode == "singlesheet"
|
|
else "%s " % (sem)
|
|
)
|
|
row[champ + "note"] = scu.fmt_note(resgroupe[0])
|
|
row[champ + "class groupe"] = "%s / %s" % (
|
|
resgroupe[2] if resgroupe[2] else "-",
|
|
resgroupe[3] if resgroupe[3] else "-",
|
|
)
|
|
row[champ + "class promo"] = "%s / %s" % (
|
|
respromo[2] if respromo[2] else "-",
|
|
respromo[3] if respromo[3] else "-",
|
|
)
|
|
row[champ + "min/moy/max groupe"] = "%s / %s / %s" % tuple(
|
|
(scu.fmt_note(x) if x is not None else "-")
|
|
for x in (resgroupe[6], resgroupe[4], resgroupe[5])
|
|
)
|
|
row[champ + "min/moy/max promo"] = "%s / %s / %s" % tuple(
|
|
(scu.fmt_note(x) if x is not None else "-")
|
|
for x in (respromo[6], respromo[4], respromo[5])
|
|
)
|
|
rows.append(row)
|
|
|
|
T = GenTable(
|
|
columns_ids=columns_ids,
|
|
rows=rows,
|
|
titles=titles,
|
|
html_sortable=True,
|
|
xls_sheet_name=sheet,
|
|
)
|
|
sT.add_genTable(sheet, T)
|
|
|
|
if mode == "singlesheet":
|
|
return sT.get_genTable("singlesheet")
|
|
else:
|
|
return sT
|
|
|
|
# **************************************************************************************************************** #
|
|
# Méthodes de classe pour gestion d'un cache de données accélérant les calculs / intérêt à débattre
|
|
# **************************************************************************************************************** #
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
def get_cache_etudInfo_d_un_etudiant(self, etudid):
|
|
"""Renvoie les informations sur le parcours d'un étudiant soit en les relisant depuis
|
|
ETUDINFO_DICT si mémorisée soit en les chargeant et en les mémorisant
|
|
"""
|
|
if etudid not in self.ETUDINFO_DICT:
|
|
self.ETUDINFO_DICT[etudid] = sco_etud.get_etud_info(
|
|
etudid=etudid, filled=True
|
|
)[0]
|
|
return self.ETUDINFO_DICT[etudid]
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
def get_cache_notes_d_un_semestre(self, formsemestre_id: int) -> NotesTableCompat:
|
|
"""Charge la table des notes d'un formsemestre"""
|
|
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
|
return res_sem.load_formsemestre_results(formsemestre)
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
|
|
# ------------------------------------------------------------------------------------------------------------------
|
|
def get_semestresBUT_d_un_etudiant(self, etudid, semestre_id=None):
|
|
"""Renvoie la liste des semestres DUT d'un étudiant
|
|
pour un semestre_id (parmi 1,2,3,4) 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"""
|
|
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"] <= nbre_semestres
|
|
]
|
|
else:
|
|
sesSems = [sem for sem in etud["sems"] if sem["semestre_id"] == semestre_id]
|
|
return sesSems
|
|
|
|
# **********************************************
|
|
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_semestresBUT_d_un_etudiant(etudid)
|
|
if semestres:
|
|
return max([get_annee_diplome_semestre(sem) for sem in semestres])
|
|
else:
|
|
return None
|
|
|
|
# *********************************************
|
|
# Fonctions d'affichage pour debug
|
|
def get_resultat_d_un_etudiant(self, etudid):
|
|
chaine = ""
|
|
for nom_sem in JuryPE.TOUS_LES_SEMESTRES:
|
|
semtagid = self.PARCOURSINFO_DICT[etudid][
|
|
nom_sem
|
|
] # le formsemestre_id du semestre taggué de l'étudiant
|
|
semtag = self.semTagDict[semtagid]
|
|
chaine += "Semestre " + nom_sem + str(semtagid) + "\n"
|
|
# le détail du calcul tag par tag
|
|
# chaine += "Détail du calcul du tag\n"
|
|
# chaine += "-----------------------\n"
|
|
# for tag in semtag.taglist:
|
|
# chaine += "Tag=" + tag + "\n"
|
|
# chaine += semtag.str_detail_resultat_d_un_tag(tag, etudid=etudid) + "\n"
|
|
# le bilan des tags
|
|
chaine += "Bilan des tags\n"
|
|
chaine += "--------------\n"
|
|
for tag in semtag.taglist:
|
|
chaine += (
|
|
tag + ";" + semtag.str_resTag_d_un_etudiant(tag, etudid) + "\n"
|
|
)
|
|
chaine += "\n"
|
|
return chaine
|
|
|
|
def get_date_entree_etudiant(self, etudid) -> str:
|
|
"""Renvoie la date d'entree d'un étudiant: "1996" """
|
|
annees_debut = [
|
|
int(sem["annee_debut"]) for sem in self.ETUDINFO_DICT[etudid]["sems"]
|
|
]
|
|
if annees_debut:
|
|
return str(min(annees_debut))
|
|
return ""
|
|
|
|
|
|
# ----------------------------------------------------------------------------------------
|
|
# Fonctions
|
|
|
|
|
|
# ----------------------------------------------------------------------------------------
|
|
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 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.
|
|
|
|
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 :
|
|
* 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"] <= 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
|
|
increment = decalage * (1 - delta)
|
|
return int(sem["annee_fin"]) + nbreAnRestant + increment
|
|
|
|
|
|
# ----------------------------------------------------------------------------------------
|
|
|
|
|
|
# ----------------------------------------------------------------------------------
|
|
def get_cosemestres_diplomants(semBase, avec_meme_formation=False):
|
|
"""Partant d'un semestre de Base = {'formsemestre_id': ..., 'semestre_id': ..., 'annee_debut': ...},
|
|
renvoie la liste de tous ses co-semestres (lui-meme inclus)
|
|
Par co-semestre, s'entend les semestres :
|
|
> dont l'annee predite pour la remise du diplome DUT est la meme
|
|
> dont la formation est la même (optionnel)
|
|
> ne prenant en compte que les etudiants sans redoublement
|
|
"""
|
|
tousLesSems = (
|
|
sco_formsemestre.do_formsemestre_list()
|
|
) # tous les semestres memorisés dans scodoc
|
|
diplome = get_annee_diplome_semestre(semBase)
|
|
|
|
if avec_meme_formation: # si une formation est imposee
|
|
nom_formation = str(semBase["formation_id"])
|
|
if pe_tools.PE_DEBUG:
|
|
pe_tools.pe_print(" - avec formation imposée : ", nom_formation)
|
|
coSems = [
|
|
sem
|
|
for sem in tousLesSems
|
|
if get_annee_diplome_semestre(sem) == diplome
|
|
and sem["formation_id"] == semBase["formation_id"]
|
|
]
|
|
else:
|
|
if pe_tools.PE_DEBUG:
|
|
pe_tools.pe_print(" - toutes formations confondues")
|
|
coSems = [
|
|
sem for sem in tousLesSems if get_annee_diplome_semestre(sem) == diplome
|
|
]
|
|
|
|
return coSems
|