WIP: reorganisation des calculs

This commit is contained in:
Emmanuel Viennet 2022-01-16 23:47:52 +01:00
parent a7324ac634
commit 02989e6c88
18 changed files with 169 additions and 87 deletions

View File

@ -147,6 +147,7 @@ class BulletinBUT(ResultatsSemestreBUT):
def bulletin_etud(self, etud, formsemestre) -> dict: def bulletin_etud(self, etud, formsemestre) -> dict:
"""Le bulletin de l'étudiant dans ce semestre""" """Le bulletin de l'étudiant dans ce semestre"""
etat_inscription = etud.etat_inscription(formsemestre.id) etat_inscription = etud.etat_inscription(formsemestre.id)
nb_inscrits = self.get_inscriptions_counts()[scu.INSCRIT]
d = { d = {
"version": "0", "version": "0",
"type": "BUT", "type": "BUT",
@ -189,7 +190,7 @@ class BulletinBUT(ResultatsSemestreBUT):
}, },
"rang": { # classement wrt moyenne général, indicatif "rang": { # classement wrt moyenne général, indicatif
"value": self.etud_moy_gen_ranks[etud.id], "value": self.etud_moy_gen_ranks[etud.id],
"total": len(self.etuds), "total": nb_inscrits,
}, },
}, },
) )
@ -212,7 +213,7 @@ class BulletinBUT(ResultatsSemestreBUT):
"moy": "", "moy": "",
"max": "", "max": "",
}, },
"rang": {"value": "DEM", "total": len(self.etuds)}, "rang": {"value": "DEM", "total": nb_inscrits},
} }
) )
d.update( d.update(

View File

@ -69,10 +69,11 @@ def bulletin_but_xml_compat(
% (formsemestre_id, etudid) % (formsemestre_id, etudid)
) )
formsemestre = FormSemestre.query.get_or_404(formsemestre_id) formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
etud = Identite.query.get_or_404(etudid) etud: Identite = Identite.query.get_or_404(etudid)
results = bulletin_but.ResultatsSemestreBUT(formsemestre) results = bulletin_but.ResultatsSemestreBUT(formsemestre)
nb_inscrits = len(results.etuds) nb_inscrits = results.get_inscriptions_counts()[scu.INSCRIT]
etat_inscription = etud.etat_inscription(formsemestre.id) # etat_inscription = etud.etat_inscription(formsemestre.id)
etat_inscription = results.formsemestre.etuds_inscriptions[etudid].etat
if (not formsemestre.bul_hide_xml) or force_publishing: if (not formsemestre.bul_hide_xml) or force_publishing:
published = 1 published = 1
else: else:

View File

@ -16,13 +16,13 @@ from app import models
# #
def df_load_modimpl_inscr(formsemestre) -> pd.DataFrame: def df_load_modimpl_inscr(formsemestre) -> pd.DataFrame:
"""Charge la matrice des inscriptions aux modules du semestre """Charge la matrice des inscriptions aux modules du semestre
rows: etudid rows: etudid (inscrits au semestre, avec DEM et DEF)
columns: moduleimpl_id (en chaîne) columns: moduleimpl_id (en chaîne)
value: bool (0/1 inscrit ou pas) value: bool (0/1 inscrit ou pas)
""" """
# méthode la moins lente: une requete par module, merge les dataframes # méthode la moins lente: une requete par module, merge les dataframes
moduleimpl_ids = [m.id for m in formsemestre.modimpls] moduleimpl_ids = [m.id for m in formsemestre.modimpls]
etudids = [i.etudid for i in formsemestre.get_inscrits(include_dem=False)] etudids = [inscr.etudid for inscr in formsemestre.inscriptions]
df = pd.DataFrame(index=etudids, dtype=int) df = pd.DataFrame(index=etudids, dtype=int)
for moduleimpl_id in moduleimpl_ids: for moduleimpl_id in moduleimpl_ids:
ins_df = pd.read_sql_query( ins_df = pd.read_sql_query(

View File

@ -75,7 +75,7 @@ class ModuleImplResults:
"{ evaluation_id: EvaluationEtat }" "{ evaluation_id: EvaluationEtat }"
# #
self.evals_notes = None self.evals_notes = None
"""DataFrame, colonnes: EVALS, Lignes: etudid """DataFrame, colonnes: EVALS, Lignes: etudid (inscrits au SEMESTRE)
valeur: notes brutes, float ou NOTES_ATTENTE, NOTES_NEUTRALISE, valeur: notes brutes, float ou NOTES_ATTENTE, NOTES_NEUTRALISE,
NOTES_ABSENCE. NOTES_ABSENCE.
Les NaN désignent les notes manquantes (non saisies). Les NaN désignent les notes manquantes (non saisies).
@ -105,7 +105,7 @@ class ModuleImplResults:
Évaluation "complete" (prise en compte dans les calculs) si: Évaluation "complete" (prise en compte dans les calculs) si:
- soit tous les étudiants inscrits au module ont des notes - soit tous les étudiants inscrits au module ont des notes
- soit elle a été déclarée "à prise ne compte immédiate" (publish_incomplete) - soit elle a été déclarée "à prise en compte immédiate" (publish_incomplete)
Évaluation "attente" (prise en compte dans les calculs, mais il y Évaluation "attente" (prise en compte dans les calculs, mais il y
manque des notes) ssi il y a des étudiants inscrits au semestre et au module manque des notes) ssi il y a des étudiants inscrits au semestre et au module
@ -178,14 +178,12 @@ class ModuleImplResults:
return eval_df return eval_df
def _etudids(self): def _etudids(self):
"""L'index du dataframe est la liste des étudiants inscrits au semestre, """L'index du dataframe est la liste de tous les étudiants inscrits au semestre"""
sans les démissionnaires.
"""
return [ return [
e.etudid inscr.etudid
for e in ModuleImpl.query.get(self.moduleimpl_id).formsemestre.get_inscrits( for inscr in ModuleImpl.query.get(
include_dem=False self.moduleimpl_id
) ).formsemestre.inscriptions
] ]
def get_evaluations_coefs(self, moduleimpl: ModuleImpl) -> np.array: def get_evaluations_coefs(self, moduleimpl: ModuleImpl) -> np.array:

View File

@ -31,8 +31,10 @@ import numpy as np
import pandas as pd import pandas as pd
def compute_sem_moys_apc(etud_moy_ue_df, modimpl_coefs_df): def compute_sem_moys_apc(
"""Calcule la moyenne générale indicative etud_moy_ue_df: pd.DataFrame, modimpl_coefs_df: pd.DataFrame
) -> pd.Series:
"""Calcule les moyennes générales indicatives de tous les étudiants
= moyenne des moyennes d'UE, pondérée par la somme de leurs coefs = moyenne des moyennes d'UE, pondérée par la somme de leurs coefs
etud_moy_ue_df: DataFrame, colonnes ue_id, lignes etudid etud_moy_ue_df: DataFrame, colonnes ue_id, lignes etudid
@ -46,10 +48,11 @@ def compute_sem_moys_apc(etud_moy_ue_df, modimpl_coefs_df):
return moy_gen return moy_gen
def comp_ranks_series(notes: pd.Series): def comp_ranks_series(notes: pd.Series) -> dict[int, str]:
"""Calcul rangs à partir d'une séries ("vecteur") de notes (index etudid, valeur numérique) """Calcul rangs à partir d'une séries ("vecteur") de notes (index etudid, valeur
en tenant compte des ex-aequos numérique) en tenant compte des ex-aequos.
Le resultat est: { etudid : rang } rang est une chaine decrivant le rang
Result: { etudid : rang:str } rang est une chaine decrivant le rang.
""" """
notes = notes.sort_values(ascending=False) # Serie, tri par ordre décroissant notes = notes.sort_values(ascending=False) # Serie, tri par ordre décroissant
rangs = pd.Series(index=notes.index, dtype=str) # le rang est une chaîne rangs = pd.Series(index=notes.index, dtype=str) # le rang est une chaîne

View File

@ -140,9 +140,14 @@ def notes_sem_assemble_cube(modimpls_notes: list[pd.DataFrame]) -> np.ndarray:
def notes_sem_load_cube(formsemestre: FormSemestre) -> tuple: def notes_sem_load_cube(formsemestre: FormSemestre) -> tuple:
"""Calcule le cube des notes du semestre """Construit le "cube" (tenseur) des notes du semestre.
(charge toutes les notes, calcule les moyenne des modules Charge toutes les notes (sql), calcule les moyennes des modules
et assemble le cube) et assemble le cube.
etuds: tous les inscrits au semestre (avec dem. et def.)
modimpls: _tous_ les modimpls de ce semestre
UEs: X?X voir quelles sont les UE considérées ici
Resultat: Resultat:
sem_cube : ndarray (etuds x modimpls x UEs) sem_cube : ndarray (etuds x modimpls x UEs)
modimpls_evals_poids dict { modimpl.id : evals_poids } modimpls_evals_poids dict { modimpl.id : evals_poids }
@ -174,14 +179,14 @@ def compute_ue_moys_apc(
) -> pd.DataFrame: ) -> pd.DataFrame:
"""Calcul de la moyenne d'UE en mode APC (BUT). """Calcul de la moyenne d'UE en mode APC (BUT).
La moyenne d'UE est un nombre (note/20), ou NI ou NA ou ERR La moyenne d'UE est un nombre (note/20), ou NI ou NA ou ERR
NI non inscrit à (au moins un) module de cette UE NI non inscrit à (au moins un) module de cette UE
NA pas de notes disponibles NA pas de notes disponibles
ERR erreur dans une formule utilisateur. [XXX pas encore gérées ici] ERR erreur dans une formule utilisateur. [XXX pas encore gérées ici]
sem_cube: notes moyennes aux modules sem_cube: notes moyennes aux modules
ndarray (etuds x modimpls x UEs) ndarray (etuds x modimpls x UEs)
(floats avec des NaN) (floats avec des NaN)
etuds : listes des étudiants (dim. 0 du cube) etuds : liste des étudiants (dim. 0 du cube)
modimpls : liste des modules à considérer (dim. 1 du cube) modimpls : liste des modules à considérer (dim. 1 du cube)
ues : liste des UE (dim. 2 du cube) ues : liste des UE (dim. 2 du cube)
modimpl_inscr_df: matrice d'inscription du semestre (etud x modimpl) modimpl_inscr_df: matrice d'inscription du semestre (etud x modimpl)
@ -235,12 +240,12 @@ def compute_ue_moys_classic(
ues: list, ues: list,
modimpl_inscr_df: pd.DataFrame, modimpl_inscr_df: pd.DataFrame,
modimpl_coefs: np.array, modimpl_coefs: np.array,
) -> tuple: ) -> tuple[pd.Series, pd.DataFrame, pd.DataFrame]:
"""Calcul de la moyenne d'UE en mode classique. """Calcul de la moyenne d'UE en mode classique.
La moyenne d'UE est un nombre (note/20), ou NI ou NA ou ERR La moyenne d'UE est un nombre (note/20), ou NI ou NA ou ERR
NI non inscrit à (au moins un) module de cette UE NI non inscrit à (au moins un) module de cette UE
NA pas de notes disponibles NA pas de notes disponibles
ERR erreur dans une formule utilisateur. [XXX pas encore gérées ici] ERR erreur dans une formule utilisateur. [XXX pas encore gérées ici]
sem_matrix: notes moyennes aux modules sem_matrix: notes moyennes aux modules
ndarray (etuds x modimpls) ndarray (etuds x modimpls)
@ -253,6 +258,9 @@ def compute_ue_moys_classic(
Résultat: Résultat:
- moyennes générales: pd.Series, index etudid - moyennes générales: pd.Series, index etudid
- moyennes d'UE: DataFrame columns UE, rows etudid - moyennes d'UE: DataFrame columns UE, rows etudid
- coefficients d'UE: DataFrame, columns UE, rows etudid
les coefficients effectifs de chaque UE pour chaque étudiant
(sommes de coefs de modules pris en compte)
""" """
nb_etuds, nb_modules = sem_matrix.shape nb_etuds, nb_modules = sem_matrix.shape
assert len(modimpl_coefs) == nb_modules assert len(modimpl_coefs) == nb_modules
@ -293,4 +301,9 @@ def compute_ue_moys_classic(
etud_moy_ue_df = pd.DataFrame( etud_moy_ue_df = pd.DataFrame(
etud_moy_ue, index=modimpl_inscr_df.index, columns=[ue.id for ue in ues] etud_moy_ue, index=modimpl_inscr_df.index, columns=[ue.id for ue in ues]
) )
return etud_moy_gen_s, etud_moy_ue_df etud_coef_ue_df = pd.DataFrame(
coefs.sum(axis=2).T,
index=modimpl_inscr_df.index, # etudids
columns=[ue.id for ue in ues],
)
return etud_moy_gen_s, etud_moy_ue_df, etud_coef_ue_df

View File

@ -6,6 +6,7 @@
"""Résultats semestres BUT """Résultats semestres BUT
""" """
import pandas as pd
from app.comp import moy_ue, moy_sem, inscr_mod from app.comp import moy_ue, moy_sem, inscr_mod
from app.comp.res_common import NotesTableCompat from app.comp.res_common import NotesTableCompat
@ -49,6 +50,10 @@ class ResultatsSemestreBUT(NotesTableCompat):
self.modimpl_inscr_df, self.modimpl_inscr_df,
self.modimpl_coefs_df, self.modimpl_coefs_df,
) )
# Les coefficients d'UE ne sont pas utilisés en APC
self.etud_coef_ue_df = pd.DataFrame(
1.0, index=self.etud_moy_ue.index, columns=self.etud_moy_ue.columns
)
self.etud_moy_gen = moy_sem.compute_sem_moys_apc( self.etud_moy_gen = moy_sem.compute_sem_moys_apc(
self.etud_moy_ue, self.modimpl_coefs_df self.etud_moy_ue, self.modimpl_coefs_df
) )

