forked from ScoDoc/DocScoDoc
WIP: moyennes modules, evals, bulletins.
This commit is contained in:
parent
4d6d7ad168
commit
25a441f7f2
@ -37,13 +37,14 @@ class ResultatsSemestreBUT:
|
||||
"modimpls_evals_notes",
|
||||
"etud_moy_gen",
|
||||
"etud_moy_gen_ranks",
|
||||
"modimpls_evaluations_complete",
|
||||
)
|
||||
|
||||
def __init__(self, formsemestre):
|
||||
self.formsemestre = formsemestre
|
||||
self.ues = formsemestre.query_ues().all()
|
||||
self.modimpls = formsemestre.modimpls.all()
|
||||
self.etuds = self.formsemestre.etuds.all()
|
||||
self.etuds = self.formsemestre.get_inscrits(include_dem=False)
|
||||
self.etud_index = {e.id: idx for idx, e in enumerate(self.etuds)}
|
||||
self.saes = [
|
||||
m for m in self.modimpls if m.module.module_type == scu.ModuleType.SAE
|
||||
@ -77,7 +78,8 @@ class ResultatsSemestreBUT:
|
||||
self.sem_cube,
|
||||
self.modimpls_evals_poids,
|
||||
self.modimpls_evals_notes,
|
||||
_,
|
||||
modimpls_evaluations,
|
||||
self.modimpls_evaluations_complete,
|
||||
) = moy_ue.notes_sem_load_cube(self.formsemestre)
|
||||
self.modimpl_inscr_df = inscr_mod.df_load_modimpl_inscr(self.formsemestre)
|
||||
self.modimpl_coefs_df, _, _ = moy_ue.df_load_modimpl_coefs(
|
||||
@ -151,10 +153,17 @@ class ResultatsSemestreBUT:
|
||||
for mi in modimpls:
|
||||
mod_idx = self.modimpl_coefs_df.columns.get_loc(mi.id)
|
||||
# moyennes indicatives (moyennes de moyennes d'UE)
|
||||
moyennes_etuds = np.nan_to_num(
|
||||
np.nanmean(self.sem_cube[:, mod_idx, :], axis=1),
|
||||
copy=False,
|
||||
)
|
||||
try:
|
||||
moyennes_etuds = np.nan_to_num(
|
||||
np.nanmean(self.sem_cube[:, mod_idx, :], axis=1),
|
||||
copy=False,
|
||||
)
|
||||
except RuntimeWarning: # all nans in np.nanmean (sur certains etuds sans notes valides)
|
||||
pass
|
||||
try:
|
||||
moy_indicative_mod = np.nanmean(self.sem_cube[etud_idx, mod_idx])
|
||||
except RuntimeWarning: # all nans in np.nanmean
|
||||
pass
|
||||
d[mi.module.code] = {
|
||||
"id": mi.id,
|
||||
"titre": mi.module.titre,
|
||||
@ -166,15 +175,16 @@ class ResultatsSemestreBUT:
|
||||
),
|
||||
"moyenne": {
|
||||
# moyenne indicative de module: moyenne des UE, ignorant celles sans notes (nan)
|
||||
"value": fmt_note(np.nanmean(self.sem_cube[etud_idx, mod_idx])),
|
||||
"value": fmt_note(moy_indicative_mod),
|
||||
"min": fmt_note(moyennes_etuds.min()),
|
||||
"max": fmt_note(moyennes_etuds.max()),
|
||||
"moy": fmt_note(moyennes_etuds.mean()),
|
||||
},
|
||||
"evaluations": [
|
||||
self.etud_eval_results(etud, e)
|
||||
for e in mi.evaluations
|
||||
for eidx, e in enumerate(mi.evaluations)
|
||||
if e.visibulletin
|
||||
and self.modimpls_evaluations_complete[mi.id][eidx]
|
||||
],
|
||||
}
|
||||
return d
|
||||
|
@ -89,7 +89,7 @@ def check_moduleimpl_conformity(
|
||||
|
||||
|
||||
def df_load_modimpl_notes(moduleimpl_id: int) -> tuple:
|
||||
"""Construit un dataframe avec toutes les notes des évaluations du module.
|
||||
"""Construit un dataframe avec toutes les notes de toutes les évaluations du module.
|
||||
colonnes: le nom de la colonne est l'evaluation_id (int)
|
||||
index (lignes): etudid (int)
|
||||
|
||||
@ -111,9 +111,12 @@ def df_load_modimpl_notes(moduleimpl_id: int) -> tuple:
|
||||
|
||||
N'utilise pas de cache ScoDoc.
|
||||
"""
|
||||
# L'index du dataframe est la liste des étudiants inscrits au semestre:
|
||||
# L'index du dataframe est la liste des étudiants inscrits au semestre, sans les démissionnaires
|
||||
etudids = [
|
||||
e.etudid for e in ModuleImpl.query.get(moduleimpl_id).formsemestre.inscriptions
|
||||
e.etudid
|
||||
for e in ModuleImpl.query.get(moduleimpl_id).formsemestre.get_inscrits(
|
||||
include_dem=False
|
||||
)
|
||||
]
|
||||
evaluations = Evaluation.query.filter_by(moduleimpl_id=moduleimpl_id).all()
|
||||
if evaluations:
|
||||
@ -138,9 +141,10 @@ def df_load_modimpl_notes(moduleimpl_id: int) -> tuple:
|
||||
index_col="etudid",
|
||||
dtype=np.float64,
|
||||
)
|
||||
evaluations_completes.append(
|
||||
is_complete = (
|
||||
len(eval_df) == nb_inscrits_module or evaluation.publish_incomplete
|
||||
)
|
||||
evaluations_completes.append(is_complete)
|
||||
# NULL en base => ABS (= -999)
|
||||
eval_df.fillna(scu.NOTES_ABSENCE, inplace=True)
|
||||
# Ce merge met à NULL les élements non présents
|
||||
|
@ -136,10 +136,12 @@ def notes_sem_load_cube(formsemestre):
|
||||
modimpls_evals_poids dict { modimpl.id : evals_poids }
|
||||
modimpls_evals_notes dict { modimpl.id : evals_notes }
|
||||
modimpls_evaluations dict { modimpl.id : liste des évaluations }
|
||||
modimpls_evaluations_complete: {modimpl_id : liste de booleens (complete/non)}
|
||||
"""
|
||||
modimpls_evals_poids = {}
|
||||
modimpls_evals_notes = {}
|
||||
modimpls_evaluations = {}
|
||||
modimpls_evaluations_complete = {}
|
||||
modimpls_notes = []
|
||||
for modimpl in formsemestre.modimpls:
|
||||
evals_notes, evaluations, evaluations_completes = moy_mod.df_load_modimpl_notes(
|
||||
@ -152,12 +154,14 @@ def notes_sem_load_cube(formsemestre):
|
||||
modimpls_evals_poids[modimpl.id] = evals_poids
|
||||
modimpls_evals_notes[modimpl.id] = evals_notes
|
||||
modimpls_evaluations[modimpl.id] = evaluations
|
||||
modimpls_evaluations_complete[modimpl.id] = evaluations_completes
|
||||
modimpls_notes.append(etuds_moy_module)
|
||||
return (
|
||||
notes_sem_assemble_cube(modimpls_notes),
|
||||
modimpls_evals_poids,
|
||||
modimpls_evals_notes,
|
||||
modimpls_evaluations,
|
||||
modimpls_evaluations_complete,
|
||||
)
|
||||
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
"""
|
||||
|
||||
from app import db
|
||||
from app import models
|
||||
from app.models import APO_CODE_STR_LEN
|
||||
from app.models import SHORT_STR_LEN
|
||||
from app.models import CODE_STR_LEN
|
||||
@ -98,6 +99,18 @@ class Identite(db.Model):
|
||||
]
|
||||
return r[0] if r else None
|
||||
|
||||
def etat_inscription(self, formsemestre_id):
|
||||
"""etat de l'inscription de cet étudiant au semestre:
|
||||
False si pas inscrit, ou scu.INSCRIT, DEMISSION, DEF
|
||||
"""
|
||||
# voir si ce n'est pas trop lent:
|
||||
ins = models.FormsemestreInscription.query.filter_by(
|
||||
etudid=self.id, formsemestre_id=formsemestre_id
|
||||
).first()
|
||||
if ins:
|
||||
return ins.etat
|
||||
return False
|
||||
|
||||
|
||||
class Adresse(db.Model):
|
||||
"""Adresse d'un étudiant
|
||||
|
@ -240,6 +240,15 @@ class FormSemestre(db.Model):
|
||||
etudid, self.date_debut.isoformat(), self.date_fin.isoformat()
|
||||
)
|
||||
|
||||
def get_inscrits(self, include_dem=False) -> list:
|
||||
"""Liste des étudiants inscrits à ce semestre
|
||||
Si all, tous les étudiants, avec les démissionnaires.
|
||||
"""
|
||||
if include_dem:
|
||||
return [ins.etud for ins in self.inscriptions]
|
||||
else:
|
||||
return [ins.etud for ins in self.inscriptions if ins.etat == scu.INSCRIT]
|
||||
|
||||
|
||||
# Association id des utilisateurs responsables (aka directeurs des etudes) du semestre
|
||||
notes_formsemestre_responsables = db.Table(
|
||||
@ -386,10 +395,11 @@ class FormsemestreInscription(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
formsemestre_inscription_id = db.synonym("id")
|
||||
|
||||
etudid = db.Column(db.Integer, db.ForeignKey("identite.id"))
|
||||
etudid = db.Column(db.Integer, db.ForeignKey("identite.id"), index=True)
|
||||
formsemestre_id = db.Column(
|
||||
db.Integer,
|
||||
db.ForeignKey("notes_formsemestre.id"),
|
||||
index=True,
|
||||
)
|
||||
etud = db.relationship(
|
||||
Identite,
|
||||
@ -400,7 +410,7 @@ class FormsemestreInscription(db.Model):
|
||||
backref=db.backref("inscriptions", cascade="all, delete-orphan"),
|
||||
)
|
||||
# I inscrit, D demission en cours de semestre, DEF si "defaillant"
|
||||
etat = db.Column(db.String(CODE_STR_LEN))
|
||||
etat = db.Column(db.String(CODE_STR_LEN), index=True)
|
||||
# etape apogee d'inscription (experimental 2020)
|
||||
etape = db.Column(db.String(APO_CODE_STR_LEN))
|
||||
|
||||
|
@ -71,6 +71,12 @@ NOTES_NEUTRALISE = -1000.0 # notes non prises en comptes dans moyennes
|
||||
NOTES_SUPPRESS = -1001.0 # note a supprimer
|
||||
NOTES_ATTENTE = -1002.0 # note "en attente" (se calcule comme une note neutralisee)
|
||||
|
||||
# ---- CODES INSCRIPTION AUX SEMESTRES
|
||||
# (champ etat de FormsemestreInscription)
|
||||
INSCRIT = "I"
|
||||
DEMISSION = "D"
|
||||
DEF = "DEF"
|
||||
|
||||
# Types de modules
|
||||
class ModuleType(IntEnum):
|
||||
"""Code des types de module."""
|
||||
|
@ -7,7 +7,7 @@
|
||||
--couleurFondTitresUE: rgb(206,255,235);
|
||||
--couleurFondTitresRes: rgb(125, 170, 255);
|
||||
--couleurFondTitresSAE: rgb(255, 190, 69);
|
||||
--couleurSecondaire: #0c9;
|
||||
--couleurSecondaire: #fec;
|
||||
--couleurIntense: #c09;
|
||||
--couleurSurlignage: rgba(232, 255, 132, 0.47);
|
||||
}
|
||||
@ -132,7 +132,7 @@ section>div:nth-child(1){
|
||||
}
|
||||
|
||||
.enteteSemestre{
|
||||
color: var(--couleurSecondaire);
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
margin-bottom: 4px;
|
||||
@ -157,7 +157,7 @@ section>div:nth-child(1){
|
||||
/***************/
|
||||
/* Evaluations */
|
||||
/***************/
|
||||
.module, .ue{
|
||||
.module, .ue {
|
||||
background: var(--couleurSecondaire);
|
||||
color: #000;
|
||||
padding: 4px 32px;
|
||||
@ -167,6 +167,9 @@ section>div:nth-child(1){
|
||||
margin: 4px 0 2px 0;
|
||||
overflow: auto;
|
||||
}
|
||||
.sae .module, .sae .module h3 {
|
||||
background: #d3ffff;
|
||||
}
|
||||
h3{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -0,0 +1,32 @@
|
||||
"""index in FormsemestreInscription
|
||||
|
||||
Revision ID: 4f98a8b02c89
|
||||
Revises: a57a6ee2e3cb
|
||||
Create Date: 2021-12-14 16:56:11.034680
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '4f98a8b02c89'
|
||||
down_revision = 'a57a6ee2e3cb'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_index(op.f('ix_notes_formsemestre_inscription_etat'), 'notes_formsemestre_inscription', ['etat'], unique=False)
|
||||
op.create_index(op.f('ix_notes_formsemestre_inscription_etudid'), 'notes_formsemestre_inscription', ['etudid'], unique=False)
|
||||
op.create_index(op.f('ix_notes_formsemestre_inscription_formsemestre_id'), 'notes_formsemestre_inscription', ['formsemestre_id'], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f('ix_notes_formsemestre_inscription_formsemestre_id'), table_name='notes_formsemestre_inscription')
|
||||
op.drop_index(op.f('ix_notes_formsemestre_inscription_etudid'), table_name='notes_formsemestre_inscription')
|
||||
op.drop_index(op.f('ix_notes_formsemestre_inscription_etat'), table_name='notes_formsemestre_inscription')
|
||||
# ### end Alembic commands ###
|
@ -69,7 +69,7 @@ def test_ue_moy(test_client):
|
||||
_ = sco_saisie_notes.notes_add(G.default_user, evaluation1.id, [(etudid, n1)])
|
||||
_ = sco_saisie_notes.notes_add(G.default_user, evaluation2.id, [(etudid, n2)])
|
||||
# Recalcul des moyennes
|
||||
sem_cube, _, _, _ = moy_ue.notes_sem_load_cube(formsemestre)
|
||||
sem_cube, _, _, _, _ = moy_ue.notes_sem_load_cube(formsemestre)
|
||||
etuds = formsemestre.etuds.all()
|
||||
etud_moy_ue = moy_ue.compute_ue_moys(
|
||||
sem_cube, etuds, modimpls, ues, modimpl_inscr_df, modimpl_coefs_df
|
||||
@ -112,7 +112,7 @@ def test_ue_moy(test_client):
|
||||
exception_raised = True
|
||||
assert exception_raised
|
||||
# Recalcule les notes:
|
||||
sem_cube, _, _, _ = moy_ue.notes_sem_load_cube(formsemestre)
|
||||
sem_cube, _, _, _, _ = moy_ue.notes_sem_load_cube(formsemestre)
|
||||
etuds = formsemestre.etuds.all()
|
||||
etud_moy_ue = moy_ue.compute_ue_moys(
|
||||
sem_cube, etuds, modimpls, ues, modimpl_inscr_df, modimpl_coefs_df
|
||||
|
Loading…
Reference in New Issue
Block a user