forked from ScoDoc/ScoDoc
PE: reformattage code, small bug fix.
This commit is contained in:
parent
86c12dee08
commit
f4c1d00046
@ -1,3 +1,11 @@
|
|||||||
|
##############################################################################
|
||||||
|
# Module "Avis de poursuite d'étude"
|
||||||
|
# conçu et développé par Cléo Baras (IUT de Grenoble)
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
"""Affichages, debug
|
||||||
|
"""
|
||||||
|
|
||||||
from app import log
|
from app import log
|
||||||
|
|
||||||
PE_DEBUG = 0
|
PE_DEBUG = 0
|
||||||
@ -15,5 +23,3 @@ else:
|
|||||||
SANS_NOTE = "-"
|
SANS_NOTE = "-"
|
||||||
NOM_STAT_GROUPE = "statistiques du groupe"
|
NOM_STAT_GROUPE = "statistiques du groupe"
|
||||||
NOM_STAT_PROMO = "statistiques de la promo"
|
NOM_STAT_PROMO = "statistiques de la promo"
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,7 +52,6 @@ from app.scodoc import sco_formsemestre
|
|||||||
from app.scodoc.sco_logos import find_logo
|
from app.scodoc.sco_logos import find_logo
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Generated LaTeX files are encoded as:
|
# Generated LaTeX files are encoded as:
|
||||||
PE_LATEX_ENCODING = "utf-8"
|
PE_LATEX_ENCODING = "utf-8"
|
||||||
|
|
||||||
@ -98,7 +97,8 @@ def calcul_age(born: datetime.date) -> int:
|
|||||||
return today.year - born.year - ((today.month, today.day) < (born.month, born.day))
|
return today.year - born.year - ((today.month, today.day) < (born.month, born.day))
|
||||||
|
|
||||||
|
|
||||||
def remove_accents(input_unicode_str):
|
# Nota: scu.suppress_accents fait la même chose mais renvoie un str et non un bytes
|
||||||
|
def remove_accents(input_unicode_str: str) -> bytes:
|
||||||
"""Supprime les accents d'une chaine unicode"""
|
"""Supprime les accents d'une chaine unicode"""
|
||||||
nfkd_form = unicodedata.normalize("NFKD", input_unicode_str)
|
nfkd_form = unicodedata.normalize("NFKD", input_unicode_str)
|
||||||
only_ascii = nfkd_form.encode("ASCII", "ignore")
|
only_ascii = nfkd_form.encode("ASCII", "ignore")
|
||||||
@ -133,15 +133,15 @@ def escape_for_latex(s):
|
|||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------------------
|
||||||
def list_directory_filenames(path):
|
def list_directory_filenames(path: str) -> list[str]:
|
||||||
"""List of regular filenames in a directory (recursive)
|
"""List of regular filenames (paths) in a directory (recursive)
|
||||||
Excludes files and directories begining with .
|
Excludes files and directories begining with .
|
||||||
"""
|
"""
|
||||||
R = []
|
paths = []
|
||||||
for root, dirs, files in os.walk(path, topdown=True):
|
for root, dirs, files in os.walk(path, topdown=True):
|
||||||
dirs[:] = [d for d in dirs if d[0] != "."]
|
dirs[:] = [d for d in dirs if d[0] != "."]
|
||||||
R += [os.path.join(root, fn) for fn in files if fn[0] != "."]
|
paths += [os.path.join(root, fn) for fn in files if fn[0] != "."]
|
||||||
return R
|
return paths
|
||||||
|
|
||||||
|
|
||||||
def add_local_file_to_zip(zipfile, ziproot, pathname, path_in_zip):
|
def add_local_file_to_zip(zipfile, ziproot, pathname, path_in_zip):
|
||||||
@ -195,13 +195,15 @@ def add_pe_stuff_to_zip(zipfile, ziproot):
|
|||||||
def get_annee_diplome_semestre(
|
def get_annee_diplome_semestre(
|
||||||
sem_base: FormSemestre | dict, nbre_sem_formation: int = 6
|
sem_base: FormSemestre | dict, nbre_sem_formation: int = 6
|
||||||
) -> int:
|
) -> int:
|
||||||
"""Pour un semestre ``sem_base`` donné (supposé être un semestre d'une formation BUT à 6 semestres)
|
"""Pour un semestre ``sem_base`` donné (supposé être un semestre d'une formation BUT
|
||||||
et connaissant le numéro du semestre, ses dates de début et de fin du semestre, prédit l'année à laquelle
|
à 6 semestres) et connaissant le numéro du semestre, ses dates de début et de fin du
|
||||||
sera remis le diplôme BUT des étudiants qui y sont scolarisés
|
semestre, prédit l'année à laquelle sera remis le diplôme BUT des étudiants qui y
|
||||||
(en supposant qu'il n'y ait pas de redoublement à venir).
|
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)
|
**Remarque sur le calcul** : Les semestres de 1ère partie d'année (S1, S3, S5 ou S4,
|
||||||
s'étalent sur deux années civiles ; contrairement au semestre de seconde partie d'année universitaire.
|
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 :
|
Par exemple :
|
||||||
|
|
||||||
@ -235,21 +237,22 @@ def get_annee_diplome_semestre(
|
|||||||
if (
|
if (
|
||||||
1 <= sem_id <= nbre_sem_formation
|
1 <= sem_id <= nbre_sem_formation
|
||||||
): # Si le semestre est un semestre BUT => problème si formation BUT en 1 an ??
|
): # Si le semestre est un semestre BUT => problème si formation BUT en 1 an ??
|
||||||
nbreSemRestant = (
|
nb_sem_restants = (
|
||||||
nbre_sem_formation - sem_id
|
nbre_sem_formation - sem_id
|
||||||
) # nombre de semestres restant avant diplome
|
) # nombre de semestres restant avant diplome
|
||||||
nbreAnRestant = nbreSemRestant // 2 # nombre d'annees restant avant diplome
|
nb_annees_restantes = (
|
||||||
# Flag permettant d'activer ou désactiver un increment à prendre en compte en cas de semestre décalé
|
nb_sem_restants // 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
|
# avec 1 - delta = 0 si semestre de 1ere partie d'année / 1 sinon
|
||||||
delta = annee_fin - annee_debut
|
delta = annee_fin - annee_debut
|
||||||
decalage = nbreSemRestant % 2 # 0 si S4, 1 si S3, 0 si S2, 1 si S1
|
decalage = nb_sem_restants % 2 # 0 si S4, 1 si S3, 0 si S2, 1 si S1
|
||||||
increment = decalage * (1 - delta)
|
increment = decalage * (1 - delta)
|
||||||
return annee_fin + nbreAnRestant + increment
|
return annee_fin + nb_annees_restantes + increment
|
||||||
|
|
||||||
|
|
||||||
def get_cosemestres_diplomants(
|
def get_cosemestres_diplomants(annee_diplome: int) -> dict[int, FormSemestre]:
|
||||||
annee_diplome: int
|
|
||||||
) -> dict[int, FormSemestre]:
|
|
||||||
"""Ensemble des cosemestres donnant lieu à diplomation à l'``annee_diplome``.
|
"""Ensemble des cosemestres donnant lieu à diplomation à l'``annee_diplome``.
|
||||||
|
|
||||||
**Définition** : Un co-semestre est un semestre :
|
**Définition** : Un co-semestre est un semestre :
|
||||||
@ -264,15 +267,15 @@ def get_cosemestres_diplomants(
|
|||||||
Returns:
|
Returns:
|
||||||
Un dictionnaire {fid: FormSemestre(fid)} contenant les cosemestres
|
Un dictionnaire {fid: FormSemestre(fid)} contenant les cosemestres
|
||||||
"""
|
"""
|
||||||
tousLesSems = (
|
tous_les_sems = (
|
||||||
sco_formsemestre.do_formsemestre_list()
|
sco_formsemestre.do_formsemestre_list()
|
||||||
) # tous les semestres memorisés dans scodoc
|
) # tous les semestres memorisés dans scodoc
|
||||||
|
|
||||||
cosemestres_fids = {
|
cosemestres_fids = {
|
||||||
sem["id"]
|
sem["id"]
|
||||||
for sem in tousLesSems
|
for sem in tous_les_sems
|
||||||
if get_annee_diplome_semestre(sem) == annee_diplome
|
if get_annee_diplome_semestre(sem) == annee_diplome
|
||||||
}
|
}
|
||||||
|
|
||||||
cosemestres = {}
|
cosemestres = {}
|
||||||
for fid in cosemestres_fids:
|
for fid in cosemestres_fids:
|
||||||
@ -281,5 +284,3 @@ def get_cosemestres_diplomants(
|
|||||||
cosemestres[fid] = cosem
|
cosemestres[fid] = cosem
|
||||||
|
|
||||||
return cosemestres
|
return cosemestres
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,13 +37,13 @@ Created on 17/01/2024
|
|||||||
"""
|
"""
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
import app.pe.pe_rcs
|
|
||||||
from app.models import FormSemestre, Identite, Formation
|
from app.models import FormSemestre, Identite, Formation
|
||||||
from app.pe import pe_comp, pe_affichage
|
from app.pe import pe_comp, pe_affichage
|
||||||
from app.scodoc import codes_cursus
|
from app.scodoc import codes_cursus
|
||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
from app.comp.res_sem import load_formsemestre_results
|
from app.comp.res_sem import load_formsemestre_results
|
||||||
|
|
||||||
|
|
||||||
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"""
|
||||||
|
|
||||||
@ -123,20 +123,15 @@ class EtudiantsJuryPE:
|
|||||||
|
|
||||||
# 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()
|
||||||
"""Les identités des étudiants diplômés"""
|
|
||||||
|
|
||||||
self.diplomes_ids = set(self.etudiants_diplomes.keys())
|
self.diplomes_ids = set(self.etudiants_diplomes.keys())
|
||||||
"""Les identifiants des étudiants diplômés"""
|
|
||||||
|
|
||||||
self.etudiants_ids = set(self.identites.keys())
|
self.etudiants_ids = set(self.identites.keys())
|
||||||
"""Les identifiants des étudiants (diplômés, redoublants ou ayant abandonnés) à traiter"""
|
|
||||||
|
|
||||||
# Les abandons (pour debug)
|
# Les abandons (pour debug)
|
||||||
self.abandons = self.get_etudiants_redoublants_ou_reorientes()
|
self.abandons = self.get_etudiants_redoublants_ou_reorientes()
|
||||||
"""Les identités des étudiants ayant redoublés ou ayant abandonnés"""
|
# Les identités des étudiants ayant redoublés ou ayant abandonnés
|
||||||
|
|
||||||
self.abandons_ids = set(self.abandons)
|
self.abandons_ids = set(self.abandons)
|
||||||
"""Les identifiants des étudiants ayant redoublés ou ayant abandonnés"""
|
# Les identifiants des étudiants ayant redoublés ou ayant abandonnés
|
||||||
|
|
||||||
# Synthèse
|
# Synthèse
|
||||||
pe_affichage.pe_print(
|
pe_affichage.pe_print(
|
||||||
@ -279,7 +274,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 i in range(1, pe_comp.NBRE_SEMESTRES_DIPLOMANT+1):
|
for i in range(1, pe_comp.NBRE_SEMESTRES_DIPLOMANT + 1):
|
||||||
# les semestres de n°i de l'étudiant:
|
# les semestres de n°i de l'étudiant:
|
||||||
semestres_i = {
|
semestres_i = {
|
||||||
fid: sem_sig
|
fid: sem_sig
|
||||||
@ -288,18 +283,20 @@ class EtudiantsJuryPE:
|
|||||||
}
|
}
|
||||||
self.cursus[etudid][f"S{i}"] = semestres_i
|
self.cursus[etudid][f"S{i}"] = semestres_i
|
||||||
|
|
||||||
|
def get_formsemestres_terminaux_aggregat(
|
||||||
|
self, aggregat: str
|
||||||
def get_formsemestres_terminaux_aggregat(self, aggregat: str):
|
) -> dict[int, FormSemestre]:
|
||||||
"""Pour un aggrégat donné, ensemble des formsemestres terminaux possibles pour l'aggrégat
|
"""Pour un aggrégat donné, ensemble des formsemestres terminaux possibles pour l'aggrégat
|
||||||
(pour l'aggrégat '3S' incluant S1+S2+S3, a pour semestre terminal S3).
|
(pour l'aggrégat '3S' incluant S1+S2+S3, a pour semestre terminal S3).
|
||||||
Ces formsemestres traduisent :
|
Ces formsemestres traduisent :
|
||||||
|
|
||||||
* les différents parcours des étudiants liés par exemple au choix de modalité (par ex: S1 FI + S2 FI + S3 FI
|
* les différents parcours des étudiants liés par exemple au choix de modalité
|
||||||
ou S1 FI + S2 FI + S3 UFA), en renvoyant les formsemestre_id du S3 FI et du S3 UFA.
|
(par ex: S1 FI + S2 FI + S3 FI ou S1 FI + S2 FI + S3 UFA), en renvoyant les
|
||||||
* les éventuelles situations de redoublement (par ex pour 1 étudiant ayant redoublé sa 2ème année :
|
formsemestre_id du S3 FI et du S3 UFA.
|
||||||
S1 + S2 + S3 (1ère session) et S1 + S2 + S3 + S4 + S3 (2ème session), en renvoyant les formsemestre_id du
|
* les éventuelles situations de redoublement (par ex pour 1 étudiant ayant
|
||||||
S3 (1ère session) et du S3 (2ème session)
|
redoublé sa 2ème année :
|
||||||
|
S1 + S2 + S3 (1ère session) et S1 + S2 + S3 + S4 + S3 (2ème session), en
|
||||||
|
renvoyant les formsemestre_id du S3 (1ère session) et du S3 (2ème session)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
aggregat: L'aggrégat
|
aggregat: L'aggrégat
|
||||||
@ -316,7 +313,6 @@ class EtudiantsJuryPE:
|
|||||||
formsemestres_terminaux[fid] = trajectoire.formsemestre_final
|
formsemestres_terminaux[fid] = trajectoire.formsemestre_final
|
||||||
return formsemestres_terminaux
|
return formsemestres_terminaux
|
||||||
|
|
||||||
|
|
||||||
def nbre_etapes_max_diplomes(self, etudids: list[int]) -> int:
|
def nbre_etapes_max_diplomes(self, etudids: list[int]) -> int:
|
||||||
"""Partant d'un ensemble d'étudiants,
|
"""Partant d'un ensemble d'étudiants,
|
||||||
nombre de semestres (étapes) maximum suivis par les étudiants du jury.
|
nombre de semestres (étapes) maximum suivis par les étudiants du jury.
|
||||||
@ -407,7 +403,7 @@ def get_etudiants_dans_semestres(semestres: dict[int, FormSemestre]) -> set:
|
|||||||
return etudiants_ids
|
return etudiants_ids
|
||||||
|
|
||||||
|
|
||||||
def get_annee_diplome(etud: Identite) -> int:
|
def get_annee_diplome(etud: Identite) -> int | None:
|
||||||
"""L'année de diplôme prévue d'un étudiant en fonction de ses semestres
|
"""L'année de diplôme prévue d'un étudiant en fonction de ses semestres
|
||||||
d'inscription (pour un BUT).
|
d'inscription (pour un BUT).
|
||||||
|
|
||||||
@ -415,13 +411,14 @@ def get_annee_diplome(etud: Identite) -> int:
|
|||||||
identite: L'identité d'un étudiant
|
identite: L'identité d'un étudiant
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
L'année prévue de sa diplômation
|
L'année prévue de sa diplômation, ou None si aucun semestre
|
||||||
"""
|
"""
|
||||||
formsemestres_apc = get_semestres_apc(etud)
|
formsemestres_apc = get_semestres_apc(etud)
|
||||||
|
|
||||||
if formsemestres_apc:
|
if formsemestres_apc:
|
||||||
dates_possibles_diplome = []
|
dates_possibles_diplome = []
|
||||||
"""Années de diplômation prédites en fonction des semestres (d'une formation APC) d'un étudiant"""
|
# Années de diplômation prédites en fonction des semestres
|
||||||
|
# (d'une formation APC) d'un étudiant
|
||||||
for sem_base in formsemestres_apc:
|
for sem_base in formsemestres_apc:
|
||||||
annee = pe_comp.get_annee_diplome_semestre(sem_base)
|
annee = pe_comp.get_annee_diplome_semestre(sem_base)
|
||||||
if annee:
|
if annee:
|
||||||
@ -452,23 +449,26 @@ def get_semestres_apc(identite: Identite) -> list:
|
|||||||
def arret_de_formation(etud: Identite, cosemestres: list[FormSemestre]) -> bool:
|
def arret_de_formation(etud: Identite, cosemestres: list[FormSemestre]) -> bool:
|
||||||
"""Détermine si un étudiant a arrêté sa formation. Il peut s'agir :
|
"""Détermine si un étudiant a arrêté sa formation. Il peut s'agir :
|
||||||
|
|
||||||
* d'une réorientation à l'initiative du jury de semestre ou d'une démission (on pourrait
|
* d'une réorientation à l'initiative du jury de semestre ou d'une démission
|
||||||
utiliser les code NAR pour réorienté & DEM pour démissionnaire des résultats du jury renseigné dans la BDD,
|
(on pourrait utiliser les code NAR pour réorienté & DEM pour démissionnaire
|
||||||
mais pas nécessaire ici)
|
des résultats du jury renseigné dans la BDD, mais pas nécessaire ici)
|
||||||
|
|
||||||
* d'un arrêt volontaire : l'étudiant disparait des listes d'inscrits (sans pour autant avoir été indiqué NAR ou DEM).
|
* d'un arrêt volontaire : l'étudiant disparait des listes d'inscrits (sans pour
|
||||||
|
autant avoir été indiqué NAR ou DEM).
|
||||||
|
|
||||||
Dans les cas, on considérera que l'étudiant a arrêté sa formation s'il n'est pas dans l'un des "derniers" cosemestres
|
Dans les cas, on considérera que l'étudiant a arrêté sa formation s'il n'est pas
|
||||||
(semestres conduisant à la même année de diplômation) connu dans Scodoc.
|
dans l'un des "derniers" cosemestres (semestres conduisant à la même année de diplômation)
|
||||||
|
connu dans Scodoc.
|
||||||
|
|
||||||
Par ex: au moment du jury PE en fin de S5 (pas de S6 renseigné dans Scodoc), l'étudiant doit appartenir à une
|
Par ex: au moment du jury PE en fin de S5 (pas de S6 renseigné dans Scodoc),
|
||||||
instance des S5 qui conduisent à la diplomation dans l'année visée. S'il n'est que dans un S4, il a sans doute
|
l'étudiant doit appartenir à une instance des S5 qui conduisent à la diplomation dans
|
||||||
arrêté. A moins qu'il ne soit parti à l'étranger et là, pas de notes.
|
l'année visée. S'il n'est que dans un S4, il a sans doute arrêté. A moins qu'il ne soit
|
||||||
|
parti à l'étranger et là, pas de notes.
|
||||||
TODO:: Cas de l'étranger, à coder/tester
|
TODO:: Cas de l'étranger, à coder/tester
|
||||||
|
|
||||||
**Attention** : Cela suppose que toutes les instances d'un semestre donné (par ex: toutes les instances de S6
|
**Attention** : Cela suppose que toutes les instances d'un semestre donné
|
||||||
accueillant un étudiant soient créées ; sinon les étudiants non inscrits dans un S6 seront considérés comme
|
(par ex: toutes les instances de S6 accueillant un étudiant soient créées ; sinon les
|
||||||
ayant abandonnés)
|
étudiants non inscrits dans un S6 seront considérés comme ayant abandonnés)
|
||||||
TODO:: Peut-être à mettre en regard avec les propositions d'inscriptions d'étudiants dans un nouveau semestre
|
TODO:: Peut-être à mettre en regard avec les propositions d'inscriptions d'étudiants dans un nouveau semestre
|
||||||
|
|
||||||
Pour chaque étudiant, recherche son dernier semestre en date (validé ou non) et
|
Pour chaque étudiant, recherche son dernier semestre en date (validé ou non) et
|
||||||
|
@ -1,16 +1,59 @@
|
|||||||
from app.comp import moy_sem
|
##############################################################################
|
||||||
|
#
|
||||||
|
# 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 pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
from app.pe.pe_tabletags import TableTag, MoyenneTag
|
from app.pe.pe_tabletags import TableTag, MoyenneTag
|
||||||
from app.pe.pe_etudiant import EtudiantsJuryPE
|
from app.pe.pe_etudiant import EtudiantsJuryPE
|
||||||
from app.pe.pe_rcs import RCS, RCSsJuryPE
|
from app.pe.pe_rcs import RCS, RCSsJuryPE
|
||||||
from app.pe.pe_rcstag import RCSTag
|
from app.pe.pe_rcstag import RCSTag
|
||||||
|
|
||||||
|
|
||||||
import pandas as pd
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
|
|
||||||
class RCSInterclasseTag(TableTag):
|
class RCSInterclasseTag(TableTag):
|
||||||
# -------------------------------------------------------------------------------------------------------------------
|
"""
|
||||||
|
Interclasse l'ensemble des étudiants diplômés à une année
|
||||||
|
donnée (celle du jury), pour un RCS donné (par ex: 'S2', '3S')
|
||||||
|
en reportant :
|
||||||
|
|
||||||
|
* les moyennes obtenues sur la trajectoire qu'il ont suivi pour atteindre
|
||||||
|
le numéro de semestre de fin de l'aggrégat (indépendamment de son
|
||||||
|
formsemestre)
|
||||||
|
* calculant le classement sur les étudiants diplômes
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
nom_rcs: str,
|
nom_rcs: str,
|
||||||
@ -18,15 +61,6 @@ class RCSInterclasseTag(TableTag):
|
|||||||
rcss_jury_pe: RCSsJuryPE,
|
rcss_jury_pe: RCSsJuryPE,
|
||||||
rcss_tags: dict[tuple, RCSTag],
|
rcss_tags: dict[tuple, RCSTag],
|
||||||
):
|
):
|
||||||
"""
|
|
||||||
Interclasse l'ensemble des étudiants diplômés à une année
|
|
||||||
donnée (celle du jury), pour un RCS donné (par ex: 'S2', '3S')
|
|
||||||
en reportant :
|
|
||||||
|
|
||||||
* les moyennes obtenues sur la trajectoire qu'il ont suivi pour atteindre le numéro de semestre de fin de l'aggrégat (indépendamment de son
|
|
||||||
formsemestres)
|
|
||||||
* calculant le classement sur les étudiants diplômes
|
|
||||||
"""
|
|
||||||
TableTag.__init__(self)
|
TableTag.__init__(self)
|
||||||
|
|
||||||
self.nom_rcs = nom_rcs
|
self.nom_rcs = nom_rcs
|
||||||
@ -43,7 +77,8 @@ class RCSInterclasseTag(TableTag):
|
|||||||
for etudid in self.diplomes_ids
|
for etudid in self.diplomes_ids
|
||||||
}
|
}
|
||||||
|
|
||||||
# Les trajectoires (et leur version tagguées), en ne gardant que celles associées à l'aggrégat
|
# Les trajectoires (et leur version tagguées), en ne gardant que
|
||||||
|
# celles associées à l'aggrégat
|
||||||
self.rcss: dict[int, RCS] = {}
|
self.rcss: dict[int, RCS] = {}
|
||||||
"""Ensemble des trajectoires associées à l'aggrégat"""
|
"""Ensemble des trajectoires associées à l'aggrégat"""
|
||||||
for trajectoire_id in rcss_jury_pe.rcss:
|
for trajectoire_id in rcss_jury_pe.rcss:
|
||||||
@ -54,9 +89,7 @@ class RCSInterclasseTag(TableTag):
|
|||||||
self.trajectoires_taggues: dict[int, RCS] = {}
|
self.trajectoires_taggues: dict[int, RCS] = {}
|
||||||
"""Ensemble des trajectoires tagguées associées à l'aggrégat"""
|
"""Ensemble des trajectoires tagguées associées à l'aggrégat"""
|
||||||
for trajectoire_id in self.rcss:
|
for trajectoire_id in self.rcss:
|
||||||
self.trajectoires_taggues[trajectoire_id] = rcss_tags[
|
self.trajectoires_taggues[trajectoire_id] = rcss_tags[trajectoire_id]
|
||||||
trajectoire_id
|
|
||||||
]
|
|
||||||
|
|
||||||
# Les trajectoires suivies par les étudiants du jury, en ne gardant que
|
# Les trajectoires suivies par les étudiants du jury, en ne gardant que
|
||||||
# celles associées aux diplomés
|
# celles associées aux diplomés
|
||||||
|
@ -47,31 +47,29 @@ import os
|
|||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
from app.pe.pe_affichage import NOM_STAT_PROMO, SANS_NOTE, NOM_STAT_GROUPE
|
from app.pe.pe_affichage import NOM_STAT_PROMO, SANS_NOTE, NOM_STAT_GROUPE
|
||||||
|
import app.pe.pe_affichage as pe_affichage
|
||||||
from app.pe.pe_etudiant import *
|
from app.pe.pe_etudiant import * # TODO A éviter -> pe_etudiant.
|
||||||
from app.pe.pe_rcs import *
|
from app.pe.pe_rcs import * # TODO A éviter
|
||||||
import app.pe.pe_comp as pe_comp
|
from app.pe.pe_rcstag import RCSTag
|
||||||
from app.pe.pe_semtag import SemestreTag
|
from app.pe.pe_semtag import SemestreTag
|
||||||
from app.pe.pe_interclasstag import RCSInterclasseTag
|
from app.pe.pe_interclasstag import RCSInterclasseTag
|
||||||
from app.pe.pe_rcstag import RCSTag
|
|
||||||
import app.pe.pe_affichage as pe_affichage
|
|
||||||
|
|
||||||
import pandas as pd
|
|
||||||
|
|
||||||
|
|
||||||
class JuryPE(object):
|
class JuryPE(object):
|
||||||
def __init__(self, diplome):
|
"""
|
||||||
"""
|
Classe mémorisant toutes les informations nécessaires pour établir un jury de PE, sur la base
|
||||||
Classe mémorisant toutes les informations nécessaires pour établir un jury de PE, sur la base
|
d'une année de diplôme. De ce semestre est déduit :
|
||||||
d'une année de diplôme. De ce semestre est déduit :
|
1. l'année d'obtention du DUT,
|
||||||
1. l'année d'obtention du DUT,
|
2. tous les étudiants susceptibles à ce stade (au regard de leur parcours) d'être diplomés.
|
||||||
2. tous les étudiants susceptibles à ce stade (au regard de leur parcours) d'être diplomés.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
diplome : l'année d'obtention du diplome BUT et du jury de PE (généralement février XXXX)
|
diplome : l'année d'obtention du diplome BUT et du jury de PE (généralement février XXXX)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def __init__(self, diplome):
|
||||||
self.diplome = diplome
|
self.diplome = diplome
|
||||||
"L'année du diplome"
|
"L'année du diplome"
|
||||||
|
|
||||||
@ -161,9 +159,7 @@ class JuryPE(object):
|
|||||||
self.rcss.cree_rcss(self.etudiants)
|
self.rcss.cree_rcss(self.etudiants)
|
||||||
|
|
||||||
# Génère les moyennes par tags des trajectoires
|
# Génère les moyennes par tags des trajectoires
|
||||||
pe_affichage.pe_print(
|
pe_affichage.pe_print("*** Calcule les moyennes par tag des RCS possibles")
|
||||||
"*** Calcule les moyennes par tag des RCS possibles"
|
|
||||||
)
|
|
||||||
self.rcss_tags = compute_trajectoires_tag(
|
self.rcss_tags = compute_trajectoires_tag(
|
||||||
self.rcss, self.etudiants, self.sems_tags
|
self.rcss, self.etudiants, self.sems_tags
|
||||||
)
|
)
|
||||||
@ -381,14 +377,13 @@ class JuryPE(object):
|
|||||||
champ = (descr, "", "note")
|
champ = (descr, "", "note")
|
||||||
notes_traj = moy_traj.get_notes()
|
notes_traj = moy_traj.get_notes()
|
||||||
donnees.loc[etudids_communs, champ] = notes_traj.loc[
|
donnees.loc[etudids_communs, champ] = notes_traj.loc[
|
||||||
etudids_communs]
|
etudids_communs
|
||||||
|
]
|
||||||
|
|
||||||
# Les rangs
|
# Les rangs
|
||||||
champ = (descr, NOM_STAT_GROUPE, "class.")
|
champ = (descr, NOM_STAT_GROUPE, "class.")
|
||||||
rangs = moy_traj.get_rangs_inscrits()
|
rangs = moy_traj.get_rangs_inscrits()
|
||||||
donnees.loc[etudids_communs, champ] = rangs.loc[
|
donnees.loc[etudids_communs, champ] = rangs.loc[etudids_communs]
|
||||||
etudids_communs
|
|
||||||
]
|
|
||||||
|
|
||||||
# Les mins
|
# Les mins
|
||||||
champ = (descr, NOM_STAT_GROUPE, "min")
|
champ = (descr, NOM_STAT_GROUPE, "min")
|
||||||
@ -487,9 +482,7 @@ class JuryPE(object):
|
|||||||
# La trajectoire de l'étudiant sur l'aggrégat
|
# La trajectoire de l'étudiant sur l'aggrégat
|
||||||
trajectoire = self.rcss.suivi[etudid][aggregat]
|
trajectoire = self.rcss.suivi[etudid][aggregat]
|
||||||
if trajectoire:
|
if trajectoire:
|
||||||
trajectoire_tagguee = self.rcss_tags[
|
trajectoire_tagguee = self.rcss_tags[trajectoire.rcs_id]
|
||||||
trajectoire.rcs_id
|
|
||||||
]
|
|
||||||
if tag in trajectoire_tagguee.moyennes_tags:
|
if tag in trajectoire_tagguee.moyennes_tags:
|
||||||
# L'interclassement
|
# L'interclassement
|
||||||
interclass = self.interclassements_taggues[aggregat]
|
interclass = self.interclassements_taggues[aggregat]
|
||||||
@ -512,6 +505,7 @@ class JuryPE(object):
|
|||||||
df.sort_values(by=[("", "", "tag")], inplace=True)
|
df.sort_values(by=[("", "", "tag")], inplace=True)
|
||||||
return df
|
return df
|
||||||
|
|
||||||
|
|
||||||
def get_formsemestres_etudiants(etudiants: EtudiantsJuryPE) -> dict:
|
def get_formsemestres_etudiants(etudiants: EtudiantsJuryPE) -> dict:
|
||||||
"""Ayant connaissance des étudiants dont il faut calculer les moyennes pour
|
"""Ayant connaissance des étudiants dont il faut calculer les moyennes pour
|
||||||
le jury PE (attribut `self.etudiant_ids) et de leur cursus (semestres
|
le jury PE (attribut `self.etudiant_ids) et de leur cursus (semestres
|
||||||
@ -534,6 +528,7 @@ def get_formsemestres_etudiants(etudiants: EtudiantsJuryPE) -> dict:
|
|||||||
semestres = semestres | etudiants.cursus[etudid][cle]
|
semestres = semestres | etudiants.cursus[etudid][cle]
|
||||||
return semestres
|
return semestres
|
||||||
|
|
||||||
|
|
||||||
def compute_semestres_tag(etudiants: EtudiantsJuryPE) -> dict:
|
def compute_semestres_tag(etudiants: EtudiantsJuryPE) -> dict:
|
||||||
"""Créé les semestres taggués, de type 'S1', 'S2', ..., pour un groupe d'étudiants donnés.
|
"""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
|
Chaque semestre taggué est rattaché à l'un des FormSemestre faisant partie du cursus scolaire
|
||||||
@ -548,7 +543,7 @@ def compute_semestres_tag(etudiants: EtudiantsJuryPE) -> dict:
|
|||||||
Un dictionnaire {fid: SemestreTag(fid)}
|
Un dictionnaire {fid: SemestreTag(fid)}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
"""Création des semestres taggués, de type 'S1', 'S2', ..."""
|
# Création des semestres taggués, de type 'S1', 'S2', ...
|
||||||
pe_affichage.pe_print("*** Création des semestres taggués")
|
pe_affichage.pe_print("*** Création des semestres taggués")
|
||||||
|
|
||||||
formsemestres = get_formsemestres_etudiants(etudiants)
|
formsemestres = get_formsemestres_etudiants(etudiants)
|
||||||
|
118
app/pe/pe_rcs.py
118
app/pe/pe_rcs.py
@ -1,5 +1,15 @@
|
|||||||
|
##############################################################################
|
||||||
|
# Module "Avis de poursuite d'étude"
|
||||||
|
# conçu et développé par Cléo Baras (IUT de Grenoble)
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
Created on 01-2024
|
||||||
|
|
||||||
|
@author: barasc
|
||||||
|
"""
|
||||||
|
|
||||||
import app.pe.pe_comp as pe_comp
|
import app.pe.pe_comp as pe_comp
|
||||||
import app.pe.pe_affichage as pe_affichage
|
|
||||||
|
|
||||||
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
|
||||||
@ -59,39 +69,41 @@ TYPES_RCS = {
|
|||||||
"descr": "Moyenne globale (S1+S2+S3+S4+S5+S6)",
|
"descr": "Moyenne globale (S1+S2+S3+S4+S5+S6)",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
"""Dictionnaire détaillant les différents regroupements cohérents
|
"""Dictionnaire détaillant les différents regroupements cohérents
|
||||||
de semestres (RCS), en leur attribuant un nom et en détaillant
|
de semestres (RCS), en leur attribuant un nom et en détaillant
|
||||||
le nom des semestres qu'ils regroupement et l'affichage qui en sera fait
|
le nom des semestres qu'ils regroupent et l'affichage qui en sera fait
|
||||||
dans les tableurs de synthèse"""
|
dans les tableurs de synthèse.
|
||||||
|
"""
|
||||||
|
|
||||||
TOUS_LES_RCS_AVEC_PLUSIEURS_SEM = [cle for cle in TYPES_RCS.keys() if not cle.startswith("S")]
|
TOUS_LES_RCS_AVEC_PLUSIEURS_SEM = [cle for cle in TYPES_RCS if not cle.startswith("S")]
|
||||||
TOUS_LES_RCS = list(TYPES_RCS.keys())
|
TOUS_LES_RCS = list(TYPES_RCS.keys())
|
||||||
TOUS_LES_SEMESTRES = [cle for cle in TYPES_RCS.keys() if cle.startswith("S")]
|
TOUS_LES_SEMESTRES = [cle for cle in TYPES_RCS if cle.startswith("S")]
|
||||||
|
|
||||||
|
|
||||||
class RCS:
|
class RCS:
|
||||||
|
"""Modélise un ensemble de semestres d'étudiants
|
||||||
|
associé à un type de regroupement cohérent de semestres
|
||||||
|
donné (par ex: 'S2', '3S', '2A').
|
||||||
|
|
||||||
|
Si le RCS est un semestre de type Si, stocke le (ou les)
|
||||||
|
formsemestres de numéro i qu'ont suivi l'étudiant pour atteindre le Si
|
||||||
|
(en général 1 si personnes n'a redoublé, mais 2 s'il y a des redoublants)
|
||||||
|
|
||||||
|
Pour le RCS de type iS ou iA (par ex, 3A=S1+S2+S3), elle identifie
|
||||||
|
les semestres que les étudiants ont suivis pour les amener jusqu'au semestre
|
||||||
|
terminal de la trajectoire (par ex: ici un S3).
|
||||||
|
|
||||||
|
Ces semestres peuvent être :
|
||||||
|
|
||||||
|
* des S1+S2+S1+S2+S3 si redoublement de la 1ère année
|
||||||
|
* des S1+S2+(année de césure)+S3 si césure, ...
|
||||||
|
|
||||||
|
Args:
|
||||||
|
nom_rcs: Un nom du RCS (par ex: '5S')
|
||||||
|
semestre_final: Le semestre final du RCS
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, nom_rcs: str, semestre_final: FormSemestre):
|
def __init__(self, nom_rcs: str, semestre_final: FormSemestre):
|
||||||
"""Modélise un ensemble de semestres d'étudiants
|
|
||||||
associé à un type de regroupement cohérent de semestres
|
|
||||||
donné (par ex: 'S2', '3S', '2A').
|
|
||||||
|
|
||||||
Si le RCS est un semestre de type Si, stocke le (ou les)
|
|
||||||
formsemestres de numéro i qu'ont suivi l'étudiant pour atteindre le Si
|
|
||||||
(en général 1 si personnes n'a redoublé, mais 2 s'il y a des redoublants)
|
|
||||||
|
|
||||||
Pour le RCS de type iS ou iA (par ex, 3A=S1+S2+S3), elle identifie
|
|
||||||
les semestres que les étudiants ont suivis pour les amener jusqu'au semestre
|
|
||||||
terminal de la trajectoire (par ex: ici un S3).
|
|
||||||
|
|
||||||
Ces semestres peuvent être :
|
|
||||||
|
|
||||||
* des S1+S2+S1+S2+S3 si redoublement de la 1ère année
|
|
||||||
* des S1+S2+(année de césure)+S3 si césure, ...
|
|
||||||
|
|
||||||
Args:
|
|
||||||
nom_rcs: Un nom du RCS (par ex: '5S')
|
|
||||||
semestre_final: Le semestre final du RCS
|
|
||||||
"""
|
|
||||||
self.nom = nom_rcs
|
self.nom = nom_rcs
|
||||||
"""Nom du RCS"""
|
"""Nom du RCS"""
|
||||||
|
|
||||||
@ -121,21 +133,22 @@ class RCS:
|
|||||||
semestre = self.semestres_aggreges[fid]
|
semestre = self.semestres_aggreges[fid]
|
||||||
noms.append(f"S{semestre.semestre_id}({fid})")
|
noms.append(f"S{semestre.semestre_id}({fid})")
|
||||||
noms = sorted(noms)
|
noms = sorted(noms)
|
||||||
repr = f"{self.nom} ({self.formsemestre_final.formsemestre_id}) {self.formsemestre_final.date_fin.year}"
|
title = f"""{self.nom} ({
|
||||||
|
self.formsemestre_final.formsemestre_id}) {self.formsemestre_final.date_fin.year}"""
|
||||||
if verbose and noms:
|
if verbose and noms:
|
||||||
repr += " - " + "+".join(noms)
|
title += " - " + "+".join(noms)
|
||||||
return repr
|
return title
|
||||||
|
|
||||||
|
|
||||||
class RCSsJuryPE:
|
class RCSsJuryPE:
|
||||||
|
"""Classe centralisant toutes les regroupements cohérents de
|
||||||
|
semestres (RCS) des étudiants à prendre en compte dans un jury PE
|
||||||
|
|
||||||
|
Args:
|
||||||
|
annee_diplome: L'année de diplomation
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, annee_diplome: int):
|
def __init__(self, annee_diplome: int):
|
||||||
"""Classe centralisant toutes les regroupements cohérents de
|
|
||||||
semestres (RCS) des étudiants à prendre en compte dans un jury PE
|
|
||||||
|
|
||||||
Args:
|
|
||||||
annee_diplome: L'année de diplomation
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.annee_diplome = annee_diplome
|
self.annee_diplome = annee_diplome
|
||||||
"""Année de diplômation"""
|
"""Année de diplômation"""
|
||||||
|
|
||||||
@ -155,7 +168,8 @@ class RCSsJuryPE:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
for nom_rcs in pe_comp.TOUS_LES_SEMESTRES + TOUS_LES_RCS_AVEC_PLUSIEURS_SEM:
|
for nom_rcs in pe_comp.TOUS_LES_SEMESTRES + TOUS_LES_RCS_AVEC_PLUSIEURS_SEM:
|
||||||
"""L'aggrégat considéré (par ex: 3S=S1+S2+S3), son nom de son semestre terminal (par ex: S3) et son numéro (par ex: 3)"""
|
# L'aggrégat considéré (par ex: 3S=S1+S2+S3), son nom de son semestre
|
||||||
|
# terminal (par ex: S3) et son numéro (par ex: 3)
|
||||||
noms_semestre_de_aggregat = TYPES_RCS[nom_rcs]["aggregat"]
|
noms_semestre_de_aggregat = TYPES_RCS[nom_rcs]["aggregat"]
|
||||||
nom_semestre_terminal = noms_semestre_de_aggregat[-1]
|
nom_semestre_terminal = noms_semestre_de_aggregat[-1]
|
||||||
|
|
||||||
@ -164,17 +178,17 @@ class RCSsJuryPE:
|
|||||||
self.suivi[etudid] = {
|
self.suivi[etudid] = {
|
||||||
aggregat: None
|
aggregat: None
|
||||||
for aggregat in pe_comp.TOUS_LES_SEMESTRES
|
for aggregat in pe_comp.TOUS_LES_SEMESTRES
|
||||||
+ TOUS_LES_RCS_AVEC_PLUSIEURS_SEM
|
+ TOUS_LES_RCS_AVEC_PLUSIEURS_SEM
|
||||||
}
|
}
|
||||||
|
|
||||||
"""Le formsemestre terminal (dernier en date) associé au
|
# Le formsemestre terminal (dernier en date) associé au
|
||||||
semestre marquant la fin de l'aggrégat
|
# semestre marquant la fin de l'aggrégat
|
||||||
(par ex: son dernier S3 en date)"""
|
# (par ex: son dernier S3 en date)
|
||||||
semestres = etudiants.cursus[etudid][nom_semestre_terminal]
|
semestres = etudiants.cursus[etudid][nom_semestre_terminal]
|
||||||
if semestres:
|
if semestres:
|
||||||
formsemestre_final = get_dernier_semestre_en_date(semestres)
|
formsemestre_final = get_dernier_semestre_en_date(semestres)
|
||||||
|
|
||||||
"""Ajout ou récupération de la trajectoire"""
|
# Ajout ou récupération de la trajectoire
|
||||||
trajectoire_id = (nom_rcs, formsemestre_final.formsemestre_id)
|
trajectoire_id = (nom_rcs, formsemestre_final.formsemestre_id)
|
||||||
if trajectoire_id not in self.rcss:
|
if trajectoire_id not in self.rcss:
|
||||||
trajectoire = RCS(nom_rcs, formsemestre_final)
|
trajectoire = RCS(nom_rcs, formsemestre_final)
|
||||||
@ -182,21 +196,22 @@ class RCSsJuryPE:
|
|||||||
else:
|
else:
|
||||||
trajectoire = self.rcss[trajectoire_id]
|
trajectoire = self.rcss[trajectoire_id]
|
||||||
|
|
||||||
"""La liste des semestres de l'étudiant à prendre en compte
|
# La liste des semestres de l'étudiant à prendre en compte
|
||||||
pour cette trajectoire"""
|
# pour cette trajectoire
|
||||||
semestres_a_aggreger = get_rcs_etudiant(
|
semestres_a_aggreger = get_rcs_etudiant(
|
||||||
etudiants.cursus[etudid], formsemestre_final, nom_rcs
|
etudiants.cursus[etudid], formsemestre_final, nom_rcs
|
||||||
)
|
)
|
||||||
|
|
||||||
"""Ajout des semestres à la trajectoire"""
|
# Ajout des semestres à la trajectoire
|
||||||
trajectoire.add_semestres_a_aggreger(semestres_a_aggreger)
|
trajectoire.add_semestres_a_aggreger(semestres_a_aggreger)
|
||||||
|
|
||||||
"""Mémoire la trajectoire suivie par l'étudiant"""
|
# Mémoire la trajectoire suivie par l'étudiant
|
||||||
self.suivi[etudid][nom_rcs] = trajectoire
|
self.suivi[etudid][nom_rcs] = trajectoire
|
||||||
|
|
||||||
|
|
||||||
def get_rcs_etudiant(semestres: dict[int:FormSemestre], formsemestre_final: FormSemestre, nom_rcs: str
|
def get_rcs_etudiant(
|
||||||
):
|
semestres: dict[int:FormSemestre], formsemestre_final: FormSemestre, nom_rcs: str
|
||||||
|
) -> dict[int, FormSemestre]:
|
||||||
"""Ensemble des semestres parcourus par un étudiant, connaissant
|
"""Ensemble des semestres parcourus par un étudiant, connaissant
|
||||||
les semestres de son cursus,
|
les semestres de son cursus,
|
||||||
dans le cadre du RCS visé et ayant pour semestre terminal `formsemestre_final`.
|
dans le cadre du RCS visé et ayant pour semestre terminal `formsemestre_final`.
|
||||||
@ -224,7 +239,7 @@ def get_rcs_etudiant(semestres: dict[int:FormSemestre], formsemestre_final: Form
|
|||||||
numero_semestre_terminal = formsemestre_final.semestre_id
|
numero_semestre_terminal = formsemestre_final.semestre_id
|
||||||
# semestres_significatifs = self.get_semestres_significatifs(etudid)
|
# semestres_significatifs = self.get_semestres_significatifs(etudid)
|
||||||
semestres_significatifs = {}
|
semestres_significatifs = {}
|
||||||
for i in range(1, pe_comp.NBRE_SEMESTRES_DIPLOMANT+1):
|
for i in range(1, pe_comp.NBRE_SEMESTRES_DIPLOMANT + 1):
|
||||||
semestres_significatifs = semestres_significatifs | semestres[f"S{i}"]
|
semestres_significatifs = semestres_significatifs | semestres[f"S{i}"]
|
||||||
|
|
||||||
if nom_rcs.startswith("S"): # les semestres
|
if nom_rcs.startswith("S"): # les semestres
|
||||||
@ -247,6 +262,7 @@ def get_rcs_etudiant(semestres: dict[int:FormSemestre], formsemestre_final: Form
|
|||||||
semestres_aggreges[fid] = semestre
|
semestres_aggreges[fid] = semestre
|
||||||
return semestres_aggreges
|
return semestres_aggreges
|
||||||
|
|
||||||
|
|
||||||
def get_descr_rcs(nom_rcs: str) -> str:
|
def get_descr_rcs(nom_rcs: str) -> str:
|
||||||
"""Renvoie la description pour les tableurs de synthèse
|
"""Renvoie la description pour les tableurs de synthèse
|
||||||
Excel d'un nom de RCS"""
|
Excel d'un nom de RCS"""
|
||||||
|
@ -35,33 +35,30 @@ Created on Fri Sep 9 09:15:05 2016
|
|||||||
|
|
||||||
@author: barasc
|
@author: barasc
|
||||||
"""
|
"""
|
||||||
import numpy as np
|
import pandas as pd
|
||||||
|
|
||||||
import app.pe.pe_etudiant
|
import app.pe.pe_etudiant
|
||||||
from app import db, log, ScoValueError
|
from app import db, ScoValueError
|
||||||
from app.comp import res_sem, moy_ue, moy_sem
|
from app import comp
|
||||||
from app.comp.moy_sem import comp_ranks_series
|
|
||||||
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
|
from app.models import FormSemestre
|
||||||
from app.models.moduleimpls import ModuleImpl
|
from app.models.moduleimpls import ModuleImpl
|
||||||
|
import app.pe.pe_affichage as pe_affichage
|
||||||
|
from app.pe.pe_tabletags import TableTag, MoyenneTag
|
||||||
from app.scodoc import sco_tag_module
|
from app.scodoc import sco_tag_module
|
||||||
from app.scodoc.codes_cursus import UE_SPORT
|
from app.scodoc.codes_cursus import UE_SPORT
|
||||||
import app.pe.pe_affichage as pe_affichage
|
|
||||||
from app.pe.pe_tabletags import TableTag, TAGS_RESERVES, MoyenneTag
|
|
||||||
import pandas as pd
|
|
||||||
|
|
||||||
|
|
||||||
class SemestreTag(TableTag):
|
class SemestreTag(TableTag):
|
||||||
|
"""
|
||||||
|
Un SemestreTag représente les résultats des étudiants à un semestre, en donnant
|
||||||
|
accès aux moyennes par tag.
|
||||||
|
Il s'appuie principalement sur FormSemestre et sur ResultatsSemestreBUT.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, formsemestre_id: int):
|
def __init__(self, formsemestre_id: int):
|
||||||
"""
|
"""
|
||||||
Un SemestreTag représente les résultats des étudiants à un semestre, en donnant
|
|
||||||
accès aux moyennes par tag.
|
|
||||||
Il s'appuie principalement sur FormSemestre et sur ResultatsSemestreBUT.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
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
|
||||||
"""
|
"""
|
||||||
TableTag.__init__(self)
|
TableTag.__init__(self)
|
||||||
@ -103,27 +100,27 @@ class SemestreTag(TableTag):
|
|||||||
dict_ues_competences = get_noms_competences_from_ues(self.nt.formsemestre)
|
dict_ues_competences = get_noms_competences_from_ues(self.nt.formsemestre)
|
||||||
noms_tags_comp = list(set(dict_ues_competences.values()))
|
noms_tags_comp = list(set(dict_ues_competences.values()))
|
||||||
noms_tags_auto = ["but"] + noms_tags_comp
|
noms_tags_auto = ["but"] + noms_tags_comp
|
||||||
self.tags = (
|
self.tags = noms_tags_perso + noms_tags_auto
|
||||||
noms_tags_perso + noms_tags_auto
|
|
||||||
)
|
|
||||||
"""Tags du semestre taggué"""
|
"""Tags du semestre taggué"""
|
||||||
|
|
||||||
## Vérifie l'unicité des tags
|
## Vérifie l'unicité des tags
|
||||||
if len(set(self.tags)) != len(self.tags):
|
if len(set(self.tags)) != len(self.tags):
|
||||||
intersection = list(set(noms_tags_perso) & set(noms_tags_auto))
|
intersection = list(set(noms_tags_perso) & set(noms_tags_auto))
|
||||||
liste_intersection = "\n".join([f"<li><code>{tag}</code></li>" for tag in intersection])
|
liste_intersection = "\n".join(
|
||||||
message = f"""Erreur dans le module PE : Un des tags saisis dans votre programme de formation
|
[f"<li><code>{tag}</code></li>" for tag in intersection]
|
||||||
fait parti des tags réservés. En particulier,
|
)
|
||||||
votre semestre <em>{self.formsemestre.titre_annee()}</em>
|
s = "s" if len(intersection) > 0 else ""
|
||||||
contient le(s) tag(s) réservé(s) suivant :
|
message = f"""Erreur dans le module PE : Un des tags saisis dans votre
|
||||||
|
programme de formation fait parti des tags réservés. En particulier,
|
||||||
|
votre semestre <em>{self.formsemestre.titre_annee()}</em>
|
||||||
|
contient le{s} tag{s} réservé{s} suivant :
|
||||||
<ul>
|
<ul>
|
||||||
{liste_intersection}
|
{liste_intersection}
|
||||||
</ul>
|
</ul>
|
||||||
Modifiez votre programme de formation pour le(s) supprimer. Il(s) sera(ont) automatiquement à vos documents de poursuites d'études.
|
Modifiez votre programme de formation pour le{s} supprimer.
|
||||||
|
Il{s} ser{'ont' if s else 'a'} automatiquement à vos documents de poursuites d'études.
|
||||||
"""
|
"""
|
||||||
raise ScoValueError(
|
raise ScoValueError(message)
|
||||||
message
|
|
||||||
)
|
|
||||||
|
|
||||||
# Calcul des moyennes & les classements de chaque étudiant à chaque tag
|
# Calcul des moyennes & les classements de chaque étudiant à chaque tag
|
||||||
self.moyennes_tags = {}
|
self.moyennes_tags = {}
|
||||||
@ -174,25 +171,25 @@ class SemestreTag(TableTag):
|
|||||||
La série des moyennes
|
La série des moyennes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
"""Adaptation du mask de calcul des moyennes au tag visé"""
|
# Adaptation du mask de calcul des moyennes au tag visé
|
||||||
modimpls_mask = [
|
modimpls_mask = [
|
||||||
modimpl.module.ue.type != UE_SPORT
|
modimpl.module.ue.type != UE_SPORT
|
||||||
for modimpl in self.formsemestre.modimpls_sorted
|
for modimpl in self.formsemestre.modimpls_sorted
|
||||||
]
|
]
|
||||||
|
|
||||||
"""Désactive tous les modules qui ne sont pas pris en compte pour ce tag"""
|
# Désactive tous les modules qui ne sont pas pris en compte pour ce tag
|
||||||
for i, modimpl in enumerate(self.formsemestre.modimpls_sorted):
|
for i, modimpl in enumerate(self.formsemestre.modimpls_sorted):
|
||||||
if modimpl.moduleimpl_id not in tags_infos[tag]:
|
if modimpl.moduleimpl_id not in tags_infos[tag]:
|
||||||
modimpls_mask[i] = False
|
modimpls_mask[i] = False
|
||||||
|
|
||||||
"""Applique la pondération des coefficients"""
|
# Applique la pondération des coefficients
|
||||||
modimpl_coefs_ponderes_df = self.modimpl_coefs_df.copy()
|
modimpl_coefs_ponderes_df = self.modimpl_coefs_df.copy()
|
||||||
for modimpl_id in tags_infos[tag]:
|
for modimpl_id in tags_infos[tag]:
|
||||||
ponderation = tags_infos[tag][modimpl_id]["ponderation"]
|
ponderation = tags_infos[tag][modimpl_id]["ponderation"]
|
||||||
modimpl_coefs_ponderes_df[modimpl_id] *= ponderation
|
modimpl_coefs_ponderes_df[modimpl_id] *= ponderation
|
||||||
|
|
||||||
"""Calcule les moyennes pour le tag visé dans chaque UE (dataframe etudid x ues)"""
|
# Calcule les moyennes pour le tag visé dans chaque UE (dataframe etudid x ues)#
|
||||||
moyennes_ues_tag = moy_ue.compute_ue_moys_apc(
|
moyennes_ues_tag = comp.moy_ue.compute_ue_moys_apc(
|
||||||
self.sem_cube,
|
self.sem_cube,
|
||||||
self.etuds,
|
self.etuds,
|
||||||
self.formsemestre.modimpls_sorted,
|
self.formsemestre.modimpls_sorted,
|
||||||
@ -203,13 +200,13 @@ class SemestreTag(TableTag):
|
|||||||
block=self.formsemestre.block_moyennes,
|
block=self.formsemestre.block_moyennes,
|
||||||
)
|
)
|
||||||
|
|
||||||
"""Les ects"""
|
# Les ects
|
||||||
ects = self.ues_inscr_parcours_df.fillna(0.0) * [
|
ects = self.ues_inscr_parcours_df.fillna(0.0) * [
|
||||||
ue.ects for ue in self.ues if ue.type != UE_SPORT
|
ue.ects for ue in self.ues if ue.type != UE_SPORT
|
||||||
]
|
]
|
||||||
|
|
||||||
"""Calcule la moyenne générale dans le semestre (pondérée par le ECTS)"""
|
# Calcule la moyenne générale dans le semestre (pondérée par le ECTS)
|
||||||
moy_gen_tag = moy_sem.compute_sem_moys_apc_using_ects(
|
moy_gen_tag = comp.moy_sem.compute_sem_moys_apc_using_ects(
|
||||||
moyennes_ues_tag,
|
moyennes_ues_tag,
|
||||||
ects,
|
ects,
|
||||||
formation_id=self.formsemestre.formation_id,
|
formation_id=self.formsemestre.formation_id,
|
||||||
@ -224,11 +221,6 @@ def get_moduleimpl(modimpl_id) -> dict:
|
|||||||
modimpl = db.session.get(ModuleImpl, modimpl_id)
|
modimpl = db.session.get(ModuleImpl, modimpl_id)
|
||||||
if modimpl:
|
if modimpl:
|
||||||
return modimpl
|
return modimpl
|
||||||
if SemestreTag.DEBUG:
|
|
||||||
log(
|
|
||||||
"SemestreTag.get_moduleimpl( %s ) : le modimpl recherche n'existe pas"
|
|
||||||
% (modimpl_id)
|
|
||||||
)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@ -260,27 +252,26 @@ def get_synthese_tags_personnalises_semestre(formsemestre: FormSemestre):
|
|||||||
"""
|
"""
|
||||||
synthese_tags = {}
|
synthese_tags = {}
|
||||||
|
|
||||||
"""Instance des modules du semestre"""
|
# Instance des modules du semestre
|
||||||
modimpls = formsemestre.modimpls_sorted
|
modimpls = formsemestre.modimpls_sorted
|
||||||
|
|
||||||
for modimpl in modimpls:
|
for modimpl in modimpls:
|
||||||
modimpl_id = modimpl.id
|
modimpl_id = modimpl.id
|
||||||
|
|
||||||
"""Liste des tags pour le module concerné"""
|
# Liste des tags pour le module concerné
|
||||||
tags = sco_tag_module.module_tag_list(modimpl.module.id)
|
tags = sco_tag_module.module_tag_list(modimpl.module.id)
|
||||||
|
|
||||||
"""Traitement des tags recensés, chacun pouvant étant de la forme
|
# Traitement des tags recensés, chacun pouvant étant de la forme
|
||||||
"mathématiques", "théorie", "pe:0", "maths:2"
|
# "mathématiques", "théorie", "pe:0", "maths:2"
|
||||||
"""
|
|
||||||
for tag in tags:
|
for tag in tags:
|
||||||
"""Extraction du nom du tag et du coeff de pondération"""
|
# Extraction du nom du tag et du coeff de pondération
|
||||||
(tagname, ponderation) = sco_tag_module.split_tagname_coeff(tag)
|
(tagname, ponderation) = sco_tag_module.split_tagname_coeff(tag)
|
||||||
|
|
||||||
"""Ajout d'une clé pour le tag"""
|
# Ajout d'une clé pour le tag
|
||||||
if tagname not in synthese_tags:
|
if tagname not in synthese_tags:
|
||||||
synthese_tags[tagname] = {}
|
synthese_tags[tagname] = {}
|
||||||
|
|
||||||
"""Ajout du module (modimpl) au tagname considéré"""
|
# Ajout du module (modimpl) au tagname considéré
|
||||||
synthese_tags[tagname][modimpl_id] = {
|
synthese_tags[tagname][modimpl_id] = {
|
||||||
"modimpl": modimpl, # les données sur le module
|
"modimpl": modimpl, # les données sur le module
|
||||||
# "coeff": modimpl.module.coefficient, # le coeff du module dans le semestre
|
# "coeff": modimpl.module.coefficient, # le coeff du module dans le semestre
|
||||||
@ -298,6 +289,8 @@ def get_noms_competences_from_ues(formsemestre: FormSemestre) -> dict[int, str]:
|
|||||||
"""Partant d'un formsemestre, extrait le nom des compétences associés
|
"""Partant d'un formsemestre, extrait le nom des compétences associés
|
||||||
à (ou aux) parcours des étudiants du formsemestre.
|
à (ou aux) parcours des étudiants du formsemestre.
|
||||||
|
|
||||||
|
Ignore les UEs non associées à un niveau de compétence.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
formsemestre: Un FormSemestre
|
formsemestre: Un FormSemestre
|
||||||
|
|
||||||
@ -310,8 +303,8 @@ def get_noms_competences_from_ues(formsemestre: FormSemestre) -> dict[int, str]:
|
|||||||
|
|
||||||
noms_competences = {}
|
noms_competences = {}
|
||||||
for ue in nt.ues:
|
for ue in nt.ues:
|
||||||
if ue.type != UE_SPORT:
|
if ue.niveau_competence and ue.type != UE_SPORT:
|
||||||
ordre = ue.niveau_competence.ordre
|
# ?? inutilisé ordre = ue.niveau_competence.ordre
|
||||||
nom = ue.niveau_competence.competence.titre
|
nom = ue.niveau_competence.competence.titre
|
||||||
noms_competences[ue.ue_id] = f"comp. {nom}"
|
noms_competences[ue.ue_id] = f"comp. {nom}"
|
||||||
return noms_competences
|
return noms_competences
|
||||||
|
@ -870,7 +870,7 @@ def stripquotes(s):
|
|||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
def suppress_accents(s):
|
def suppress_accents(s: str) -> str:
|
||||||
"remove accents and suppress non ascii characters from string s"
|
"remove accents and suppress non ascii characters from string s"
|
||||||
if isinstance(s, str):
|
if isinstance(s, str):
|
||||||
return (
|
return (
|
||||||
|
Loading…
Reference in New Issue
Block a user