View File

@ -8,6 +8,7 @@
""" """
import numpy as np import numpy as np
import pandas as pd import pandas as pd
from app.comp import moy_mod, moy_ue, moy_sem, inscr_mod from app.comp import moy_mod, moy_ue, moy_sem, inscr_mod
from app.comp.res_common import NotesTableCompat from app.comp.res_common import NotesTableCompat
from app.models.formsemestre import FormSemestre from app.models.formsemestre import FormSemestre
@ -45,7 +46,11 @@ class ResultatsSemestreClassic(NotesTableCompat):
self.modimpl_idx = {m.id: i for i, m in enumerate(self.formsemestre.modimpls)} self.modimpl_idx = {m.id: i for i, m in enumerate(self.formsemestre.modimpls)}
"l'idx de la colonne du mod modimpl.id est modimpl_idx[modimpl.id]" "l'idx de la colonne du mod modimpl.id est modimpl_idx[modimpl.id]"
self.etud_moy_gen, self.etud_moy_ue = moy_ue.compute_ue_moys_classic( (
self.etud_moy_gen,
self.etud_moy_ue,
self.etud_coef_ue_df,
) = moy_ue.compute_ue_moys_classic(
self.formsemestre, self.formsemestre,
self.sem_matrix, self.sem_matrix,
self.ues, self.ues,

View File

@ -4,13 +4,13 @@
# See LICENSE # See LICENSE
############################################################################## ##############################################################################
from collections import defaultdict from collections import defaultdict, Counter
from functools import cached_property from functools import cached_property
import numpy as np import numpy as np
import pandas as pd import pandas as pd
from app.comp.aux import StatsMoyenne from app.comp.aux import StatsMoyenne
from app.comp.moy_mod import ModuleImplResults from app.comp.moy_mod import ModuleImplResults
from app.models import FormSemestre, ModuleImpl from app.models import FormSemestre, Identite, ModuleImpl
from app.models.ues import UniteEns from app.models.ues import UniteEns
from app.scodoc import sco_utils as scu from app.scodoc import sco_utils as scu
from app.scodoc.sco_cache import ResultatsSemestreCache from app.scodoc.sco_cache import ResultatsSemestreCache
@ -32,6 +32,7 @@ class ResultatsSemestre:
"etud_moy_ue", "etud_moy_ue",
"modimpl_inscr_df", "modimpl_inscr_df",
"modimpls_results", "modimpls_results",
"etud_coef_ue_df",
) )
def __init__(self, formsemestre: FormSemestre): def __init__(self, formsemestre: FormSemestre):
@ -45,7 +46,10 @@ class ResultatsSemestre:
self.etud_moy_gen = {} self.etud_moy_gen = {}
self.etud_moy_gen_ranks = {} self.etud_moy_gen_ranks = {}
self.modimpls_results: ModuleImplResults = None self.modimpls_results: ModuleImplResults = None
# TODO self.etud_coef_ue_df = None
"""coefs d'UE effectifs pour chaque etudiant (pour form. classiques)"""
# TODO ?
def load_cached(self) -> bool: def load_cached(self) -> bool:
"Load cached dataframes, returns False si pas en cache" "Load cached dataframes, returns False si pas en cache"
@ -68,24 +72,34 @@ class ResultatsSemestre:
# voir ce qui est chargé / calculé ici et dans les sous-classes # voir ce qui est chargé / calculé ici et dans les sous-classes
raise NotImplementedError() raise NotImplementedError()
@cached_property def get_inscriptions_counts(self) -> Counter:
def etuds(self): """Nombre d'inscrits, défaillants, démissionnaires.
"Liste des inscrits au semestre, sans les démissionnaires"
# nb: si la liste des inscrits change, ResultatsSemestre devient invalide Exemple: res.get_inscriptions_counts()[scu.INSCRIT]
return self.formsemestre.get_inscrits(include_dem=False)
Result: a collections.Counter instance
"""
return Counter(ins.etat for ins in self.formsemestre.inscriptions)
@cached_property @cached_property
def etud_index(self): def etuds(self) -> list[Identite]:
"Liste des inscrits au semestre, avec les démissionnaires et les défaillants"
# nb: si la liste des inscrits change, ResultatsSemestre devient invalide
return self.formsemestre.get_inscrits(include_demdef=True)
@cached_property
def etud_index(self) -> dict[int, int]:
"dict { etudid : indice dans les inscrits }" "dict { etudid : indice dans les inscrits }"
return {e.id: idx for idx, e in enumerate(self.etuds)} return {e.id: idx for idx, e in enumerate(self.etuds)}
@cached_property @cached_property
def etuds_dict(self): def etuds_dict(self) -> dict[int, Identite]:
"dict { etudid : Identite } inscrits au semestre, sans les démissionnaires" """dict { etudid : Identite } inscrits au semestre,
avec les démissionnaires et defs."""
return {etud.id: etud for etud in self.etuds} return {etud.id: etud for etud in self.etuds}
@cached_property @cached_property
def ues(self) -> list: def ues(self) -> list[UniteEns]:
"""Liste des UEs du semestre """Liste des UEs du semestre
(indices des DataFrames) (indices des DataFrames)
""" """
@ -153,6 +167,7 @@ class NotesTableCompat(ResultatsSemestre):
def __init__(self, formsemestre: FormSemestre): def __init__(self, formsemestre: FormSemestre):
super().__init__(formsemestre) super().__init__(formsemestre)
nb_etuds = len(self.etuds) nb_etuds = len(self.etuds)
self.bonus = defaultdict(lambda: 0.0) # XXX TODO self.bonus = defaultdict(lambda: 0.0) # XXX TODO
self.ue_rangs = {u.id: (defaultdict(lambda: 0.0), nb_etuds) for u in self.ues} self.ue_rangs = {u.id: (defaultdict(lambda: 0.0), nb_etuds) for u in self.ues}
@ -178,12 +193,18 @@ class NotesTableCompat(ResultatsSemestre):
return [x["etudid"] for x in self.inscrlist] return [x["etudid"] for x in self.inscrlist]
@cached_property
def sem(self) -> dict:
"""le formsemestre, comme un dict (nt.sem)"""
return self.formsemestre.to_dict()
@cached_property @cached_property
def inscrlist(self) -> list[dict]: # utilisé par PE seulement def inscrlist(self) -> list[dict]: # utilisé par PE seulement
"""Liste de dict etud, avec démissionnaires """Liste des inscrits au semestre (avec DEM et DEF),
sous forme de dict etud,
classée dans l'ordre alphabétique de noms. classée dans l'ordre alphabétique de noms.
""" """
etuds = self.formsemestre.get_inscrits(include_dem=True) etuds = self.formsemestre.get_inscrits(include_demdef=True)
etuds.sort(key=lambda e: e.sort_key) etuds.sort(key=lambda e: e.sort_key)
return [e.to_dict_scodoc7() for e in etuds] return [e.to_dict_scodoc7() for e in etuds]
@ -256,9 +277,12 @@ class NotesTableCompat(ResultatsSemestre):
raise NotImplementedError() # virtual method raise NotImplementedError() # virtual method
def get_etud_ue_status(self, etudid: int, ue_id: int): def get_etud_ue_status(self, etudid: int, ue_id: int):
coef_ue = self.etud_coef_ue_df[ue_id][etudid]
return { return {
"cur_moy_ue": self.etud_moy_ue[ue_id][etudid], "cur_moy_ue": self.etud_moy_ue[ue_id][etudid],
"moy": self.etud_moy_ue[ue_id][etudid],
"is_capitalized": False, # XXX TODO "is_capitalized": False, # XXX TODO
"coef_ue": coef_ue, # XXX TODO
} }
def get_etud_rang(self, etudid: int): def get_etud_rang(self, etudid: int):
@ -277,18 +301,20 @@ class NotesTableCompat(ResultatsSemestre):
for e in modimpl.evaluations: for e in modimpl.evaluations:
if self.modimpls_results[moduleimpl_id].evaluations_completes_dict[e.id]: if self.modimpls_results[moduleimpl_id].evaluations_completes_dict[e.id]:
d = e.to_dict() d = e.to_dict()
moduleimpl_results = self.modimpls_results[e.moduleimpl_id]
d["heure_debut"] = e.heure_debut # datetime.time d["heure_debut"] = e.heure_debut # datetime.time
d["heure_fin"] = e.heure_fin d["heure_fin"] = e.heure_fin
d["jour"] = e.jour # datetime d["jour"] = e.jour # datetime
d["notes"] = { d["notes"] = {
etud.id: { etud.id: {
"etudid": etud.id, "etudid": etud.id,
"value": self.modimpls_evals_notes[e.moduleimpl_id][e.id][ "value": moduleimpl_results.evals_notes[e.id][etud.id],
etud.id
],
} }
for etud in self.etuds for etud in self.etuds
} }
d["etat"] = {
"evalattente": moduleimpl_results.evaluations_etat[e.id].nb_attente,
}
evals_results.append(d) evals_results.append(d)
return evals_results return evals_results

