Update opolka/ScoDoc from ScoDoc/ScoDoc #2

Merged
opolka merged 1272 commits from ScoDoc/ScoDoc:master into master 2024-05-27 09:11:04 +02:00
3 changed files with 594 additions and 20 deletions
Showing only changes of commit c9336dd01c - Show all commits

363
app/pe/pe_etudiant.py Normal file
View 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

View File

@ -46,9 +46,11 @@ from flask import g
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app import log from app import log
from app.models import FormSemestre
from app.scodoc import sco_formsemestre
from app.scodoc.sco_logos import find_logo from app.scodoc.sco_logos import find_logo
PE_DEBUG = 0 PE_DEBUG = 1
if not PE_DEBUG: if not PE_DEBUG:
# log to notes.log # 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, def print_semestres_description(sems, avec_affichage_debug=False):
avec_affichage_debug=False):
"""Dediee a l'affichage d'un semestre pour debug du module""" """Dediee a l'affichage d'un semestre pour debug du module"""
def chaine_semestre(sem): def chaine_semestre(sem):
@ -166,10 +254,7 @@ def list_directory_filenames(path):
return R return R
def add_local_file_to_zip(zipfile, def add_local_file_to_zip(zipfile, ziproot, pathname, path_in_zip):
ziproot,
pathname,
path_in_zip):
"""Read pathname server file and add content to zip under 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) rooted_path_in_zip = os.path.join(ziproot, path_in_zip)
zipfile.write(filename=pathname, arcname=rooted_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) # zipfile.writestr(rooted_path_in_zip, data)
def add_refs_to_register(register, def add_refs_to_register(register, directory):
directory):
"""Ajoute les fichiers trouvés dans directory au registre (dictionaire) sous la forme """Ajoute les fichiers trouvés dans directory au registre (dictionaire) sous la forme
filename => pathname filename => pathname
""" """
@ -188,8 +272,7 @@ def add_refs_to_register(register,
register[filename] = pathname register[filename] = pathname
def add_pe_stuff_to_zip(zipfile, def add_pe_stuff_to_zip(zipfile, ziproot):
ziproot):
"""Add auxiliary files to (already opened) zip """Add auxiliary files to (already opened) zip
Put all local files found under config/doc_poursuites_etudes/local Put all local files found under config/doc_poursuites_etudes/local
and config/doc_poursuites_etudes/distrib 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) # Variable pour le debug des avislatex (en squeezant le calcul du jury souvent long)
JURY_SYNTHESE_POUR_DEBUG = { JURY_SYNTHESE_POUR_DEBUG = {

View File

@ -51,12 +51,38 @@ from app.pe import pe_avislatex
def _pe_view_sem_recap_form(formsemestre_id): 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 = [ H = [
html_sco_header.sco_header(page_title="Avis de poursuite d'études"), 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> f"""<h2 class="formsemestre">Génération des avis de poursuites d'études</h2>
<p class="help"> <p class="help">
Cette fonction génère un ensemble de fichiers permettant d'éditer des avis de 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> <br>
De nombreux aspects sont paramétrables: De nombreux aspects sont paramétrables:
<a href="https://scodoc.org/AvisPoursuiteEtudes" <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" "Le module de Poursuites d'Etudes avec Scodoc 9 n'est disponible que pour des formations BUT"
) )
# TODO: CB: A supprimer à long terme if sem_base.formation.get_cursus().NB_SEM < 6:
semBase = sco_formsemestre.get_formsemestre( raise ScoValueError(
formsemestre_id "Le module de Poursuites d'Etudes avec Scodoc 9 n'est pas prévu pour une formation de moins de 6 semestres"
) # FormSemestre.get_formsemestre() )
# 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: # Ajout avis LaTeX au même zip:
etudids = list(jury.syntheseJury.keys()) etudids = list(jury.syntheseJury.keys())
@ -160,7 +190,7 @@ def pe_view_sem_recap(
sT = pe_avislatex.table_syntheseAnnotationPE(jury.syntheseJury, tag_annotation_pe) sT = pe_avislatex.table_syntheseAnnotationPE(jury.syntheseJury, tag_annotation_pe)
if sT: if sT:
jury.add_file_to_zip( 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: if False:
@ -184,13 +214,13 @@ def pe_view_sem_recap(
jury.add_file_to_zip("avis/avis_poursuite.tex", doc_latex) jury.add_file_to_zip("avis/avis_poursuite.tex", doc_latex)
# Ajoute image, LaTeX class file(s) and modeles # 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() data = jury.get_zipped_data()
return send_file( return send_file(
data, data,
mimetype="application/zip", 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, as_attachment=True,
) )