forked from ScoDoc/ScoDoc
WIP: moyennes modules, evals, bulletins.
This commit is contained in:
parent
4d6d7ad168
commit
25a441f7f2
@ -37,13 +37,14 @@ class ResultatsSemestreBUT:
|
|||||||
"modimpls_evals_notes",
|
"modimpls_evals_notes",
|
||||||
"etud_moy_gen",
|
"etud_moy_gen",
|
||||||
"etud_moy_gen_ranks",
|
"etud_moy_gen_ranks",
|
||||||
|
"modimpls_evaluations_complete",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, formsemestre):
|
def __init__(self, formsemestre):
|
||||||
self.formsemestre = formsemestre
|
self.formsemestre = formsemestre
|
||||||
self.ues = formsemestre.query_ues().all()
|
self.ues = formsemestre.query_ues().all()
|
||||||
self.modimpls = formsemestre.modimpls.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.etud_index = {e.id: idx for idx, e in enumerate(self.etuds)}
|
||||||
self.saes = [
|
self.saes = [
|
||||||
m for m in self.modimpls if m.module.module_type == scu.ModuleType.SAE
|
m for m in self.modimpls if m.module.module_type == scu.ModuleType.SAE
|
||||||
@ -77,7 +78,8 @@ class ResultatsSemestreBUT:
|
|||||||
self.sem_cube,
|
self.sem_cube,
|
||||||
self.modimpls_evals_poids,
|
self.modimpls_evals_poids,
|
||||||
self.modimpls_evals_notes,
|
self.modimpls_evals_notes,
|
||||||
_,
|
modimpls_evaluations,
|
||||||
|
self.modimpls_evaluations_complete,
|
||||||
) = moy_ue.notes_sem_load_cube(self.formsemestre)
|
) = moy_ue.notes_sem_load_cube(self.formsemestre)
|
||||||
self.modimpl_inscr_df = inscr_mod.df_load_modimpl_inscr(self.formsemestre)
|
self.modimpl_inscr_df = inscr_mod.df_load_modimpl_inscr(self.formsemestre)
|
||||||
self.modimpl_coefs_df, _, _ = moy_ue.df_load_modimpl_coefs(
|
self.modimpl_coefs_df, _, _ = moy_ue.df_load_modimpl_coefs(
|
||||||
@ -151,10 +153,17 @@ class ResultatsSemestreBUT:
|
|||||||
for mi in modimpls:
|
for mi in modimpls:
|
||||||
mod_idx = self.modimpl_coefs_df.columns.get_loc(mi.id)
|
mod_idx = self.modimpl_coefs_df.columns.get_loc(mi.id)
|
||||||
# moyennes indicatives (moyennes de moyennes d'UE)
|
# moyennes indicatives (moyennes de moyennes d'UE)
|
||||||
moyennes_etuds = np.nan_to_num(
|
try:
|
||||||
np.nanmean(self.sem_cube[:, mod_idx, :], axis=1),
|
moyennes_etuds = np.nan_to_num(
|
||||||
copy=False,
|
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] = {
|
d[mi.module.code] = {
|
||||||
"id": mi.id,
|
"id": mi.id,
|
||||||
"titre": mi.module.titre,
|
"titre": mi.module.titre,
|
||||||
@ -166,15 +175,16 @@ class ResultatsSemestreBUT:
|
|||||||
),
|
),
|
||||||
"moyenne": {
|
"moyenne": {
|
||||||
# moyenne indicative de module: moyenne des UE, ignorant celles sans notes (nan)
|
# 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()),
|
"min": fmt_note(moyennes_etuds.min()),
|
||||||
"max": fmt_note(moyennes_etuds.max()),
|
"max": fmt_note(moyennes_etuds.max()),
|
||||||
"moy": fmt_note(moyennes_etuds.mean()),
|
"moy": fmt_note(moyennes_etuds.mean()),
|
||||||
},
|
},
|
||||||
"evaluations": [
|
"evaluations": [
|
||||||
self.etud_eval_results(etud, e)
|
self.etud_eval_results(etud, e)
|
||||||
for e in mi.evaluations
|
for eidx, e in enumerate(mi.evaluations)
|
||||||
if e.visibulletin
|
if e.visibulletin
|
||||||
|
and self.modimpls_evaluations_complete[mi.id][eidx]
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
return d
|
return d
|
||||||
|
@ -89,7 +89,7 @@ def check_moduleimpl_conformity(
|
|||||||
|
|
||||||
|
|
||||||
def df_load_modimpl_notes(moduleimpl_id: int) -> tuple:
|
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)
|
colonnes: le nom de la colonne est l'evaluation_id (int)
|
||||||
index (lignes): etudid (int)
|
index (lignes): etudid (int)
|
||||||
|
|
||||||
@ -111,9 +111,12 @@ def df_load_modimpl_notes(moduleimpl_id: int) -> tuple:
|
|||||||
|
|
||||||
N'utilise pas de cache ScoDoc.
|
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 = [
|
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()
|
evaluations = Evaluation.query.filter_by(moduleimpl_id=moduleimpl_id).all()
|
||||||
if evaluations:
|
if evaluations:
|
||||||
@ -138,9 +141,10 @@ def df_load_modimpl_notes(moduleimpl_id: int) -> tuple:
|
|||||||
index_col="etudid",
|
index_col="etudid",
|
||||||
dtype=np.float64,
|
dtype=np.float64,
|
||||||
)
|
)
|
||||||
evaluations_completes.append(
|
is_complete = (
|
||||||
len(eval_df) == nb_inscrits_module or evaluation.publish_incomplete
|
len(eval_df) == nb_inscrits_module or evaluation.publish_incomplete
|
||||||
)
|
)
|
||||||
|
evaluations_completes.append(is_complete)
|
||||||
# NULL en base => ABS (= -999)
|
# NULL en base => ABS (= -999)
|
||||||
eval_df.fillna(scu.NOTES_ABSENCE, inplace=True)
|
eval_df.fillna(scu.NOTES_ABSENCE, inplace=True)
|
||||||
# Ce merge met à NULL les élements non présents
|
# 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_poids dict { modimpl.id : evals_poids }
|
||||||
modimpls_evals_notes dict { modimpl.id : evals_notes }
|
modimpls_evals_notes dict { modimpl.id : evals_notes }
|
||||||
modimpls_evaluations dict { modimpl.id : liste des évaluations }
|
modimpls_evaluations dict { modimpl.id : liste des évaluations }
|
||||||
|
modimpls_evaluations_complete: {modimpl_id : liste de booleens (complete/non)}
|
||||||
"""
|
"""
|
||||||
modimpls_evals_poids = {}
|
modimpls_evals_poids = {}
|
||||||
modimpls_evals_notes = {}
|
modimpls_evals_notes = {}
|
||||||
modimpls_evaluations = {}
|
modimpls_evaluations = {}
|
||||||
|
modimpls_evaluations_complete = {}
|
||||||
modimpls_notes = []
|
modimpls_notes = []
|
||||||
for modimpl in formsemestre.modimpls:
|
for modimpl in formsemestre.modimpls:
|
||||||
evals_notes, evaluations, evaluations_completes = moy_mod.df_load_modimpl_notes(
|
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_poids[modimpl.id] = evals_poids
|
||||||
modimpls_evals_notes[modimpl.id] = evals_notes
|
modimpls_evals_notes[modimpl.id] = evals_notes
|
||||||
modimpls_evaluations[modimpl.id] = evaluations
|
modimpls_evaluations[modimpl.id] = evaluations
|
||||||
|
modimpls_evaluations_complete[modimpl.id] = evaluations_completes
|
||||||
modimpls_notes.append(etuds_moy_module)
|
modimpls_notes.append(etuds_moy_module)
|
||||||
return (
|
return (
|
||||||
notes_sem_assemble_cube(modimpls_notes),
|
notes_sem_assemble_cube(modimpls_notes),
|
||||||
modimpls_evals_poids,
|
modimpls_evals_poids,
|
||||||
modimpls_evals_notes,
|
modimpls_evals_notes,
|
||||||
modimpls_evaluations,
|
modimpls_evaluations,
|
||||||
|
modimpls_evaluations_complete,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
|
from app import models
|
||||||
from app.models import APO_CODE_STR_LEN
|
from app.models import APO_CODE_STR_LEN
|
||||||
from app.models import SHORT_STR_LEN
|
from app.models import SHORT_STR_LEN
|
||||||
from app.models import CODE_STR_LEN
|
from app.models import CODE_STR_LEN
|
||||||
@ -98,6 +99,18 @@ class Identite(db.Model):
|
|||||||
]
|
]
|
||||||
return r[0] if r else None
|
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):
|
class Adresse(db.Model):
|
||||||
"""Adresse d'un étudiant
|
"""Adresse d'un étudiant
|
||||||
|
@ -240,6 +240,15 @@ 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:
|
||||||
|
"""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
|
# Association id des utilisateurs responsables (aka directeurs des etudes) du semestre
|
||||||
notes_formsemestre_responsables = db.Table(
|
notes_formsemestre_responsables = db.Table(
|
||||||
@ -386,10 +395,11 @@ class FormsemestreInscription(db.Model):
|
|||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
formsemestre_inscription_id = db.synonym("id")
|
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(
|
formsemestre_id = db.Column(
|
||||||
db.Integer,
|
db.Integer,
|
||||||
db.ForeignKey("notes_formsemestre.id"),
|
db.ForeignKey("notes_formsemestre.id"),
|
||||||
|
index=True,
|
||||||
)
|
)
|
||||||
etud = db.relationship(
|
etud = db.relationship(
|
||||||
Identite,
|
Identite,
|
||||||
@ -400,7 +410,7 @@ class FormsemestreInscription(db.Model):
|
|||||||
backref=db.backref("inscriptions", cascade="all, delete-orphan"),
|
backref=db.backref("inscriptions", cascade="all, delete-orphan"),
|
||||||
)
|
)
|
||||||
# I inscrit, D demission en cours de semestre, DEF si "defaillant"
|
# 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 apogee d'inscription (experimental 2020)
|
||||||
etape = db.Column(db.String(APO_CODE_STR_LEN))
|
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_SUPPRESS = -1001.0 # note a supprimer
|
||||||
NOTES_ATTENTE = -1002.0 # note "en attente" (se calcule comme une note neutralisee)
|
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
|
# Types de modules
|
||||||
class ModuleType(IntEnum):
|
class ModuleType(IntEnum):
|
||||||
"""Code des types de module."""
|
"""Code des types de module."""
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
--couleurFondTitresUE: rgb(206,255,235);
|
--couleurFondTitresUE: rgb(206,255,235);
|
||||||
--couleurFondTitresRes: rgb(125, 170, 255);
|
--couleurFondTitresRes: rgb(125, 170, 255);
|
||||||
--couleurFondTitresSAE: rgb(255, 190, 69);
|
--couleurFondTitresSAE: rgb(255, 190, 69);
|
||||||
--couleurSecondaire: #0c9;
|
--couleurSecondaire: #fec;
|
||||||
--couleurIntense: #c09;
|
--couleurIntense: #c09;
|
||||||
--couleurSurlignage: rgba(232, 255, 132, 0.47);
|
--couleurSurlignage: rgba(232, 255, 132, 0.47);
|
||||||
}
|
}
|
||||||
@ -132,7 +132,7 @@ section>div:nth-child(1){
|
|||||||
}
|
}
|
||||||
|
|
||||||
.enteteSemestre{
|
.enteteSemestre{
|
||||||
color: var(--couleurSecondaire);
|
color: black;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
@ -157,7 +157,7 @@ section>div:nth-child(1){
|
|||||||
/***************/
|
/***************/
|
||||||
/* Evaluations */
|
/* Evaluations */
|
||||||
/***************/
|
/***************/
|
||||||
.module, .ue{
|
.module, .ue {
|
||||||
background: var(--couleurSecondaire);
|
background: var(--couleurSecondaire);
|
||||||
color: #000;
|
color: #000;
|
||||||
padding: 4px 32px;
|
padding: 4px 32px;
|
||||||
@ -167,6 +167,9 @@ section>div:nth-child(1){
|
|||||||
margin: 4px 0 2px 0;
|
margin: 4px 0 2px 0;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
.sae .module, .sae .module h3 {
|
||||||
|
background: #d3ffff;
|
||||||
|
}
|
||||||
h3{
|
h3{
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
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, evaluation1.id, [(etudid, n1)])
|
||||||
_ = sco_saisie_notes.notes_add(G.default_user, evaluation2.id, [(etudid, n2)])
|
_ = sco_saisie_notes.notes_add(G.default_user, evaluation2.id, [(etudid, n2)])
|
||||||
# Recalcul des moyennes
|
# 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()
|
etuds = formsemestre.etuds.all()
|
||||||
etud_moy_ue = moy_ue.compute_ue_moys(
|
etud_moy_ue = moy_ue.compute_ue_moys(
|
||||||
sem_cube, etuds, modimpls, ues, modimpl_inscr_df, modimpl_coefs_df
|
sem_cube, etuds, modimpls, ues, modimpl_inscr_df, modimpl_coefs_df
|
||||||
@ -112,7 +112,7 @@ def test_ue_moy(test_client):
|
|||||||
exception_raised = True
|
exception_raised = True
|
||||||
assert exception_raised
|
assert exception_raised
|
||||||
# Recalcule les notes:
|
# 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()
|
etuds = formsemestre.etuds.all()
|
||||||
etud_moy_ue = moy_ue.compute_ue_moys(
|
etud_moy_ue = moy_ue.compute_ue_moys(
|
||||||
sem_cube, etuds, modimpls, ues, modimpl_inscr_df, modimpl_coefs_df
|
sem_cube, etuds, modimpls, ues, modimpl_inscr_df, modimpl_coefs_df
|
||||||
|
Loading…
Reference in New Issue
Block a user