View File

@ -117,11 +117,18 @@ class FormSemestre(db.Model):
d.pop("_sa_instance_state", None) d.pop("_sa_instance_state", None)
# ScoDoc7 output_formators: (backward compat) # ScoDoc7 output_formators: (backward compat)
d["formsemestre_id"] = self.id d["formsemestre_id"] = self.id
d["date_debut"] = ( if self.date_debut:
self.date_debut.strftime("%d/%m/%Y") if self.date_debut else "" d["date_debut"] = self.date_debut.strftime("%d/%m/%Y")
) d["date_debut_iso"] = self.date_debut.isoformat()
d["date_fin"] = self.date_fin.strftime("%d/%m/%Y") if self.date_fin else "" else:
d["date_debut"] = d["date_debut_iso"] = ""
if self.date_fin:
d["date_fin"] = self.date_fin.strftime("%d/%m/%Y")
d["date_fin_iso"] = self.date_fin.isoformat()
else:
d["date_fin"] = d["date_fin_iso"] = ""
d["responsables"] = [u.id for u in self.responsables] d["responsables"] = [u.id for u in self.responsables]
return d return d
def query_ues(self, with_sport=False) -> flask_sqlalchemy.BaseQuery: def query_ues(self, with_sport=False) -> flask_sqlalchemy.BaseQuery:
@ -271,18 +278,19 @@ class FormSemestre(db.Model):
etudid, self.date_debut.isoformat(), self.date_fin.isoformat() etudid, self.date_debut.isoformat(), self.date_fin.isoformat()
) )
def get_inscrits(self, include_dem=False) -> list[Identite]: def get_inscrits(self, include_demdef=False) -> list[Identite]:
"""Liste des étudiants inscrits à ce semestre """Liste des étudiants inscrits à ce semestre
Si all, tous les étudiants, avec les démissionnaires. Si include_demdef, tous les étudiants, avec les démissionnaires
et défaillants.
""" """
if include_dem: if include_demdef:
return [ins.etud for ins in self.inscriptions] return [ins.etud for ins in self.inscriptions]
else: else:
return [ins.etud for ins in self.inscriptions if ins.etat == scu.INSCRIT] return [ins.etud for ins in self.inscriptions if ins.etat == scu.INSCRIT]
@cached_property @cached_property
def etuds_inscriptions(self) -> dict: def etuds_inscriptions(self) -> dict:
"""Map { etudid : inscription }""" """Map { etudid : inscription } (incluant DEM et DEF)"""
return {ins.etud.id: ins for ins in self.inscriptions} return {ins.etud.id: ins for ins in self.inscriptions}

