############################################################################## # ScoDoc # Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## """Cursus en BUT Classe raccordant avec ScoDoc 7: ScoDoc 7 utilisait sco_cursus_dut.SituationEtudCursus Ce module définit une classe SituationEtudCursusBUT avec la même interface. """ import collections from typing import Union from flask import g, url_for from app import db from app import log from app.comp.res_but import ResultatsSemestreBUT from app.comp.res_compat import NotesTableCompat from app.comp import res_sem from app.models import formsemestre from app.models.but_refcomp import ( ApcAnneeParcours, ApcCompetence, ApcNiveau, ApcParcours, ApcParcoursNiveauCompetence, ) from app.models import Scolog, ScolarAutorisationInscription from app.models.but_validations import ( ApcValidationAnnee, ApcValidationRCUE, RegroupementCoherentUE, ) from app.models.etudiants import Identite from app.models.formations import Formation from app.models.formsemestre import FormSemestre, FormSemestreInscription from app.models.ues import UniteEns from app.models.validations import ScolarFormSemestreValidation from app.scodoc import sco_codes_parcours as sco_codes from app.scodoc.sco_codes_parcours import RED, UE_STANDARD from app.scodoc import sco_utils as scu from app.scodoc.sco_exceptions import ScoNoReferentielCompetences, ScoValueError from app.scodoc import sco_cursus_dut class SituationEtudCursusBUT(sco_cursus_dut.SituationEtudCursusClassic): """Pour compat ScoDoc 7: à revoir pour le BUT""" def __init__(self, etud: dict, formsemestre_id: int, res: ResultatsSemestreBUT): super().__init__(etud, formsemestre_id, res) # Ajustements pour le BUT self.can_compensate_with_prev = False # jamais de compensation à la mode DUT def check_compensation_dut(self, semc: dict, ntc: NotesTableCompat): "Jamais de compensation façon DUT" return False def parcours_validated(self): "True si le parcours est validé" return False # XXX TODO class EtudCursusBUT: """L'état de l'étudiant dans son cursus BUT Liste des niveaux validés/à valider """ def __init__(self, etud: Identite, formation: Formation): """formation indique la spécialité préparée""" # Vérifie que l'étudiant est bien inscrit à un sem. de cette formation if formation.id not in ( ins.formsemestre.formation.id for ins in etud.formsemestre_inscriptions ): raise ScoValueError( f"{etud.nomprenom} non inscrit dans {formation.titre} v{formation.version}" ) if not formation.referentiel_competence: raise ScoNoReferentielCompetences(formation=formation) # self.etud = etud self.formation = formation self.inscriptions = sorted( [ ins for ins in etud.formsemestre_inscriptions if ins.formsemestre.formation.referentiel_competence.id == formation.referentiel_competence.id ], key=lambda s: (s.formsemestre.semestre_id, s.formsemestre.date_debut), ) "Liste des inscriptions aux sem. de la formation, triées par indice et chronologie" self.parcour: ApcParcours = self.inscriptions[-1].parcour "Le parcour à valider: celui du DERNIER semestre suivi (peut être None)" self.niveaux_by_annee = {} "{ annee : liste des niveaux à valider }" self.niveaux: dict[int, ApcNiveau] = {} "cache les niveaux" for annee in (1, 2, 3): niveaux_d = formation.referentiel_competence.get_niveaux_by_parcours( annee, self.parcour )[1] # groupe les niveaux de tronc commun et ceux spécifiques au parcour self.niveaux_by_annee[annee] = niveaux_d["TC"] + ( niveaux_d[self.parcour.id] if self.parcour else [] ) self.niveaux.update( {niveau.id: niveau for niveau in self.niveaux_by_annee[annee]} ) # Probablement inutile: # # Cherche les validations de jury enregistrées pour chaque niveau # self.validations_by_niveau = collections.defaultdict(lambda: []) # " { niveau_id : [ ApcValidationRCUE ] }" # for validation_rcue in ApcValidationRCUE.query.filter_by(etud=etud): # self.validations_by_niveau[validation_rcue.niveau().id].append( # validation_rcue # ) # self.validation_by_niveau = { # niveau_id: sorted( # validations, key=lambda v: sco_codes.BUT_CODES_ORDERED[v.code] # )[0] # for niveau_id, validations in self.validations_by_niveau.items() # } # "{ niveau_id : meilleure validation pour ce niveau }" self.validation_par_competence_et_annee = {} "{ competence_id : { 'BUT1' : validation_rcue, ... } }" for validation_rcue in ApcValidationRCUE.query.filter_by(etud=etud): niveau = validation_rcue.niveau() if not niveau.competence.id in self.validation_par_competence_et_annee: self.validation_par_competence_et_annee[niveau.competence.id] = {} previous_validation = self.validation_par_competence_et_annee.get( niveau.competence.id ) # prend la "meilleure" validation if (not previous_validation) or ( sco_codes.BUT_CODES_ORDERED[validation_rcue.code] > sco_codes.BUT_CODES_ORDERED[previous_validation.code] ): self.validation_par_competence_et_annee[niveau.competence.id][ niveau.annee ] = validation_rcue self.competences = { competence.id: competence for competence in ( self.parcour.query_competences() if self.parcour else self.formation.referentiel_competence.get_competences_tronc_commun() ) } "cache { competence_id : competence }" def to_dict(self): """ { competence_id : { annee : meilleure_validation } } """ return { competence.id: { annee: { self.validation_par_competence_et_annee.get(competence.id, {}).get( annee ) } for annee in ("BUT1", "BUT2", "BUT3") } for competence in self.competences.values() }