Compare commits
6 Commits
787e514dca
...
64d7e1ed42
Author | SHA1 | Date | |
---|---|---|---|
64d7e1ed42 | |||
d310304e9e | |||
fce23aa066 | |||
9c6d988fc3 | |||
cb5df2fffd | |||
3550e4290a |
@ -1,5 +1,16 @@
|
||||
from app.models import Formation, FormSemestre
|
||||
from app.scodoc import codes_cursus
|
||||
from app import log
|
||||
|
||||
PE_DEBUG = 0
|
||||
|
||||
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
|
||||
|
||||
|
||||
def nom_semestre_etape(semestre: FormSemestre, avec_fid=False) -> str:
|
||||
@ -29,7 +40,7 @@ def nom_semestre_etape(semestre: FormSemestre, avec_fid=False) -> str:
|
||||
f"{semestre.date_debut.year}-{semestre.date_fin.year}",
|
||||
]
|
||||
if avec_fid:
|
||||
description.append(f"({semestre.forsemestre_id})")
|
||||
description.append(f"({semestre.formsemestre_id})")
|
||||
|
||||
return " ".join(description)
|
||||
|
||||
|
@ -45,21 +45,11 @@ 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 = 0
|
||||
|
||||
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:
|
||||
@ -382,3 +372,5 @@ def get_cosemestres_diplomants(
|
||||
cosemestres[fid] = cosem
|
||||
|
||||
return cosemestres
|
||||
|
||||
|
||||
|
@ -35,42 +35,56 @@ Created on 17/01/2024
|
||||
|
||||
@author: barasc
|
||||
"""
|
||||
import pandas as pd
|
||||
|
||||
import app.pe.pe_comp as pe_comp
|
||||
from app.models import FormSemestre, Identite
|
||||
from app.pe.pe_comp import pe_print
|
||||
import app.pe.pe_affichage as pe_affichage
|
||||
import app.pe.pe_comp as pe_comp
|
||||
from app.pe import pe_comp, pe_affichage
|
||||
|
||||
|
||||
class EtudiantsJuryPE:
|
||||
"""Classe centralisant la gestion des étudiants à prendre en compte dans un jury de PE"""
|
||||
|
||||
def __init__(self, annee_diplome: int):
|
||||
"""
|
||||
Classe centralisant la gestion des étudiants à prendre en compte dans un jury de PE
|
||||
|
||||
Args:
|
||||
annee_diplome: L'année de diplomation
|
||||
"""
|
||||
|
||||
self.annee_diplome = annee_diplome
|
||||
"""L'année du diplôme"""
|
||||
|
||||
"Les identités des étudiants traités pour le jury"
|
||||
self.identites = {} # ex. ETUDINFO_DICT
|
||||
"Les cursus (semestres suivis, abandons) des étudiants"
|
||||
"Les identités des étudiants traités pour le jury"
|
||||
|
||||
self.cursus = {}
|
||||
"""Les aggrégats des semestres suivis (par ex: 3S=S1+S2+S3 à prendre en compte avec d'éventuels redoublements) des étudiants"""
|
||||
"Les cursus (semestres suivis, abandons) des étudiants"
|
||||
|
||||
self.trajectoires = {}
|
||||
"""Les trajectoires/chemins de semestres suivis par les étudiants
|
||||
pour atteindre un aggrégat donné
|
||||
(par ex: 3S=S1+S2+S3 à prendre en compte avec d'éventuels redoublements)"""
|
||||
|
||||
"Les etudids des étudiants à considérer au jury (ceux qui seront effectivement diplômés)"
|
||||
self.etudiants_diplomes = {}
|
||||
self.diplomes_ids = {}
|
||||
"""Les identités des étudiants à considérer au jury (ceux qui seront effectivement diplômés)"""
|
||||
|
||||
self.diplomes_ids = {}
|
||||
"""Les etudids des étudiants diplômés"""
|
||||
|
||||
"Les etudids des étudiants dont il faut calculer les moyennes/classements (même si d'éventuels abandons)"
|
||||
self.etudiants_ids = {}
|
||||
"""Les étudiants inscrits dans les co-semestres (ceux du jury mais aussi d'autres ayant été réorientés ou ayant abandonnés)"""
|
||||
"""Les etudids des étudiants dont il faut calculer les moyennes/classements (même si d'éventuels abandons).
|
||||
Il s'agit des étudiants inscrits dans les co-semestres (ceux du jury mais aussi d'autres ayant
|
||||
été réorientés ou ayant abandonnés)"""
|
||||
|
||||
self.cosemestres: dict[int, FormSemestre] = None
|
||||
"Les cosemestres donnant lieu à même année de diplome"
|
||||
|
||||
self.abandons = {}
|
||||
"""Les étudiants qui ne seront pas diplômés à ce jury (redoublants/réorientés)"""
|
||||
self.abandons_ids = {}
|
||||
"""Les etudids des étudiants redoublants/réorientés"""
|
||||
|
||||
def find_etudiants(self, formation_id: int):
|
||||
"""Liste des étudiants à prendre en compte dans le jury PE, en les recherchant
|
||||
de manière automatique par rapport à leur année de diplomation ``annee_diplome``
|
||||
@ -86,17 +100,19 @@ class EtudiantsJuryPE:
|
||||
cosemestres = pe_comp.get_cosemestres_diplomants(self.annee_diplome, None)
|
||||
self.cosemestres = cosemestres
|
||||
|
||||
pe_comp.pe_print(f"1) Recherche des coSemestres -> {len(cosemestres)} trouvés")
|
||||
pe_affichage.pe_print(
|
||||
f"1) Recherche des coSemestres -> {len(cosemestres)} trouvés"
|
||||
)
|
||||
|
||||
pe_comp.pe_print("2) Liste des étudiants dans les différents co-semestres")
|
||||
pe_affichage.pe_print("2) Liste des étudiants dans les différents co-semestres")
|
||||
self.etudiants_ids = get_etudiants_dans_semestres(cosemestres)
|
||||
pe_comp.pe_print(
|
||||
pe_affichage.pe_print(
|
||||
" => %d étudiants trouvés dans les cosemestres" % len(self.etudiants_ids)
|
||||
)
|
||||
|
||||
# Analyse des parcours étudiants pour déterminer leur année effective de diplome
|
||||
# avec prise en compte des redoublements, des abandons, ....
|
||||
pe_comp.pe_print("3) Analyse des parcours individuels des étudiants")
|
||||
pe_affichage.pe_print("3) Analyse des parcours individuels des étudiants")
|
||||
|
||||
no_etud = 0
|
||||
for no_etud, etudid in enumerate(self.etudiants_ids):
|
||||
@ -110,46 +126,43 @@ class EtudiantsJuryPE:
|
||||
# Analyse son parcours pour atteindre chaque semestre de la formation
|
||||
self.structure_cursus_etudiant(etudid)
|
||||
|
||||
if (no_etud + 1) % 10 == 0:
|
||||
pe_comp.pe_print(f"{no_etud + 1}")
|
||||
no_etud += 1
|
||||
pe_comp.pe_print()
|
||||
|
||||
# Les étudiants à prendre dans le diplôme, étudiants ayant abandonnés non compris
|
||||
self.etudiants_diplomes = self.get_etudiants_diplomes()
|
||||
self.diplomes_ids = set(self.etudiants_diplomes.keys())
|
||||
|
||||
self.etudiants_ids = set(self.identites)
|
||||
self.etudiants_ids = set(self.identites.keys())
|
||||
"""Les étudiants dont il faut calculer les moyennes"""
|
||||
|
||||
self.formsemestres_jury_ids = self.get_formsemestres()
|
||||
"""Les formsemestres (des étudiants) dont il faut calculer les moyennes"""
|
||||
|
||||
# Les abandons (pour debug)
|
||||
self.abandons = self.get_etudiants_redoublants_ou_reorientes()
|
||||
self.abandons_ids = set(self.abandons)
|
||||
|
||||
# Synthèse
|
||||
pe_comp.pe_print(
|
||||
pe_affichage.pe_print(
|
||||
f" => {len(self.etudiants_diplomes)} étudiants à diplômer en {self.annee_diplome}"
|
||||
)
|
||||
nbre_abandons = len(self.etudiants_ids) - len(self.etudiants_diplomes)
|
||||
pe_comp.pe_print(f" => {nbre_abandons} étudiants éliminer pour abandon")
|
||||
pe_comp.pe_print(
|
||||
assert nbre_abandons == len(self.abandons_ids)
|
||||
|
||||
pe_affichage.pe_print(
|
||||
f" => {nbre_abandons} étudiants non considérés (redoublement, réorientation, abandon"
|
||||
)
|
||||
pe_affichage.pe_print(
|
||||
f" => {len(self.formsemestres_jury_ids)} semestres dont il faut calculer la moyenne"
|
||||
)
|
||||
pe_comp.pe_print(
|
||||
" => quelques étudiants futurs diplômés : "
|
||||
+ ", ".join([str(etudid) for etudid in list(self.etudiants_diplomes)[:10]])
|
||||
)
|
||||
pe_comp.pe_print(
|
||||
" => semestres dont il faut calculer les moyennes : "
|
||||
+ ", ".join([str(fid) for fid in list(self.formsemestres_jury_ids)])
|
||||
)
|
||||
# Les abandons :
|
||||
self.abandons = sorted(
|
||||
[
|
||||
cursus["nom"]
|
||||
for etudid, cursus in self.cursus.items()
|
||||
if etudid not in self.diplomes_ids
|
||||
]
|
||||
)
|
||||
# pe_affichage.pe_print(
|
||||
# " => quelques étudiants futurs diplômés : "
|
||||
# + ", ".join([str(etudid) for etudid in list(self.etudiants_diplomes)[:10]])
|
||||
# )
|
||||
# pe_affichage.pe_print(
|
||||
# " => semestres dont il faut calculer les moyennes : "
|
||||
# + ", ".join([str(fid) for fid in list(self.formsemestres_jury_ids)])
|
||||
# )
|
||||
|
||||
|
||||
|
||||
def get_etudiants_diplomes(self) -> dict[int, Identite]:
|
||||
"""Identités des étudiants (sous forme d'un dictionnaire `{etudid: Identite(etudid)}`
|
||||
@ -169,6 +182,23 @@ class EtudiantsJuryPE:
|
||||
etudiants = {etudid: self.identites[etudid] for etudid in etudids}
|
||||
return etudiants
|
||||
|
||||
def get_etudiants_redoublants_ou_reorientes(self) -> dict[int, Identite]:
|
||||
"""Identités des étudiants (sous forme d'un dictionnaire `{etudid: Identite(etudid)}`
|
||||
dont les notes seront prises en compte (pour les classements) mais qui n'apparaitront
|
||||
pas dans le jury car diplômé une autre année (redoublants) ou réorienté ou démissionnaire.
|
||||
|
||||
Returns:
|
||||
Un dictionnaire `{etudid: Identite(etudid)}`
|
||||
"""
|
||||
etudids = [
|
||||
etudid
|
||||
for etudid in self.cursus
|
||||
if self.cursus[etudid]["diplome"] != self.annee_diplome
|
||||
or self.cursus[etudid]["abandon"] is True
|
||||
]
|
||||
etudiants = {etudid: self.identites[etudid] for etudid in etudids}
|
||||
return etudiants
|
||||
|
||||
def analyse_etat_etudiant(self, etudid: int, cosemestres: dict[int, FormSemestre]):
|
||||
"""Analyse le cursus d'un étudiant pouvant être :
|
||||
|
||||
@ -259,10 +289,19 @@ class EtudiantsJuryPE:
|
||||
} # les semestres de n°i de l'étudiant
|
||||
self.cursus[etudid][nom_sem] = semestres_i
|
||||
|
||||
def get_trajectoire(self, etudid: int, formsemestre_final: FormSemestre):
|
||||
def get_trajectoire(
|
||||
self, etudid: int, formsemestre_final: FormSemestre, nom_aggregat: str
|
||||
):
|
||||
"""Ensemble des semestres parcourus par
|
||||
un étudiant pour l'amener à un semestre terminal.
|
||||
|
||||
Si nom_aggregat est de type "Si", limite les semestres à ceux de numéro i.
|
||||
Par ex: si formsemestre_terminal est un S3 et nom_agrregat "S3", ne prend en compte que les
|
||||
semestres 3.
|
||||
|
||||
Si nom_aggregat est de type "iA" ou "iS" (incluant plusieurs numéros de semestres), prend en
|
||||
compte les dit numéros de semestres.
|
||||
|
||||
Par ex: si formsemestre_terminal est un S3, ensemble des S1,
|
||||
S2, S3 suivi pour l'amener au S3 (il peut y avoir plusieurs S1,
|
||||
ou S2, ou S3 s'il a redoublé).
|
||||
@ -277,12 +316,21 @@ class EtudiantsJuryPE:
|
||||
numero_semestre_terminal = formsemestre_final.semestre_id
|
||||
semestres_significatifs = self.get_semestres_significatifs(etudid)
|
||||
|
||||
# Semestres de n° inférieur (pax ex: des S1, S2, S3 pour un S3 terminal)
|
||||
# et qui lui sont antérieurs
|
||||
if nom_aggregat.startswith("S"): # les semestres
|
||||
numero_semestres_possibles = [numero_semestre_terminal]
|
||||
elif nom_aggregat.endswith("A"): # les années
|
||||
numero_semestres_possibles = [
|
||||
int(sem[-1]) for sem in pe_comp.PARCOURS[nom_aggregat]["aggregat"]
|
||||
]
|
||||
assert numero_semestre_terminal in numero_semestres_possibles
|
||||
else: # les xS = tous les semestres jusqu'à Sx (pax ex: des S1, S2, S3 pour un S3 terminal)
|
||||
numero_semestres_possibles = list(range(1, numero_semestre_terminal + 1))
|
||||
|
||||
semestres_aggreges = {}
|
||||
for fid, semestre in semestres_significatifs.items():
|
||||
# Semestres parmi ceux de n° possibles & qui lui sont antérieurs
|
||||
if (
|
||||
semestre.semestre_id <= numero_semestre_terminal
|
||||
semestre.semestre_id in numero_semestres_possibles
|
||||
and semestre.date_fin <= formsemestre_final.date_fin
|
||||
):
|
||||
semestres_aggreges[fid] = semestre
|
||||
@ -373,14 +421,65 @@ class EtudiantsJuryPE:
|
||||
else:
|
||||
raise ValueError("Probleme de paramètres d'appel dans get_formsemestreids")
|
||||
|
||||
def nbre_etapes_max_diplomes(self):
|
||||
"""Connaissant les étudiants diplomes du jury PE,
|
||||
def nbre_etapes_max_diplomes(self, etudids: list[int]) -> int:
|
||||
"""Partant d'un ensemble d'étudiants,
|
||||
nombre de semestres (étapes) maximum suivis par les étudiants du jury.
|
||||
|
||||
Args:
|
||||
etudids: Liste d'étudid d'étudiants
|
||||
"""
|
||||
nbres_semestres = []
|
||||
for etudid in self.diplomes_ids:
|
||||
for etudid in etudids:
|
||||
nbres_semestres.append(self.cursus[etudid]["nb_semestres"])
|
||||
return max(nbres_semestres)
|
||||
if not nbres_semestres:
|
||||
return 0
|
||||
else:
|
||||
return max(nbres_semestres)
|
||||
|
||||
def df_administratif(self, etudids: list[int]) -> pd.DataFrame:
|
||||
"""Synthétise toutes les données administratives d'un groupe
|
||||
d'étudiants fournis par les etudid dans un dataFrame
|
||||
|
||||
Args:
|
||||
etudids: La liste des étudiants à prendre en compte
|
||||
"""
|
||||
|
||||
etudids = list(etudids)
|
||||
|
||||
# Récupération des données des étudiants
|
||||
administratif = {}
|
||||
nbre_semestres_max = self.nbre_etapes_max_diplomes(etudids)
|
||||
|
||||
for etudid in etudids:
|
||||
etudiant = self.identites[etudid]
|
||||
cursus = self.cursus[etudid]
|
||||
formsemestres = cursus["formsemestres"]
|
||||
|
||||
if cursus["diplome"]:
|
||||
diplome = cursus["diplome"]
|
||||
else:
|
||||
diplome = "indéterminé"
|
||||
|
||||
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": 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")
|
||||
|
||||
# Tri par nom/prénom
|
||||
df.sort_values(by=["Nom", "Prenom"], inplace=True)
|
||||
return df
|
||||
|
||||
|
||||
def get_etudiants_dans_semestres(semestres: dict[int, FormSemestre]) -> set:
|
||||
@ -402,7 +501,7 @@ def get_etudiants_dans_semestres(semestres: dict[int, FormSemestre]) -> set:
|
||||
for sem in semestres.values(): # pour chacun des semestres de la liste
|
||||
etudiants_du_sem = {ins.etudid for ins in sem.inscriptions}
|
||||
|
||||
pe_print(f" --> {sem} : {len(etudiants_du_sem)} etudiants")
|
||||
pe_affichage.pe_print(f" --> {sem} : {len(etudiants_du_sem)} etudiants")
|
||||
etudiants_ids = (
|
||||
etudiants_ids | etudiants_du_sem
|
||||
) # incluant la suppression des doublons
|
||||
|
@ -1,22 +1,15 @@
|
||||
from app.comp import moy_sem
|
||||
from app.pe.pe_tabletags import TableTag
|
||||
from app.pe.pe_etudiant import EtudiantsJuryPE
|
||||
from app.pe.pe_trajectoire import Trajectoire, TrajectoiresJuryPE
|
||||
from app.pe.pe_trajectoiretag import TrajectoireTag
|
||||
from app.comp import moy_sem
|
||||
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
|
||||
class AggregatInterclasseTag(TableTag):
|
||||
"""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')
|
||||
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
|
||||
"""
|
||||
|
||||
# -------------------------------------------------------------------------------------------------------------------
|
||||
def __init__(
|
||||
@ -26,12 +19,26 @@ class AggregatInterclasseTag(TableTag):
|
||||
trajectoires_jury_pe: TrajectoiresJuryPE,
|
||||
trajectoires_taggues: dict[tuple, TrajectoireTag],
|
||||
):
|
||||
# Table nommée au nom de l'aggrégat (par ex: 3S)
|
||||
TableTag.__init__(self, nom_aggregat)
|
||||
"""
|
||||
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')
|
||||
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)
|
||||
|
||||
# Le nom
|
||||
self.aggregat = nom_aggregat
|
||||
self.nom = self.get_repr()
|
||||
|
||||
"""Les étudiants diplômés et leurs trajectoires (cf. trajectoires.suivis)""" # TODO
|
||||
self.diplomes_ids = etudiants.etudiants_diplomes
|
||||
self.etudiants_diplomes = {etudid for etudid in self.diplomes_ids}
|
||||
# pour les exports sous forme de dataFrame
|
||||
self.etudiants = {etudid: etudiants.identites[etudid].etat_civil for etudid in self.diplomes_ids}
|
||||
|
||||
# Les trajectoires (et leur version tagguées), en ne gardant que celles associées à l'aggrégat
|
||||
self.trajectoires: dict[int, Trajectoire] = {}
|
||||
@ -72,9 +79,13 @@ class AggregatInterclasseTag(TableTag):
|
||||
"nb_inscrits": len(moy_gen_tag),
|
||||
}
|
||||
|
||||
# Est significatif ? (aka a-t-il des tags et des notes)
|
||||
self.significatif = len(self.tags_sorted) > 0
|
||||
|
||||
|
||||
def get_repr(self) -> str:
|
||||
"""Une représentation textuelle"""
|
||||
return f"Aggrégat {self.nom}"
|
||||
return f"Aggrégat {self.aggregat}"
|
||||
|
||||
def do_taglist(self):
|
||||
"""Synthétise les tags à partir des trajectoires_tagguées
|
||||
|
@ -46,14 +46,7 @@ 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.scodoc.gen_tables import SeqGenTable
|
||||
from app.pe.pe_etudiant import EtudiantsJuryPE
|
||||
from app.pe.pe_trajectoire import TrajectoiresJuryPE, Trajectoire
|
||||
import app.pe.pe_comp as pe_comp
|
||||
@ -63,12 +56,8 @@ 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.
|
||||
@ -110,9 +99,10 @@ class JuryPE(object):
|
||||
"Nom du zip où ranger les fichiers générés"
|
||||
|
||||
self.zipdata = io.BytesIO()
|
||||
|
||||
with ZipFile(self.zipdata, "w") as zipfile:
|
||||
# Chargement des étudiants à prendre en compte dans le jury
|
||||
pe_comp.pe_print(
|
||||
pe_affichage.pe_print(
|
||||
f"""*** Recherche et chargement des étudiants diplômés en {
|
||||
self.diplome} pour la formation {self.formation_id}"""
|
||||
)
|
||||
@ -122,85 +112,126 @@ class JuryPE(object):
|
||||
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 formsemestretag in self.semestres_taggues.values():
|
||||
filename = formsemestretag.nom.replace(" ", "_") + ".csv"
|
||||
pe_comp.pe_print(f" - Export csv de {filename} ")
|
||||
self.add_file_to_zip(
|
||||
zipfile,
|
||||
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_tagguee in self.trajectoires_tagguees.values():
|
||||
filename = trajectoire_tagguee.get_repr().replace(" ", "_") + ".csv"
|
||||
pe_comp.pe_print(f" - Export csv de {filename} ")
|
||||
self.add_file_to_zip(
|
||||
zipfile,
|
||||
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 interclass_tag in self.interclassements_taggues.values():
|
||||
filename = interclass_tag.get_repr().replace(" ", "_") + ".csv"
|
||||
pe_comp.pe_print(f" - Export csv de {filename} ")
|
||||
self.add_file_to_zip(
|
||||
zipfile,
|
||||
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")
|
||||
# Intègre le bilan des semestres taggués au zip final
|
||||
output = io.BytesIO()
|
||||
|
||||
with pd.ExcelWriter(output, engine="openpyxl") as writer:
|
||||
for onglet, df in self.synthese.items():
|
||||
# écriture dans l'onglet:
|
||||
df.to_excel(writer, onglet, index=True, header=True)
|
||||
if self.diplomes_ids:
|
||||
onglet = "diplômés"
|
||||
df_diplome = self.etudiants.df_administratif(self.diplomes_ids)
|
||||
df_diplome.to_excel(writer, onglet, index=True, header=True)
|
||||
if self.etudiants.abandons_ids:
|
||||
onglet = "redoublants-réorientés"
|
||||
df_abandon = self.etudiants.df_administratif(
|
||||
self.etudiants.abandons_ids
|
||||
)
|
||||
df_abandon.to_excel(writer, onglet, index=True, header=True)
|
||||
output.seek(0)
|
||||
|
||||
self.add_file_to_zip(
|
||||
zipfile, f"synthese_jury_{self.diplome}.xlsx", output.read()
|
||||
zipfile,
|
||||
f"etudiants_{self.diplome}.xlsx",
|
||||
output.read(),
|
||||
path="details",
|
||||
)
|
||||
|
||||
if not self.diplomes_ids:
|
||||
pe_affichage.pe_tools("*** Aucun étudiant diplômé")
|
||||
else:
|
||||
# Génère les semestres taggués (avec le calcul des moyennes) pour le jury PE
|
||||
pe_affichage.pe_print("*** Génère les semestres taggués")
|
||||
self.semestres_taggues = compute_semestres_tag(self.etudiants)
|
||||
|
||||
# Intègre le bilan des semestres taggués au zip final
|
||||
output = io.BytesIO()
|
||||
with pd.ExcelWriter(output, engine="openpyxl") as writer:
|
||||
for formsemestretag in self.semestres_taggues.values():
|
||||
onglet = formsemestretag.nom
|
||||
df = formsemestretag.df_moyennes_et_classements()
|
||||
# écriture dans l'onglet
|
||||
df.to_excel(writer, onglet, index=True, header=True)
|
||||
output.seek(0)
|
||||
|
||||
self.add_file_to_zip(
|
||||
zipfile,
|
||||
f"semestres_taggues_{self.diplome}.xlsx",
|
||||
output.read(),
|
||||
path="details",
|
||||
)
|
||||
|
||||
# Génère les trajectoires (combinaison de semestres suivis
|
||||
# par un étudiant pour atteindre le semestre final d'un aggrégat)
|
||||
pe_affichage.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_affichage.pe_print(
|
||||
"*** Calcule les moyennes par tag des trajectoires possibles"
|
||||
)
|
||||
self.trajectoires_tagguees = compute_trajectoires_tag(
|
||||
self.trajectoires, self.etudiants, self.semestres_taggues
|
||||
)
|
||||
|
||||
# Intègre le bilan des trajectoires tagguées au zip final
|
||||
output = io.BytesIO()
|
||||
with pd.ExcelWriter(output, engine="openpyxl") as writer:
|
||||
for trajectoire_tagguee in self.trajectoires_tagguees.values():
|
||||
onglet = trajectoire_tagguee.get_repr()
|
||||
df = trajectoire_tagguee.df_moyennes_et_classements()
|
||||
# écriture dans l'onglet
|
||||
df.to_excel(writer, onglet, index=True, header=True)
|
||||
output.seek(0)
|
||||
|
||||
self.add_file_to_zip(
|
||||
zipfile,
|
||||
f"trajectoires_taggues_{self.diplome}.xlsx",
|
||||
output.read(),
|
||||
path="details",
|
||||
)
|
||||
|
||||
# Génère les interclassements (par promo et) par (nom d') aggrégat
|
||||
pe_affichage.pe_print("*** Génère les interclassements par aggrégat")
|
||||
self.interclassements_taggues = compute_interclassements(
|
||||
self.etudiants, self.trajectoires, self.trajectoires_tagguees
|
||||
)
|
||||
|
||||
# Intègre le bilan des aggrégats (interclassé par promo) au zip final
|
||||
output = io.BytesIO()
|
||||
with pd.ExcelWriter(output, engine="openpyxl") as writer:
|
||||
for interclass_tag in self.interclassements_taggues.values():
|
||||
if interclass_tag.significatif: # Avec des notes
|
||||
onglet = interclass_tag.get_repr()
|
||||
df = interclass_tag.df_moyennes_et_classements()
|
||||
# écriture dans l'onglet
|
||||
df.to_excel(writer, onglet, index=True, header=True)
|
||||
output.seek(0)
|
||||
|
||||
self.add_file_to_zip(
|
||||
zipfile,
|
||||
f"interclassements_taggues_{self.diplome}.xlsx",
|
||||
output.read(),
|
||||
path="details",
|
||||
)
|
||||
|
||||
# Synthèse des éléments du jury PE
|
||||
self.synthese = self.synthetise_juryPE()
|
||||
|
||||
# Export des données => mode 1 seule feuille -> supprimé
|
||||
pe_affichage.pe_print("*** Export du jury de synthese")
|
||||
output = io.BytesIO()
|
||||
|
||||
with pd.ExcelWriter(output, engine="openpyxl") as writer:
|
||||
for onglet, df in self.synthese.items():
|
||||
# écriture dans l'onglet:
|
||||
df.to_excel(writer, onglet, index=True, header=True)
|
||||
output.seek(0)
|
||||
|
||||
self.add_file_to_zip(
|
||||
zipfile, f"synthese_jury_{self.diplome}.xlsx", output.read()
|
||||
)
|
||||
|
||||
# Fin !!!! Tada :)
|
||||
|
||||
def add_file_to_zip(self, zipfile: ZipFile, filename: str, data, path=""):
|
||||
@ -241,58 +272,18 @@ class JuryPE(object):
|
||||
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 ***")
|
||||
pe_affichage.pe_print("*** Synthèse finale des moyennes ***")
|
||||
|
||||
synthese = {}
|
||||
pe_comp.pe_print(" -> Synthèse des données administratives")
|
||||
synthese["administratif"] = self.df_administratif()
|
||||
pe_affichage.pe_print(" -> Synthèse des données administratives")
|
||||
synthese["administratif"] = self.etudiants.df_administratif(self.diplomes_ids)
|
||||
|
||||
tags = self.do_tags_list(self.interclassements_taggues)
|
||||
for tag in tags:
|
||||
pe_comp.pe_print(f" -> Synthèse du tag {tag}")
|
||||
pe_affichage.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"]
|
||||
|
||||
if cursus["diplome"]:
|
||||
diplome = cursus["diplome"]
|
||||
else:
|
||||
diplome = "indéterminé"
|
||||
|
||||
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": 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")
|
||||
|
||||
# Tri par nom/prénom
|
||||
df.sort_values(by=["Nom", "Prenom"], inplace=True)
|
||||
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,
|
||||
@ -318,40 +309,40 @@ class JuryPE(object):
|
||||
}
|
||||
|
||||
for aggregat in aggregats:
|
||||
"""La trajectoire de l'étudiant sur l'aggrégat"""
|
||||
# La trajectoire de l'étudiant sur l'aggrégat
|
||||
trajectoire = self.trajectoires.suivi[etudid][aggregat]
|
||||
"""Les moyennes par tag de cette trajectoire"""
|
||||
|
||||
# Les moyennes par tag de cette trajectoire
|
||||
donnees[etudid] |= {
|
||||
f"{aggregat} notes ": "-",
|
||||
f"{aggregat} class. (groupe)": "-",
|
||||
f"{aggregat} min | moy | max (groupe)": "-",
|
||||
}
|
||||
if trajectoire:
|
||||
trajectoire_tagguee = self.trajectoires_tagguees[
|
||||
trajectoire.trajectoire_id
|
||||
]
|
||||
bilan = trajectoire_tagguee.moyennes_tags[tag]
|
||||
if tag in trajectoire_tagguee.moyennes_tags:
|
||||
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)": "-",
|
||||
}
|
||||
donnees[etudid] |= {
|
||||
f"{aggregat} notes ": round(bilan["notes"].loc[etudid], 2),
|
||||
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}",
|
||||
}
|
||||
|
||||
"""L'interclassement"""
|
||||
interclass = self.interclassements_taggues[aggregat]
|
||||
donnees[etudid] |= {
|
||||
f"{aggregat} class. (promo)": "-",
|
||||
f"{aggregat} min | moy | max (promo)": "-",
|
||||
}
|
||||
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)": "-",
|
||||
f"{aggregat} min | moy | max (promo)": f"{bilan['min']:.1f} | {bilan['moy']:.1f} | {bilan['max']:.1f}",
|
||||
}
|
||||
|
||||
# Fin de l'aggrégat
|
||||
@ -362,17 +353,6 @@ class JuryPE(object):
|
||||
df.sort_values(by=["Nom", "Prenom"], inplace=True)
|
||||
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) -> dict:
|
||||
"""Créé les semestres taggués, de type 'S1', 'S2', ..., pour un groupe d'étudiants donnés.
|
||||
@ -389,7 +369,7 @@ def compute_semestres_tag(etudiants: EtudiantsJuryPE) -> dict:
|
||||
"""
|
||||
|
||||
"""Création des semestres taggués, de type 'S1', 'S2', ..."""
|
||||
pe_comp.pe_print("*** Création des semestres taggués")
|
||||
pe_affichage.pe_print("*** Création des semestres taggués")
|
||||
|
||||
formsemestres = etudiants.get_formsemestres(
|
||||
semestres_recherches=pe_comp.TOUS_LES_SEMESTRES
|
||||
@ -397,19 +377,11 @@ def compute_semestres_tag(etudiants: EtudiantsJuryPE) -> dict:
|
||||
|
||||
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ée le semestre_tag et exécute les calculs de moyennes
|
||||
formsemestretag = SemestreTag(nom, frmsem_id)
|
||||
|
||||
formsemestretag = SemestreTag(frmsem_id)
|
||||
pe_affichage.pe_print(
|
||||
f" --> Semestre taggué {formsemestretag.nom} sur la base de {formsemestre}"
|
||||
)
|
||||
# Stocke le semestre taggué
|
||||
semestres_tags[frmsem_id] = formsemestretag
|
||||
|
||||
@ -442,23 +414,16 @@ def compute_trajectoires_tag(
|
||||
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)} }
|
||||
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]
|
||||
for trajectoire_id, trajectoire in trajectoires.trajectoires.items():
|
||||
nom = trajectoire.get_repr()
|
||||
|
||||
pe_comp.pe_print(f" --> Fusion {nom}")
|
||||
|
||||
trajectoire_tagguee = TrajectoireTag(nom, trajectoire, semestres_taggues)
|
||||
"""Trajectoire_tagguee associée"""
|
||||
|
||||
"""Mémorise le résultat"""
|
||||
pe_affichage.pe_print(f" --> Aggrégat {nom}")
|
||||
# Trajectoire_tagguee associée
|
||||
trajectoire_tagguee = TrajectoireTag(trajectoire, semestres_taggues)
|
||||
# Mémorise le résultat
|
||||
trajectoires_tagguees[trajectoire_id] = trajectoire_tagguee
|
||||
|
||||
return trajectoires_tagguees
|
||||
@ -473,11 +438,9 @@ def compute_interclassements(
|
||||
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}")
|
||||
pe_affichage.pe_print(f" --> Interclassement {nom_aggregat}")
|
||||
interclass = AggregatInterclasseTag(
|
||||
nom_aggregat, etudiants, trajectoires_jury_pe, trajectoires_tagguees
|
||||
)
|
||||
|
@ -45,63 +45,63 @@ from app.models.moduleimpls import ModuleImpl
|
||||
|
||||
from app.scodoc import sco_tag_module
|
||||
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)
|
||||
import app.pe.pe_affichage as pe_affichage
|
||||
from app.pe.pe_tabletags import TableTag, TAGS_RESERVES
|
||||
import pandas as pd
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Fonctions d'initialisation
|
||||
# -----------------------------------------------------------------------------
|
||||
def __init__(self, nom: str, 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:
|
||||
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, nom=nom)
|
||||
TableTag.__init__(self)
|
||||
|
||||
"""Le semestre"""
|
||||
# Le semestre
|
||||
self.formsemestre_id = formsemestre_id
|
||||
self.formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
|
||||
"""Les résultats du semestre"""
|
||||
# Le nom du semestre taggué
|
||||
self.nom = self.get_repr()
|
||||
|
||||
# Les résultats du semestre
|
||||
self.nt = load_formsemestre_results(self.formsemestre)
|
||||
|
||||
"""Les étudiants"""
|
||||
# Les étudiants
|
||||
self.etuds = self.nt.etuds
|
||||
self.etudiants = {etud.etudid: etud.etat_civil for etud in self.etuds}
|
||||
|
||||
"""Les notes, les modules implémentés triés, les étudiants, les coeffs,
|
||||
récupérés notamment de py:mod:`res_but`
|
||||
"""
|
||||
# Les notes, les modules implémentés triés, les étudiants, les coeffs,
|
||||
# récupérés notamment de py:mod:`res_but`
|
||||
self.sem_cube = self.nt.sem_cube
|
||||
self.modimpls_sorted = self.nt.formsemestre.modimpls_sorted
|
||||
self.modimpl_coefs_df = self.nt.modimpl_coefs_df
|
||||
|
||||
"""Les inscriptions au module et les dispenses d'UE"""
|
||||
# Les inscriptions au module et les dispenses d'UE
|
||||
self.modimpl_inscr_df = self.nt.modimpl_inscr_df
|
||||
self.ues = self.nt.ues
|
||||
self.ues_inscr_parcours_df = self.nt.load_ues_inscr_parcours()
|
||||
self.dispense_ues = self.nt.dispense_ues
|
||||
|
||||
"""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)
|
||||
for tag in TAGS_RESERVES:
|
||||
if tag in self.tags:
|
||||
del self.tags[tag]
|
||||
|
||||
"""Calcul des moyennes & les classements de chaque étudiant à chaque tag"""
|
||||
# Calcul des moyennes & les classements de chaque étudiant à chaque tag
|
||||
self.moyennes_tags = {}
|
||||
|
||||
for tag in self.tags:
|
||||
pe_comp.pe_print(f" -> Traitement du tag {tag}")
|
||||
# pe_affichage.pe_print(f" -> Traitement du 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] = {
|
||||
"notes": moy_gen_tag,
|
||||
"classements": class_gen_tag,
|
||||
@ -111,8 +111,7 @@ class SemestreTag(TableTag):
|
||||
"nb_inscrits": len(moy_gen_tag),
|
||||
}
|
||||
|
||||
"""Ajoute les moyennes générales de BUT pour le semestre considéré"""
|
||||
pe_comp.pe_print(f" -> Traitement du tag but")
|
||||
# Ajoute les moyennes générales de BUT pour le semestre considéré
|
||||
moy_gen_but = self.nt.etud_moy_gen
|
||||
class_gen_but = self.nt.etud_moy_gen_ranks_int
|
||||
self.moyennes_tags["but"] = {
|
||||
@ -124,16 +123,18 @@ class SemestreTag(TableTag):
|
||||
"nb_inscrits": len(moy_gen_but),
|
||||
}
|
||||
|
||||
"""Synthétise l'ensemble des moyennes dans un dataframe"""
|
||||
self.tags_sorted = sorted(self.moyennes_tags) # les tags par ordre alphabétique
|
||||
self.notes = self.df_tagtable() # Le dataframe synthétique des notes (=moyennes par tag)
|
||||
# Synthétise l'ensemble des moyennes dans un dataframe
|
||||
self.tags_sorted = sorted(self.moyennes_tags) # les tags par ordre alphabétique
|
||||
self.notes = (
|
||||
self.df_notes()
|
||||
) # Le dataframe synthétique des notes (=moyennes par tag)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
def get_etudids(self):
|
||||
"""Renvoie la liste des etud_id des étudiants inscrits au semestre"""
|
||||
return [etud["etudid"] for etud in self.inscrlist]
|
||||
pe_affichage.pe_print(f" => Traitement des tags {', '.join(self.tags_sorted)}")
|
||||
|
||||
def get_repr(self):
|
||||
"""Nom affiché pour le semestre taggué"""
|
||||
return pe_affichage.nom_semestre_etape(self.formsemestre, avec_fid=True)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
def compute_moyenne_tag(self, tag: str) -> list:
|
||||
"""Calcule la moyenne des étudiants pour le tag indiqué,
|
||||
pour ce SemestreTag.
|
||||
@ -192,14 +193,22 @@ class SemestreTag(TableTag):
|
||||
|
||||
return moy_gen_tag
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
def get_noteEtCoeff_modimpl(self, modimpl_id, etudid, profondeur=2):
|
||||
"""Renvoie un couple donnant la note et le coeff normalisé d'un étudiant à un module d'id modimpl_id.
|
||||
La note et le coeff sont extraits :
|
||||
1) soit des données du semestre en normalisant le coefficient par rapport à la somme des coefficients des modules du semestre,
|
||||
2) soit des données des UE précédemment capitalisées, en recherchant un module de même CODE que le modimpl_id proposé,
|
||||
le coefficient normalisé l'étant alors par rapport au total des coefficients du semestre auquel appartient l'ue capitalisée
|
||||
|
||||
TODO:: A rependre si nécessaire
|
||||
"""
|
||||
|
||||
def get_ue_capitalisees(etudid) -> list[dict]:
|
||||
"""Renvoie la liste des capitalisation effectivement capitalisées par un étudiant"""
|
||||
if etudid in self.nt.validations.ue_capitalisees.index:
|
||||
return self.nt.validations.ue_capitalisees.loc[[etudid]].to_dict("records")
|
||||
return []
|
||||
|
||||
(note, coeff_norm) = (None, None)
|
||||
|
||||
modimpl = get_moduleimpl(modimpl_id) # Le module considéré
|
||||
@ -207,7 +216,7 @@ class SemestreTag(TableTag):
|
||||
return (None, None)
|
||||
|
||||
# Y-a-t-il eu capitalisation d'UE ?
|
||||
ue_capitalisees = self.get_ue_capitalisees(
|
||||
ue_capitalisees = get_ue_capitalisees(
|
||||
etudid
|
||||
) # les ue capitalisées des étudiants
|
||||
ue_capitalisees_id = {
|
||||
@ -273,140 +282,7 @@ class SemestreTag(TableTag):
|
||||
# Sinon - pas de notes à prendre en compte
|
||||
return (note, coeff_norm)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
def get_ue_capitalisees(self, etudid) -> list[dict]:
|
||||
"""Renvoie la liste des capitalisation effectivement capitalisées par un étudiant"""
|
||||
if etudid in self.nt.validations.ue_capitalisees.index:
|
||||
return self.nt.validations.ue_capitalisees.loc[[etudid]].to_dict("records")
|
||||
return []
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Fonctions d'affichage (et d'export csv) des données du semestre en mode debug
|
||||
# -----------------------------------------------------------------------------
|
||||
def str_detail_resultat_d_un_tag(self, tag, etudid=None, delim=";"):
|
||||
"""Renvoie une chaine de caractère décrivant les résultats d'étudiants à un tag :
|
||||
rappelle les notes obtenues dans les modules à prendre en compte, les moyennes et les rangs calculés.
|
||||
Si etudid=None, tous les étudiants inscrits dans le semestre sont pris en compte. Sinon seuls les étudiants indiqués sont affichés.
|
||||
"""
|
||||
# Entete
|
||||
chaine = delim.join(["%15s" % "nom", "etudid"]) + delim
|
||||
taglist = self.get_all_tags()
|
||||
if tag in taglist:
|
||||
for mod in self.tagdict[tag].values():
|
||||
chaine += mod["module_code"] + delim
|
||||
chaine += ("%1.1f" % mod["ponderation"]) + delim
|
||||
chaine += "coeff" + delim
|
||||
chaine += delim.join(
|
||||
["moyenne", "rang", "nbinscrit", "somme_coeff", "somme_coeff"]
|
||||
) # ligne 1
|
||||
chaine += "\n"
|
||||
|
||||
# Différents cas de boucles sur les étudiants (de 1 à plusieurs)
|
||||
if etudid == None:
|
||||
lesEtuds = self.get_etudids()
|
||||
elif isinstance(etudid, str) and etudid in self.get_etudids():
|
||||
lesEtuds = [etudid]
|
||||
elif isinstance(etudid, list):
|
||||
lesEtuds = [eid for eid in self.get_etudids() if eid in etudid]
|
||||
else:
|
||||
lesEtuds = []
|
||||
|
||||
for etudid in lesEtuds:
|
||||
descr = (
|
||||
"%15s" % self.nt.get_nom_short(etudid)[:15]
|
||||
+ delim
|
||||
+ str(etudid)
|
||||
+ delim
|
||||
)
|
||||
if tag in taglist:
|
||||
for modimpl_id in self.tagdict[tag]:
|
||||
(note, coeff) = self.get_noteEtCoeff_modimpl(modimpl_id, etudid)
|
||||
descr += (
|
||||
(
|
||||
"%2.2f" % note
|
||||
if note != None and isinstance(note, float)
|
||||
else str(note)
|
||||
)
|
||||
+ delim
|
||||
+ (
|
||||
"%1.5f" % coeff
|
||||
if coeff != None and isinstance(coeff, float)
|
||||
else str(coeff)
|
||||
)
|
||||
+ delim
|
||||
+ (
|
||||
"%1.5f" % (coeff * self.somme_coeffs)
|
||||
if coeff != None and isinstance(coeff, float)
|
||||
else "???" # str(coeff * self._sum_coeff_semestre) # voir avec Cléo
|
||||
)
|
||||
+ delim
|
||||
)
|
||||
moy = self.get_moy_from_resultats(tag, etudid)
|
||||
rang = self.get_rang_from_resultats(tag, etudid)
|
||||
coeff = self.get_coeff_from_resultats(tag, etudid)
|
||||
tot = (
|
||||
coeff * self.somme_coeffs
|
||||
if coeff != None
|
||||
and self.somme_coeffs != None
|
||||
and isinstance(coeff, float)
|
||||
else None
|
||||
)
|
||||
descr += (
|
||||
pe_tagtable.TableTag.str_moytag(
|
||||
moy, rang, len(self.get_etudids()), delim=delim
|
||||
)
|
||||
+ delim
|
||||
)
|
||||
descr += (
|
||||
(
|
||||
"%1.5f" % coeff
|
||||
if coeff != None and isinstance(coeff, float)
|
||||
else str(coeff)
|
||||
)
|
||||
+ delim
|
||||
+ (
|
||||
"%.2f" % (tot)
|
||||
if tot != None
|
||||
else str(coeff) + "*" + str(self.somme_coeffs)
|
||||
)
|
||||
)
|
||||
chaine += descr
|
||||
chaine += "\n"
|
||||
return chaine
|
||||
|
||||
def str_tagsModulesEtCoeffs(self):
|
||||
"""Renvoie une chaine affichant la liste des tags associés au semestre,
|
||||
les modules qui les concernent et les coeffs de pondération.
|
||||
Plus concrètement permet d'afficher le contenu de self._tagdict"""
|
||||
chaine = "Semestre %s d'id %d" % (self.nom, id(self)) + "\n"
|
||||
chaine += " -> somme de coeffs: " + str(self.somme_coeffs) + "\n"
|
||||
taglist = self.get_all_tags()
|
||||
for tag in taglist:
|
||||
chaine += " > " + tag + ": "
|
||||
for modid, mod in self.tagdict[tag].items():
|
||||
chaine += (
|
||||
mod["module_code"]
|
||||
+ " ("
|
||||
+ str(mod["coeff"])
|
||||
+ "*"
|
||||
+ str(mod["ponderation"])
|
||||
+ ") "
|
||||
+ str(modid)
|
||||
+ ", "
|
||||
)
|
||||
chaine += "\n"
|
||||
return chaine
|
||||
|
||||
|
||||
# ************************************************************************
|
||||
# Fonctions diverses
|
||||
# ************************************************************************
|
||||
|
||||
|
||||
# *********************************************
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
def get_moduleimpl(modimpl_id) -> dict:
|
||||
"""Renvoie l'objet modimpl dont l'id est modimpl_id"""
|
||||
modimpl = db.session.get(ModuleImpl, modimpl_id)
|
||||
@ -420,7 +296,6 @@ def get_moduleimpl(modimpl_id) -> dict:
|
||||
return None
|
||||
|
||||
|
||||
# **********************************************
|
||||
def get_moy_ue_from_nt(nt, etudid, modimpl_id) -> float:
|
||||
"""Renvoie la moyenne de l'UE d'un etudid dans laquelle se trouve
|
||||
le module de modimpl_id
|
||||
@ -437,8 +312,8 @@ def get_moy_ue_from_nt(nt, etudid, modimpl_id) -> float:
|
||||
def get_synthese_tags_semestre(formsemestre: FormSemestre):
|
||||
"""Etant données les implémentations des modules du semestre (modimpls),
|
||||
synthétise les tags les concernant (tags saisis dans le programme pédagogique)
|
||||
en les associant aux modimpls qui les concernent (modimpl_id, module_id,
|
||||
le code du module, coeff et pondération fournie avec le tag (par défaut 1 si non indiquée)).
|
||||
en les associant aux modimpls qui les concernent (modimpl_id) et
|
||||
aucoeff et pondération fournie avec le tag (par défaut 1 si non indiquée)).
|
||||
|
||||
{ tagname1: { modimpl_id1: { 'module_id': ...,
|
||||
'coeff': ...,
|
||||
@ -451,6 +326,9 @@ def get_synthese_tags_semestre(formsemestre: FormSemestre):
|
||||
|
||||
Args:
|
||||
formsemestre: Le formsemestre à la base de la recherche des tags
|
||||
|
||||
Return:
|
||||
Un dictionnaire de tags
|
||||
"""
|
||||
synthese_tags = {}
|
||||
|
||||
|
@ -48,38 +48,13 @@ TAGS_RESERVES = ["but"]
|
||||
|
||||
|
||||
class TableTag(object):
|
||||
"""
|
||||
Classe mémorisant les moyennes des étudiants à différents tags et permettant de
|
||||
calculer des rangs et des statistiques.
|
||||
|
||||
Ses attributs sont:
|
||||
|
||||
* nom : Nom représentatif des données de la Table
|
||||
* inscrlist : Les étudiants inscrits dans le TagTag avec leur information de la forme :
|
||||
{ etudid : dictionnaire d'info extrait de Scodoc, ...}
|
||||
* taglist : Liste triée des noms des tags
|
||||
* resultats : Dictionnaire donnant les notes-moyennes de chaque étudiant par tag et la somme commulée
|
||||
des coeff utilisées dans le calcul de la moyenne pondérée, sous la forme :
|
||||
{ tag : { etudid: (note_moy, somme_coeff_norm),
|
||||
...}
|
||||
* rangs : Dictionnaire donnant les rang par tag de chaque étudiant de la forme :
|
||||
{ tag : {etudid: rang, ...} }
|
||||
* nbinscrits : Nombre d'inscrits dans le semestre (pas de distinction entre les tags)
|
||||
* statistiques : Dictionnaire donnant les statistiques (moyenne, min, max) des résultats par tag de la forme :
|
||||
{ tag : (moy, min, max), ...}
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, nom: str):
|
||||
"""Les attributs basiques des TagTable, qui seront initialisés
|
||||
dans les classes dérivées
|
||||
def __init__(self):
|
||||
"""Classe centralisant différentes méthodes communes aux
|
||||
SemestreTag, TrajectoireTag, AggregatInterclassTag
|
||||
"""
|
||||
self.nom = nom
|
||||
"""Les étudiants"""
|
||||
self.etudiants = {}
|
||||
"""Les moyennes par tag"""
|
||||
self.moyennes_tags = {}
|
||||
|
||||
pass
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
def get_all_tags(self):
|
||||
@ -90,190 +65,21 @@ class TableTag(object):
|
||||
"""
|
||||
return sorted(self.moyennes_tags.keys())
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# Accesseurs
|
||||
# *****************************************************************************************************************
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
def get_moy_from_resultats(self, tag, etudid):
|
||||
"""Renvoie la moyenne obtenue par un étudiant à un tag donné au regard du format de self.resultats"""
|
||||
return (
|
||||
self.moyennes_tags[tag][etudid][0]
|
||||
if tag in self.moyennes_tags and etudid in self.moyennes_tags[tag]
|
||||
else None
|
||||
)
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
def get_rang_from_resultats(self, tag, etudid):
|
||||
"""Renvoie le rang à un tag d'un étudiant au regard du format de self.resultats"""
|
||||
return (
|
||||
self.rangs[tag][etudid]
|
||||
if tag in self.moyennes_tags and etudid in self.moyennes_tags[tag]
|
||||
else None
|
||||
)
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
def get_coeff_from_resultats(self, tag, etudid):
|
||||
"""Renvoie la somme des coeffs de pondération normalisée utilisés dans le calcul de la moyenne à un tag d'un étudiant
|
||||
au regard du format de self.resultats.
|
||||
def df_moyennes_et_classements(self):
|
||||
"""Renvoie un dataframe listant toutes les moyennes,
|
||||
et les classements des étudiants pour tous les tags
|
||||
"""
|
||||
return (
|
||||
self.moyennes_tags[tag][etudid][1]
|
||||
if tag in self.moyennes_tags and etudid in self.moyennes_tags[tag]
|
||||
else None
|
||||
)
|
||||
|
||||
etudiants = self.etudiants
|
||||
df = pd.DataFrame.from_dict(etudiants, orient="index", columns=["nom"])
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
def get_nbinscrits(self):
|
||||
"""Renvoie le nombre d'inscrits"""
|
||||
return len(self.inscrlist)
|
||||
for tag in self.get_all_tags():
|
||||
df = df.join(self.moyennes_tags[tag]["notes"].rename(f"Moy {tag}"))
|
||||
df = df.join(self.moyennes_tags[tag]["classements"].rename(f"Class {tag}"))
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
def get_moy_from_stats(self, tag):
|
||||
"""Renvoie la moyenne des notes calculées pour d'un tag donné"""
|
||||
return self.statistiques[tag][0] if tag in self.statistiques else None
|
||||
return df
|
||||
|
||||
def get_min_from_stats(self, tag):
|
||||
"""Renvoie la plus basse des notes calculées pour d'un tag donné"""
|
||||
return self.statistiques[tag][1] if tag in self.statistiques else None
|
||||
|
||||
def get_max_from_stats(self, tag):
|
||||
"""Renvoie la plus haute des notes calculées pour d'un tag donné"""
|
||||
return self.statistiques[tag][2] if tag in self.statistiques else None
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
# La structure des données mémorisées pour chaque tag dans le dictionnaire de synthèse
|
||||
# d'un jury PE
|
||||
FORMAT_DONNEES_ETUDIANTS = (
|
||||
"note",
|
||||
"coeff",
|
||||
"rang",
|
||||
"nbinscrits",
|
||||
"moy",
|
||||
"max",
|
||||
"min",
|
||||
)
|
||||
|
||||
def get_resultatsEtud(self, tag, etudid):
|
||||
"""Renvoie un tuple (note, coeff, rang, nb_inscrit, moy, min, max) synthétisant les résultats d'un étudiant
|
||||
à un tag donné. None sinon"""
|
||||
return (
|
||||
self.get_moy_from_resultats(tag, etudid),
|
||||
self.get_coeff_from_resultats(tag, etudid),
|
||||
self.get_rang_from_resultats(tag, etudid),
|
||||
self.get_nbinscrits(),
|
||||
self.get_moy_from_stats(tag),
|
||||
self.get_min_from_stats(tag),
|
||||
self.get_max_from_stats(tag),
|
||||
)
|
||||
|
||||
# return self.tag_stats[tag]
|
||||
# else :
|
||||
# return self.pe_stats
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# Ajout des notes
|
||||
# *****************************************************************************************************************
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
def add_moyennesTag(self, tag, listMoyEtCoeff) -> bool:
|
||||
"""
|
||||
Mémorise les moyennes, les coeffs de pondération et les etudid dans resultats
|
||||
avec calcul du rang
|
||||
:param tag: Un tag
|
||||
:param listMoyEtCoeff: Une liste donnant [ (moy, coeff, etudid) ]
|
||||
|
||||
TODO:: Inutile maintenant ?
|
||||
"""
|
||||
# ajout des moyennes au dictionnaire résultat
|
||||
if listMoyEtCoeff:
|
||||
self.moyennes_tags[tag] = {
|
||||
etudid: (moyenne, somme_coeffs)
|
||||
for (moyenne, somme_coeffs, etudid) in listMoyEtCoeff
|
||||
}
|
||||
|
||||
# Calcule les rangs
|
||||
lesMoyennesTriees = sorted(
|
||||
listMoyEtCoeff,
|
||||
reverse=True,
|
||||
key=lambda col: col[0]
|
||||
if isinstance(col[0], float)
|
||||
else 0, # remplace les None et autres chaines par des zéros
|
||||
) # triées
|
||||
self.rangs[tag] = scu.comp_ranks(lesMoyennesTriees) # les rangs
|
||||
|
||||
# calcul des stats
|
||||
self.comp_stats_d_un_tag(tag)
|
||||
return True
|
||||
return False
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# Méthodes dévolues aux calculs de statistiques (min, max, moy) sur chaque moyenne taguée
|
||||
# *****************************************************************************************************************
|
||||
|
||||
def comp_stats_d_un_tag(self, tag):
|
||||
"""
|
||||
Calcule la moyenne generale, le min, le max pour un tag donné,
|
||||
en ne prenant en compte que les moyennes significatives. Mémorise le resultat dans
|
||||
self.statistiques
|
||||
"""
|
||||
stats = ("-NA-", "-", "-")
|
||||
if tag not in self.moyennes_tags:
|
||||
return stats
|
||||
|
||||
notes = [
|
||||
self.get_moy_from_resultats(tag, etudid)
|
||||
for etudid in self.moyennes_tags[tag]
|
||||
] # les notes du tag
|
||||
notes_valides = [
|
||||
note for note in notes if isinstance(note, float) and note != None
|
||||
]
|
||||
nb_notes_valides = len(notes_valides)
|
||||
if nb_notes_valides > 0:
|
||||
(moy, _) = moyenne_ponderee_terme_a_terme(notes_valides, force=True)
|
||||
self.statistiques[tag] = (moy, max(notes_valides), min(notes_valides))
|
||||
|
||||
# ************************************************************************
|
||||
# Méthodes dévolues aux affichages -> a revoir
|
||||
# ************************************************************************
|
||||
def str_resTag_d_un_etudiant(self, tag, etudid, delim=";"):
|
||||
"""Renvoie une chaine de caractères (valable pour un csv)
|
||||
décrivant la moyenne et le rang d'un étudiant, pour un tag donné ;
|
||||
"""
|
||||
if tag not in self.get_all_tags() or etudid not in self.moyennes_tags[tag]:
|
||||
return ""
|
||||
|
||||
moystr = TableTag.str_moytag(
|
||||
self.get_moy_from_resultats(tag, etudid),
|
||||
self.get_rang_from_resultats(tag, etudid),
|
||||
self.get_nbinscrits(),
|
||||
delim=delim,
|
||||
)
|
||||
return moystr
|
||||
|
||||
def str_res_d_un_etudiant(self, etudid, delim=";"):
|
||||
"""Renvoie sur une ligne les résultats d'un étudiant à tous les tags (par ordre alphabétique)."""
|
||||
return delim.join(
|
||||
[self.str_resTag_d_un_etudiant(tag, etudid) for tag in self.get_all_tags()]
|
||||
)
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
def str_moytag(cls, moyenne, rang, nbinscrit, delim=";"):
|
||||
"""Renvoie une chaine de caractères représentant une moyenne (float ou string) et un rang
|
||||
pour différents formats d'affichage : HTML, debug ligne de commande, csv"""
|
||||
moystr = (
|
||||
"%2.2f%s%s%s%d" % (moyenne, delim, rang, delim, nbinscrit)
|
||||
if isinstance(moyenne, float)
|
||||
else str(moyenne) + delim + str(rang) + delim + str(nbinscrit)
|
||||
)
|
||||
return moystr
|
||||
|
||||
str_moytag = classmethod(str_moytag)
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
def df_tagtable(self):
|
||||
def df_notes(self):
|
||||
"""Renvoie un dataframe (etudid x tag) listant toutes les moyennes par tags
|
||||
|
||||
Returns:
|
||||
@ -299,69 +105,3 @@ class TableTag(object):
|
||||
df = df.join(self.moyennes_tags[tag]["classements"].rename(f"class {tag}"))
|
||||
|
||||
return df.to_csv(sep=";")
|
||||
|
||||
|
||||
# ************************************************************************
|
||||
# Fonctions diverses
|
||||
# ************************************************************************
|
||||
|
||||
|
||||
# *********************************************
|
||||
def moyenne_ponderee_terme_a_terme(notes, coefs=None, force=False):
|
||||
"""
|
||||
Calcule la moyenne pondérée d'une liste de notes avec d'éventuels coeffs de pondération.
|
||||
Renvoie le résultat sous forme d'un tuple (moy, somme_coeff)
|
||||
|
||||
La liste de notes contient soit :
|
||||
1) des valeurs numériques
|
||||
2) des strings "-NA-" (pas de notes) ou "-NI-" (pas inscrit) ou "-c-" ue capitalisée,
|
||||
3) None.
|
||||
|
||||
Le paramètre force indique si le calcul de la moyenne doit être forcée ou non, c'est à
|
||||
dire s'il y a ou non omission des notes non numériques (auquel cas la moyenne est
|
||||
calculée sur les notes disponibles) ; sinon renvoie (None, None).
|
||||
"""
|
||||
# Vérification des paramètres d'entrée
|
||||
if not isinstance(notes, list) or (
|
||||
coefs != None and not isinstance(coefs, list) and len(coefs) != len(notes)
|
||||
):
|
||||
raise ValueError("Erreur de paramètres dans moyenne_ponderee_terme_a_terme")
|
||||
|
||||
# Récupération des valeurs des paramètres d'entrée
|
||||
coefs = [1] * len(notes) if coefs is None else coefs
|
||||
|
||||
# S'il n'y a pas de notes
|
||||
if not notes: # Si notes = []
|
||||
return (None, None)
|
||||
|
||||
# Liste indiquant les notes valides
|
||||
notes_valides = [
|
||||
(isinstance(note, float) and not np.isnan(note)) or isinstance(note, int)
|
||||
for note in notes
|
||||
]
|
||||
# Si on force le calcul de la moyenne ou qu'on ne le force pas
|
||||
# et qu'on a le bon nombre de notes
|
||||
if force or sum(notes_valides) == len(notes):
|
||||
moyenne, ponderation = 0.0, 0.0
|
||||
for i in range(len(notes)):
|
||||
if notes_valides[i]:
|
||||
moyenne += coefs[i] * notes[i]
|
||||
ponderation += coefs[i]
|
||||
return (
|
||||
(moyenne / (ponderation * 1.0), ponderation)
|
||||
if ponderation != 0
|
||||
else (None, 0)
|
||||
)
|
||||
# Si on ne force pas le calcul de la moyenne
|
||||
return (None, None)
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------------------
|
||||
def conversionDate_StrToDate(date_fin):
|
||||
"""Conversion d'une date fournie sous la forme d'une chaine de caractère de
|
||||
type 'jj/mm/aaaa' en un objet date du package datetime.
|
||||
Fonction servant au tri des semestres par date
|
||||
"""
|
||||
(d, m, y) = [int(x) for x in date_fin.split("/")]
|
||||
date_fin_dst = datetime.date(y, m, d)
|
||||
return date_fin_dst
|
||||
|
@ -1,4 +1,6 @@
|
||||
import app.pe.pe_comp as pe_comp
|
||||
import app.pe.pe_affichage as pe_affichage
|
||||
|
||||
from app.models import FormSemestre
|
||||
from app.pe.pe_etudiant import EtudiantsJuryPE, get_dernier_semestre_en_date
|
||||
|
||||
@ -36,8 +38,7 @@ class Trajectoire:
|
||||
"""Les semestres à aggréger"""
|
||||
self.semestres_aggreges = {}
|
||||
|
||||
|
||||
def add_semestres_a_aggreger(self, semestres: dict[int: FormSemestre]):
|
||||
def add_semestres_a_aggreger(self, semestres: dict[int:FormSemestre]):
|
||||
"""Ajoute des semestres au semestre à aggréger
|
||||
|
||||
Args:
|
||||
@ -45,18 +46,17 @@ class Trajectoire:
|
||||
"""
|
||||
self.semestres_aggreges = self.semestres_aggreges | semestres
|
||||
|
||||
|
||||
|
||||
def get_repr(self):
|
||||
def get_repr(self, verbose=True) -> str:
|
||||
"""Représentation textuelle d'une trajectoire
|
||||
basée sur ses semestres aggrégés"""
|
||||
|
||||
noms = []
|
||||
for fid in self.semestres_aggreges:
|
||||
semestre = self.semestres_aggreges[fid]
|
||||
noms.append(f"S{semestre.semestre_id}({fid})")
|
||||
noms = sorted(noms)
|
||||
repr = f"{self.nom} ({self.semestre_final.formsemestre_id}) {self.semestre_final.date_fin.year}"
|
||||
if noms:
|
||||
if verbose and noms:
|
||||
repr += " - " + "+".join(noms)
|
||||
return repr
|
||||
|
||||
@ -72,11 +72,10 @@ class TrajectoiresJuryPE:
|
||||
|
||||
self.annee_diplome = annee_diplome
|
||||
"""Toutes les trajectoires possibles"""
|
||||
self.trajectoires: dict[tuple: Trajectoire] = {}
|
||||
self.trajectoires: dict[tuple:Trajectoire] = {}
|
||||
"""Quelle trajectoires pour quel étudiant :
|
||||
dictionnaire {etudid: {nom_aggregat: Trajectoire}}"""
|
||||
self.suivi: dict[int: str] = {}
|
||||
|
||||
self.suivi: dict[int:str] = {}
|
||||
|
||||
def cree_trajectoires(self, etudiants: EtudiantsJuryPE):
|
||||
"""Créé toutes les trajectoires, au regard du cursus des étudiants
|
||||
@ -84,15 +83,17 @@ class TrajectoiresJuryPE:
|
||||
"""
|
||||
|
||||
for nom_aggregat in pe_comp.TOUS_LES_SEMESTRES + pe_comp.TOUS_LES_AGGREGATS:
|
||||
|
||||
"""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 = pe_comp.PARCOURS[nom_aggregat]["aggregat"]
|
||||
nom_semestre_terminal = noms_semestre_de_aggregat[-1]
|
||||
|
||||
for etudid in etudiants.cursus:
|
||||
if etudid not in self.suivi:
|
||||
self.suivi[etudid] = {aggregat: None
|
||||
for aggregat in pe_comp.TOUS_LES_SEMESTRES + pe_comp.TOUS_LES_AGGREGATS}
|
||||
self.suivi[etudid] = {
|
||||
aggregat: None
|
||||
for aggregat in pe_comp.TOUS_LES_SEMESTRES
|
||||
+ pe_comp.TOUS_LES_AGGREGATS
|
||||
}
|
||||
|
||||
"""Le formsemestre terminal (dernier en date) associé au
|
||||
semestre marquant la fin de l'aggrégat
|
||||
@ -111,7 +112,9 @@ class TrajectoiresJuryPE:
|
||||
|
||||
"""La liste des semestres de l'étudiant à prendre en compte
|
||||
pour cette trajectoire"""
|
||||
semestres_a_aggreger = etudiants.get_trajectoire(etudid, formsemestre_final)
|
||||
semestres_a_aggreger = etudiants.get_trajectoire(
|
||||
etudid, formsemestre_final, nom_aggregat
|
||||
)
|
||||
|
||||
"""Ajout des semestres à la trajectoire"""
|
||||
trajectoire.add_semestres_a_aggreger(semestres_a_aggreger)
|
||||
@ -129,7 +132,7 @@ def get_trajectoires_etudid(trajectoires, etudid):
|
||||
trajectoires suivies par un étudiant
|
||||
"""
|
||||
if etudid not in trajectoires.suivi:
|
||||
pe_comp.pe_print(f"{etudid} fait-il bien partie du jury ?")
|
||||
pe_affichage.pe_print(f"{etudid} fait-il bien partie du jury ?")
|
||||
|
||||
liste = []
|
||||
for aggregat in pe_comp.TOUS_LES_PARCOURS:
|
||||
@ -138,7 +141,7 @@ def get_trajectoires_etudid(trajectoires, etudid):
|
||||
liste.append(trajet.trajectoire_id)
|
||||
return liste
|
||||
|
||||
|
||||
|
||||
def get_semestres_a_aggreger(self, aggregat: str, formsemestre_id_terminal: int):
|
||||
"""Pour un nom d'aggrégat donné (par ex: 'S3') et un semestre terminal cible
|
||||
identifié par son formsemestre_id (par ex: 'S3 2022-2023'),
|
||||
@ -162,4 +165,3 @@ def get_semestres_a_aggreger(self, aggregat: str, formsemestre_id_terminal: int)
|
||||
formsemestres_etudiant = cursus_etudiant[formsemestre_id_terminal]
|
||||
formsemestres = formsemestres | formsemestres_etudiant
|
||||
return formsemestres
|
||||
|
||||
|
@ -39,40 +39,38 @@ Created on Fri Sep 9 09:15:05 2016
|
||||
from app.comp import moy_sem
|
||||
from app.comp.res_sem import load_formsemestre_results
|
||||
from app.pe.pe_semtag import SemestreTag
|
||||
from app.pe import pe_tabletags
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from app.pe.pe_trajectoire import Trajectoire
|
||||
|
||||
from app.pe.pe_etudiant import EtudiantsJuryPE
|
||||
from app.pe.pe_tabletags import TableTag
|
||||
|
||||
|
||||
class TrajectoireTag(TableTag):
|
||||
"""Calcule les moyennes par tag d'une combinaison de semestres
|
||||
(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
|
||||
groupe d'étudiants donnés. Le groupe d'étudiants est formé par ceux ayant tous
|
||||
participé au semestre terminal.
|
||||
|
||||
Par ex: fusion d'un parcours ['S1', 'S2', 'S3'] donnant un nom_combinaison = '3S'
|
||||
|
||||
"""
|
||||
|
||||
# -------------------------------------------------------------------------------------------------------------------
|
||||
def __init__(
|
||||
self,
|
||||
nom: str,
|
||||
trajectoire: Trajectoire,
|
||||
semestres_taggues: dict[int, SemestreTag]
|
||||
):
|
||||
""" """
|
||||
TableTag.__init__(self, nom=nom)
|
||||
"""Calcule les moyennes par tag d'une combinaison de semestres
|
||||
(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
|
||||
groupe d'étudiants donnés. Le groupe d'étudiants est formé par ceux ayant tous
|
||||
participé au semestre terminal.
|
||||
|
||||
"""La trajectoire associée"""
|
||||
Par ex: fusion d'un parcours ['S1', 'S2', 'S3'] donnant un nom_combinaison = '3S'
|
||||
|
||||
"""
|
||||
TableTag.__init__(self)
|
||||
|
||||
# La trajectoire associée
|
||||
self.trajectoire_id = trajectoire.trajectoire_id
|
||||
self.trajectoire = trajectoire
|
||||
|
||||
# Le nom de la trajectoire tagguée (identique à la trajectoire)
|
||||
self.nom = self.get_repr()
|
||||
|
||||
"""Le formsemestre terminal et les semestres aggrégés"""
|
||||
self.formsemestre_terminal = trajectoire.semestre_final
|
||||
nt = load_formsemestre_results(self.formsemestre_terminal)
|
||||
@ -116,10 +114,10 @@ class TrajectoireTag(TableTag):
|
||||
"nb_inscrits": len(moy_gen_tag),
|
||||
}
|
||||
|
||||
def get_repr(self):
|
||||
def get_repr(self, verbose=False) -> str:
|
||||
"""Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle
|
||||
est basée)"""
|
||||
return self.trajectoire.get_repr()
|
||||
return self.trajectoire.get_repr(verbose=verbose)
|
||||
|
||||
def compute_notes_cube(self):
|
||||
"""Construit le cube de notes (etudid x tags x semestre_aggregé)
|
||||
@ -152,6 +150,10 @@ class TrajectoireTag(TableTag):
|
||||
etudids_communs, tags_communs
|
||||
]
|
||||
|
||||
# Supprime tout ce qui n'est pas numérique
|
||||
for col in df.columns:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
|
||||
"""Stocke le df"""
|
||||
dfs[frmsem_id] = df
|
||||
|
||||
@ -161,8 +163,6 @@ class TrajectoireTag(TableTag):
|
||||
|
||||
return etudids_x_tags_x_semestres
|
||||
|
||||
|
||||
|
||||
def do_taglist(self):
|
||||
"""Synthétise les tags à partir des semestres (taggués) aggrégés
|
||||
|
||||
@ -175,6 +175,8 @@ class TrajectoireTag(TableTag):
|
||||
return sorted(set(tags))
|
||||
|
||||
|
||||
|
||||
|
||||
def compute_tag_moy(set_cube: np.array, etudids: list, tags: list):
|
||||
"""Calcul de la moyenne par tag sur plusieurs semestres.
|
||||
La moyenne est un nombre (note/20), ou NaN si pas de notes disponibles
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.6.86"
|
||||
SCOVERSION = "9.6.87"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user