View File

@ -62,7 +62,7 @@ Pour modifier les moyennes d'UE:
La valeur retournée est: La valeur retournée est:
- formations classiques: ajoutée à la moyenne générale - formations classiques: ajoutée à la moyenne générale
- BUT: ajoutée à chaque UE si le coef XXX - BUT: valeur multipliée par la somme des coefs modules sport ajoutée à chaque UE.
""" """

View File

@ -171,6 +171,7 @@ class NotesTable:
def __init__(self, formsemestre_id): def __init__(self, formsemestre_id):
log(f"NotesTable( formsemestre_id={formsemestre_id} )") log(f"NotesTable( formsemestre_id={formsemestre_id} )")
# raise NotImplementedError() # XXX
if not formsemestre_id: if not formsemestre_id:
raise ValueError("invalid formsemestre_id (%s)" % formsemestre_id) raise ValueError("invalid formsemestre_id (%s)" % formsemestre_id)
self.formsemestre_id = formsemestre_id self.formsemestre_id = formsemestre_id
@ -409,7 +410,7 @@ class NotesTable:
return "" return ""
def get_etud_etat_html(self, etudid): def get_etud_etat_html(self, etudid):
etat = self.inscrdict[etudid]["etat"]
if etat == "I": if etat == "I":
return "" return ""
elif etat == "D": elif etat == "D":

