forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -100,7 +100,7 @@ def compute_sem_moys_apc_using_ects(
|
|||||||
|
|
||||||
|
|
||||||
def comp_ranks_series(notes: pd.Series) -> tuple[pd.Series, pd.Series]:
|
def comp_ranks_series(notes: pd.Series) -> tuple[pd.Series, pd.Series]:
|
||||||
"""Calcul rangs à partir d'une séries ("vecteur") de notes (index etudid, valeur
|
"""Calcul rangs à partir d'une série ("vecteur") de notes (index etudid, valeur
|
||||||
numérique) en tenant compte des ex-aequos.
|
numérique) en tenant compte des ex-aequos.
|
||||||
|
|
||||||
Result: couple (tuple)
|
Result: couple (tuple)
|
||||||
|
@ -273,7 +273,7 @@ class ResultatsSemestreBUT(NotesTableCompat):
|
|||||||
return s.index[s.notna()]
|
return s.index[s.notna()]
|
||||||
|
|
||||||
def etud_parcours_ues_ids(self, etudid: int) -> set[int]:
|
def etud_parcours_ues_ids(self, etudid: int) -> set[int]:
|
||||||
"""Ensemble les id des UEs que l'étudiant doit valider dans ce semestre compte tenu
|
"""Ensemble des id des UEs que l'étudiant doit valider dans ce semestre compte tenu
|
||||||
du parcours dans lequel il est inscrit.
|
du parcours dans lequel il est inscrit.
|
||||||
Se base sur le parcours dans ce semestre, et le référentiel de compétences.
|
Se base sur le parcours dans ce semestre, et le référentiel de compétences.
|
||||||
Note: il n'est pas nécessairement inscrit à toutes ces UEs.
|
Note: il n'est pas nécessairement inscrit à toutes ces UEs.
|
||||||
|
@ -85,16 +85,12 @@ class EtudiantsJuryPE:
|
|||||||
self.abandons_ids = {}
|
self.abandons_ids = {}
|
||||||
"""Les etudids des étudiants redoublants/réorientés"""
|
"""Les etudids des étudiants redoublants/réorientés"""
|
||||||
|
|
||||||
def find_etudiants(self, formation_id: int):
|
def find_etudiants(self):
|
||||||
"""Liste des étudiants à prendre en compte dans le jury PE, en les recherchant
|
"""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``
|
de manière automatique par rapport à leur année de diplomation ``annee_diplome``.
|
||||||
dans la formation ``formation_id``. XXX TODO voir si on garde formation_id qui n'est pas utilisé ici
|
|
||||||
|
|
||||||
Les données obtenues sont stockées dans les attributs de EtudiantsJuryPE.
|
Les données obtenues sont stockées dans les attributs de EtudiantsJuryPE.
|
||||||
|
|
||||||
|
|
||||||
formation_id: L'identifiant de la formation (inutilisé)
|
|
||||||
|
|
||||||
*Remarque* : ex: JuryPE.get_etudiants_in_jury()
|
*Remarque* : ex: JuryPE.get_etudiants_in_jury()
|
||||||
"""
|
"""
|
||||||
cosemestres = pe_comp.get_cosemestres_diplomants(self.annee_diplome, None)
|
cosemestres = pe_comp.get_cosemestres_diplomants(self.annee_diplome, None)
|
||||||
|
@ -59,8 +59,9 @@ class AggregatInterclasseTag(TableTag):
|
|||||||
for etudid in self.diplomes_ids:
|
for etudid in self.diplomes_ids:
|
||||||
self.suivi[etudid] = trajectoires_jury_pe.suivi[etudid][nom_aggregat]
|
self.suivi[etudid] = trajectoires_jury_pe.suivi[etudid][nom_aggregat]
|
||||||
|
|
||||||
"""Les tags"""
|
|
||||||
self.tags_sorted = self.do_taglist()
|
self.tags_sorted = self.do_taglist()
|
||||||
|
"""Liste des tags (triés par ordre alphabétique)"""
|
||||||
|
|
||||||
# Construit la matrice de notes
|
# Construit la matrice de notes
|
||||||
self.notes = self.compute_notes_matrice()
|
self.notes = self.compute_notes_matrice()
|
||||||
@ -69,15 +70,7 @@ class AggregatInterclasseTag(TableTag):
|
|||||||
self.moyennes_tags = {}
|
self.moyennes_tags = {}
|
||||||
for tag in self.tags_sorted:
|
for tag in self.tags_sorted:
|
||||||
moy_gen_tag = self.notes[tag]
|
moy_gen_tag = self.notes[tag]
|
||||||
class_gen_tag = moy_sem.comp_ranks_series(moy_gen_tag)[1] # en int
|
self.moyennes_tags[tag] = self.comp_moy_et_stat(moy_gen_tag)
|
||||||
self.moyennes_tags[tag] = {
|
|
||||||
"notes": moy_gen_tag,
|
|
||||||
"classements": class_gen_tag,
|
|
||||||
"min": moy_gen_tag.min(),
|
|
||||||
"max": moy_gen_tag.max(),
|
|
||||||
"moy": moy_gen_tag.mean(),
|
|
||||||
"nb_inscrits": len(moy_gen_tag),
|
|
||||||
}
|
|
||||||
|
|
||||||
# Est significatif ? (aka a-t-il des tags et des notes)
|
# Est significatif ? (aka a-t-il des tags et des notes)
|
||||||
self.significatif = len(self.tags_sorted) > 0
|
self.significatif = len(self.tags_sorted) > 0
|
||||||
@ -125,4 +118,7 @@ class AggregatInterclasseTag(TableTag):
|
|||||||
etudids_communs, tags_communs
|
etudids_communs, tags_communs
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Force les nan
|
||||||
|
df.fillna(np.nan)
|
||||||
|
|
||||||
return df
|
return df
|
||||||
|
@ -83,7 +83,7 @@ class JuryPE(object):
|
|||||||
# leur affichage dans les avis latex
|
# leur affichage dans les avis latex
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------------------------------------------
|
||||||
def __init__(self, diplome, formation_id):
|
def __init__(self, diplome):
|
||||||
"""
|
"""
|
||||||
Création d'une table PE sur la base d'un semestre selectionné. De ce semestre est déduit :
|
Création d'une table PE sur la base d'un semestre selectionné. De ce semestre est déduit :
|
||||||
1. l'année d'obtention du DUT,
|
1. l'année d'obtention du DUT,
|
||||||
@ -98,19 +98,16 @@ class JuryPE(object):
|
|||||||
self.diplome = diplome
|
self.diplome = diplome
|
||||||
"L'année du diplome"
|
"L'année du diplome"
|
||||||
|
|
||||||
self.formation_id = formation_id
|
|
||||||
"La formation associée au diplome"
|
|
||||||
|
|
||||||
self.nom_export_zip = f"Jury_PE_{self.diplome}"
|
self.nom_export_zip = f"Jury_PE_{self.diplome}"
|
||||||
"Nom du zip où ranger les fichiers générés"
|
"Nom du zip où ranger les fichiers générés"
|
||||||
|
|
||||||
# Chargement des étudiants à prendre en compte dans le jury
|
# Chargement des étudiants à prendre en compte dans le jury
|
||||||
pe_affichage.pe_print(
|
pe_affichage.pe_print(
|
||||||
f"""*** Recherche et chargement des étudiants diplômés en {
|
f"""*** Recherche et chargement des étudiants diplômés en {
|
||||||
self.diplome} pour la formation {self.formation_id}"""
|
self.diplome}"""
|
||||||
)
|
)
|
||||||
self.etudiants = EtudiantsJuryPE(self.diplome) # Les infos sur les étudiants
|
self.etudiants = EtudiantsJuryPE(self.diplome) # Les infos sur les étudiants
|
||||||
self.etudiants.find_etudiants(self.formation_id)
|
self.etudiants.find_etudiants()
|
||||||
self.diplomes_ids = self.etudiants.diplomes_ids
|
self.diplomes_ids = self.etudiants.diplomes_ids
|
||||||
|
|
||||||
self.zipdata = io.BytesIO()
|
self.zipdata = io.BytesIO()
|
||||||
@ -610,7 +607,7 @@ def get_dict_synthese_aggregat(
|
|||||||
note = TableTag.get_note_for_df(bilan, etudid)
|
note = TableTag.get_note_for_df(bilan, etudid)
|
||||||
|
|
||||||
# Statistiques sur le groupe
|
# Statistiques sur le groupe
|
||||||
if note != np.nan:
|
if not pd.isna(note) and note != np.nan:
|
||||||
# Les moyennes de cette trajectoire
|
# Les moyennes de cette trajectoire
|
||||||
donnees |= {
|
donnees |= {
|
||||||
(descr, "", "note"): note,
|
(descr, "", "note"): note,
|
||||||
@ -640,7 +637,7 @@ def get_dict_synthese_aggregat(
|
|||||||
if tag in interclassement_taggue.moyennes_tags:
|
if tag in interclassement_taggue.moyennes_tags:
|
||||||
bilan = interclassement_taggue.moyennes_tags[tag]
|
bilan = interclassement_taggue.moyennes_tags[tag]
|
||||||
|
|
||||||
if note != np.nan:
|
if not pd.isna(note) and note != np.nan:
|
||||||
nom_stat_promo = f"{NOM_STAT_PROMO} {diplome}"
|
nom_stat_promo = f"{NOM_STAT_PROMO} {diplome}"
|
||||||
|
|
||||||
donnees |= {
|
donnees |= {
|
||||||
|
@ -35,9 +35,12 @@ Created on Fri Sep 9 09:15:05 2016
|
|||||||
|
|
||||||
@author: barasc
|
@author: barasc
|
||||||
"""
|
"""
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
import app.pe.pe_etudiant
|
import app.pe.pe_etudiant
|
||||||
from app import db, log
|
from app import db, log
|
||||||
from app.comp import res_sem, moy_ue, moy_sem
|
from app.comp import res_sem, moy_ue, moy_sem
|
||||||
|
from app.comp.moy_sem import comp_ranks_series
|
||||||
from app.comp.res_compat import NotesTableCompat
|
from app.comp.res_compat import NotesTableCompat
|
||||||
from app.comp.res_sem import load_formsemestre_results
|
from app.comp.res_sem import load_formsemestre_results
|
||||||
from app.models import FormSemestre
|
from app.models import FormSemestre
|
||||||
@ -49,8 +52,8 @@ import app.pe.pe_affichage as pe_affichage
|
|||||||
from app.pe.pe_tabletags import TableTag, TAGS_RESERVES
|
from app.pe.pe_tabletags import TableTag, TAGS_RESERVES
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
class SemestreTag(TableTag):
|
|
||||||
|
|
||||||
|
class SemestreTag(TableTag):
|
||||||
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
|
Un SemestreTag représente les résultats des étudiants à un semestre, en donnant
|
||||||
@ -89,47 +92,50 @@ class SemestreTag(TableTag):
|
|||||||
self.ues_inscr_parcours_df = self.nt.load_ues_inscr_parcours()
|
self.ues_inscr_parcours_df = self.nt.load_ues_inscr_parcours()
|
||||||
self.dispense_ues = self.nt.dispense_ues
|
self.dispense_ues = self.nt.dispense_ues
|
||||||
|
|
||||||
# Les tags (en supprimant les tags réservés)
|
# Les tags :
|
||||||
self.tags = get_synthese_tags_semestre(self.nt.formsemestre)
|
## Saisis par l'utilisateur
|
||||||
for tag in TAGS_RESERVES:
|
self.tags_personnalises = get_synthese_tags_personnalises_semestre(
|
||||||
if tag in self.tags:
|
self.nt.formsemestre
|
||||||
del self.tags[tag]
|
)
|
||||||
|
## Déduit des compétences
|
||||||
|
self.tags_competences = get_noms_competences_from_ues(self.nt.formsemestre)
|
||||||
|
|
||||||
|
# Supprime les doublons dans les tags
|
||||||
|
tags_reserves = TAGS_RESERVES + list(self.tags_competences.values())
|
||||||
|
for tag in self.tags_personnalises:
|
||||||
|
if tag in tags_reserves:
|
||||||
|
del self.tags_personnalises[tag]
|
||||||
|
pe_affichage.pe_print(f"Supprime le tag {tag}")
|
||||||
|
|
||||||
# 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 = {}
|
||||||
|
|
||||||
for tag in self.tags:
|
for tag in self.tags_personnalises:
|
||||||
# pe_affichage.pe_print(f" -> Traitement du tag {tag}")
|
# pe_affichage.pe_print(f" -> Traitement du tag {tag}")
|
||||||
moy_gen_tag = self.compute_moyenne_tag(tag)
|
moy_gen_tag = self.compute_moyenne_tag(tag)
|
||||||
class_gen_tag = moy_sem.comp_ranks_series(moy_gen_tag)[1] # en int
|
self.moyennes_tags[tag] = self.comp_moy_et_stat(moy_gen_tag)
|
||||||
self.moyennes_tags[tag] = {
|
|
||||||
"notes": moy_gen_tag,
|
|
||||||
"classements": class_gen_tag,
|
|
||||||
"min": moy_gen_tag.min(),
|
|
||||||
"max": moy_gen_tag.max(),
|
|
||||||
"moy": moy_gen_tag.mean(),
|
|
||||||
"nb_inscrits": len(moy_gen_tag),
|
|
||||||
}
|
|
||||||
|
|
||||||
# Ajoute les moyennes générales de BUT pour le semestre considéré
|
# Ajoute les moyennes générales de BUT pour le semestre considéré
|
||||||
moy_gen_but = self.nt.etud_moy_gen
|
moy_gen_but = self.nt.etud_moy_gen
|
||||||
class_gen_but = self.nt.etud_moy_gen_ranks_int
|
moy_gen_but = pd.to_numeric(moy_gen_but, errors="coerce")
|
||||||
self.moyennes_tags["but"] = {
|
self.moyennes_tags["but"] = self.comp_moy_et_stat(moy_gen_but)
|
||||||
"notes": moy_gen_but,
|
|
||||||
"classements": class_gen_but,
|
# Ajoute les moyennes par compétence
|
||||||
"min": moy_gen_but.min(),
|
for ue_id, competence in self.tags_competences.items():
|
||||||
"max": moy_gen_but.max(),
|
moy_ue = self.nt.etud_moy_ue[ue_id]
|
||||||
"moy": moy_gen_but.mean(),
|
self.moyennes_tags[competence] = self.comp_moy_et_stat(moy_ue)
|
||||||
"nb_inscrits": len(moy_gen_but),
|
|
||||||
}
|
|
||||||
|
|
||||||
# Synthétise l'ensemble des moyennes dans un dataframe
|
# Synthétise l'ensemble des moyennes dans un dataframe
|
||||||
self.tags_sorted = sorted(self.moyennes_tags) # les tags par ordre alphabétique
|
self.tags_sorted = sorted(
|
||||||
|
self.moyennes_tags
|
||||||
|
) # les tags (personnalisés+compétences) par ordre alphabétique
|
||||||
self.notes = (
|
self.notes = (
|
||||||
self.df_notes()
|
self.df_notes()
|
||||||
) # Le dataframe synthétique des notes (=moyennes par tag)
|
) # Le dataframe synthétique des notes (=moyennes par tag)
|
||||||
|
|
||||||
pe_affichage.pe_print(f" => Traitement des tags {', '.join(self.tags_sorted)}")
|
pe_affichage.pe_print(
|
||||||
|
f" => Traitement des tags {', '.join(self.tags_sorted)}"
|
||||||
|
)
|
||||||
|
|
||||||
def get_repr(self):
|
def get_repr(self):
|
||||||
"""Nom affiché pour le semestre taggué"""
|
"""Nom affiché pour le semestre taggué"""
|
||||||
@ -157,13 +163,13 @@ class SemestreTag(TableTag):
|
|||||||
|
|
||||||
"""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 self.tags[tag]:
|
if modimpl.moduleimpl_id not in self.tags_personnalises[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 self.tags[tag]:
|
for modimpl_id in self.tags_personnalises[tag]:
|
||||||
ponderation = self.tags[tag][modimpl_id]["ponderation"]
|
ponderation = self.tags_personnalises[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)"""
|
||||||
@ -193,95 +199,6 @@ class SemestreTag(TableTag):
|
|||||||
|
|
||||||
return moy_gen_tag
|
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é
|
|
||||||
if modimpl == None or profondeur < 0:
|
|
||||||
return (None, None)
|
|
||||||
|
|
||||||
# Y-a-t-il eu capitalisation d'UE ?
|
|
||||||
ue_capitalisees = get_ue_capitalisees(
|
|
||||||
etudid
|
|
||||||
) # les ue capitalisées des étudiants
|
|
||||||
ue_capitalisees_id = {
|
|
||||||
ue_cap["ue_id"] for ue_cap in ue_capitalisees
|
|
||||||
} # les id des ue capitalisées
|
|
||||||
|
|
||||||
# Si le module ne fait pas partie des UE capitalisées
|
|
||||||
if modimpl.module.ue.id not in ue_capitalisees_id:
|
|
||||||
note = self.nt.get_etud_mod_moy(modimpl_id, etudid) # lecture de la note
|
|
||||||
coeff = modimpl.module.coefficient or 0.0 # le coeff (! non compatible BUT)
|
|
||||||
coeff_norm = (
|
|
||||||
coeff / self.somme_coeffs if self.somme_coeffs != 0 else 0
|
|
||||||
) # le coeff normalisé
|
|
||||||
|
|
||||||
# Si le module fait partie d'une UE capitalisée
|
|
||||||
elif len(ue_capitalisees) > 0:
|
|
||||||
moy_ue_actuelle = get_moy_ue_from_nt(
|
|
||||||
self.nt, etudid, modimpl_id
|
|
||||||
) # la moyenne actuelle
|
|
||||||
# A quel semestre correspond l'ue capitalisée et quelles sont ses notes ?
|
|
||||||
fids_prec = [
|
|
||||||
ue_cap["formsemestre_id"]
|
|
||||||
for ue_cap in ue_capitalisees
|
|
||||||
if ue_cap["ue_code"] == modimpl.module.ue.ue_code
|
|
||||||
] # and ue['semestre_id'] == semestre_id]
|
|
||||||
if len(fids_prec) > 0:
|
|
||||||
# => le formsemestre_id du semestre dont vient la capitalisation
|
|
||||||
fid_prec = fids_prec[0]
|
|
||||||
# Lecture des notes de ce semestre
|
|
||||||
# le tableau de note du semestre considéré:
|
|
||||||
formsemestre_prec = FormSemestre.get_formsemestre(fid_prec)
|
|
||||||
nt_prec: NotesTableCompat = res_sem.load_formsemestre_results(
|
|
||||||
formsemestre_prec
|
|
||||||
)
|
|
||||||
|
|
||||||
# Y-a-t-il un module équivalent c'est à dire correspondant au même code (le module_id n'étant pas significatif en cas de changement de PPN)
|
|
||||||
|
|
||||||
modimpl_prec = [
|
|
||||||
modi
|
|
||||||
for modi in nt_prec.formsemestre.modimpls_sorted
|
|
||||||
if modi.module.code == modimpl.module.code
|
|
||||||
]
|
|
||||||
if len(modimpl_prec) > 0: # si une correspondance est trouvée
|
|
||||||
modprec_id = modimpl_prec[0].id
|
|
||||||
moy_ue_capitalisee = get_moy_ue_from_nt(nt_prec, etudid, modprec_id)
|
|
||||||
if (
|
|
||||||
moy_ue_capitalisee is None
|
|
||||||
) or moy_ue_actuelle >= moy_ue_capitalisee: # on prend la meilleure ue
|
|
||||||
note = self.nt.get_etud_mod_moy(
|
|
||||||
modimpl_id, etudid
|
|
||||||
) # lecture de la note
|
|
||||||
coeff = modimpl.module.coefficient # le coeff
|
|
||||||
# nota: self.somme_coeffs peut être None
|
|
||||||
coeff_norm = (
|
|
||||||
coeff / self.somme_coeffs if self.somme_coeffs else 0
|
|
||||||
) # le coeff normalisé
|
|
||||||
else:
|
|
||||||
semtag_prec = SemestreTag(nt_prec, nt_prec.sem)
|
|
||||||
(note, coeff_norm) = semtag_prec.get_noteEtCoeff_modimpl(
|
|
||||||
modprec_id, etudid, profondeur=profondeur - 1
|
|
||||||
) # lecture de la note via le semtag associé au modimpl capitalisé
|
|
||||||
|
|
||||||
# Sinon - pas de notes à prendre en compte
|
|
||||||
return (note, coeff_norm)
|
|
||||||
|
|
||||||
|
|
||||||
def get_moduleimpl(modimpl_id) -> dict:
|
def get_moduleimpl(modimpl_id) -> dict:
|
||||||
"""Renvoie l'objet modimpl dont l'id est modimpl_id"""
|
"""Renvoie l'objet modimpl dont l'id est modimpl_id"""
|
||||||
@ -308,21 +225,13 @@ def get_moy_ue_from_nt(nt, etudid, modimpl_id) -> float:
|
|||||||
return ue_status["moy"]
|
return ue_status["moy"]
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
def get_synthese_tags_personnalises_semestre(formsemestre: FormSemestre):
|
||||||
def get_synthese_tags_semestre(formsemestre: FormSemestre):
|
|
||||||
"""Etant données les implémentations des modules du semestre (modimpls),
|
"""Etant données les implémentations des modules du semestre (modimpls),
|
||||||
synthétise les tags les concernant (tags saisis dans le programme pédagogique)
|
synthétise les tags renseignés dans le programme pédagogique &
|
||||||
|
associés aux modules du semestre,
|
||||||
en les associant aux modimpls qui les concernent (modimpl_id) et
|
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)).
|
aucoeff et pondération fournie avec le tag (par défaut 1 si non indiquée)).
|
||||||
|
|
||||||
{ tagname1: { modimpl_id1: { 'module_id': ...,
|
|
||||||
'coeff': ...,
|
|
||||||
'coeff_norm': ...,
|
|
||||||
'ponderation': ...,
|
|
||||||
'module_code': ...,
|
|
||||||
'ue_xxx': ...},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
formsemestre: Le formsemestre à la base de la recherche des tags
|
formsemestre: Le formsemestre à la base de la recherche des tags
|
||||||
@ -364,3 +273,26 @@ def get_synthese_tags_semestre(formsemestre: FormSemestre):
|
|||||||
}
|
}
|
||||||
|
|
||||||
return synthese_tags
|
return synthese_tags
|
||||||
|
|
||||||
|
|
||||||
|
def get_noms_competences_from_ues(formsemestre: FormSemestre) -> dict[int, str]:
|
||||||
|
"""Partant d'un formsemestre, extrait le nom des compétences associés
|
||||||
|
à (ou aux) parcours des étudiants du formsemestre.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
formsemestre: Un FormSemestre
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionnaire {ue_id: nom_competence} lisant tous les noms des compétences
|
||||||
|
en les raccrochant à leur ue
|
||||||
|
"""
|
||||||
|
# Les résultats du semestre
|
||||||
|
nt = load_formsemestre_results(formsemestre)
|
||||||
|
|
||||||
|
noms_competences = {}
|
||||||
|
for ue in nt.ues:
|
||||||
|
if ue.type != UE_SPORT:
|
||||||
|
ordre = ue.niveau_competence.ordre
|
||||||
|
nom = ue.niveau_competence.competence.titre
|
||||||
|
noms_competences[ue.ue_id] = f"comp. {nom}"
|
||||||
|
return noms_competences
|
||||||
|
@ -40,6 +40,8 @@ Created on Thu Sep 8 09:36:33 2016
|
|||||||
import datetime
|
import datetime
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
from app.comp.moy_sem import comp_ranks_series
|
||||||
|
from app.pe import pe_affichage
|
||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
@ -104,6 +106,44 @@ class TableTag(object):
|
|||||||
|
|
||||||
return df.to_csv(sep=";")
|
return df.to_csv(sep=";")
|
||||||
|
|
||||||
|
def comp_moy_et_stat(self, notes: pd.Series) -> dict:
|
||||||
|
"""Calcule et structure les données nécessaires au PE pour une série
|
||||||
|
de notes (souvent une moyenne par tag) dans un dictionnaire spécifique.
|
||||||
|
|
||||||
|
Partant des notes, sont calculés les classements (en ne tenant compte
|
||||||
|
que des notes non nulles).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
notes: Une série de notes (avec des éventuels NaN)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Un dictionnaire stockant les notes, les classements, le min,
|
||||||
|
le max, la moyenne, le nb de notes (donc d'inscrits)
|
||||||
|
"""
|
||||||
|
# Supprime d'éventuels chaines de caractères dans les notes
|
||||||
|
notes = pd.to_numeric(notes, errors="coerce")
|
||||||
|
|
||||||
|
# Les indices des ... et les notes non nulles/pertinentes
|
||||||
|
indices = notes.notnull()
|
||||||
|
notes_non_nulles = notes[indices]
|
||||||
|
|
||||||
|
# Les classements sur les notes non nulles
|
||||||
|
(_, class_gen_ue_non_nul) = comp_ranks_series(notes_non_nulles)
|
||||||
|
|
||||||
|
# Les classements (toutes notes confondues, avec NaN si pas de notes)
|
||||||
|
class_gen_ue = pd.Series(np.nan, index=notes.index, dtype="Int64")
|
||||||
|
class_gen_ue[indices] = class_gen_ue_non_nul[indices]
|
||||||
|
|
||||||
|
synthese = {
|
||||||
|
"notes": notes,
|
||||||
|
"classements": class_gen_ue,
|
||||||
|
"min": notes.min(),
|
||||||
|
"max": notes.max(),
|
||||||
|
"moy": notes.mean(),
|
||||||
|
"nb_inscrits": len(indices),
|
||||||
|
}
|
||||||
|
return synthese
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_min_for_df(cls, bilan: dict) -> float:
|
def get_min_for_df(cls, bilan: dict) -> float:
|
||||||
"""Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné,
|
"""Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné,
|
||||||
@ -127,12 +167,15 @@ class TableTag(object):
|
|||||||
"""Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné,
|
"""Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné,
|
||||||
renvoie le classement ramené au nombre d'inscrits,
|
renvoie le classement ramené au nombre d'inscrits,
|
||||||
pour un étudiant donné par son etudid"""
|
pour un étudiant donné par son etudid"""
|
||||||
return f"{bilan['classements'].loc[etudid]}/{bilan['nb_inscrits']}"
|
classement = bilan['classements'].loc[etudid]
|
||||||
|
if not pd.isna(classement):
|
||||||
|
return f"{classement}/{bilan['nb_inscrits']}"
|
||||||
|
else:
|
||||||
|
return pe_affichage.SANS_NOTE
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_note_for_df(cls, bilan: dict, etudid: int):
|
def get_note_for_df(cls, bilan: dict, etudid: int):
|
||||||
"""Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné,
|
"""Partant d'un dictionnaire `bilan` généralement une moyennes_tags pour un tag donné,
|
||||||
renvoie la note (moyenne)
|
renvoie la note (moyenne)
|
||||||
pour un étudiant donné par son etudid"""
|
pour un étudiant donné par son etudid"""
|
||||||
return round(bilan["notes"].loc[etudid], 2)
|
return round(bilan["notes"].loc[etudid], 2)
|
||||||
|
@ -47,11 +47,8 @@ from app.pe.pe_tabletags import TableTag
|
|||||||
|
|
||||||
|
|
||||||
class TrajectoireTag(TableTag):
|
class TrajectoireTag(TableTag):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self, trajectoire: Trajectoire, semestres_taggues: dict[int, SemestreTag]
|
||||||
trajectoire: Trajectoire,
|
|
||||||
semestres_taggues: dict[int, SemestreTag]
|
|
||||||
):
|
):
|
||||||
"""Calcule les moyennes par tag d'une combinaison de semestres
|
"""Calcule les moyennes par tag d'une combinaison de semestres
|
||||||
(trajectoires), identifiée par un nom d'aggrégat (par ex: '3S') et
|
(trajectoires), identifiée par un nom d'aggrégat (par ex: '3S') et
|
||||||
@ -71,11 +68,13 @@ class TrajectoireTag(TableTag):
|
|||||||
# Le nom de la trajectoire tagguée (identique à la trajectoire)
|
# Le nom de la trajectoire tagguée (identique à la trajectoire)
|
||||||
self.nom = self.get_repr()
|
self.nom = self.get_repr()
|
||||||
|
|
||||||
"""Le formsemestre terminal et les semestres aggrégés"""
|
|
||||||
self.formsemestre_terminal = trajectoire.semestre_final
|
self.formsemestre_terminal = trajectoire.semestre_final
|
||||||
|
"""Le formsemestre terminal"""
|
||||||
|
# Les résultats du formsemestre terminal
|
||||||
nt = load_formsemestre_results(self.formsemestre_terminal)
|
nt = load_formsemestre_results(self.formsemestre_terminal)
|
||||||
|
|
||||||
self.semestres_aggreges = trajectoire.semestres_aggreges
|
self.semestres_aggreges = trajectoire.semestres_aggreges
|
||||||
|
"""Les semestres aggrégés"""
|
||||||
|
|
||||||
self.semestres_tags_aggreges = {}
|
self.semestres_tags_aggreges = {}
|
||||||
"""Les semestres tags associés aux semestres aggrégés"""
|
"""Les semestres tags associés aux semestres aggrégés"""
|
||||||
@ -87,32 +86,25 @@ class TrajectoireTag(TableTag):
|
|||||||
|
|
||||||
"""Les étudiants (état civil + cursus connu)"""
|
"""Les étudiants (état civil + cursus connu)"""
|
||||||
self.etuds = nt.etuds
|
self.etuds = nt.etuds
|
||||||
|
|
||||||
# assert self.etuds == trajectoire.suivi # manque-t-il des étudiants ?
|
# assert self.etuds == trajectoire.suivi # manque-t-il des étudiants ?
|
||||||
self.etudiants = {etud.etudid: etud.etat_civil for etud in self.etuds}
|
self.etudiants = {etud.etudid: etud.etat_civil for etud in self.etuds}
|
||||||
|
|
||||||
"""Les tags extraits de tous les semestres"""
|
|
||||||
self.tags_sorted = self.do_taglist()
|
self.tags_sorted = self.do_taglist()
|
||||||
|
"""Tags extraits de tous les semestres"""
|
||||||
|
|
||||||
"""Construit le cube de notes"""
|
|
||||||
self.notes_cube = self.compute_notes_cube()
|
self.notes_cube = self.compute_notes_cube()
|
||||||
|
"""Cube de notes"""
|
||||||
|
|
||||||
"""Calcul les moyennes par tag sous forme d'un dataframe"""
|
|
||||||
etudids = list(self.etudiants.keys())
|
etudids = list(self.etudiants.keys())
|
||||||
self.notes = compute_tag_moy(self.notes_cube, etudids, self.tags_sorted)
|
self.notes = compute_tag_moy(self.notes_cube, etudids, self.tags_sorted)
|
||||||
|
"""Calcul les moyennes par tag sous forme d'un dataframe"""
|
||||||
|
|
||||||
"""Synthétise les moyennes/classements par tag"""
|
|
||||||
self.moyennes_tags = {}
|
self.moyennes_tags = {}
|
||||||
|
"""Synthétise les moyennes/classements par tag (qu'ils soient personnalisé ou de compétences)"""
|
||||||
for tag in self.tags_sorted:
|
for tag in self.tags_sorted:
|
||||||
moy_gen_tag = self.notes[tag]
|
moy_gen_tag = self.notes[tag]
|
||||||
class_gen_tag = moy_sem.comp_ranks_series(moy_gen_tag)[1] # en int
|
self.moyennes_tags[tag] = self.comp_moy_et_stat(moy_gen_tag)
|
||||||
self.moyennes_tags[tag] = {
|
|
||||||
"notes": moy_gen_tag,
|
|
||||||
"classements": class_gen_tag,
|
|
||||||
"min": moy_gen_tag.min(),
|
|
||||||
"max": moy_gen_tag.max(),
|
|
||||||
"moy": moy_gen_tag.mean(),
|
|
||||||
"nb_inscrits": len(moy_gen_tag),
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_repr(self, verbose=False) -> str:
|
def get_repr(self, verbose=False) -> str:
|
||||||
"""Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle
|
"""Renvoie une représentation textuelle (celle de la trajectoire sur laquelle elle
|
||||||
@ -123,11 +115,11 @@ class TrajectoireTag(TableTag):
|
|||||||
"""Construit le cube de notes (etudid x tags x semestre_aggregé)
|
"""Construit le cube de notes (etudid x tags x semestre_aggregé)
|
||||||
nécessaire au calcul des moyennes de l'aggrégat
|
nécessaire au calcul des moyennes de l'aggrégat
|
||||||
"""
|
"""
|
||||||
nb_tags = len(self.tags_sorted)
|
# nb_tags = len(self.tags_sorted)
|
||||||
nb_etudiants = len(self.etuds)
|
# nb_etudiants = len(self.etuds)
|
||||||
nb_semestres = len(self.semestres_tags_aggreges)
|
# nb_semestres = len(self.semestres_tags_aggreges)
|
||||||
|
|
||||||
"""Index du cube (etudids -> dim 0, tags -> dim 1)"""
|
# Index du cube (etudids -> dim 0, tags -> dim 1)
|
||||||
etudids = [etud.etudid for etud in self.etuds]
|
etudids = [etud.etudid for etud in self.etuds]
|
||||||
tags = self.tags_sorted
|
tags = self.tags_sorted
|
||||||
semestres_id = list(self.semestres_tags_aggreges.keys())
|
semestres_id = list(self.semestres_tags_aggreges.keys())
|
||||||
@ -135,17 +127,17 @@ class TrajectoireTag(TableTag):
|
|||||||
dfs = {}
|
dfs = {}
|
||||||
|
|
||||||
for frmsem_id in semestres_id:
|
for frmsem_id in semestres_id:
|
||||||
"""Partant d'un dataframe vierge"""
|
# Partant d'un dataframe vierge
|
||||||
df = pd.DataFrame(np.nan, index=etudids, columns=tags)
|
df = pd.DataFrame(np.nan, index=etudids, columns=tags)
|
||||||
|
|
||||||
"""Charge les notes du semestre tag"""
|
# Charge les notes du semestre tag
|
||||||
notes = self.semestres_tags_aggreges[frmsem_id].notes
|
notes = self.semestres_tags_aggreges[frmsem_id].notes
|
||||||
|
|
||||||
"""Les étudiants & les tags commun au dataframe final et aux notes du semestre)"""
|
# Les étudiants & les tags commun au dataframe final et aux notes du semestre)
|
||||||
etudids_communs = df.index.intersection(notes.index)
|
etudids_communs = df.index.intersection(notes.index)
|
||||||
tags_communs = df.columns.intersection(notes.columns)
|
tags_communs = df.columns.intersection(notes.columns)
|
||||||
|
|
||||||
"""Injecte les notes par tag"""
|
# Injecte les notes par tag
|
||||||
df.loc[etudids_communs, tags_communs] = notes.loc[
|
df.loc[etudids_communs, tags_communs] = notes.loc[
|
||||||
etudids_communs, tags_communs
|
etudids_communs, tags_communs
|
||||||
]
|
]
|
||||||
@ -154,7 +146,7 @@ class TrajectoireTag(TableTag):
|
|||||||
for col in df.columns:
|
for col in df.columns:
|
||||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||||
|
|
||||||
"""Stocke le df"""
|
# Stocke le df
|
||||||
dfs[frmsem_id] = df
|
dfs[frmsem_id] = df
|
||||||
|
|
||||||
"""Réunit les notes sous forme d'un cube etdids x tags x semestres"""
|
"""Réunit les notes sous forme d'un cube etdids x tags x semestres"""
|
||||||
@ -175,8 +167,6 @@ class TrajectoireTag(TableTag):
|
|||||||
return sorted(set(tags))
|
return sorted(set(tags))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def compute_tag_moy(set_cube: np.array, etudids: list, tags: list):
|
def compute_tag_moy(set_cube: np.array, etudids: list, tags: list):
|
||||||
"""Calcul de la moyenne par tag sur plusieurs semestres.
|
"""Calcul de la moyenne par tag sur plusieurs semestres.
|
||||||
La moyenne est un nombre (note/20), ou NaN si pas de notes disponibles
|
La moyenne est un nombre (note/20), ou NaN si pas de notes disponibles
|
||||||
@ -214,4 +204,6 @@ def compute_tag_moy(set_cube: np.array, etudids: list, tags: list):
|
|||||||
columns=tags, # les tags
|
columns=tags, # les tags
|
||||||
)
|
)
|
||||||
|
|
||||||
|
etud_moy_tag_df.fillna(np.nan)
|
||||||
|
|
||||||
return etud_moy_tag_df
|
return etud_moy_tag_df
|
||||||
|
@ -78,7 +78,7 @@ def pe_view_sem_recap(formsemestre_id: int):
|
|||||||
sco=ScoData(formsemestre=formsemestre),
|
sco=ScoData(formsemestre=formsemestre),
|
||||||
)
|
)
|
||||||
|
|
||||||
jury = pe_jury.JuryPE(annee_diplome, formsemestre.formation.formation_id)
|
jury = pe_jury.JuryPE(annee_diplome)
|
||||||
if not jury.diplomes_ids:
|
if not jury.diplomes_ids:
|
||||||
flash("aucun étudiant à considérer !")
|
flash("aucun étudiant à considérer !")
|
||||||
return redirect(
|
return redirect(
|
||||||
|
@ -882,7 +882,7 @@ du programme" (menu "Semestre") si vous avez un semestre en cours);
|
|||||||
<form>
|
<form>
|
||||||
<input type="checkbox" class="sco_tag_checkbox"
|
<input type="checkbox" class="sco_tag_checkbox"
|
||||||
{'checked' if show_tags else ''}
|
{'checked' if show_tags else ''}
|
||||||
>montrer les tags des modules</input>
|
> Montrer les tags des modules voire en ajouter <i>(ceux correspondant aux titres des compétences étant ajoutés par défaut)</i></input>
|
||||||
</form>
|
</form>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user