BUT: dispenses d'UE capitalisées. Voir #537.

This commit is contained in:
Emmanuel Viennet 2022-12-01 13:00:14 +01:00 committed by iziram
parent 02ec55ca18
commit c8c05ecd77
9 changed files with 39 additions and 71 deletions

View File

@ -33,7 +33,10 @@ import pandas as pd
from app import db from app import db
from app import models from app import models
from app.models import ( from app.models import (
DispenseUE,
FormSemestre, FormSemestre,
FormSemestreInscription,
Identite,
Module, Module,
ModuleImpl, ModuleImpl,
ModuleUECoef, ModuleUECoef,
@ -215,6 +218,31 @@ def notes_sem_load_cube(formsemestre: FormSemestre) -> tuple:
) )
def load_dispense_ues(
formsemestre: FormSemestre, etudids: pd.Index, ues: list[UniteEns]
) -> set[tuple[int, int]]:
"""Construit l'ensemble des
etudids = modimpl_inscr_df.index, # les etudids
ue_ids : modimpl_coefs_df.index, # les UE du formsemestre sans les UE bonus sport
Résultat: set de (etudid, ue_id).
"""
dispense_ues = set()
ue_sem_by_code = {ue.ue_code: ue for ue in ues}
# Prend toutes les dispenses obtenues par des étudiants de ce formsemestre,
# puis filtre sur inscrits et code d'UE UE
for dispense_ue in DispenseUE.query.join(
Identite, FormSemestreInscription
).filter_by(formsemestre_id=formsemestre.id):
if dispense_ue.etudid in etudids:
# UE dans le semestre avec même code ?
ue = ue_sem_by_code.get(dispense_ue.ue.ue_code)
if ue is not None:
dispense_ues.add((dispense_ue.etudid, ue.id))
return dispense_ues
def compute_ue_moys_apc( def compute_ue_moys_apc(
sem_cube: np.array, sem_cube: np.array,
etuds: list, etuds: list,

View File

@ -72,7 +72,7 @@ class ResultatsSemestreBUT(NotesTableCompat):
modimpl.module.ue.type != UE_SPORT modimpl.module.ue.type != UE_SPORT
for modimpl in self.formsemestre.modimpls_sorted for modimpl in self.formsemestre.modimpls_sorted
] ]
self.dispense_ues = DispenseUE.load_formsemestre_dispense_ues_set( self.dispense_ues = moy_ue.load_dispense_ues(
self.formsemestre, self.modimpl_inscr_df.index, self.ues self.formsemestre, self.modimpl_inscr_df.index, self.ues
) )
self.etud_moy_ue = moy_ue.compute_ue_moys_apc( self.etud_moy_ue = moy_ue.compute_ue_moys_apc(

View File

@ -65,10 +65,6 @@ class Identite(db.Model):
passive_deletes=True, passive_deletes=True,
) )
# Relations avec les assiduites et les justificatifs
assiduites = db.relationship("Assiduite", backref="etudiant", lazy="dynamic")
justificatifs = db.relationship("Justificatif", backref="etudiant", lazy="dynamic")
def __repr__(self): def __repr__(self):
return ( return (
f"<Etud {self.id}/{self.departement.acronym} {self.nom!r} {self.prenom!r}>" f"<Etud {self.id}/{self.departement.acronym} {self.nom!r} {self.prenom!r}>"

View File

@ -256,23 +256,12 @@ class UniteEns(db.Model):
class DispenseUE(db.Model): class DispenseUE(db.Model):
"""Dispense d'UE """Dispense d'UE
Utilisé en APC (BUT) pour indiquer les étudiants redoublants avec une UE capitalisée Utilisé en PCC (BUT) pour indiquer les étudiants redoublants avec une UE capitalisée
qu'ils ne refont pas. qu'ils ne refont pas.
La dispense d'UE n'est PAS une validation:
- elle n'est pas affectée par les décisions de jury (pas effacée)
- elle est associée à un formsemestre
- elle ne permet pas la délivrance d'ECTS ou du diplôme.
On utilise cette dispense et non une "inscription" par souci d'efficacité:
en général, la grande majorité des étudiants suivront toutes les UEs de leur parcours,
la dispense étant une exception.
""" """
__table_args__ = (db.UniqueConstraint("formsemestre_id", "ue_id", "etudid"),) __table_args__ = (db.UniqueConstraint("ue_id", "etudid"),)
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
formsemestre_id = formsemestre_id = db.Column(
db.Integer, db.ForeignKey("notes_formsemestre.id"), index=True, nullable=True
)
ue_id = db.Column( ue_id = db.Column(
db.Integer, db.Integer,
db.ForeignKey(UniteEns.id, ondelete="CASCADE"), db.ForeignKey(UniteEns.id, ondelete="CASCADE"),
@ -291,25 +280,3 @@ class DispenseUE(db.Model):
def __repr__(self) -> str: def __repr__(self) -> str:
return f"""<{self.__class__.__name__} {self.id} etud={ return f"""<{self.__class__.__name__} {self.id} etud={
repr(self.etud)} ue={repr(self.ue)}>""" repr(self.etud)} ue={repr(self.ue)}>"""
@classmethod
def load_formsemestre_dispense_ues_set(
cls, formsemestre: "FormSemestre", etudids: pd.Index, ues: list[UniteEns]
) -> set[tuple[int, int]]:
"""Construit l'ensemble des
etudids = modimpl_inscr_df.index, # les etudids
ue_ids : modimpl_coefs_df.index, # les UE du formsemestre sans les UE bonus sport
Résultat: set de (etudid, ue_id).
"""
# Prend toutes les dispenses obtenues par des étudiants de ce formsemestre,
# puis filtre sur inscrits et ues
ue_ids = {ue.id for ue in ues}
dispense_ues = {
(dispense_ue.etudid, dispense_ue.ue_id)
for dispense_ue in DispenseUE.query.filter_by(
formsemestre_id=formsemestre.id
)
if dispense_ue.etudid in etudids and dispense_ue.ue_id in ue_ids
}
return dispense_ues

View File

@ -36,7 +36,7 @@ from flask_login import current_user
from app import db, log from app import db, log
from app.models import Evaluation, ModuleImpl, ScolarNews from app.models import ModuleImpl, ScolarNews
from app.models.evaluations import evaluation_enrich_dict, check_evaluation_args from app.models.evaluations import evaluation_enrich_dict, check_evaluation_args
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb

View File

@ -400,9 +400,7 @@ def moduleimpl_inscriptions_stats(formsemestre_id):
# Etudiants "dispensés" d'une UE (capitalisée) # Etudiants "dispensés" d'une UE (capitalisée)
ues_cap_info = get_etuds_with_capitalized_ue(formsemestre_id) ues_cap_info = get_etuds_with_capitalized_ue(formsemestre_id)
if ues_cap_info: if ues_cap_info:
H.append( H.append('<h3>Étudiants avec UEs capitalisées:</h3><ul class="ue_inscr_list">')
'<h3>Étudiants avec UEs capitalisées (ADM):</h3><ul class="ue_inscr_list">'
)
ues = [ ues = [
sco_edit_ue.ue_list({"ue_id": ue_id})[0] for ue_id in ues_cap_info.keys() sco_edit_ue.ue_list({"ue_id": ue_id})[0] for ue_id in ues_cap_info.keys()
] ]
@ -470,9 +468,8 @@ def moduleimpl_inscriptions_stats(formsemestre_id):
if can_change: if can_change:
H.append( H.append(
f"""<div><a class="stdlink" href="{ f"""<div><a class="stdlink" href="{
url_for("notes.etud_inscrit_ue", url_for("notes.etud_inscrit_ue", scodoc_dept=g.scodoc_dept, etudid=etud["etudid"],
scodoc_dept=g.scodoc_dept, etudid=etud["etudid"], formsemestre_id=formsemestre_id, ue_id=ue["ue_id"])
formsemestre_id=formsemestre_id, ue_id=ue["ue_id"])
}">inscrire à {"" if is_apc else "tous les modules de"} cette UE</a></div> }">inscrire à {"" if is_apc else "tous les modules de"} cette UE</a></div>
""" """
) )

View File

@ -1621,24 +1621,10 @@ def etud_desinscrit_ue(etudid, formsemestre_id, ue_id):
ue = UniteEns.query.get_or_404(ue_id) ue = UniteEns.query.get_or_404(ue_id)
formsemestre = FormSemestre.query.get_or_404(formsemestre_id) formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
if ue.formation.is_apc(): if ue.formation.is_apc():
if ( if DispenseUE.query.filter_by(etudid=etudid, ue_id=ue_id).count() == 0:
DispenseUE.query.filter_by( disp = DispenseUE(ue_id=ue_id, etudid=etudid)
formsemestre_id=formsemestre_id, etudid=etudid, ue_id=ue_id
).count()
== 0
):
disp = DispenseUE(
formsemestre_id=formsemestre_id, ue_id=ue_id, etudid=etudid
)
db.session.add(disp) db.session.add(disp)
db.session.commit() db.session.commit()
log(f"etud_desinscrit_ue {etud} {ue}")
Scolog.logdb(
method="etud_desinscrit_ue",
etudid=etud.id,
msg=f"Désinscription de l'UE {ue.acronyme} de {formsemestre.titre_annee()}",
commit=True,
)
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre.id) sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre.id)
else: else:
sco_moduleimpl_inscriptions.do_etud_desinscrit_ue_classic( sco_moduleimpl_inscriptions.do_etud_desinscrit_ue_classic(

View File

@ -1,7 +1,7 @@
# -*- mode: python -*- # -*- mode: python -*-
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
SCOVERSION = "9.4.34" SCOVERSION = "9.4.7"
SCONAME = "ScoDoc" SCONAME = "ScoDoc"

View File

@ -123,13 +123,7 @@ def test_ue_moy(test_client):
modimpl.module.ue.type != UE_SPORT for modimpl in formsemestre.modimpls_sorted modimpl.module.ue.type != UE_SPORT for modimpl in formsemestre.modimpls_sorted
] ]
etud_moy_ue = moy_ue.compute_ue_moys_apc( etud_moy_ue = moy_ue.compute_ue_moys_apc(
sem_cube, sem_cube, etuds, modimpls, modimpl_inscr_df, modimpl_coefs_df, modimpl_mask
etuds,
modimpls,
modimpl_inscr_df,
modimpl_coefs_df,
modimpl_mask,
set(),
) )
assert etud_moy_ue[ue1.id][etudid] == n1 assert etud_moy_ue[ue1.id][etudid] == n1
assert etud_moy_ue[ue2.id][etudid] == n1 assert etud_moy_ue[ue2.id][etudid] == n1