View File

@ -48,6 +48,9 @@ import app.scodoc.sco_utils as scu
from app.scodoc.sco_utils import ModuleType from app.scodoc.sco_utils import ModuleType
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
from app import log from app import log
from app.comp import res_sem
from app.comp.res_common import NotesTableCompat
from app.models import FormSemestre
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
from app.scodoc import html_sco_header from app.scodoc import html_sco_header
@ -136,7 +139,9 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
raise ValueError("invalid version code !") raise ValueError("invalid version code !")
prefs = sco_preferences.SemPreferences(formsemestre_id) prefs = sco_preferences.SemPreferences(formsemestre_id)
nt = sco_cache.NotesTableCache.get(formsemestre_id) # > toutes notes # nt = sco_cache.NotesTableCache.get(formsemestre_id) # > toutes notes
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
nt: NotesTableCompat = res_sem.load_formsemestre_result(formsemestre)
if not nt.get_etud_etat(etudid): if not nt.get_etud_etat(etudid):
raise ScoValueError("Etudiant non inscrit à ce semestre") raise ScoValueError("Etudiant non inscrit à ce semestre")
I = scu.DictDefault(defaultvalue="") I = scu.DictDefault(defaultvalue="")
@ -191,7 +196,9 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
I["decision_sem"] = "" I["decision_sem"] = ""
I.update(infos) I.update(infos)
I["etud_etat_html"] = nt.get_etud_etat_html(etudid) I["etud_etat_html"] = _get_etud_etat_html(
formsemestre.etuds_inscriptions[etudid].etat
)
I["etud_etat"] = nt.get_etud_etat(etudid) I["etud_etat"] = nt.get_etud_etat(etudid)
I["filigranne"] = "" I["filigranne"] = ""
I["demission"] = "" I["demission"] = ""
@ -261,17 +268,18 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
# notes en attente dans ce semestre # notes en attente dans ce semestre
rang = scu.RANG_ATTENTE_STR rang = scu.RANG_ATTENTE_STR
rang_gr = scu.DictDefault(defaultvalue=scu.RANG_ATTENTE_STR) rang_gr = scu.DictDefault(defaultvalue=scu.RANG_ATTENTE_STR)
inscriptions_counts = nt.get_inscriptions_counts()
I["rang"] = rang I["rang"] = rang
I["rang_gr"] = rang_gr I["rang_gr"] = rang_gr
I["gr_name"] = gr_name I["gr_name"] = gr_name
I["ninscrits_gr"] = ninscrits_gr I["ninscrits_gr"] = ninscrits_gr
I["nbetuds"] = len(nt.etud_moy_gen_ranks) I["nbetuds"] = len(nt.etud_moy_gen_ranks)
I["nb_demissions"] = nt.nb_demissions I["nb_demissions"] = inscriptions_counts[scu.DEMISSION]
I["nb_defaillants"] = nt.nb_defaillants I["nb_defaillants"] = inscriptions_counts[scu.DEF]
if prefs["bul_show_rangs"]: if prefs["bul_show_rangs"]:
I["rang_nt"] = "%s / %d" % ( I["rang_nt"] = "%s / %d" % (
rang, rang,
I["nbetuds"] - nt.nb_demissions - nt.nb_defaillants, inscriptions_counts[scu.INSCRIT],
) )
I["rang_txt"] = "Rang " + I["rang_nt"] I["rang_txt"] = "Rang " + I["rang_nt"]
else: else:
@ -379,7 +387,8 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
I["ues"].append(u) # ne montre pas les UE si non inscrit I["ues"].append(u) # ne montre pas les UE si non inscrit
# Accès par matieres # Accès par matieres
I["matieres_modules"].update(_sort_mod_by_matiere(modules, nt, etudid)) # voir si on supporte encore cela en #sco92 XXX
# I["matieres_modules"].update(_sort_mod_by_matiere(modules, nt, etudid))
# #
C = make_context_dict(I["sem"], I["etud"]) C = make_context_dict(I["sem"], I["etud"])
@ -389,6 +398,18 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
return C return C
def _get_etud_etat_html(etat: str) -> str:
"""chaine html représentant l'état (backward compat sco7)"""
if etat == scu.INSCRIT: # "I"
return ""
elif etat == scu.DEMISSION: # "D"
return ' <font color="red">(DEMISSIONNAIRE)</font> '
elif etat == scu.DEF: # "DEF"
return ' <font color="red">(DEFAILLANT)</font> '
else:
return ' <font color="red">(%s)</font> ' % etat
def _sort_mod_by_matiere(modlist, nt, etudid): def _sort_mod_by_matiere(modlist, nt, etudid):
matmod = {} # { matiere_id : [] } matmod = {} # { matiere_id : [] }
for mod in modlist: for mod in modlist:

