ScoDoc/app/comp/jury.py

175 lines
6.3 KiB
Python

##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved.
# See LICENSE
##############################################################################
"""Stockage des décisions de jury
"""
import pandas as pd
import sqlalchemy as sa
from app import db
from app.models import FormSemestre, Identite, ScolarFormSemestreValidation, UniteEns
from app.comp.res_cache import ResultatsCache
from app.scodoc import sco_cache
from app.scodoc import codes_cursus
class ValidationsSemestre(ResultatsCache):
"""Les décisions de jury pour un semestre"""
_cached_attrs = (
"decisions_jury",
"decisions_jury_ues",
"ue_capitalisees",
)
def __init__(self, formsemestre: FormSemestre):
super().__init__(formsemestre, sco_cache.ValidationsSemestreCache)
self.decisions_jury = {}
"""Décisions prises dans ce semestre:
{ etudid : { 'code' : None|ATT|..., 'assidu' : 0|1 }}"""
self.decisions_jury_ues = {}
"""Décisions sur des UEs dans ce semestre:
{ etudid : { ue_id : { 'code' : Note|ADM|CMP, 'event_date': "d/m/y", "ects" : x}}}
"""
self.ue_capitalisees: pd.DataFrame = None
"""DataFrame, index etudid
formsemestre_id : origine de l'UE capitalisée
is_external : vrai si validation effectuée dans un semestre extérieur
ue_id : dans le semestre origine (pas toujours de la même formation)
ue_code : code de l'UE
moy_ue : note enregistrée
event_date : date de la validation (jury)"""
if not self.load_cached():
self.compute()
self.store()
def compute(self):
"Charge les résultats de jury et UEs capitalisées"
self.ue_capitalisees = formsemestre_get_ue_capitalisees(self.formsemestre)
self.comp_decisions_jury()
def comp_decisions_jury(self):
"""Cherche les decisions du jury pour le semestre (pas les RCUE).
Calcule les attributs:
decisions_jury = { etudid : { 'code' : None|ATT|..., 'assidu' : 0|1 }}
decision_jury_ues={ etudid :
{ ue_id : { 'code' : Note|ADM|CMP, 'event_date' : "d/m/y", 'ects' : x }}
}
Si la décision n'a pas été prise, la clé etudid n'est pas présente.
Si l'étudiant est défaillant, pas de décisions d'UE.
"""
# repris de NotesTable.comp_decisions_jury pour la compatibilité
decisions_jury_q = ScolarFormSemestreValidation.query.filter_by(
formsemestre_id=self.formsemestre.id
)
decisions_jury = {}
for decision in decisions_jury_q.filter(
db.text("ue_id is NULL") # slt dec. sem.
):
decisions_jury[decision.etudid] = {
"code": decision.code,
"assidu": decision.assidu,
"compense_formsemestre_id": decision.compense_formsemestre_id,
"event_date": decision.event_date.strftime("%d/%m/%Y"),
}
self.decisions_jury = decisions_jury
# UEs: { etudid : { ue_id : {"code":, "ects":, "event_date":} }}
decisions_jury_ues = {}
# Parcours les décisions d'UE:
for decision in (
decisions_jury_q.filter(db.text("ue_id is not NULL"))
.join(UniteEns)
.order_by(UniteEns.numero)
):
if decision.etudid not in decisions_jury_ues:
decisions_jury_ues[decision.etudid] = {}
# Calcul des ECTS associés à cette UE:
if codes_cursus.code_ue_validant(decision.code) and decision.ue:
ects = decision.ue.ects or 0.0 # 0 if None
else:
ects = 0.0
decisions_jury_ues[decision.etudid][decision.ue.id] = {
"code": decision.code,
"ects": ects, # 0. si UE non validée
"event_date": decision.event_date.strftime("%d/%m/%Y"),
}
self.decisions_jury_ues = decisions_jury_ues
def has_decision(self, etud: Identite) -> bool:
"""Vrai si etud a au moins une décision enregistrée depuis
ce semestre (quelle qu'elle soit)
"""
return (etud.id in self.decisions_jury_ues) or (etud.id in self.decisions_jury)
def formsemestre_get_ue_capitalisees(formsemestre: FormSemestre) -> pd.DataFrame:
"""Liste des UE capitalisées (ADM) utilisables dans ce formsemestre
Recherche dans les semestres des formations de même code, avec le même semestre_id
et une date de début antérieure que celle du formsemestre.
Prend aussi les UE externes validées.
Attention: fonction très coûteuse, cacher le résultat.
Résultat: DataFrame avec
etudid (index)
formsemestre_id : origine de l'UE capitalisée
is_external : vrai si validation effectuée dans un semestre extérieur
ue_id : dans le semestre origine (pas toujours de la même formation)
ue_code : code de l'UE
moy_ue :
event_date :
} ]
"""
# Note: pour récupérer aussi les UE validées en CMp ou ADJ, changer une ligne
# and ( SFV.code = 'ADM' or SFV.code = 'ADJ' or SFV.code = 'CMP' )
query = sa.text(
"""
SELECT DISTINCT SFV.*, ue.ue_code
FROM
notes_ue ue,
notes_formations nf,
notes_formations nf2,
scolar_formsemestre_validation SFV,
notes_formsemestre sem,
notes_formsemestre_inscription ins
WHERE ue.formation_id = nf.id
and nf.formation_code = nf2.formation_code
and nf2.id=:formation_id
and ins.etudid = SFV.etudid
and ins.formsemestre_id = :formsemestre_id
and SFV.ue_id = ue.id
and SFV.code = 'ADM'
and ( (sem.id = SFV.formsemestre_id
and sem.date_debut < :date_debut
and sem.semestre_id = :semestre_id )
or (
((SFV.formsemestre_id is NULL) OR (SFV.is_external)) -- les UE externes ou "anterieures"
AND (SFV.semestre_id is NULL OR SFV.semestre_id=:semestre_id)
) )
"""
)
params = {
"formation_id": formsemestre.formation.id,
"formsemestre_id": formsemestre.id,
"semestre_id": formsemestre.semestre_id,
"date_debut": formsemestre.date_debut,
}
with db.engine.begin() as connection:
df = pd.read_sql_query(query, connection, params=params, index_col="etudid")
return df