forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
68
app/pe/pe_affichage.py
Normal file
68
app/pe/pe_affichage.py
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
from app.models import Formation, FormSemestre
|
||||||
|
from app.scodoc import codes_cursus
|
||||||
|
|
||||||
|
|
||||||
|
def nom_semestre_etape(semestre: FormSemestre, avec_fid=False) -> str:
|
||||||
|
"""Nom d'un semestre à afficher dans le descriptif des étapes de la scolarité
|
||||||
|
d'un étudiant.
|
||||||
|
|
||||||
|
Par ex: Pour un S2, affiche ``"Semestre 2 FI S014-2015 (129)"`` avec :
|
||||||
|
|
||||||
|
* 2 le numéro du semestre,
|
||||||
|
* FI la modalité,
|
||||||
|
* 2014-2015 les dates
|
||||||
|
|
||||||
|
Args:
|
||||||
|
semestre: Un ``FormSemestre``
|
||||||
|
avec_fid: Ajoute le n° du semestre à la description
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
La chaine de caractères décrivant succintement le semestre
|
||||||
|
"""
|
||||||
|
formation: Formation = semestre.formation
|
||||||
|
parcours = codes_cursus.get_cursus_from_code(formation.type_parcours)
|
||||||
|
|
||||||
|
description = [
|
||||||
|
parcours.SESSION_NAME.capitalize(),
|
||||||
|
str(semestre.semestre_id),
|
||||||
|
semestre.modalite, # eg FI ou FC
|
||||||
|
f"{semestre.date_debut.year}-{semestre.date_fin.year}",
|
||||||
|
]
|
||||||
|
if avec_fid:
|
||||||
|
description.append(f"({semestre.forsemestre_id})")
|
||||||
|
|
||||||
|
return " ".join(description)
|
||||||
|
|
||||||
|
|
||||||
|
def etapes_du_cursus(semestres: dict[int, FormSemestre], nbre_etapes_max: int) -> list[str]:
|
||||||
|
"""Partant d'un dictionnaire de semestres (qui retrace
|
||||||
|
la scolarité d'un étudiant), liste les noms des
|
||||||
|
semestres (en version abbrégée)
|
||||||
|
qu'un étudiant a suivi au cours de sa scolarité à l'IUT.
|
||||||
|
Les noms des semestres sont renvoyés dans un dictionnaire
|
||||||
|
``{"etape i": nom_semestre_a_etape_i}``
|
||||||
|
avec i variant jusqu'à nbre_semestres_max. (S'il n'y a pas de semestre à l'étape i,
|
||||||
|
le nom affiché est vide.
|
||||||
|
|
||||||
|
La fonction suppose la liste des semestres triées par ordre
|
||||||
|
décroissant de date.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
semestres: une liste de ``FormSemestre``
|
||||||
|
nbre_etapes_max: le nombre d'étapes max prise en compte
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Une liste de nom de semestre (dans le même ordre que les ``semestres``)
|
||||||
|
|
||||||
|
See also:
|
||||||
|
app.pe.pe_affichage.nom_semestre_etape
|
||||||
|
"""
|
||||||
|
assert len(semestres) <= nbre_etapes_max
|
||||||
|
|
||||||
|
noms = [nom_semestre_etape(sem, avec_fid=False) for (fid, sem) in semestres.items()]
|
||||||
|
noms = noms[::-1] # trie par ordre croissant
|
||||||
|
|
||||||
|
dico = {f"Etape {i+1}": "" for i in range(nbre_etapes_max)}
|
||||||
|
for (i, nom) in enumerate(noms): # Charge les noms de semestres
|
||||||
|
dico[f"Etape {i+1}"] = nom
|
||||||
|
return dico
|
384
app/pe/pe_comp.py
Normal file
384
app/pe/pe_comp.py
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
# -*- 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 Thu Sep 8 09:36:33 2016
|
||||||
|
|
||||||
|
@author: barasc
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import datetime
|
||||||
|
import re
|
||||||
|
import unicodedata
|
||||||
|
|
||||||
|
|
||||||
|
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 = 1
|
||||||
|
|
||||||
|
if not PE_DEBUG:
|
||||||
|
# log to notes.log
|
||||||
|
def pe_print(*a, **kw):
|
||||||
|
# kw is ignored. log always add a newline
|
||||||
|
log(" ".join(a))
|
||||||
|
|
||||||
|
else:
|
||||||
|
pe_print = print # print function
|
||||||
|
|
||||||
|
|
||||||
|
# Generated LaTeX files are encoded as:
|
||||||
|
PE_LATEX_ENCODING = "utf-8"
|
||||||
|
|
||||||
|
# /opt/scodoc/tools/doc_poursuites_etudes
|
||||||
|
REP_DEFAULT_AVIS = os.path.join(scu.SCO_TOOLS_DIR, "doc_poursuites_etudes/")
|
||||||
|
REP_LOCAL_AVIS = os.path.join(scu.SCODOC_CFG_DIR, "doc_poursuites_etudes/")
|
||||||
|
|
||||||
|
PE_DEFAULT_AVIS_LATEX_TMPL = REP_DEFAULT_AVIS + "distrib/modeles/un_avis.tex"
|
||||||
|
PE_LOCAL_AVIS_LATEX_TMPL = REP_LOCAL_AVIS + "local/modeles/un_avis.tex"
|
||||||
|
PE_DEFAULT_FOOTER_TMPL = REP_DEFAULT_AVIS + "distrib/modeles/un_footer.tex"
|
||||||
|
PE_LOCAL_FOOTER_TMPL = REP_LOCAL_AVIS + "local/modeles/un_footer.tex"
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
"""
|
||||||
|
Descriptif d'un parcours classique BUT
|
||||||
|
|
||||||
|
TODO:: A améliorer si BUT en moins de 6 semestres
|
||||||
|
"""
|
||||||
|
|
||||||
|
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)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
NBRE_SEMESTRES_DIPLOMANT = 6
|
||||||
|
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 calcul_age(born: datetime.date) -> int:
|
||||||
|
"""Calcule l'age connaissant la date de naissance ``born``. (L'age est calculé
|
||||||
|
à partir de l'horloge système).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
born: La date de naissance
|
||||||
|
|
||||||
|
Return:
|
||||||
|
L'age (au regard de la date actuelle)
|
||||||
|
"""
|
||||||
|
if not born or not isinstance(born, datetime.date):
|
||||||
|
return None
|
||||||
|
|
||||||
|
today = datetime.date.today()
|
||||||
|
return (
|
||||||
|
today.year
|
||||||
|
- born.year
|
||||||
|
- ((today.month, today.day) < (born.month, born.day))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def remove_accents(input_unicode_str):
|
||||||
|
"""Supprime les accents d'une chaine unicode"""
|
||||||
|
nfkd_form = unicodedata.normalize("NFKD", input_unicode_str)
|
||||||
|
only_ascii = nfkd_form.encode("ASCII", "ignore")
|
||||||
|
return only_ascii
|
||||||
|
|
||||||
|
|
||||||
|
def escape_for_latex(s):
|
||||||
|
"""Protège les caractères pour inclusion dans du source LaTeX"""
|
||||||
|
if not s:
|
||||||
|
return ""
|
||||||
|
conv = {
|
||||||
|
"&": r"\&",
|
||||||
|
"%": r"\%",
|
||||||
|
"$": r"\$",
|
||||||
|
"#": r"\#",
|
||||||
|
"_": r"\_",
|
||||||
|
"{": r"\{",
|
||||||
|
"}": r"\}",
|
||||||
|
"~": r"\textasciitilde{}",
|
||||||
|
"^": r"\^{}",
|
||||||
|
"\\": r"\textbackslash{}",
|
||||||
|
"<": r"\textless ",
|
||||||
|
">": r"\textgreater ",
|
||||||
|
}
|
||||||
|
exp = re.compile(
|
||||||
|
"|".join(
|
||||||
|
re.escape(key)
|
||||||
|
for key in sorted(list(conv.keys()), key=lambda item: -len(item))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return exp.sub(lambda match: conv[match.group()], s)
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------------------
|
||||||
|
def list_directory_filenames(path):
|
||||||
|
"""List of regular filenames in a directory (recursive)
|
||||||
|
Excludes files and directories begining with .
|
||||||
|
"""
|
||||||
|
R = []
|
||||||
|
for root, dirs, files in os.walk(path, topdown=True):
|
||||||
|
dirs[:] = [d for d in dirs if d[0] != "."]
|
||||||
|
R += [os.path.join(root, fn) for fn in files if fn[0] != "."]
|
||||||
|
return R
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
# data = open(pathname).read()
|
||||||
|
# zipfile.writestr(rooted_path_in_zip, data)
|
||||||
|
|
||||||
|
|
||||||
|
def add_refs_to_register(register, directory):
|
||||||
|
"""Ajoute les fichiers trouvés dans directory au registre (dictionaire) sous la forme
|
||||||
|
filename => pathname
|
||||||
|
"""
|
||||||
|
length = len(directory)
|
||||||
|
for pathname in list_directory_filenames(directory):
|
||||||
|
filename = pathname[length + 1 :]
|
||||||
|
register[filename] = pathname
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
If a file is present in both subtrees, take the one in local.
|
||||||
|
|
||||||
|
Also copy logos
|
||||||
|
"""
|
||||||
|
register = {}
|
||||||
|
# first add standard (distrib references)
|
||||||
|
distrib_dir = os.path.join(REP_DEFAULT_AVIS, "distrib")
|
||||||
|
add_refs_to_register(register=register, directory=distrib_dir)
|
||||||
|
# then add local references (some oh them may overwrite distrib refs)
|
||||||
|
local_dir = os.path.join(REP_LOCAL_AVIS, "local")
|
||||||
|
add_refs_to_register(register=register, directory=local_dir)
|
||||||
|
# at this point register contains all refs (filename, pathname) to be saved
|
||||||
|
for filename, pathname in register.items():
|
||||||
|
add_local_file_to_zip(zipfile, ziproot, pathname, "avis/" + filename)
|
||||||
|
|
||||||
|
# Logos: (add to logos/ directory in zip)
|
||||||
|
logos_names = ["header", "footer"]
|
||||||
|
for name in logos_names:
|
||||||
|
logo = find_logo(logoname=name, dept_id=g.scodoc_dept_id)
|
||||||
|
if logo is not None:
|
||||||
|
add_local_file_to_zip(
|
||||||
|
zipfile, ziproot, logo.filepath, "avis/logos/" + logo.filename
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------------------
|
||||||
|
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
|
||||||
|
|
@ -36,9 +36,10 @@ Created on 17/01/2024
|
|||||||
@author: barasc
|
@author: barasc
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import app.pe.pe_tools as pe_tools
|
import app.pe.pe_comp as pe_comp
|
||||||
from app.models import FormSemestre, Identite
|
from app.models import FormSemestre, Identite
|
||||||
from app.pe.pe_tools import pe_print
|
from app.pe.pe_comp import pe_print
|
||||||
|
|
||||||
|
|
||||||
class EtudiantsJuryPE:
|
class EtudiantsJuryPE:
|
||||||
"""Classe centralisant la gestion des étudiants à prendre en compte dans un jury de PE"""
|
"""Classe centralisant la gestion des étudiants à prendre en compte dans un jury de PE"""
|
||||||
@ -79,22 +80,22 @@ class EtudiantsJuryPE:
|
|||||||
*Remarque* : ex: JuryPE.get_etudiants_in_jury()
|
*Remarque* : ex: JuryPE.get_etudiants_in_jury()
|
||||||
"""
|
"""
|
||||||
"Les cosemestres donnant lieu à même année de diplome"
|
"Les cosemestres donnant lieu à même année de diplome"
|
||||||
cosemestres = pe_tools.get_cosemestres_diplomants(self.annee_diplome, None)
|
cosemestres = pe_comp.get_cosemestres_diplomants(self.annee_diplome, None)
|
||||||
self.cosemestres = cosemestres
|
self.cosemestres = cosemestres
|
||||||
pe_tools.pe_print(
|
pe_comp.pe_print(
|
||||||
"1) Recherche des coSemestres -> %d trouvés" % len(cosemestres)
|
"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)"""
|
"""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")
|
pe_comp.pe_print("2) Liste des étudiants dans les différents co-semestres")
|
||||||
self.etudiants_ids = get_etudiants_dans_semestres(cosemestres)
|
self.etudiants_ids = get_etudiants_dans_semestres(cosemestres)
|
||||||
pe_tools.pe_print(
|
pe_comp.pe_print(
|
||||||
" => %d étudiants trouvés dans les cosemestres" % len(self.etudiants_ids)
|
" => %d étudiants trouvés dans les cosemestres" % len(self.etudiants_ids)
|
||||||
)
|
)
|
||||||
|
|
||||||
"""Analyse des parcours étudiants pour déterminer leur année effective de diplome
|
"""Analyse des parcours étudiants pour déterminer leur année effective de diplome
|
||||||
avec prise en compte des redoublements, des abandons, ...."""
|
avec prise en compte des redoublements, des abandons, ...."""
|
||||||
pe_tools.pe_print("3) Analyse des parcours individuels des étudiants")
|
pe_comp.pe_print("3) Analyse des parcours individuels des étudiants")
|
||||||
|
|
||||||
no_etud = 0
|
no_etud = 0
|
||||||
for no_etud, etudid in enumerate(self.etudiants_ids):
|
for no_etud, etudid in enumerate(self.etudiants_ids):
|
||||||
@ -109,9 +110,9 @@ class EtudiantsJuryPE:
|
|||||||
self.structure_cursus_etudiant(etudid)
|
self.structure_cursus_etudiant(etudid)
|
||||||
|
|
||||||
if (no_etud + 1) % 10 == 0:
|
if (no_etud + 1) % 10 == 0:
|
||||||
pe_tools.pe_print((no_etud + 1), " ", end="")
|
pe_comp.pe_print((no_etud + 1), " ", end="")
|
||||||
no_etud += 1
|
no_etud += 1
|
||||||
pe_tools.pe_print()
|
pe_comp.pe_print()
|
||||||
|
|
||||||
"""Les étudiants à prendre dans le diplôme, étudiants ayant abandonnés non compris"""
|
"""Les étudiants à prendre dans le diplôme, étudiants ayant abandonnés non compris"""
|
||||||
self.etudiants_diplomes = self.get_etudiants_diplomes()
|
self.etudiants_diplomes = self.get_etudiants_diplomes()
|
||||||
@ -124,19 +125,19 @@ class EtudiantsJuryPE:
|
|||||||
self.formsemestres_jury_ids = self.get_formsemestres()
|
self.formsemestres_jury_ids = self.get_formsemestres()
|
||||||
|
|
||||||
# Synthèse
|
# Synthèse
|
||||||
pe_tools.pe_print(
|
pe_comp.pe_print(
|
||||||
f" => {len(self.etudiants_diplomes)} étudiants à diplômer en {self.annee_diplome}"
|
f" => {len(self.etudiants_diplomes)} étudiants à diplômer en {self.annee_diplome}"
|
||||||
)
|
)
|
||||||
nbre_abandons = len(self.etudiants_ids) - len(self.etudiants_diplomes)
|
nbre_abandons = len(self.etudiants_ids) - len(self.etudiants_diplomes)
|
||||||
pe_tools.pe_print(f" => {nbre_abandons} étudiants éliminer pour abandon")
|
pe_comp.pe_print(f" => {nbre_abandons} étudiants éliminer pour abandon")
|
||||||
pe_tools.pe_print(
|
pe_comp.pe_print(
|
||||||
f" => {len(self.formsemestres_jury_ids)} semestres dont il faut calculer la moyenne"
|
f" => {len(self.formsemestres_jury_ids)} semestres dont il faut calculer la moyenne"
|
||||||
)
|
)
|
||||||
pe_tools.pe_print(
|
pe_comp.pe_print(
|
||||||
f" => quelques étudiants futurs diplômés : "
|
f" => quelques étudiants futurs diplômés : "
|
||||||
+ ", ".join([str(etudid) for etudid in list(self.etudiants_diplomes)[:10]])
|
+ ", ".join([str(etudid) for etudid in list(self.etudiants_diplomes)[:10]])
|
||||||
)
|
)
|
||||||
pe_tools.pe_print(
|
pe_comp.pe_print(
|
||||||
f" => semestres dont il faut calculer les moyennes : "
|
f" => semestres dont il faut calculer les moyennes : "
|
||||||
+ ", ".join([str(fid) for fid in list(self.formsemestres_jury_ids)])
|
+ ", ".join([str(fid) for fid in list(self.formsemestres_jury_ids)])
|
||||||
)
|
)
|
||||||
@ -183,9 +184,11 @@ class EtudiantsJuryPE:
|
|||||||
identite = Identite.get_etud(etudid)
|
identite = Identite.get_etud(etudid)
|
||||||
|
|
||||||
"""Le cursus global de l'étudiant (restreint aux semestres APC)"""
|
"""Le cursus global de l'étudiant (restreint aux semestres APC)"""
|
||||||
|
formsemestres = identite.get_formsemestres()
|
||||||
|
|
||||||
semestres_etudiant = {
|
semestres_etudiant = {
|
||||||
frmsem.formsemestre_id: frmsem
|
frmsem.formsemestre_id: frmsem
|
||||||
for frmsem in identite.get_formsemestres()
|
for frmsem in formsemestres
|
||||||
if frmsem.formation.is_apc()
|
if frmsem.formation.is_apc()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,8 +196,10 @@ class EtudiantsJuryPE:
|
|||||||
"etudid": etudid, # les infos sur l'étudiant
|
"etudid": etudid, # les infos sur l'étudiant
|
||||||
"etat_civil": identite.etat_civil, # Ajout à la table jury
|
"etat_civil": identite.etat_civil, # Ajout à la table jury
|
||||||
"nom": identite.nom,
|
"nom": identite.nom,
|
||||||
|
"entree": formsemestres[-1].date_debut.year, # La date d'entrée à l'IUT
|
||||||
"diplome": annee_diplome(identite), # Le date prévisionnelle de son diplôme
|
"diplome": annee_diplome(identite), # Le date prévisionnelle de son diplôme
|
||||||
"formsemestres": semestres_etudiant, # les semestres de l'étudiant
|
"formsemestres": semestres_etudiant, # les semestres de l'étudiant
|
||||||
|
"nb_semestres": len(semestres_etudiant), # le nombre de semestres de l'étudiant
|
||||||
"abandon": False, # va être traité en dessous
|
"abandon": False, # va être traité en dessous
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,7 +223,7 @@ class EtudiantsJuryPE:
|
|||||||
semestres_significatifs = {}
|
semestres_significatifs = {}
|
||||||
for fid in semestres_etudiant:
|
for fid in semestres_etudiant:
|
||||||
semestre = semestres_etudiant[fid]
|
semestre = semestres_etudiant[fid]
|
||||||
if pe_tools.get_annee_diplome_semestre(semestre) <= self.annee_diplome:
|
if pe_comp.get_annee_diplome_semestre(semestre) <= self.annee_diplome:
|
||||||
semestres_significatifs[fid] = semestre
|
semestres_significatifs[fid] = semestre
|
||||||
return semestres_significatifs
|
return semestres_significatifs
|
||||||
|
|
||||||
@ -234,7 +239,7 @@ class EtudiantsJuryPE:
|
|||||||
semestres_significatifs = self.get_semestres_significatifs(etudid)
|
semestres_significatifs = self.get_semestres_significatifs(etudid)
|
||||||
|
|
||||||
"""Tri des semestres par numéro de semestre"""
|
"""Tri des semestres par numéro de semestre"""
|
||||||
for nom_sem in pe_tools.TOUS_LES_SEMESTRES:
|
for nom_sem in pe_comp.TOUS_LES_SEMESTRES:
|
||||||
i = int(nom_sem[1]) # le n° du semestre
|
i = int(nom_sem[1]) # le n° du semestre
|
||||||
semestres_i = {
|
semestres_i = {
|
||||||
fid: semestres_significatifs[fid]
|
fid: semestres_significatifs[fid]
|
||||||
@ -324,7 +329,7 @@ class EtudiantsJuryPE:
|
|||||||
"""
|
"""
|
||||||
if semestres_recherches is None:
|
if semestres_recherches is None:
|
||||||
"""Appel récursif pour obtenir tous les semestres (validants)"""
|
"""Appel récursif pour obtenir tous les semestres (validants)"""
|
||||||
semestres = self.get_formsemestres(pe_tools.AGGREGAT_DIPLOMANT)
|
semestres = self.get_formsemestres(pe_comp.AGGREGAT_DIPLOMANT)
|
||||||
return semestres
|
return semestres
|
||||||
elif isinstance(semestres_recherches, list):
|
elif isinstance(semestres_recherches, list):
|
||||||
"""Appel récursif sur tous les éléments de la liste"""
|
"""Appel récursif sur tous les éléments de la liste"""
|
||||||
@ -335,16 +340,16 @@ class EtudiantsJuryPE:
|
|||||||
return semestres
|
return semestres
|
||||||
elif (
|
elif (
|
||||||
isinstance(semestres_recherches, str)
|
isinstance(semestres_recherches, str)
|
||||||
and semestres_recherches in pe_tools.TOUS_LES_AGGREGATS
|
and semestres_recherches in pe_comp.TOUS_LES_AGGREGATS
|
||||||
):
|
):
|
||||||
"""Cas d'un aggrégat avec appel récursif sur toutes les entrées de l'aggrégat"""
|
"""Cas d'un aggrégat avec appel récursif sur toutes les entrées de l'aggrégat"""
|
||||||
semestres = self.get_formsemestres(
|
semestres = self.get_formsemestres(
|
||||||
pe_tools.PARCOURS[semestres_recherches]["aggregat"]
|
pe_comp.PARCOURS[semestres_recherches]["aggregat"]
|
||||||
)
|
)
|
||||||
return semestres
|
return semestres
|
||||||
elif (
|
elif (
|
||||||
isinstance(semestres_recherches, str)
|
isinstance(semestres_recherches, str)
|
||||||
and semestres_recherches in pe_tools.TOUS_LES_SEMESTRES
|
and semestres_recherches in pe_comp.TOUS_LES_SEMESTRES
|
||||||
):
|
):
|
||||||
"""semestres_recherches est un nom de semestre de type S1,
|
"""semestres_recherches est un nom de semestre de type S1,
|
||||||
pour une recherche parmi les étudiants à prendre en compte
|
pour une recherche parmi les étudiants à prendre en compte
|
||||||
@ -359,6 +364,15 @@ class EtudiantsJuryPE:
|
|||||||
else:
|
else:
|
||||||
raise ValueError("Probleme de paramètres d'appel dans get_formsemestreids")
|
raise ValueError("Probleme de paramètres d'appel dans get_formsemestreids")
|
||||||
|
|
||||||
|
def nbre_etapes_max_diplomes(self):
|
||||||
|
"""Connaissant les étudiants diplomes du jury PE,
|
||||||
|
nombre de semestres (étapes) maximum suivis par les étudiants du jury.
|
||||||
|
"""
|
||||||
|
nbres_semestres = []
|
||||||
|
for etudid in self.diplomes_ids:
|
||||||
|
nbres_semestres.append( self.cursus[etudid]["nb_semestres"] )
|
||||||
|
return max(nbres_semestres)
|
||||||
|
|
||||||
|
|
||||||
def get_etudiants_dans_semestres(semestres: dict[int, FormSemestre]) -> set:
|
def get_etudiants_dans_semestres(semestres: dict[int, FormSemestre]) -> set:
|
||||||
"""Ensemble d'identifiants des étudiants (identifiés via leur ``etudid``)
|
"""Ensemble d'identifiants des étudiants (identifiés via leur ``etudid``)
|
||||||
@ -402,7 +416,7 @@ def annee_diplome(identite: Identite) -> int:
|
|||||||
if formsemestres:
|
if formsemestres:
|
||||||
return max(
|
return max(
|
||||||
[
|
[
|
||||||
pe_tools.get_annee_diplome_semestre(sem_base)
|
pe_comp.get_annee_diplome_semestre(sem_base)
|
||||||
for sem_base in formsemestres
|
for sem_base in formsemestres
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -459,14 +473,14 @@ def arret_de_formation(identite: Identite, cosemestres: list[FormSemestre]) -> b
|
|||||||
# semestre impair => passage de droit en semestre pair suivant (effet de l'annualisation)
|
# semestre impair => passage de droit en semestre pair suivant (effet de l'annualisation)
|
||||||
if numero_dernier_formsemestre % 2 == 1:
|
if numero_dernier_formsemestre % 2 == 1:
|
||||||
numeros_possibles = list(
|
numeros_possibles = list(
|
||||||
range(numero_dernier_formsemestre + 1, pe_tools.NBRE_SEMESTRES_DIPLOMANT)
|
range(numero_dernier_formsemestre + 1, pe_comp.NBRE_SEMESTRES_DIPLOMANT)
|
||||||
)
|
)
|
||||||
# semestre pair => passage en année supérieure ou redoublement
|
# semestre pair => passage en année supérieure ou redoublement
|
||||||
else: #
|
else: #
|
||||||
numeros_possibles = list(
|
numeros_possibles = list(
|
||||||
range(
|
range(
|
||||||
max(numero_dernier_formsemestre - 1, 1),
|
max(numero_dernier_formsemestre - 1, 1),
|
||||||
pe_tools.NBRE_SEMESTRES_DIPLOMANT,
|
pe_comp.NBRE_SEMESTRES_DIPLOMANT,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -486,9 +500,11 @@ def arret_de_formation(identite: Identite, cosemestres: list[FormSemestre]) -> b
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_dernier_semestre_en_date(semestres: dict[int, FormSemestre]):
|
|
||||||
|
|
||||||
|
def get_dernier_semestre_en_date(semestres: dict[int, FormSemestre]) -> FormSemestre:
|
||||||
"""Renvoie le dernier semestre en **date de fin** d'un dictionnaire
|
"""Renvoie le dernier semestre en **date de fin** d'un dictionnaire
|
||||||
de semestres de la forme ``{fid: FormSemestre(fid)}``.
|
de semestres (potentiellement non trié) de la forme ``{fid: FormSemestre(fid)}``.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
semestres: Un dictionnaire de semestres
|
semestres: Un dictionnaire de semestres
|
||||||
@ -505,3 +521,5 @@ def get_dernier_semestre_en_date(semestres: dict[int, FormSemestre]):
|
|||||||
return dernier_semestre
|
return dernier_semestre
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import pandas as pd
|
|
||||||
|
|
||||||
from app.pe import pe_tagtable
|
from app.pe.pe_tabletags import TableTag
|
||||||
from app.pe.pe_tools import PE_DEBUG, pe_print
|
|
||||||
import app.pe.pe_etudiant as pe_etudiant
|
|
||||||
from app.pe.pe_etudiant import EtudiantsJuryPE
|
from app.pe.pe_etudiant import EtudiantsJuryPE
|
||||||
from app.pe.pe_trajectoire import Trajectoire, TrajectoiresJuryPE
|
from app.pe.pe_trajectoire import Trajectoire, TrajectoiresJuryPE
|
||||||
from app.pe.pe_trajectoiretag import TrajectoireTag
|
from app.pe.pe_trajectoiretag import TrajectoireTag
|
||||||
@ -12,7 +9,7 @@ import pandas as pd
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
class AggregatInterclasseTag(pe_tagtable.TableTag):
|
class AggregatInterclasseTag(TableTag):
|
||||||
"""Interclasse l'ensemble des étudiants diplômés à une année
|
"""Interclasse l'ensemble des étudiants diplômés à une année
|
||||||
donnée (celle du jury), pour un aggrégat donné (par ex: 'S2', '3S')
|
donnée (celle du jury), pour un aggrégat donné (par ex: 'S2', '3S')
|
||||||
en reportant :
|
en reportant :
|
||||||
@ -32,7 +29,7 @@ class AggregatInterclasseTag(pe_tagtable.TableTag):
|
|||||||
):
|
):
|
||||||
""""""
|
""""""
|
||||||
"""Table nommée au nom de l'aggrégat (par ex: 3S"""
|
"""Table nommée au nom de l'aggrégat (par ex: 3S"""
|
||||||
pe_tagtable.TableTag.__init__(self, nom_aggregat)
|
TableTag.__init__(self, nom_aggregat)
|
||||||
|
|
||||||
"""Les étudiants diplômés et leurs trajectoires (cf. trajectoires.suivis)"""
|
"""Les étudiants diplômés et leurs trajectoires (cf. trajectoires.suivis)"""
|
||||||
self.diplomes_ids = etudiants.etudiants_diplomes
|
self.diplomes_ids = etudiants.etudiants_diplomes
|
||||||
|
477
app/pe/pe_jury.py
Normal file
477
app/pe/pe_jury.py
Normal file
@ -0,0 +1,477 @@
|
|||||||
|
# -*- 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 FormSemestre
|
||||||
|
from app.models.etudiants import Identite
|
||||||
|
|
||||||
|
from app.scodoc.gen_tables import GenTable, SeqGenTable
|
||||||
|
import app.scodoc.sco_utils as scu
|
||||||
|
from app.pe.pe_etudiant import EtudiantsJuryPE
|
||||||
|
from app.pe.pe_trajectoire import TrajectoiresJuryPE, Trajectoire
|
||||||
|
import app.pe.pe_comp as pe_comp
|
||||||
|
from app.pe.pe_semtag import SemestreTag
|
||||||
|
from app.pe.pe_interclasstag import AggregatInterclasseTag
|
||||||
|
from app.pe.pe_trajectoiretag import TrajectoireTag
|
||||||
|
import app.pe.pe_affichage as pe_affichage
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------------------
|
||||||
|
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'année d'obtention du diplome BUT et du jury de PE (généralement février 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
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------------------------------------------
|
||||||
|
def __init__(self, diplome, formation_id):
|
||||||
|
"""
|
||||||
|
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.promoTagDict = {}
|
||||||
|
|
||||||
|
"L'année du diplome"
|
||||||
|
self.diplome = diplome
|
||||||
|
|
||||||
|
"La formation associée au diplome"
|
||||||
|
self.formation_id = formation_id
|
||||||
|
|
||||||
|
"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")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"""Chargement des étudiants à prendre en compte dans le jury"""
|
||||||
|
pe_comp.pe_print(
|
||||||
|
f"*** Recherche et chargement des étudiants diplômés en {self.diplome} pour la formation {self.formation_id}"
|
||||||
|
)
|
||||||
|
self.etudiants = EtudiantsJuryPE(self.diplome) # Les infos sur les étudiants
|
||||||
|
self.etudiants.find_etudiants(self.formation_id)
|
||||||
|
self.diplomes_ids = self.etudiants.diplomes_ids
|
||||||
|
|
||||||
|
"""Génère les semestres taggués (avec le calcul des moyennes) pour le jury PE"""
|
||||||
|
pe_comp.pe_print("*** Génère les semestres taggués")
|
||||||
|
self.semestres_taggues = compute_semestres_tag(self.etudiants)
|
||||||
|
|
||||||
|
if pe_comp.PE_DEBUG:
|
||||||
|
"""Intègre le bilan des semestres taggués au zip final"""
|
||||||
|
for fid in self.semestres_taggues:
|
||||||
|
formsemestretag = self.semestres_taggues[fid]
|
||||||
|
filename = formsemestretag.nom.replace(" ", "_") + ".csv"
|
||||||
|
pe_comp.pe_print(f" - Export csv de {filename} ")
|
||||||
|
self.add_file_to_zip(
|
||||||
|
filename, formsemestretag.str_tagtable(), path="details_semestres"
|
||||||
|
)
|
||||||
|
|
||||||
|
"""Génère les trajectoires (combinaison de semestres suivis
|
||||||
|
par un étudiant pour atteindre le semestre final d'un aggrégat)
|
||||||
|
"""
|
||||||
|
pe_comp.pe_print(
|
||||||
|
"*** Génère les trajectoires (différentes combinaisons de semestres) des étudiants"
|
||||||
|
)
|
||||||
|
self.trajectoires = TrajectoiresJuryPE(self.diplome)
|
||||||
|
self.trajectoires.cree_trajectoires(self.etudiants)
|
||||||
|
|
||||||
|
"""Génère les moyennes par tags des trajectoires"""
|
||||||
|
pe_comp.pe_print("*** Calcule les moyennes par tag des trajectoires possibles")
|
||||||
|
self.trajectoires_tagguees = compute_trajectoires_tag(
|
||||||
|
self.trajectoires, self.etudiants, self.semestres_taggues
|
||||||
|
)
|
||||||
|
|
||||||
|
if pe_comp.PE_DEBUG:
|
||||||
|
"""Intègre le bilan des trajectoires tagguées au zip final"""
|
||||||
|
for trajectoire_id in self.trajectoires_tagguees:
|
||||||
|
trajectoire_tagguee = self.trajectoires_tagguees[trajectoire_id]
|
||||||
|
filename = trajectoire_tagguee.get_repr().replace(" ", "_") + ".csv"
|
||||||
|
pe_comp.pe_print(f" - Export csv de {filename} ")
|
||||||
|
self.add_file_to_zip(
|
||||||
|
filename,
|
||||||
|
trajectoire_tagguee.str_tagtable(),
|
||||||
|
path="details_semestres",
|
||||||
|
)
|
||||||
|
|
||||||
|
"""Génère les interclassements (par promo et) par (nom d') aggrégat"""
|
||||||
|
pe_comp.pe_print("*** Génère les interclassements par aggrégat")
|
||||||
|
self.interclassements_taggues = compute_interclassements(
|
||||||
|
self.etudiants, self.trajectoires, self.trajectoires_tagguees
|
||||||
|
)
|
||||||
|
|
||||||
|
if pe_comp.PE_DEBUG:
|
||||||
|
"""Intègre le bilan des aggrégats (par promo) au zip final"""
|
||||||
|
for nom_aggregat in self.interclassements_taggues:
|
||||||
|
interclass_tag = self.interclassements_taggues[nom_aggregat]
|
||||||
|
filename = interclass_tag.get_repr().replace(" ", "_") + ".csv"
|
||||||
|
pe_comp.pe_print(f" - Export csv de {filename} ")
|
||||||
|
self.add_file_to_zip(
|
||||||
|
filename,
|
||||||
|
interclass_tag.str_tagtable(),
|
||||||
|
path="details_semestres",
|
||||||
|
)
|
||||||
|
|
||||||
|
"""Synthèse des éléments du jury PE"""
|
||||||
|
self.synthese = self.synthetise_juryPE()
|
||||||
|
|
||||||
|
# Export des données => mode 1 seule feuille -> supprimé
|
||||||
|
pe_comp.pe_print("*** Export du jury de synthese")
|
||||||
|
filename = "synthese_jury_" + str(self.diplome) + '.xls'
|
||||||
|
with pd.ExcelWriter(filename, engine="openpyxl") as writer:
|
||||||
|
for onglet in self.synthese:
|
||||||
|
df = self.synthese[onglet]
|
||||||
|
df.to_excel(writer, onglet, index=True, header=True) # écriture dans l'onglet
|
||||||
|
# worksheet = writer.sheets[onglet] # l'on
|
||||||
|
|
||||||
|
self.zipfile.write(filename)
|
||||||
|
|
||||||
|
"""Fin !!!! Tada :)"""
|
||||||
|
|
||||||
|
def add_file_to_zip(self, filename: str, data, path=""):
|
||||||
|
"""Add a file to our zip
|
||||||
|
All files under NOM_EXPORT_ZIP/
|
||||||
|
path may specify a subdirectory
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filename: Le nom du fichier à intégrer au zip
|
||||||
|
data: Les données du fichier
|
||||||
|
path: Un dossier dans l'arborescence du zip
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
def do_tags_list(self, interclassements: dict[str, AggregatInterclasseTag]):
|
||||||
|
"""La liste des tags extraites des interclassements"""
|
||||||
|
tags = []
|
||||||
|
for aggregat in interclassements:
|
||||||
|
interclass = interclassements[aggregat]
|
||||||
|
if interclass.tags_sorted:
|
||||||
|
tags.extend(interclass.tags_sorted)
|
||||||
|
tags = sorted(set(tags))
|
||||||
|
return tags
|
||||||
|
|
||||||
|
|
||||||
|
# **************************************************************************************************************** #
|
||||||
|
# Méthodes pour la synthèse du juryPE
|
||||||
|
# *****************************************************************************************************************
|
||||||
|
|
||||||
|
def synthetise_juryPE(self):
|
||||||
|
"""Synthétise tous les résultats du jury PE dans des dataframes"""
|
||||||
|
|
||||||
|
pe_comp.pe_print("*** Synthèse finale des moyennes ***")
|
||||||
|
|
||||||
|
synthese = {}
|
||||||
|
pe_comp.pe_print(" -> Synthèse des données administratives")
|
||||||
|
synthese["administratif"] = self.df_administratif()
|
||||||
|
|
||||||
|
tags = self.do_tags_list(self.interclassements_taggues)
|
||||||
|
for tag in tags:
|
||||||
|
pe_comp.pe_print(f" -> Synthèse du tag {tag}")
|
||||||
|
synthese[tag] = self.df_tag(tag)
|
||||||
|
return synthese
|
||||||
|
|
||||||
|
|
||||||
|
def df_administratif(self):
|
||||||
|
"""Synthétise toutes les données administratives des étudiants"""
|
||||||
|
|
||||||
|
etudids = list(self.diplomes_ids)
|
||||||
|
|
||||||
|
"""Récupération des données des étudiants"""
|
||||||
|
administratif = {}
|
||||||
|
nbre_semestres_max = self.etudiants.nbre_etapes_max_diplomes()
|
||||||
|
|
||||||
|
for etudid in etudids:
|
||||||
|
etudiant = self.etudiants.identites[etudid]
|
||||||
|
cursus = self.etudiants.cursus[etudid]
|
||||||
|
formsemestres = cursus["formsemestres"]
|
||||||
|
|
||||||
|
administratif[etudid] = {
|
||||||
|
"Nom": etudiant.nom,
|
||||||
|
"Prenom": etudiant.prenom,
|
||||||
|
"Civilite": etudiant.civilite_str,
|
||||||
|
"Age": pe_comp.calcul_age(etudiant.date_naissance),
|
||||||
|
"Date d'entree": cursus["entree"],
|
||||||
|
"Date de diplome": cursus["diplome"],
|
||||||
|
"Nbre de semestres": len(formsemestres)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ajout des noms de semestres parcourus
|
||||||
|
etapes = pe_affichage.etapes_du_cursus(formsemestres, nbre_semestres_max)
|
||||||
|
administratif[etudid] |= etapes
|
||||||
|
|
||||||
|
"""Construction du dataframe"""
|
||||||
|
df = pd.DataFrame.from_dict(administratif, orient='index')
|
||||||
|
return df
|
||||||
|
|
||||||
|
|
||||||
|
def df_tag(self, tag):
|
||||||
|
"""Génère le DataFrame synthétisant les moyennes/classements (groupe,
|
||||||
|
interclassement promo) pour tous les aggrégats prévus,
|
||||||
|
tels que fourni dans l'excel final.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tag: Un des tags (a minima `but`)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
"""
|
||||||
|
|
||||||
|
etudids = list(self.diplomes_ids)
|
||||||
|
aggregats = pe_comp.TOUS_LES_PARCOURS
|
||||||
|
|
||||||
|
|
||||||
|
donnees = {}
|
||||||
|
|
||||||
|
for etudid in etudids:
|
||||||
|
etudiant = self.etudiants.identites[etudid]
|
||||||
|
donnees[etudid] = {
|
||||||
|
"Nom": etudiant.nom,
|
||||||
|
"Prenom": etudiant.prenom,
|
||||||
|
"Civilite": etudiant.civilite_str,
|
||||||
|
}
|
||||||
|
|
||||||
|
for aggregat in aggregats:
|
||||||
|
"""La trajectoire de l'étudiant sur l'aggrégat"""
|
||||||
|
trajectoire = self.trajectoires.suivi[etudid][aggregat]
|
||||||
|
"""Les moyennes par tag de cette trajectoire"""
|
||||||
|
if trajectoire:
|
||||||
|
trajectoire_tagguee = self.trajectoires_tagguees[trajectoire.trajectoire_id]
|
||||||
|
bilan = trajectoire_tagguee.moyennes_tags[tag]
|
||||||
|
|
||||||
|
donnees[etudid] |= {
|
||||||
|
f"{aggregat} notes ": f"{bilan['notes'].loc[etudid]:.1f}",
|
||||||
|
f"{aggregat} class. (groupe)": f"{bilan['classements'].loc[etudid]}/{bilan['nb_inscrits']}",
|
||||||
|
f"{aggregat} min/moy/max (groupe)": f"{bilan['min']:.1f}/{bilan['moy']:.1f}/{bilan['max']:.1f}"}
|
||||||
|
else:
|
||||||
|
donnees[etudid] |= {
|
||||||
|
f"{aggregat} notes ": "-",
|
||||||
|
f"{aggregat} class. (groupe)": "-",
|
||||||
|
f"{aggregat} min/moy/max (groupe)": "-"
|
||||||
|
}
|
||||||
|
|
||||||
|
"""L'interclassement"""
|
||||||
|
interclass = self.interclassements_taggues[aggregat]
|
||||||
|
if tag in interclass.moyennes_tags:
|
||||||
|
bilan = interclass.moyennes_tags[tag]
|
||||||
|
|
||||||
|
donnees[etudid] |= {
|
||||||
|
f"{aggregat} class. (promo)": f"{bilan['classements'].loc[etudid]}/{bilan['nb_inscrits']}",
|
||||||
|
f"{aggregat} min/moy/max (promo)": f"{bilan['min']:.1f}/{bilan['moy']:.1f}/{bilan['max']:.1f}"
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
donnees[etudid] |= {
|
||||||
|
f"{aggregat} class. (promo)": "-",
|
||||||
|
f"{aggregat} min/moy/max (promo)": "-"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fin de l'aggrégat
|
||||||
|
|
||||||
|
df = pd.DataFrame.from_dict(donnees, orient='index')
|
||||||
|
return df
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
if mode == "singlesheet":
|
||||||
|
return sT.get_genTable("singlesheet")
|
||||||
|
else:
|
||||||
|
return sT
|
||||||
|
|
||||||
|
|
||||||
|
def compute_semestres_tag(etudiants: EtudiantsJuryPE):
|
||||||
|
"""Créé les semestres taggués, de type 'S1', 'S2', ..., pour un groupe d'étudiants donnés.
|
||||||
|
Chaque semestre taggué est rattaché à l'un des FormSemestre faisant partie du cursus scolaire
|
||||||
|
des étudiants (cf. attribut etudiants.cursus).
|
||||||
|
En crééant le semestre taggué, sont calculées les moyennes/classements par tag associé.
|
||||||
|
.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
etudiants: Un groupe d'étudiants participant au jury
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Un dictionnaire {fid: SemestreTag(fid)}
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""Création des semestres taggués, de type 'S1', 'S2', ..."""
|
||||||
|
pe_comp.pe_print("*** Création des semestres taggués")
|
||||||
|
|
||||||
|
formsemestres = etudiants.get_formsemestres(
|
||||||
|
semestres_recherches=pe_comp.TOUS_LES_SEMESTRES
|
||||||
|
)
|
||||||
|
|
||||||
|
semestres_tags = {}
|
||||||
|
for frmsem_id, formsemestre in formsemestres.items():
|
||||||
|
"""Choix d'un nom pour le semestretag"""
|
||||||
|
nom = "S%d %d %d-%d" % (
|
||||||
|
formsemestre.semestre_id,
|
||||||
|
frmsem_id,
|
||||||
|
formsemestre.date_debut.year,
|
||||||
|
formsemestre.date_fin.year,
|
||||||
|
)
|
||||||
|
|
||||||
|
pe_comp.pe_print(f" --> Semestre taggué {nom} sur la base de {formsemestre}")
|
||||||
|
|
||||||
|
"""Créé le semestre_tag et exécute les calculs de moyennes"""
|
||||||
|
formsemestretag = SemestreTag(nom, frmsem_id)
|
||||||
|
|
||||||
|
"""Stocke le semestre taggué"""
|
||||||
|
semestres_tags[frmsem_id] = formsemestretag
|
||||||
|
|
||||||
|
return semestres_tags
|
||||||
|
|
||||||
|
|
||||||
|
def compute_trajectoires_tag(
|
||||||
|
trajectoires: TrajectoiresJuryPE,
|
||||||
|
etudiants: EtudiantsJuryPE,
|
||||||
|
semestres_taggues: dict[int, SemestreTag],
|
||||||
|
):
|
||||||
|
"""Créée les trajectoires tagguées (combinaison aggrégeant plusieurs semestres au sens
|
||||||
|
d'un aggrégat (par ex: '3S')),
|
||||||
|
en calculant les moyennes et les classements par tag pour chacune.
|
||||||
|
|
||||||
|
Pour rappel : Chaque trajectoire est identifiée un nom d'aggrégat et par un formsemestre terminal.
|
||||||
|
|
||||||
|
Par exemple :
|
||||||
|
|
||||||
|
* combinaisons '3S' : S1+S2+S3 en prenant en compte tous les S3 qu'ont fréquenté les
|
||||||
|
étudiants du jury PE. Ces S3 marquent les formsemestre terminal de chaque combinaison.
|
||||||
|
|
||||||
|
* combinaisons 'S2' : 1 seul S2 pour des étudiants n'ayant pas redoublé, 2 pour des redoublants (dont les
|
||||||
|
notes seront moyennées sur leur 2 semestres S2). Ces combinaisons ont pour formsemestre le dernier S2 en
|
||||||
|
date (le S2 redoublé par les redoublants est forcément antérieur)
|
||||||
|
|
||||||
|
|
||||||
|
Args:
|
||||||
|
etudiants: Les données des étudiants
|
||||||
|
semestres_tag: Les semestres tag (pour lesquels des moyennes par tag ont été calculés)
|
||||||
|
|
||||||
|
Return:
|
||||||
|
Un dictionnaire de la forme {nom_aggregat: {fid_terminal: SetTag(fid_terminal)} }
|
||||||
|
"""
|
||||||
|
|
||||||
|
pe_comp.pe_print(" *** Création des aggrégats ")
|
||||||
|
|
||||||
|
trajectoires_tagguees = {}
|
||||||
|
|
||||||
|
for trajectoire_id in trajectoires.trajectoires:
|
||||||
|
trajectoire = trajectoires.trajectoires[trajectoire_id]
|
||||||
|
nom = trajectoire.get_repr()
|
||||||
|
|
||||||
|
pe_comp.pe_print(f" --> Fusion {nom}")
|
||||||
|
|
||||||
|
"""Création de la trajectoire_tagguee associée"""
|
||||||
|
trajectoire_tagguee = TrajectoireTag(
|
||||||
|
nom, trajectoire, semestres_taggues, etudiants
|
||||||
|
)
|
||||||
|
|
||||||
|
"""Mémorise le résultat"""
|
||||||
|
trajectoires_tagguees[trajectoire_id] = trajectoire_tagguee
|
||||||
|
|
||||||
|
return trajectoires_tagguees
|
||||||
|
|
||||||
|
|
||||||
|
def compute_interclassements(
|
||||||
|
etudiants: EtudiantsJuryPE,
|
||||||
|
trajectoires_jury_pe: TrajectoiresJuryPE,
|
||||||
|
trajectoires_tagguees: dict[tuple, Trajectoire],
|
||||||
|
):
|
||||||
|
"""Interclasse les étudiants, (nom d') aggrégat par aggrégat,
|
||||||
|
pour fournir un classement sur la promo. Le classement est établi au regard du nombre
|
||||||
|
d'étudiants ayant participé au même aggrégat.
|
||||||
|
"""
|
||||||
|
pe_comp.pe_print(" Interclassement sur la promo")
|
||||||
|
|
||||||
|
aggregats_interclasses_taggues = {}
|
||||||
|
for nom_aggregat in pe_comp.TOUS_LES_SEMESTRES + pe_comp.TOUS_LES_AGGREGATS:
|
||||||
|
pe_comp.pe_print(f" --> {nom_aggregat}")
|
||||||
|
interclass = AggregatInterclasseTag(
|
||||||
|
nom_aggregat, etudiants, trajectoires_jury_pe, trajectoires_tagguees
|
||||||
|
)
|
||||||
|
aggregats_interclasses_taggues[nom_aggregat] = interclass
|
||||||
|
return aggregats_interclasses_taggues
|
@ -1,736 +0,0 @@
|
|||||||
# -*- 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
|
|
||||||
"""
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
|
||||||
# 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.comp.res_sem import load_formsemestre_results
|
|
||||||
from app.models import Formation, FormSemestre
|
|
||||||
from app.models.etudiants import Identite
|
|
||||||
from app.pe.pe_semestretag import SemestreTag
|
|
||||||
from app.pe.pe_interclasstag import AggregatInterclasseTag
|
|
||||||
from app.pe.pe_trajectoiretag import TrajectoireTag
|
|
||||||
|
|
||||||
from app.scodoc.gen_tables import GenTable, SeqGenTable
|
|
||||||
import app.scodoc.sco_utils as scu
|
|
||||||
from app.scodoc import codes_cursus
|
|
||||||
from app.pe import pe_tools
|
|
||||||
from app.pe import pe_semestretag
|
|
||||||
from app.pe.pe_etudiant import EtudiantsJuryPE
|
|
||||||
from app.pe.pe_trajectoire import TrajectoiresJuryPE, Trajectoire
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------------------
|
|
||||||
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'année d'obtention du diplome BUT et du jury de PE (généralement février 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
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------------------------------------------
|
|
||||||
def __init__(self, diplome, formation_id):
|
|
||||||
"""
|
|
||||||
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.promoTagDict = {}
|
|
||||||
|
|
||||||
"L'année du diplome"
|
|
||||||
self.diplome = diplome
|
|
||||||
|
|
||||||
"La formation associée au diplome"
|
|
||||||
self.formation_id = formation_id
|
|
||||||
|
|
||||||
"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.syntheseJury = {} # Le jury de synthèse
|
|
||||||
|
|
||||||
"""Chargement des étudiants à prendre en compte dans le jury"""
|
|
||||||
pe_tools.pe_print(
|
|
||||||
f"*** Recherche et chargement des étudiants diplômés en {self.diplome} pour la formation {self.formation_id}"
|
|
||||||
)
|
|
||||||
self.etudiants = EtudiantsJuryPE(self.diplome) # Les infos sur les étudiants
|
|
||||||
self.etudiants.find_etudiants(self.formation_id)
|
|
||||||
|
|
||||||
"""Génère les semestres taggués (avec le calcul des moyennes) pour le jury PE"""
|
|
||||||
pe_tools.pe_print("*** Génère les semestres taggués")
|
|
||||||
self.semestres_taggues = compute_semestres_tag(self.etudiants)
|
|
||||||
|
|
||||||
if pe_tools.PE_DEBUG:
|
|
||||||
"""Intègre le bilan des semestres taggués au zip final"""
|
|
||||||
for fid in self.semestres_taggues:
|
|
||||||
formsemestretag = self.semestres_taggues[fid]
|
|
||||||
filename = formsemestretag.nom.replace(" ", "_") + ".csv"
|
|
||||||
pe_tools.pe_print(f" - Export csv de {filename} ")
|
|
||||||
self.add_file_to_zip(
|
|
||||||
filename, formsemestretag.str_tagtable(), path="details_semestres"
|
|
||||||
)
|
|
||||||
|
|
||||||
"""Génère les trajectoires (combinaison de semestres suivis
|
|
||||||
par un étudiant pour atteindre le semestre final d'un aggrégat)
|
|
||||||
"""
|
|
||||||
pe_tools.pe_print(
|
|
||||||
"*** Génère les trajectoires (différentes combinaisons de semestres) des étudiants"
|
|
||||||
)
|
|
||||||
self.trajectoires = TrajectoiresJuryPE(self.diplome)
|
|
||||||
self.trajectoires.cree_trajectoires(self.etudiants)
|
|
||||||
|
|
||||||
"""Génère les moyennes par tags des trajectoires"""
|
|
||||||
pe_tools.pe_print("*** Calcule les moyennes par tag des trajectoires possibles")
|
|
||||||
self.trajectoires_tagguees = compute_trajectoires_tag(
|
|
||||||
self.trajectoires, self.etudiants, self.semestres_taggues
|
|
||||||
)
|
|
||||||
|
|
||||||
if pe_tools.PE_DEBUG:
|
|
||||||
"""Intègre le bilan des trajectoires tagguées au zip final"""
|
|
||||||
for trajectoire_id in self.trajectoires_tagguees:
|
|
||||||
trajectoire_tagguee = self.trajectoires_tagguees[trajectoire_id]
|
|
||||||
filename = trajectoire_tagguee.get_repr().replace(" ", "_") + ".csv"
|
|
||||||
pe_tools.pe_print(f" - Export csv de {filename} ")
|
|
||||||
self.add_file_to_zip(
|
|
||||||
filename,
|
|
||||||
trajectoire_tagguee.str_tagtable(),
|
|
||||||
path="details_semestres",
|
|
||||||
)
|
|
||||||
|
|
||||||
"""Génère les interclassements (par promo et) par (nom d') aggrégat"""
|
|
||||||
pe_tools.pe_print("*** Génère les interclassements par aggrégat")
|
|
||||||
self.interclassements_taggues = compute_interclassements(
|
|
||||||
self.etudiants, self.trajectoires, self.trajectoires_tagguees
|
|
||||||
)
|
|
||||||
|
|
||||||
if pe_tools.PE_DEBUG:
|
|
||||||
"""Intègre le bilan des aggrégats (par promo) au zip final"""
|
|
||||||
for nom_aggregat in self.interclassements_taggues:
|
|
||||||
interclass_tag = self.interclassements_taggues[nom_aggregat]
|
|
||||||
filename = interclass_tag.get_repr().replace(" ", "_") + ".csv"
|
|
||||||
pe_tools.pe_print(f" - Export csv de {filename} ")
|
|
||||||
self.add_file_to_zip(
|
|
||||||
filename,
|
|
||||||
interclass_tag.str_tagtable(),
|
|
||||||
path="details_semestres",
|
|
||||||
)
|
|
||||||
|
|
||||||
"""Synthèse des éléments du jury PE"""
|
|
||||||
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
|
|
||||||
if False:
|
|
||||||
filename = self.nom_export_zip + "_jurySyntheseDict" + scu.XLSX_SUFFIX
|
|
||||||
self.xlsV2 = self.table_syntheseJury(mode="multiplesheet")
|
|
||||||
if self.xlsV2:
|
|
||||||
pe_tools.add_file_to_zip(
|
|
||||||
self.nom_export_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: str, data, path=""):
|
|
||||||
"""Add a file to our zip
|
|
||||||
All files under NOM_EXPORT_ZIP/
|
|
||||||
path may specify a subdirectory
|
|
||||||
|
|
||||||
Args:
|
|
||||||
filename: Le nom du fichier à intégrer au zip
|
|
||||||
data: Les données du fichier
|
|
||||||
path: Un dossier dans l'arborescence du zip
|
|
||||||
"""
|
|
||||||
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
|
|
||||||
|
|
||||||
# **************************************************************************************************************** #
|
|
||||||
# Traitements des semestres impliqués dans le jury
|
|
||||||
# **************************************************************************************************************** #
|
|
||||||
|
|
||||||
# **************************************************************************************************************** #
|
|
||||||
# Méthodes pour la synthèse du juryPE
|
|
||||||
# *****************************************************************************************************************
|
|
||||||
def synthetise_juryPE(self):
|
|
||||||
"""Synthétise tous les résultats du jury PE dans des dataframess"""
|
|
||||||
self.syntheseJury = {}
|
|
||||||
for etudid in self.etudiants.get_etudids(self.diplome):
|
|
||||||
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 pe_tools.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.etudiants.cursus[etudid][nom] != None
|
|
||||||
): # Un parcours valide existe
|
|
||||||
if nom in pe_tools.TOUS_LES_SEMESTRES:
|
|
||||||
tagtable = self.semestres_taggues[
|
|
||||||
self.etudiants.cursus[etudid][nom]
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
tagtable = self.trajectoires_tagguees[nom][
|
|
||||||
self.etudiants.cursus[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.etudiants.semestres_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 pe_tools.TOUS_LES_PARCOURS:
|
|
||||||
entete += [nom_sem, "descr"]
|
|
||||||
chaine = delim.join(entete) + "\n"
|
|
||||||
|
|
||||||
for etudid in self.etudiants.cursus:
|
|
||||||
donnees = self.etudiants.cursus[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 pe_tools.TOUS_LES_PARCOURS:
|
|
||||||
table = (
|
|
||||||
self.semestres_taggues[donnees[nom_sem]].nom
|
|
||||||
if donnees[nom_sem] in self.semestres_taggues
|
|
||||||
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.etudiants.get_etudids():
|
|
||||||
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 pe_tools.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(pe_tools.PARCOURS.keys()) # ['S1', 'S2', ..., '1A', '4S']
|
|
||||||
# aggregats = sorted(
|
|
||||||
# aggregats, key=lambda t: pe_tools.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 pe_tools.TOUS_LES_PARCOURS:
|
|
||||||
entete.extend(["%s %s" % (sem, champ) for champ in champs])
|
|
||||||
else: # "singlesheet"
|
|
||||||
allSheets = ["singlesheet"]
|
|
||||||
for (
|
|
||||||
sem
|
|
||||||
) in (
|
|
||||||
pe_tools.TOUS_LES_PARCOURS
|
|
||||||
): # pe_tools.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: # pe_tools.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
|
|
||||||
|
|
||||||
TODO:: A supprimer à long terme
|
|
||||||
"""
|
|
||||||
if etudid not in self.ETUDINFO_DICT:
|
|
||||||
self.ETUDINFO_DICT[etudid] = Identite.get_etud(etudid=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, identite: Identite, semestre_id=None):
|
|
||||||
"""cf. pe_etudiant.semestres_etudiant()"""
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
# *********************************************
|
|
||||||
# Fonctions d'affichage pour debug
|
|
||||||
def get_resultat_d_un_etudiant(self, etudid):
|
|
||||||
chaine = ""
|
|
||||||
for nom_sem in pe_tools.TOUS_LES_SEMESTRES:
|
|
||||||
semtagid = self.etudiants.cursus[etudid][
|
|
||||||
nom_sem
|
|
||||||
] # le formsemestre_id du semestre taggué de l'étudiant
|
|
||||||
semtag = self.semestres_taggues[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 ""
|
|
||||||
|
|
||||||
|
|
||||||
def compute_semestres_tag(etudiants: EtudiantsJuryPE):
|
|
||||||
"""Créé les semestres taggués, de type 'S1', 'S2', ..., pour un groupe d'étudiants donnés.
|
|
||||||
Chaque semestre taggué est rattaché à l'un des FormSemestre faisant partie du cursus scolaire
|
|
||||||
des étudiants (cf. attribut etudiants.cursus).
|
|
||||||
En crééant le semestre taggué, sont calculées les moyennes/classements par tag associé.
|
|
||||||
.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
etudiants: Un groupe d'étudiants participant au jury
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Un dictionnaire {fid: SemestreTag(fid)}
|
|
||||||
"""
|
|
||||||
|
|
||||||
"""Création des semestres taggués, de type 'S1', 'S2', ..."""
|
|
||||||
pe_tools.pe_print("*** Création des semestres taggués")
|
|
||||||
|
|
||||||
formsemestres = etudiants.get_formsemestres(
|
|
||||||
semestres_recherches=pe_tools.TOUS_LES_SEMESTRES
|
|
||||||
)
|
|
||||||
|
|
||||||
semestres_tags = {}
|
|
||||||
for frmsem_id, formsemestre in formsemestres.items():
|
|
||||||
"""Choix d'un nom pour le semestretag"""
|
|
||||||
nom = "S%d %d %d-%d" % (
|
|
||||||
formsemestre.semestre_id,
|
|
||||||
frmsem_id,
|
|
||||||
formsemestre.date_debut.year,
|
|
||||||
formsemestre.date_fin.year,
|
|
||||||
)
|
|
||||||
|
|
||||||
pe_tools.pe_print(f" --> Semestre taggué {nom} sur la base de {formsemestre}")
|
|
||||||
|
|
||||||
"""Créé le semestre_tag et exécute les calculs de moyennes"""
|
|
||||||
formsemestretag = pe_semestretag.SemestreTag(nom, frmsem_id)
|
|
||||||
|
|
||||||
"""Stocke le semestre taggué"""
|
|
||||||
semestres_tags[frmsem_id] = formsemestretag
|
|
||||||
|
|
||||||
return semestres_tags
|
|
||||||
|
|
||||||
|
|
||||||
def compute_trajectoires_tag(
|
|
||||||
trajectoires: TrajectoiresJuryPE,
|
|
||||||
etudiants: EtudiantsJuryPE,
|
|
||||||
semestres_taggues: dict[int, SemestreTag],
|
|
||||||
):
|
|
||||||
"""Créée les trajectoires tagguées (combinaison aggrégeant plusieurs semestres au sens
|
|
||||||
d'un aggrégat (par ex: '3S')),
|
|
||||||
en calculant les moyennes et les classements par tag pour chacune.
|
|
||||||
|
|
||||||
Pour rappel : Chaque trajectoire est identifiée un nom d'aggrégat et par un formsemestre terminal.
|
|
||||||
|
|
||||||
Par exemple :
|
|
||||||
|
|
||||||
* combinaisons '3S' : S1+S2+S3 en prenant en compte tous les S3 qu'ont fréquenté les
|
|
||||||
étudiants du jury PE. Ces S3 marquent les formsemestre terminal de chaque combinaison.
|
|
||||||
|
|
||||||
* combinaisons 'S2' : 1 seul S2 pour des étudiants n'ayant pas redoublé, 2 pour des redoublants (dont les
|
|
||||||
notes seront moyennées sur leur 2 semestres S2). Ces combinaisons ont pour formsemestre le dernier S2 en
|
|
||||||
date (le S2 redoublé par les redoublants est forcément antérieur)
|
|
||||||
|
|
||||||
|
|
||||||
Args:
|
|
||||||
etudiants: Les données des étudiants
|
|
||||||
semestres_tag: Les semestres tag (pour lesquels des moyennes par tag ont été calculés)
|
|
||||||
|
|
||||||
Return:
|
|
||||||
Un dictionnaire de la forme {nom_aggregat: {fid_terminal: SetTag(fid_terminal)} }
|
|
||||||
"""
|
|
||||||
|
|
||||||
pe_tools.pe_print(" *** Création des aggrégats ")
|
|
||||||
|
|
||||||
trajectoires_tagguees = {}
|
|
||||||
|
|
||||||
for trajectoire_id in trajectoires.trajectoires:
|
|
||||||
trajectoire = trajectoires.trajectoires[trajectoire_id]
|
|
||||||
nom = trajectoire.get_repr()
|
|
||||||
|
|
||||||
pe_tools.pe_print(f" --> Fusion {nom}")
|
|
||||||
|
|
||||||
"""Création de la trajectoire_tagguee associée"""
|
|
||||||
trajectoire_tagguee = TrajectoireTag(
|
|
||||||
nom, trajectoire, semestres_taggues, etudiants
|
|
||||||
)
|
|
||||||
|
|
||||||
"""Mémorise le résultat"""
|
|
||||||
trajectoires_tagguees[trajectoire_id] = trajectoire_tagguee
|
|
||||||
|
|
||||||
return trajectoires_tagguees
|
|
||||||
|
|
||||||
|
|
||||||
def compute_interclassements(
|
|
||||||
etudiants: EtudiantsJuryPE,
|
|
||||||
trajectoires_jury_pe: TrajectoiresJuryPE,
|
|
||||||
trajectoires_tagguees: dict[tuple, Trajectoire],
|
|
||||||
):
|
|
||||||
"""Interclasse les étudiants, (nom d') aggrégat par aggrégat,
|
|
||||||
pour fournir un classement sur la promo. Le classement est établi au regard du nombre
|
|
||||||
d'étudiants ayant participé au même aggrégat.
|
|
||||||
"""
|
|
||||||
pe_tools.pe_print(" Interclassement sur la promo")
|
|
||||||
|
|
||||||
aggregats_interclasses_taggues = {}
|
|
||||||
for nom_aggregat in pe_tools.TOUS_LES_SEMESTRES + pe_tools.TOUS_LES_AGGREGATS:
|
|
||||||
pe_tools.pe_print(f" --> {nom_aggregat}")
|
|
||||||
interclass = AggregatInterclasseTag(
|
|
||||||
nom_aggregat, etudiants, trajectoires_jury_pe, trajectoires_tagguees
|
|
||||||
)
|
|
||||||
aggregats_interclasses_taggues[nom_aggregat] = interclass
|
|
||||||
return aggregats_interclasses_taggues
|
|
@ -37,22 +37,18 @@ Created on Fri Sep 9 09:15:05 2016
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from app import db, log
|
from app import db, log
|
||||||
from app.comp import res_sem, inscr_mod, moy_ue, moy_sem
|
from app.comp import res_sem, moy_ue, moy_sem
|
||||||
from app.comp.res_common import ResultatsSemestre
|
|
||||||
from app.comp.res_compat import NotesTableCompat
|
from app.comp.res_compat import NotesTableCompat
|
||||||
from app.comp.res_sem import load_formsemestre_results
|
from app.comp.res_sem import load_formsemestre_results
|
||||||
from app.models import FormSemestre, Identite, DispenseUE
|
from app.models import FormSemestre
|
||||||
from app.models.moduleimpls import ModuleImpl
|
from app.models.moduleimpls import ModuleImpl
|
||||||
from app.pe import pe_tagtable
|
|
||||||
from app.pe import pe_tools
|
|
||||||
|
|
||||||
from app.scodoc import codes_cursus, sco_preferences
|
|
||||||
from app.scodoc import sco_tag_module
|
from app.scodoc import sco_tag_module
|
||||||
from app.scodoc import sco_utils as scu
|
|
||||||
from app.scodoc.codes_cursus import UE_SPORT
|
from app.scodoc.codes_cursus import UE_SPORT
|
||||||
|
import app.pe.pe_comp as pe_comp
|
||||||
|
from app.pe.pe_tabletags import (TableTag, TAGS_RESERVES)
|
||||||
|
|
||||||
|
class SemestreTag(TableTag):
|
||||||
class SemestreTag(pe_tagtable.TableTag):
|
|
||||||
"""Un SemestreTag représente les résultats des étudiants à un semestre, en donnant
|
"""Un SemestreTag représente les résultats des étudiants à un semestre, en donnant
|
||||||
accès aux moyennes par tag.
|
accès aux moyennes par tag.
|
||||||
Il s'appuie principalement sur FormSemestre et sur ResultatsSemestreBUT.
|
Il s'appuie principalement sur FormSemestre et sur ResultatsSemestreBUT.
|
||||||
@ -67,10 +63,8 @@ class SemestreTag(pe_tagtable.TableTag):
|
|||||||
nom: Nom à donner au SemestreTag
|
nom: Nom à donner au SemestreTag
|
||||||
formsemestre_id: Identifiant du FormSemestre sur lequel il se base
|
formsemestre_id: Identifiant du FormSemestre sur lequel il se base
|
||||||
"""
|
"""
|
||||||
pe_tagtable.TableTag.__init__(
|
TableTag.__init__(self, nom=nom)
|
||||||
self,
|
|
||||||
nom=nom
|
|
||||||
)
|
|
||||||
"""Le semestre"""
|
"""Le semestre"""
|
||||||
self.formsemestre_id = formsemestre_id
|
self.formsemestre_id = formsemestre_id
|
||||||
self.formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
self.formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||||
@ -97,7 +91,7 @@ class SemestreTag(pe_tagtable.TableTag):
|
|||||||
|
|
||||||
"""Les tags (en supprimant les tags réservés)"""
|
"""Les tags (en supprimant les tags réservés)"""
|
||||||
self.tags = get_synthese_tags_semestre(self.nt.formsemestre)
|
self.tags = get_synthese_tags_semestre(self.nt.formsemestre)
|
||||||
for tag in pe_tagtable.TAGS_RESERVES:
|
for tag in TAGS_RESERVES:
|
||||||
if tag in self.tags:
|
if tag in self.tags:
|
||||||
del self.tags[tag]
|
del self.tags[tag]
|
||||||
|
|
||||||
@ -105,7 +99,7 @@ class SemestreTag(pe_tagtable.TableTag):
|
|||||||
self.moyennes_tags = {}
|
self.moyennes_tags = {}
|
||||||
|
|
||||||
for tag in self.tags:
|
for tag in self.tags:
|
||||||
pe_tools.pe_print(f" -> Traitement du tag {tag}")
|
pe_comp.pe_print(f" -> Traitement du tag {tag}")
|
||||||
moy_gen_tag = self.compute_moyenne_tag(tag)
|
moy_gen_tag = self.compute_moyenne_tag(tag)
|
||||||
class_gen_tag = moy_sem.comp_ranks_series(moy_gen_tag)[1] # en int
|
class_gen_tag = moy_sem.comp_ranks_series(moy_gen_tag)[1] # en int
|
||||||
self.moyennes_tags[tag] = {
|
self.moyennes_tags[tag] = {
|
||||||
@ -118,7 +112,7 @@ class SemestreTag(pe_tagtable.TableTag):
|
|||||||
}
|
}
|
||||||
|
|
||||||
"""Ajoute les moyennes générales de BUT pour le semestre considéré"""
|
"""Ajoute les moyennes générales de BUT pour le semestre considéré"""
|
||||||
pe_tools.pe_print(f" -> Traitement du tag but")
|
pe_comp.pe_print(f" -> Traitement du tag but")
|
||||||
moy_gen_but = self.nt.etud_moy_gen
|
moy_gen_but = self.nt.etud_moy_gen
|
||||||
class_gen_but = self.nt.etud_moy_gen_ranks_int
|
class_gen_but = self.nt.etud_moy_gen_ranks_int
|
||||||
self.moyennes_tags["but"] = {
|
self.moyennes_tags["but"] = {
|
@ -1,128 +0,0 @@
|
|||||||
from app.pe import pe_tagtable
|
|
||||||
from app.pe.pe_tools import PE_DEBUG, pe_print
|
|
||||||
import app.pe.pe_etudiant as pe_etudiant
|
|
||||||
from app.pe.pe_etudiant import EtudiantsJuryPE
|
|
||||||
|
|
||||||
class SetTagInterClasse(pe_tagtable.TableTag):
|
|
||||||
"""Interclasse les étudiants d'une promo (ceux diplômé) par aggrégat de même nom
|
|
||||||
(par ex: un "3S"), pour stocker leur moyenne et fournir un classement de "promo".
|
|
||||||
|
|
||||||
Les
|
|
||||||
"""
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------------------------------------------
|
|
||||||
def __init__(self, nom:str, etudiants: EtudiantsJuryPE, aggregats_taggues: dict[str, dict]):
|
|
||||||
""""""
|
|
||||||
pe_tagtable.TableTag.__init__(self, nom)
|
|
||||||
self.etudiants = etudiants
|
|
||||||
self.aggregats_taggues = aggregats_taggues # Les moyennes par aggrégats
|
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------------------
|
|
||||||
def set_Etudiants(self, etudiants, juryPEDict, etudInfoDict, nom_sem_final=None):
|
|
||||||
"""Détermine la liste des étudiants à prendre en compte, en partant de
|
|
||||||
la liste fournie en paramètre et en vérifiant que l'étudiant dispose bien d'un parcours valide pour la combinaison demandée.
|
|
||||||
Renvoie le nombre d'étudiants effectivement inscrits."""
|
|
||||||
if nom_sem_final:
|
|
||||||
self.nom += "_" + nom_sem_final
|
|
||||||
for etudid in etudiants:
|
|
||||||
if juryPEDict[etudid][self.combinaison] != None:
|
|
||||||
self.inscrlist.append(etudInfoDict[etudid])
|
|
||||||
self.identdict[etudid] = etudInfoDict[etudid]
|
|
||||||
self.parcoursDict[etudid] = juryPEDict[etudid]
|
|
||||||
return len(self.inscrlist)
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------------------
|
|
||||||
def get_Fids_in_settag(self):
|
|
||||||
"""Renvoie la liste des semestres (les formsemestre_id finissant la combinaison par ex. '3S' dont les fid des S3) à prendre en compte
|
|
||||||
pour les moyennes, en considérant tous les étudiants inscrits"""
|
|
||||||
return list(
|
|
||||||
{self.parcoursDict[etudid][self.combinaison] for etudid in self.identdict}
|
|
||||||
)
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------------
|
|
||||||
def set_SetTagDict(self, SetTagDict):
|
|
||||||
"""Mémorise les settag nécessaires au jury."""
|
|
||||||
self.SetTagDict = {
|
|
||||||
fid: SetTagDict[fid] for fid in self.get_Fids_in_settag() if fid != None
|
|
||||||
}
|
|
||||||
if PE_DEBUG >= 1:
|
|
||||||
pe_print(" => %d semestres utilisés" % len(self.SetTagDict))
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------------------------------------------
|
|
||||||
def comp_data_settag(self):
|
|
||||||
"""Calcule tous les données numériques relatives au settag"""
|
|
||||||
# Attributs relatifs aux tag pour les modules pris en compte
|
|
||||||
self.taglist = self.do_taglist()
|
|
||||||
|
|
||||||
# if PE_DEBUG >= 1: pe_print(" => Tags = " + ", ".join( self.taglist ))
|
|
||||||
|
|
||||||
# Calcul des moyennes de chaque étudiant par tag
|
|
||||||
reussiteAjoutTag = {"OK": [], "KO": []}
|
|
||||||
for tag in self.taglist:
|
|
||||||
moyennes = self.get_MoyennesSetTag(tag, force=False)
|
|
||||||
res = self.add_moyennesTag(tag, moyennes) # pas de notes => pas de moyenne
|
|
||||||
reussiteAjoutTag["OK" if res else "KO"].append(tag)
|
|
||||||
if len(reussiteAjoutTag["OK"]) > 0 and PE_DEBUG:
|
|
||||||
pe_print(
|
|
||||||
" => Interclassement de %d tags : " % (len(reussiteAjoutTag["OK"]))
|
|
||||||
+ ", ".join(reussiteAjoutTag["OK"])
|
|
||||||
)
|
|
||||||
if len(reussiteAjoutTag["KO"]) > 0 and PE_DEBUG:
|
|
||||||
pe_print(
|
|
||||||
" => %d tags manquants : " % (len(reussiteAjoutTag["KO"]))
|
|
||||||
+ ", ".join(reussiteAjoutTag["KO"])
|
|
||||||
)
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------------------------------------------
|
|
||||||
def get_etudids(self):
|
|
||||||
return list(self.identdict.keys())
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------------------------------------------
|
|
||||||
def do_taglist(self):
|
|
||||||
"""Parcourt les tags des semestres taggués et les synthétise sous la forme
|
|
||||||
d'une liste en supprimant les doublons
|
|
||||||
"""
|
|
||||||
ensemble = []
|
|
||||||
for settag in self.SetTagDict.values():
|
|
||||||
ensemble.extend(settag.get_all_tags())
|
|
||||||
return sorted(list(set(ensemble)))
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------------------------------------------
|
|
||||||
def get_NotesEtCoeffsSetTagEtudiant(self, tag, etudid):
|
|
||||||
"""Récupère tous les notes et les coeffs d'un étudiant relatives à un tag dans ses semestres valides et les renvoie dans un tuple (notes, coeffs)
|
|
||||||
avec notes et coeffs deux listes"""
|
|
||||||
leSetTagDeLetudiant = self.parcoursDict[etudid][self.combinaison]
|
|
||||||
|
|
||||||
note = self.SetTagDict[leSetTagDeLetudiant].get_moy_from_resultats(tag, etudid)
|
|
||||||
coeff = self.SetTagDict[leSetTagDeLetudiant].get_coeff_from_resultats(
|
|
||||||
tag, etudid
|
|
||||||
)
|
|
||||||
return (note, coeff)
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------------------------------------------
|
|
||||||
def get_MoyennesSetTag(self, tag, force=False):
|
|
||||||
"""Renvoie les "moyennes" des étudiants à un tag donné, en prenant en compte tous les settag de l'aggrégat,
|
|
||||||
et leur coeff Par moyenne, s'entend une note moyenne, la somme des coefficients de pondération
|
|
||||||
appliqué dans cette moyenne.
|
|
||||||
|
|
||||||
Force ou non le calcul de la moyenne lorsque des notes sont manquantes.
|
|
||||||
|
|
||||||
Renvoie les informations sous la forme d'une liste [etudid: (moy, somme_coeff_normalisée, rang), ...}
|
|
||||||
"""
|
|
||||||
# if tag not in self.get_all_tags() : return None
|
|
||||||
|
|
||||||
# Calcule les moyennes
|
|
||||||
lesMoyennes = []
|
|
||||||
for (
|
|
||||||
etudid
|
|
||||||
) in (
|
|
||||||
self.get_etudids()
|
|
||||||
): # Pour tous les étudiants non défaillants du semestre inscrits dans des modules relatifs au tag
|
|
||||||
(moyenne, somme_coeffs) = self.get_NotesEtCoeffsSetTagEtudiant(
|
|
||||||
tag, etudid
|
|
||||||
) # lecture des notes associées au tag
|
|
||||||
lesMoyennes += [
|
|
||||||
(moyenne, somme_coeffs, etudid)
|
|
||||||
] # Un tuple (pour classement résumant les données)
|
|
||||||
return lesMoyennes
|
|
1126
app/pe/pe_tools.py
1126
app/pe/pe_tools.py
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
|||||||
import app.pe.pe_tools as pe_tools
|
import app.pe.pe_comp as pe_tools
|
||||||
from app.models import FormSemestre
|
from app.models import FormSemestre
|
||||||
from app.pe.pe_etudiant import EtudiantsJuryPE, get_dernier_semestre_en_date
|
from app.pe.pe_etudiant import EtudiantsJuryPE, get_dernier_semestre_en_date
|
||||||
|
|
||||||
|
@ -38,18 +38,17 @@ Created on Fri Sep 9 09:15:05 2016
|
|||||||
|
|
||||||
from app.comp import moy_sem
|
from app.comp import moy_sem
|
||||||
from app.comp.res_sem import load_formsemestre_results
|
from app.comp.res_sem import load_formsemestre_results
|
||||||
from app.models import FormSemestre
|
from app.pe.pe_semtag import SemestreTag
|
||||||
from app.pe.pe_semestretag import SemestreTag
|
from app.pe import pe_tabletags
|
||||||
from app.pe import pe_tagtable
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from app.pe.pe_trajectoire import Trajectoire
|
from app.pe.pe_trajectoire import Trajectoire
|
||||||
|
|
||||||
from app.pe.pe_etudiant import EtudiantsJuryPE
|
from app.pe.pe_etudiant import EtudiantsJuryPE
|
||||||
from app.pe.pe_trajectoire import TrajectoiresJuryPE
|
from app.pe.pe_tabletags import TableTag
|
||||||
|
|
||||||
|
|
||||||
class TrajectoireTag(pe_tagtable.TableTag):
|
class TrajectoireTag(TableTag):
|
||||||
"""Calcule les moyennes par tag d'une combinaison de semestres
|
"""Calcule les moyennes par tag d'une combinaison de semestres
|
||||||
(trajectoires), identifiée par un nom d'aggrégat (par ex: '3S') et
|
(trajectoires), identifiée par un nom d'aggrégat (par ex: '3S') et
|
||||||
par un semestre terminal, pour extraire les classements par tag pour un
|
par un semestre terminal, pour extraire les classements par tag pour un
|
||||||
@ -69,7 +68,7 @@ class TrajectoireTag(pe_tagtable.TableTag):
|
|||||||
donnees_etudiants: EtudiantsJuryPE,
|
donnees_etudiants: EtudiantsJuryPE,
|
||||||
):
|
):
|
||||||
""" """
|
""" """
|
||||||
pe_tagtable.TableTag.__init__(self, nom=nom)
|
TableTag.__init__(self, nom=nom)
|
||||||
|
|
||||||
"""La trajectoire associée"""
|
"""La trajectoire associée"""
|
||||||
self.trajectoire_id = trajectoire.trajectoire_id
|
self.trajectoire_id = trajectoire.trajectoire_id
|
||||||
|
Loading…
Reference in New Issue
Block a user