View File

@ -1433,18 +1433,19 @@ def create_etapes_partition(formsemestre_id, partition_name="apo_etapes"):
def do_evaluation_listeetuds_groups( def do_evaluation_listeetuds_groups(
evaluation_id, groups=None, getallstudents=False, include_dems=False evaluation_id, groups=None, getallstudents=False, include_demdef=False
): ):
"""Donne la liste des etudids inscrits a cette evaluation dans les """Donne la liste des etudids inscrits a cette evaluation dans les
groupes indiqués. groupes indiqués.
Si getallstudents==True, donne tous les etudiants inscrits a cette Si getallstudents==True, donne tous les etudiants inscrits a cette
evaluation. evaluation.
Si include_dems, compte aussi les etudiants démissionnaires Si include_demdef, compte aussi les etudiants démissionnaires et défaillants
(sinon, par défaut, seulement les 'I') (sinon, par défaut, seulement les 'I')
Résultat: [ (etudid, etat) ], etat='I', 'D', 'DEF' Résultat: [ (etudid, etat) ], etat='I', 'D', 'DEF'
""" """
# nb: pour notes_table / do_evaluation_etat, getallstudents est vrai et include_dems faux # nb: pour notes_table / do_evaluation_etat, getallstudents est vrai et
# include_demdef faux
fromtables = [ fromtables = [
"notes_moduleimpl_inscription Im", "notes_moduleimpl_inscription Im",
"notes_formsemestre_inscription Isem", "notes_formsemestre_inscription Isem",
@ -1476,7 +1477,7 @@ def do_evaluation_listeetuds_groups(
and E.id = %(evaluation_id)s and E.id = %(evaluation_id)s
""" """
) )
if not include_dems: if not include_demdef:
req += " and Isem.etat='I'" req += " and Isem.etat='I'"
req += r req += r
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()

View File

@ -309,7 +309,7 @@ def _make_table_notes(
anonymous_lst_key = "etudid" anonymous_lst_key = "etudid"
etudid_etats = sco_groups.do_evaluation_listeetuds_groups( etudid_etats = sco_groups.do_evaluation_listeetuds_groups(
E["evaluation_id"], groups, include_dems=True E["evaluation_id"], groups, include_demdef=True
) )
for etudid, etat in etudid_etats: for etudid, etat in etudid_etats:
css_row_class = None css_row_class = None

View File

@ -307,7 +307,7 @@ class PlacementRunner:
self.evaluation_id, self.evaluation_id,
self.groups, self.groups,
getallstudents=get_all_students, getallstudents=get_all_students,
include_dems=True, include_demdef=True,
) )
listetud = [] # liste de couples (nom,prenom) listetud = [] # liste de couples (nom,prenom)
for etudid, etat in etudid_etats: for etudid, etat in etudid_etats:

