############################################################################## # ScoDoc # Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## """Stockage des décisions de jury """ import pandas as pd 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 = """ 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)s and ins.etudid = SFV.etudid and ins.formsemestre_id = %(formsemestre_id)s and SFV.ue_id = ue.id and SFV.code = 'ADM' and ( (sem.id = SFV.formsemestre_id and sem.date_debut < %(date_debut)s and sem.semestre_id = %(semestre_id)s ) 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)s) ) ) """ params = { "formation_id": formsemestre.formation.id, "formsemestre_id": formsemestre.id, "semestre_id": formsemestre.semestre_id, "date_debut": formsemestre.date_debut, } df = pd.read_sql_query(query, db.engine, params=params, index_col="etudid") return df