Refonte de la recherche des étudiants à prendre en compte dans le JuryPE
This commit is contained in:
parent
df20372abb
commit
c9336dd01c
363
app/pe/pe_etudiant.py
Normal file
363
app/pe/pe_etudiant.py
Normal file
@ -0,0 +1,363 @@
|
||||
# -*- 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
|
||||
for (no_etud, etudid) in enumerate(self.etudiants_ids):
|
||||
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
|
||||
pe_tools.pe_print(f" => {len(self.etudiants_jury_ids)} étudiants à diplômer en {annee_diplome}")
|
||||
nbre_abandons = len(self.etudiants_ids) - len(self.etudiants_ids)
|
||||
pe_tools.pe_print(f" => {nbre_abandons} étudiants éliminer pour abandon")
|
||||
pe_tools.pe_print(f" => quelques étudiants futurs diplômés : " + ", ".join([str(etudid) for etudid in list(self.etudiants_jury_ids)[:10]]))
|
||||
|
||||
def get_etudids(self, annee_diplome: int, ordre="aucun") -> list:
|
||||
"""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é.
|
||||
|
||||
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()
|
||||
"""
|
||||
etudids = [
|
||||
etudid
|
||||
for (etudid, donnees) in self.cursus.items()
|
||||
if donnees["diplome"] == annee_diplome and not donnees["abandon"]
|
||||
]
|
||||
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
|
||||
|
||||
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 1A=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
|
||||
|
||||
"""Le cursus global de l'étudiant"""
|
||||
semestres_etudiant = {
|
||||
frmsem.formsemestre_id: frmsem for frmsem in identite.get_formsemestres()
|
||||
}
|
||||
|
||||
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
|
||||
"formsemestres": semestres_etudiant # les semestres de l'étudiant
|
||||
}
|
||||
|
||||
""" Est-il réorienté / démissionnaire ou a-t-il arrêté volontairement sa formation ?"""
|
||||
self.cursus[etudid]["abandon"] = arret_de_formation(
|
||||
identite, cosemestres
|
||||
)
|
||||
|
||||
"""Tri des semestres par n° de semestre"""
|
||||
for nom_sem in pe_tools.TOUS_LES_SEMESTRES:
|
||||
numero_sem = int(nom_sem[1]) + 1
|
||||
self.cursus[etudid][nom_sem] = {fid: semestres_etudiant[fid]
|
||||
for fid in semestres_etudiant
|
||||
if semestres_etudiant[fid].semestre_id == numero_sem}
|
||||
|
||||
|
||||
"""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:
|
||||
self.cursus[etudid][parcours] = self.cursus[etudid][parcours] | self.cursus[etudid][nom_sem]
|
||||
|
||||
if pe_tools.PE_DEBUG and pe_tools.PE_DEBUG >= 2:
|
||||
pe_tools.pe_print(
|
||||
parcours + "=" + str(self.cursus[etudid][parcours]),
|
||||
end="",
|
||||
)
|
||||
|
||||
|
||||
def get_formsemestres_jury(self):
|
||||
"""Ayant connaissance des étudiants dont il faut calculer les moyennes pour
|
||||
le jury PE (attribut `self.etudiant_ids), renvoie l'ensemble des formsemestres
|
||||
de leur cursus, dont il faudra calculer la moyenne.
|
||||
|
||||
Returns:
|
||||
Un ensemble de formsemestres
|
||||
"""
|
||||
formsemestres = {}
|
||||
for etudid in self.etudiants_ids:
|
||||
formsem_etudid = set(self.cursus[etudid].keys())
|
||||
formsemestres = formsemestres | formsem_etudid
|
||||
return formsemestres
|
||||
|
||||
|
||||
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()
|
||||
for (fid, sem) in semestres.items(): # pour chacun des semestres de la liste
|
||||
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
|
@ -46,9 +46,11 @@ from flask import g
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app import log
|
||||
from app.models import FormSemestre
|
||||
from app.scodoc import sco_formsemestre
|
||||
from app.scodoc.sco_logos import find_logo
|
||||
|
||||
PE_DEBUG = 0
|
||||
PE_DEBUG = 1
|
||||
|
||||
if not PE_DEBUG:
|
||||
# log to notes.log
|
||||
@ -74,10 +76,96 @@ PE_LOCAL_FOOTER_TMPL = REP_LOCAL_AVIS + "local/modeles/un_footer.tex"
|
||||
|
||||
# ----------------------------------------------------------------------------------------
|
||||
|
||||
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[AGGREGAT_DIPLOMANT]["aggregat"]
|
||||
TOUS_LES_AGGREGATS = [cle for cle in PARCOURS.keys() if not cle.startswith("S")]
|
||||
TOUS_LES_PARCOURS = list(PARCOURS.keys())
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------
|
||||
def print_semestres_description(sems,
|
||||
avec_affichage_debug=False):
|
||||
def print_semestres_description(sems, avec_affichage_debug=False):
|
||||
"""Dediee a l'affichage d'un semestre pour debug du module"""
|
||||
|
||||
def chaine_semestre(sem):
|
||||
@ -166,10 +254,7 @@ def list_directory_filenames(path):
|
||||
return R
|
||||
|
||||
|
||||
def add_local_file_to_zip(zipfile,
|
||||
ziproot,
|
||||
pathname,
|
||||
path_in_zip):
|
||||
def add_local_file_to_zip(zipfile, ziproot, pathname, path_in_zip):
|
||||
"""Read pathname server file and add content to zip under path_in_zip"""
|
||||
rooted_path_in_zip = os.path.join(ziproot, path_in_zip)
|
||||
zipfile.write(filename=pathname, arcname=rooted_path_in_zip)
|
||||
@ -177,8 +262,7 @@ def add_local_file_to_zip(zipfile,
|
||||
# zipfile.writestr(rooted_path_in_zip, data)
|
||||
|
||||
|
||||
def add_refs_to_register(register,
|
||||
directory):
|
||||
def add_refs_to_register(register, directory):
|
||||
"""Ajoute les fichiers trouvés dans directory au registre (dictionaire) sous la forme
|
||||
filename => pathname
|
||||
"""
|
||||
@ -188,8 +272,7 @@ def add_refs_to_register(register,
|
||||
register[filename] = pathname
|
||||
|
||||
|
||||
def add_pe_stuff_to_zip(zipfile,
|
||||
ziproot):
|
||||
def add_pe_stuff_to_zip(zipfile, ziproot):
|
||||
"""Add auxiliary files to (already opened) zip
|
||||
Put all local files found under config/doc_poursuites_etudes/local
|
||||
and config/doc_poursuites_etudes/distrib
|
||||
@ -218,6 +301,104 @@ def add_pe_stuff_to_zip(zipfile,
|
||||
)
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------
|
||||
def get_annee_diplome_semestre(sem_base, nbre_sem_formation=6) -> int:
|
||||
"""Pour un semestre ``sem_base`` donné (supposé être un semestre d'une formation BUT à 6 semestres)
|
||||
et connaissant le numéro du semestre, ses dates de début et de fin du semestre, prédit l'année à laquelle
|
||||
sera remis le diplôme BUT des étudiants qui y sont scolarisés
|
||||
(en supposant qu'il n'y ait pas de redoublement à venir).
|
||||
|
||||
**Remarque sur le calcul** : 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'année 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
|
||||
|
||||
La fonction est adaptée au cas des semestres décalés.
|
||||
|
||||
Par exemple :
|
||||
|
||||
* S5 décalé débutant en 2025 et finissant en 2025 : diplome en 2026
|
||||
* S3 décalé débutant en 2025 et finissant en 2025 : diplome en 2027
|
||||
|
||||
Args:
|
||||
sem_base: Le semestre à partir duquel est prédit l'année de diplomation, soit :
|
||||
|
||||
* un ``FormSemestre`` (Scodoc9)
|
||||
* un dict (format compatible avec Scodoc7)
|
||||
|
||||
nbre_sem_formation: Le nombre de semestre prévu dans la formation (par défaut 6 pour un BUT)
|
||||
"""
|
||||
|
||||
if isinstance(sem_base, FormSemestre):
|
||||
sem_id = sem_base.semestre_id
|
||||
annee_fin = sem_base.date_fin.year
|
||||
annee_debut = sem_base.date_debut.year
|
||||
else: # sem_base est un dictionnaire (Scodoc 7)
|
||||
sem_id = sem_base["semestre_id"]
|
||||
annee_fin = int(sem_base["annee_fin"])
|
||||
annee_debut = int(sem_base["annee_debut"])
|
||||
if (
|
||||
1 <= sem_id <= nbre_sem_formation
|
||||
): # Si le semestre est un semestre BUT => problème si formation BUT en 1 an ??
|
||||
nbreSemRestant = (
|
||||
nbre_sem_formation - sem_id
|
||||
) # nombre de semestres restant avant diplome
|
||||
nbreAnRestant = nbreSemRestant // 2 # nombre d'annees restant avant diplome
|
||||
# Flag permettant d'activer ou désactiver un increment à prendre en compte en cas de semestre décalé
|
||||
# avec 1 - delta = 0 si semestre de 1ere partie d'année / 1 sinon
|
||||
delta = annee_fin - annee_debut
|
||||
decalage = nbreSemRestant % 2 # 0 si S4, 1 si S3, 0 si S2, 1 si S1
|
||||
increment = decalage * (1 - delta)
|
||||
return annee_fin + nbreAnRestant + increment
|
||||
|
||||
|
||||
def get_cosemestres_diplomants(annee_diplome: int, formation_id: int) -> list:
|
||||
"""Ensemble des cosemestres donnant lieu à diplomation à l'``annee_diplome``
|
||||
et s'intégrant à la formation donnée par son ``formation_id``.
|
||||
|
||||
**Définition** : Un co-semestre est un semestre :
|
||||
|
||||
* dont l'année de diplômation prédite (sans redoublement) est la même
|
||||
* dont la formation est la même (optionnel)
|
||||
* qui a des étudiants inscrits
|
||||
|
||||
Si formation_id == None, ne prend pas en compte l'identifiant de formation
|
||||
TODO:: A raccrocher à un programme
|
||||
|
||||
Args:
|
||||
annee_diplome: L'année de diplomation
|
||||
formation_id: L'identifiant de la formation
|
||||
"""
|
||||
tousLesSems = (
|
||||
sco_formsemestre.do_formsemestre_list()
|
||||
) # tous les semestres memorisés dans scodoc
|
||||
|
||||
if formation_id:
|
||||
cosemestres_fids = {
|
||||
sem["id"]
|
||||
for sem in tousLesSems
|
||||
if get_annee_diplome_semestre(sem) == annee_diplome
|
||||
and sem["formation_id"] == formation_id
|
||||
}
|
||||
else:
|
||||
cosemestres_fids = {
|
||||
sem["id"]
|
||||
for sem in tousLesSems
|
||||
if get_annee_diplome_semestre(sem) == annee_diplome
|
||||
}
|
||||
|
||||
cosemestres = {}
|
||||
for fid in cosemestres_fids:
|
||||
cosem = FormSemestre.get_formsemestre(fid)
|
||||
if len(cosem.etuds_inscriptions) > 0:
|
||||
cosemestres[fid] = cosem
|
||||
|
||||
return cosemestres
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------
|
||||
# Variable pour le debug des avislatex (en squeezant le calcul du jury souvent long)
|
||||
JURY_SYNTHESE_POUR_DEBUG = {
|
||||
|
@ -51,12 +51,38 @@ from app.pe import pe_avislatex
|
||||
|
||||
|
||||
def _pe_view_sem_recap_form(formsemestre_id):
|
||||
sem_base = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
if not sem_base.formation.is_apc() or sem_base.formation.get_cursus().NB_SEM < 6:
|
||||
H = [
|
||||
html_sco_header.sco_header(page_title="Avis de poursuite d'études"),
|
||||
f"""<h2 class="formsemestre">Génération des avis de poursuites d'études</h2>
|
||||
<p class="help">
|
||||
Cette fonction génère un ensemble de fichiers permettant d'éditer des avis de
|
||||
poursuites d'études.
|
||||
<br>
|
||||
De nombreux aspects sont paramétrables:
|
||||
<a href="https://scodoc.org/AvisPoursuiteEtudes"
|
||||
target="_blank" rel="noopener noreferrer">
|
||||
voir la documentation
|
||||
</a>.
|
||||
Cette fonction (en Scodoc9) n'est prévue que pour le BUT.
|
||||
<br>
|
||||
Rendez-vous donc sur un semestre de BUT.
|
||||
</p>
|
||||
<p class=
|
||||
""",
|
||||
]
|
||||
return "\n".join(H) + html_sco_header.sco_footer()
|
||||
|
||||
# L'année du diplome
|
||||
diplome = pe_tools.get_annee_diplome_semestre(sem_base)
|
||||
|
||||
H = [
|
||||
html_sco_header.sco_header(page_title="Avis de poursuite d'études"),
|
||||
f"""<h2 class="formsemestre">Génération des avis de poursuites d'études</h2>
|
||||
<p class="help">
|
||||
Cette fonction génère un ensemble de fichiers permettant d'éditer des avis de
|
||||
poursuites d'études.
|
||||
poursuites d'études pour les étudiants diplômés en {diplome}.
|
||||
<br>
|
||||
De nombreux aspects sont paramétrables:
|
||||
<a href="https://scodoc.org/AvisPoursuiteEtudes"
|
||||
@ -104,12 +130,16 @@ def pe_view_sem_recap(
|
||||
"Le module de Poursuites d'Etudes avec Scodoc 9 n'est disponible que pour des formations BUT"
|
||||
)
|
||||
|
||||
# TODO: CB: A supprimer à long terme
|
||||
semBase = sco_formsemestre.get_formsemestre(
|
||||
formsemestre_id
|
||||
) # FormSemestre.get_formsemestre()
|
||||
if sem_base.formation.get_cursus().NB_SEM < 6:
|
||||
raise ScoValueError(
|
||||
"Le module de Poursuites d'Etudes avec Scodoc 9 n'est pas prévu pour une formation de moins de 6 semestres"
|
||||
)
|
||||
|
||||
# L'année du diplome
|
||||
diplome = pe_tools.get_annee_diplome_semestre(sem_base)
|
||||
|
||||
jury = pe_jurype.JuryPE(diplome, sem_base.formation.formation_id)
|
||||
|
||||
jury = pe_jurype.JuryPE(sem_base, semBase)
|
||||
# Ajout avis LaTeX au même zip:
|
||||
etudids = list(jury.syntheseJury.keys())
|
||||
|
||||
@ -160,7 +190,7 @@ def pe_view_sem_recap(
|
||||
sT = pe_avislatex.table_syntheseAnnotationPE(jury.syntheseJury, tag_annotation_pe)
|
||||
if sT:
|
||||
jury.add_file_to_zip(
|
||||
jury.NOM_EXPORT_ZIP + "_annotationsPE" + scu.XLSX_SUFFIX, sT.excel()
|
||||
jury.nom_export_zip + "_annotationsPE" + scu.XLSX_SUFFIX, sT.excel()
|
||||
)
|
||||
|
||||
if False:
|
||||
@ -184,13 +214,13 @@ def pe_view_sem_recap(
|
||||
jury.add_file_to_zip("avis/avis_poursuite.tex", doc_latex)
|
||||
|
||||
# Ajoute image, LaTeX class file(s) and modeles
|
||||
pe_tools.add_pe_stuff_to_zip(jury.zipfile, jury.NOM_EXPORT_ZIP)
|
||||
pe_tools.add_pe_stuff_to_zip(jury.zipfile, jury.nom_export_zip)
|
||||
|
||||
data = jury.get_zipped_data()
|
||||
|
||||
return send_file(
|
||||
data,
|
||||
mimetype="application/zip",
|
||||
download_name=scu.sanitize_filename(jury.NOM_EXPORT_ZIP + ".zip"),
|
||||
download_name=scu.sanitize_filename(jury.nom_export_zip + ".zip"),
|
||||
as_attachment=True,
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user