View File

@ -306,8 +306,8 @@ def make_formsemestre_recapcomplet(
)[0] )[0]
parcours = formsemestre.formation.get_parcours() parcours = formsemestre.formation.get_parcours()
# nt = sco_cache.NotesTableCache.get(formsemestre_id) # nt = sco_cache.NotesTableCache.get(formsemestre_id) # sco91
# XXX EXPERIMENTAL # sco92 :
nt: NotesTableCompat = res_sem.load_formsemestre_result(formsemestre) nt: NotesTableCompat = res_sem.load_formsemestre_result(formsemestre)
modimpls = nt.get_modimpls_dict() modimpls = nt.get_modimpls_dict()
ues = nt.get_ues_stat_dict() # incluant le(s) UE de sport ues = nt.get_ues_stat_dict() # incluant le(s) UE de sport
@ -434,13 +434,12 @@ def make_formsemestre_recapcomplet(
e["admission"] = {} e["admission"] = {}
if not hidebac: if not hidebac:
if etud_etat == scu.INSCRIT: e["admission"] = nt.etuds_dict[etudid].admission.first()
e["admission"] = nt.etuds_dict[etudid].admission.first() if e["admission"]:
if e["admission"]: bac = nt.etuds_dict[etudid].admission[0].get_bac()
bac = nt.etuds_dict[etudid].admission[0].get_bac() l.append(bac.abbrev())
l.append(bac.abbrev()) else:
else: l.append("")
l.append("")
if format[:3] == "xls" or format == "csv": # tous les groupes if format[:3] == "xls" or format == "csv": # tous les groupes
for partition in partitions: for partition in partitions:

View File

@ -310,7 +310,7 @@ def do_evaluation_set_missing(evaluation_id, value, dialog_confirmed=False):
# #
NotesDB = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id) NotesDB = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id)
etudid_etats = sco_groups.do_evaluation_listeetuds_groups( etudid_etats = sco_groups.do_evaluation_listeetuds_groups(
evaluation_id, getallstudents=True, include_dems=False evaluation_id, getallstudents=True, include_demdef=False
) )
notes = [] notes = []
for etudid, _ in etudid_etats: # pour tous les inscrits for etudid, _ in etudid_etats: # pour tous les inscrits
@ -482,7 +482,7 @@ def notes_add(
inscrits = { inscrits = {
x[0] x[0]
for x in sco_groups.do_evaluation_listeetuds_groups( for x in sco_groups.do_evaluation_listeetuds_groups(
evaluation_id, getallstudents=True, include_dems=True evaluation_id, getallstudents=True, include_demdef=True
) )
} }
for (etudid, value) in notes: for (etudid, value) in notes:
@ -833,7 +833,7 @@ def feuille_saisie_notes(evaluation_id, group_ids=[]):
etudids = [ etudids = [
x[0] x[0]
for x in sco_groups.do_evaluation_listeetuds_groups( for x in sco_groups.do_evaluation_listeetuds_groups(
evaluation_id, groups, getallstudents=getallstudents, include_dems=True evaluation_id, groups, getallstudents=getallstudents, include_demdef=True
) )
] ]
@ -1079,7 +1079,7 @@ def _form_saisie_notes(E, M, group_ids, destination=""):
etudids = [ etudids = [
x[0] x[0]
for x in sco_groups.do_evaluation_listeetuds_groups( for x in sco_groups.do_evaluation_listeetuds_groups(
evaluation_id, getallstudents=True, include_dems=True evaluation_id, getallstudents=True, include_demdef=True
) )
] ]
if not etudids: if not etudids: