diff --git a/app/__init__.py b/app/__init__.py index 6946415f..6fb6cf05 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -502,12 +502,10 @@ def clear_scodoc_cache(): # --------- Logging -def log(msg: str, silent_test=True): +def log(msg: str): """log a message. If Flask app, use configured logger, else stderr. """ - if silent_test and current_app and current_app.config["TESTING"]: - return try: dept = getattr(g, "scodoc_dept", "") msg = f" ({dept}) {msg}" diff --git a/app/api/absences.py b/app/api/absences.py index e098c196..b049bfb7 100644 --- a/app/api/absences.py +++ b/app/api/absences.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## """ScoDoc 9 API : Absences diff --git a/app/api/billets_absences.py b/app/api/billets_absences.py index f15a9b49..69d6186f 100644 --- a/app/api/billets_absences.py +++ b/app/api/billets_absences.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/api/departements.py b/app/api/departements.py index 0782453c..bc6c38d6 100644 --- a/app/api/departements.py +++ b/app/api/departements.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/api/etudiants.py b/app/api/etudiants.py index c1ebd07f..cb150fe7 100644 --- a/app/api/etudiants.py +++ b/app/api/etudiants.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/api/evaluations.py b/app/api/evaluations.py index a8fb2c26..69e18e7b 100644 --- a/app/api/evaluations.py +++ b/app/api/evaluations.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/api/formations.py b/app/api/formations.py index 002ce739..c784f110 100644 --- a/app/api/formations.py +++ b/app/api/formations.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/api/formsemestres.py b/app/api/formsemestres.py index 1c1db110..acc749ca 100644 --- a/app/api/formsemestres.py +++ b/app/api/formsemestres.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/api/jury.py b/app/api/jury.py index b58147c7..f31d1a78 100644 --- a/app/api/jury.py +++ b/app/api/jury.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/api/logos.py b/app/api/logos.py index 5c31ed75..677c2b7b 100644 --- a/app/api/logos.py +++ b/app/api/logos.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/api/partitions.py b/app/api/partitions.py index a6283f48..699f18e7 100644 --- a/app/api/partitions.py +++ b/app/api/partitions.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/api/tools.py b/app/api/tools.py index 90750e12..ec0b8561 100644 --- a/app/api/tools.py +++ b/app/api/tools.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## """ScoDoc 9 API : outils diff --git a/app/api/users.py b/app/api/users.py index ee2cce24..962aa9e7 100644 --- a/app/api/users.py +++ b/app/api/users.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/but/apc_edit_ue.py b/app/but/apc_edit_ue.py index 2222dc65..01b1aacd 100644 --- a/app/but/apc_edit_ue.py +++ b/app/but/apc_edit_ue.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/but/bulletin_but.py b/app/but/bulletin_but.py index f21e8ff6..d8dbd61c 100644 --- a/app/but/bulletin_but.py +++ b/app/but/bulletin_but.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## @@ -361,7 +361,7 @@ class BulletinBUT: "formsemestre_id": formsemestre.id, "etat_inscription": etat_inscription, "options": sco_preferences.bulletin_option_affichage( - formsemestre.id, self.prefs + formsemestre, self.prefs ), } if not published: @@ -465,6 +465,7 @@ class BulletinBUT: "ressources": {}, "saes": {}, "ues": {}, + "ues_capitalisees": {}, } ) diff --git a/app/but/bulletin_but_pdf.py b/app/but/bulletin_but_pdf.py index 526465f5..1ba7a760 100644 --- a/app/but/bulletin_but_pdf.py +++ b/app/but/bulletin_but_pdf.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/but/bulletin_but_xml_compat.py b/app/but/bulletin_but_xml_compat.py index 01e5c7cc..00564f14 100644 --- a/app/but/bulletin_but_xml_compat.py +++ b/app/but/bulletin_but_xml_compat.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/but/cursus_but.py b/app/but/cursus_but.py index e3ae8bb6..15ac1fa3 100644 --- a/app/but/cursus_but.py +++ b/app/but/cursus_but.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/but/forms/jury_but_forms.py b/app/but/forms/jury_but_forms.py index 0f371960..67bd955a 100644 --- a/app/but/forms/jury_but_forms.py +++ b/app/but/forms/jury_but_forms.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/but/forms/refcomp_forms.py b/app/but/forms/refcomp_forms.py index 41ffff4a..2b817473 100644 --- a/app/but/forms/refcomp_forms.py +++ b/app/but/forms/refcomp_forms.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/but/import_refcomp.py b/app/but/import_refcomp.py index e1e62d48..666deb54 100644 --- a/app/but/import_refcomp.py +++ b/app/but/import_refcomp.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## from xml.etree import ElementTree diff --git a/app/but/jury_but.py b/app/but/jury_but.py index 6cc201b6..bf0e75eb 100644 --- a/app/but/jury_but.py +++ b/app/but/jury_but.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## @@ -91,9 +91,15 @@ from app.models.ues import UniteEns from app.models.validations import ScolarFormSemestreValidation from app.scodoc import sco_cache from app.scodoc import sco_codes_parcours as sco_codes -from app.scodoc.sco_codes_parcours import CODES_UE_VALIDES, RED, UE_STANDARD +from app.scodoc.sco_codes_parcours import ( + BUT_CODES_ORDERED, + CODES_RCUE_VALIDES, + CODES_UE_VALIDES, + RED, + UE_STANDARD, +) from app.scodoc import sco_utils as scu -from app.scodoc.sco_exceptions import ScoException, ScoValueError +from app.scodoc.sco_exceptions import ScoNoReferentielCompetences, ScoValueError class NoRCUEError(ScoValueError): @@ -170,7 +176,7 @@ class DecisionsProposees: def __repr__(self) -> str: return f"""<{self.__class__.__name__} valid={self.code_valide - } codes={self.codes} explanation={self.explanation}""" + } codes={self.codes} explanation={self.explanation}>""" class DecisionsProposeesAnnee(DecisionsProposees): @@ -205,6 +211,8 @@ class DecisionsProposeesAnnee(DecisionsProposees): formsemestre: FormSemestre, ): assert formsemestre.formation.is_apc() + if formsemestre.formation.referentiel_competence is None: + raise ScoNoReferentielCompetences(formation=formsemestre.formation) super().__init__(etud=etud) self.formsemestre = formsemestre "le formsemestre utilisé pour construire ce deca" @@ -348,8 +356,9 @@ class DecisionsProposeesAnnee(DecisionsProposees): ) "vrai si l'année est réussie, tous niveaux validables ou validés par le jury" self.valide_moitie_rcue = self.nb_validables > (self.nb_competences // 2) - "Peut passer si plus de la moitié validables et tous > 8" + "Vrai si plus de la moitié des RCUE validables" self.passage_de_droit = self.valide_moitie_rcue and (self.nb_rcues_under_8 == 0) + "Vrai si peut passer dans l'année BUT suivante: plus de la moitié validables et tous > 8" # XXX TODO ajouter condition pour passage en S5 # Enfin calcule les codes des UE: @@ -362,7 +371,6 @@ class DecisionsProposeesAnnee(DecisionsProposees): "s" if plural else ""} sur {self.nb_competences}""" if self.admis: self.codes = [sco_codes.ADM] + self.codes - self.explanation = expl_rcues # elif not self.jury_annuel: # self.codes = [] # pas de décision annuelle sur semestres impairs elif self.inscription_etat != scu.INSCRIT: @@ -378,9 +386,9 @@ class DecisionsProposeesAnnee(DecisionsProposees): sco_codes.ABL, sco_codes.EXCLU, ] + expl_rcues = "" elif self.passage_de_droit: self.codes = [sco_codes.PASD, sco_codes.ADJ] + self.codes - self.explanation = expl_rcues elif self.valide_moitie_rcue: # mais au moins 1 rcue insuffisante self.codes = [ sco_codes.RED, @@ -388,7 +396,7 @@ class DecisionsProposeesAnnee(DecisionsProposees): sco_codes.PAS1NCI, sco_codes.ADJ, ] + self.codes - self.explanation = expl_rcues + f" et {self.nb_rcues_under_8} < 8" + expl_rcues += f" et {self.nb_rcues_under_8} < 8" else: self.codes = [ sco_codes.RED, @@ -397,17 +405,21 @@ class DecisionsProposeesAnnee(DecisionsProposees): sco_codes.ADJ, sco_codes.PASD, # voir #488 (discutable, conventions locales) ] + self.codes - self.explanation = ( - expl_rcues - + f""" et {self.nb_rcues_under_8} - niveau{'x' if self.nb_rcues_under_8 > 1 else ''} < 8""" - ) + expl_rcues += f""" et {self.nb_rcues_under_8} niveau{'x' if self.nb_rcues_under_8 > 1 else ''} < 8""" + # Si l'un des semestres est extérieur, propose ADM if ( self.formsemestre_impair and self.formsemestre_impair.modalite == "EXT" ) or (self.formsemestre_pair and self.formsemestre_pair.modalite == "EXT"): self.codes.insert(0, sco_codes.ADM) - + self.explanation = f"
{expl_rcues}
" + messages = self.descr_pb_coherence() + if messages: + self.explanation += ( + '
' + + '
'.join(messages) + + "
" + ) # def infos(self) -> str: @@ -581,9 +593,10 @@ class DecisionsProposeesAnnee(DecisionsProposees): def compute_decisions_niveaux(self) -> dict[int, "DecisionsProposeesRCUE"]: """Pour chaque niveau de compétence de cette année, construit - le DecisionsProposeesRCUE, - ou None s'il n'y en a pas + le DecisionsProposeesRCUE, ou None s'il n'y en a pas (ne devrait pas arriver car compute_rcues_annee vérifie déjà cela). + + Appelé à la construction du deca, donc avant décisions manuelles. Return: { niveau_id : DecisionsProposeesRCUE } """ # Retrouve le RCUE associé à chaque niveau @@ -630,6 +643,7 @@ class DecisionsProposeesAnnee(DecisionsProposees): def record_form(self, form: dict): """Enregistre les codes de jury en base + à partir d'un dict représentant le formulaire jury BUT: form dict: - 'code_ue_1896' : 'AJ' code pour l'UE id 1896 - 'code_rcue_6" : 'ADM' code pour le RCUE du niveau 6 @@ -639,32 +653,42 @@ class DecisionsProposeesAnnee(DecisionsProposees): et qu'il n'y en a pas déjà, enregistre ceux par défaut. """ log("jury_but.DecisionsProposeesAnnee.record_form") - with sco_cache.DeferredSemCacheManager(): - for key in form: - code = form[key] - # Codes d'UE - m = re.match(r"^code_ue_(\d+)$", key) + code_annee = None + codes_rcues = [] # [ (dec_rcue, code), ... ] + codes_ues = [] # [ (dec_ue, code), ... ] + for key in form: + code = form[key] + # Codes d'UE + m = re.match(r"^code_ue_(\d+)$", key) + if m: + ue_id = int(m.group(1)) + dec_ue = self.decisions_ues.get(ue_id) + if not dec_ue: + raise ScoValueError(f"UE invalide ue_id={ue_id}") + codes_ues.append((dec_ue, code)) + else: + # Codes de RCUE + m = re.match(r"^code_rcue_(\d+)$", key) if m: - ue_id = int(m.group(1)) - dec_ue = self.decisions_ues.get(ue_id) - if not dec_ue: - raise ScoValueError(f"UE invalide ue_id={ue_id}") - dec_ue.record(code) - else: - # Codes de RCUE - m = re.match(r"^code_rcue_(\d+)$", key) - if m: - niveau_id = int(m.group(1)) - dec_rcue = self.decisions_rcue_by_niveau.get(niveau_id) - if not dec_rcue: - raise ScoValueError(f"RCUE invalide niveau_id={niveau_id}") - dec_rcue.record(code) - elif key == "code_annee": - # Code annuel - self.record(code) + niveau_id = int(m.group(1)) + dec_rcue = self.decisions_rcue_by_niveau.get(niveau_id) + if not dec_rcue: + raise ScoValueError(f"RCUE invalide niveau_id={niveau_id}") + codes_rcues.append((dec_rcue, code)) + elif key == "code_annee": + # Code annuel + code_annee = code + with sco_cache.DeferredSemCacheManager(): + # Enregistre les codes, dans l'ordre UE, RCUE, Année + for dec_ue, code in codes_ues: + dec_ue.record(code) + for dec_rcue, code in codes_rcues: + dec_rcue.record(code) + self.record(code_annee) self.record_all() - db.session.commit() + + db.session.commit() def record(self, code: str, no_overwrite=False): """Enregistre le code de l'année, et au besoin l'autorisation d'inscription. @@ -682,7 +706,7 @@ class DecisionsProposeesAnnee(DecisionsProposees): return # no change if self.validation: db.session.delete(self.validation) - db.session.flush() + db.session.commit() if code is None: self.validation = None else: @@ -693,12 +717,15 @@ class DecisionsProposeesAnnee(DecisionsProposees): annee_scolaire=self.annee_scolaire(), code=code, ) + db.session.add(self.validation) + db.session.commit() + log(f"Recording {self}: {code}") Scolog.logdb( method="jury_but", etudid=self.etud.id, msg=f"Validation année BUT{self.annee_but}: {code}", ) - db.session.add(self.validation) + # --- Autorisation d'inscription dans semestre suivant ? if self.formsemestre_pair is not None: if code is None: @@ -729,29 +756,49 @@ class DecisionsProposeesAnnee(DecisionsProposees): if self.formsemestre_pair is not None: sco_cache.invalidate_formsemestre(formsemestre_id=self.formsemestre_pair.id) - def record_all(self): + def record_all(self, no_overwrite: bool = True): """Enregistre les codes qui n'ont pas été spécifiés par le formulaire, - et sont donc en mode "automatique" + et sont donc en mode "automatique". + - Si "à cheval", ne modifie pas les codes UE de l'année scolaire précédente. + - Pour les RCUE: n'enregistre que si la nouvelle décision est plus favorable que l'ancienne. """ - decisions = ( - list(self.decisions_ues.values()) - + list(self.decisions_rcue_by_niveau.values()) - + [self] - ) - for dec in decisions: - if not dec.recorded: + # Toujours valider dans l'ordre UE, RCUE, Année: + annee_scolaire = self.formsemestre.annee_scolaire() + # UEs + for dec_ue in self.decisions_ues.values(): + if ( + not dec_ue.recorded + ) and dec_ue.formsemestre.annee_scolaire() == annee_scolaire: # rappel: le code par défaut est en tête - code = dec.codes[0] if dec.codes else None + code = dec_ue.codes[0] if dec_ue.codes else None # enregistre le code jury seulement s'il n'y a pas déjà de code - dec.record(code, no_overwrite=True) + # (no_overwrite=True) sauf en mode test yaml + dec_ue.record(code, no_overwrite=no_overwrite) + # RCUE : enregistre seulement si pas déjà validé "mieux" + for dec_rcue in self.decisions_rcue_by_niveau.values(): + code = dec_rcue.codes[0] if dec_rcue.codes else None + if (not dec_rcue.recorded) and ( + (not dec_rcue.validation) + or BUT_CODES_ORDERED.get(dec_rcue.validation.code, 0) + < BUT_CODES_ORDERED.get(code, 0) + ): + dec_rcue.record(code, no_overwrite=no_overwrite) + # Année: + if not self.recorded: + # rappel: le code par défaut est en tête + code = self.codes[0] if self.codes else None + # enregistre le code jury seulement s'il n'y a pas déjà de code + # (no_overwrite=True) sauf en mode test yaml + self.record(code, no_overwrite=no_overwrite) def erase(self, only_one_sem=False): """Efface les décisions de jury de cet étudiant pour cette année: décisions d'UE, de RCUE, d'année, et autorisations d'inscription émises. Efface même si étudiant DEM ou DEF. + Si à cheval, n'efface que pour le semestre d'origine du deca. """ - if only_one_sem: + if only_one_sem or self.a_cheval: # N'efface que les autorisations venant de ce semestre, # et les validations de ses UEs ScolarAutorisationInscription.delete_autorisation_etud( @@ -779,13 +826,23 @@ class DecisionsProposeesAnnee(DecisionsProposees): ordre=self.annee_but, ) for validation in validations: + db.session.delete(validation) Scolog.logdb( "jury_but", etudid=self.etud.id, msg=f"Validation année BUT{self.annee_but}: effacée", ) - db.session.delete(validation) - db.session.flush() + + # Efface éventuelles validations de semestre + # (en principe inutilisées en BUT) + # et autres UEs (en cas de changement d'architecture de formation depuis le jury ?) + # + for validation in ScolarFormSemestreValidation.query.filter_by( + etudid=self.etud.id, formsemestre_id=self.formsemestre_id + ): + db.session.delete(validation) + + db.session.commit() self.invalidate_formsemestre_cache() def get_autorisations_passage(self) -> list[int]: @@ -828,6 +885,27 @@ class DecisionsProposeesAnnee(DecisionsProposees): validations.append(", ".join(v for v in valids if v)) return line_sep.join(validations) + def descr_pb_coherence(self) -> list[str]: + """Description d'éventuels problèmes de cohérence entre + les décisions *enregistrées* d'UE et de RCUE. + Note: en principe, la cohérence RCUE/UE est assurée au moment de + l'enregistrement (record). + Mais la base peut avoir été modifiée par d'autres voies. + """ + messages = [] + for dec_rcue in self.decisions_rcue_by_niveau.values(): + if dec_rcue.code_valide in CODES_RCUE_VALIDES: + for ue in (dec_rcue.rcue.ue_1, dec_rcue.rcue.ue_2): + dec_ue = self.decisions_ues.get(ue.id) + if dec_ue: + if dec_ue.code_valide not in CODES_UE_VALIDES: + messages.append( + f"L'UE {ue.acronyme} n'est pas validée mais son RCUE l'est !" + ) + else: + messages.append(f"L'UE {ue.acronyme} n'a pas décision (???)") + return messages + def list_ue_parcour_etud( formsemestre: FormSemestre, etud: Identite, res: ResultatsSemestreBUT @@ -873,6 +951,7 @@ class DecisionsProposeesRCUE(DecisionsProposees): inscription_etat: str = scu.INSCRIT, ): super().__init__(etud=dec_prop_annee.etud) + self.deca = dec_prop_annee self.rcue = rcue if rcue is None: # RCUE non dispo, eg un seul semestre self.codes = [] @@ -904,9 +983,30 @@ class DecisionsProposeesRCUE(DecisionsProposees): or dec_prop_annee.formsemestre_pair.modalite == "EXT" ): self.codes.insert(0, sco_codes.ADM) + # S'il y a une décision enregistrée: si elle est plus favorable que celle que l'on + # proposerait, la place en tête. + # Sinon, la place en seconde place + if self.code_valide and self.code_valide != self.codes[0]: + code_default = self.codes[0] + if self.code_valide in self.codes: + self.codes.remove(self.code_valide) + if sco_codes.BUT_CODES_ORDERED.get( + self.code_valide, 0 + ) > sco_codes.BUT_CODES_ORDERED.get(code_default, 0): + self.codes.insert(0, self.code_valide) + else: + self.codes.insert(1, self.code_valide) + + def __repr__(self) -> str: + return f"""<{self.__class__.__name__} rcue={self.rcue} valid={self.code_valide + } codes={self.codes} explanation={self.explanation}""" def record(self, code: str, no_overwrite=False): - """Enregistre le code""" + """Enregistre le code RCUE. + Note: + - si le RCUE est ADJ, les UE non validées sont passées à ADJ + XXX on pourra imposer ici d'autres règles de cohérence + """ if self.rcue is None: return # pas de RCUE a enregistrer if self.inscription_etat != scu.INSCRIT: @@ -921,13 +1021,10 @@ class DecisionsProposeesRCUE(DecisionsProposees): parcours_id = self.parcour.id if self.parcour is not None else None if self.validation: db.session.delete(self.validation) - db.session.flush() + db.session.commit() if code is None: self.validation = None else: - # log( - # f"RCUE.record(etudid={self.etud.id}, ue1_id={self.rcue.ue_1.id}, ue2_id={self.rcue.ue_2.id}, code={code} )" - # ) self.validation = ApcValidationRCUE( etudid=self.etud.id, formsemestre_id=self.rcue.formsemestre_2.id, @@ -936,12 +1033,25 @@ class DecisionsProposeesRCUE(DecisionsProposees): parcours_id=parcours_id, code=code, ) + db.session.add(self.validation) + db.session.commit() Scolog.logdb( method="jury_but", etudid=self.etud.id, msg=f"Validation {self.rcue}: {code}", + commit=True, ) - db.session.add(self.validation) + log(f"rcue.record {self}: {code}") + + # Modifie au besoin les codes d'UE + if code == "ADJ": + deca = self.deca + for ue_id in (self.rcue.ue_1.id, self.rcue.ue_2.id): + dec_ue = deca.decisions_ues.get(ue_id) + if dec_ue and dec_ue.code_valide not in CODES_UE_VALIDES: + log(f"rcue.record: force ADJ sur {dec_ue}") + dec_ue.record("ADJ") + if self.rcue.formsemestre_1 is not None: sco_cache.invalidate_formsemestre( formsemestre_id=self.rcue.formsemestre_1.id @@ -950,6 +1060,7 @@ class DecisionsProposeesRCUE(DecisionsProposees): sco_cache.invalidate_formsemestre( formsemestre_id=self.rcue.formsemestre_2.id ) + self.code_valide = code # mise à jour état self.recorded = True def erase(self): @@ -957,6 +1068,7 @@ class DecisionsProposeesRCUE(DecisionsProposees): # par prudence, on requete toutes les validations, en cas de doublons validations = self.rcue.query_validations() for validation in validations: + log(f"DecisionsProposeesRCUE: deleting {validation}") db.session.delete(validation) db.session.flush() @@ -1010,14 +1122,14 @@ class DecisionsProposeesUE(DecisionsProposees): ): # Une UE peut être validée plusieurs fois en cas de redoublement (qu'elle soit capitalisée ou non) # mais ici on a restreint au formsemestre donc une seule (prend la première) - self.validation = ScolarFormSemestreValidation.query.filter_by( + validation = ScolarFormSemestreValidation.query.filter_by( etudid=etud.id, formsemestre_id=formsemestre.id, ue_id=ue.id ).first() super().__init__( etud=etud, - code_valide=self.validation.code if self.validation is not None else None, + code_valide=validation.code if validation is not None else None, ) - # log(f"built {self}") + self.validation = validation self.formsemestre = formsemestre self.ue: UniteEns = ue self.rcue: RegroupementCoherentUE = None @@ -1056,11 +1168,11 @@ class DecisionsProposeesUE(DecisionsProposees): def __repr__(self) -> str: return f"""<{self.__class__.__name__} ue={self.ue.acronyme} valid={self.code_valide - } codes={self.codes} explanation={self.explanation}""" + } codes={self.codes} explanation={self.explanation}>""" def set_rcue(self, rcue: RegroupementCoherentUE): """Rattache cette UE à un RCUE. Cela peut modifier les codes - proposés (si compensation)""" + proposés par compute_codes() (si compensation)""" self.rcue = rcue def compute_codes(self): @@ -1098,6 +1210,7 @@ class DecisionsProposeesUE(DecisionsProposees): method="jury_but", etudid=self.etud.id, msg=f"Validation UE {self.ue.id} {self.ue.acronyme}: effacée", + commit=True, ) else: self.validation = ScolarFormSemestreValidation( @@ -1107,15 +1220,18 @@ class DecisionsProposeesUE(DecisionsProposees): code=code, moy_ue=self.moy_ue, ) + db.session.add(self.validation) + db.session.commit() Scolog.logdb( method="jury_but", etudid=self.etud.id, msg=f"Validation UE {self.ue.id} {self.ue.acronyme}({self.moy_ue}): {code}", + commit=True, ) - db.session.add(self.validation) log(f"DecisionsProposeesUE: recording {self.validation}") sco_cache.invalidate_formsemestre(formsemestre_id=self.formsemestre.id) + self.code_valide = code # mise à jour self.recorded = True def erase(self): @@ -1126,13 +1242,14 @@ class DecisionsProposeesUE(DecisionsProposees): ) for validation in validations: log(f"DecisionsProposeesUE: deleting {validation}") + db.session.delete(validation) Scolog.logdb( method="jury_but", etudid=self.etud.id, msg=f"Validation UE {validation.ue.id} {validation.ue.acronyme}: effacée", ) - db.session.delete(validation) - db.session.flush() + + db.session.commit() def descr_validation(self) -> str: """Description validation niveau enregistrée, pour PV jury. @@ -1148,7 +1265,7 @@ class BUTCursusEtud: # WIP TODO def __init__(self, formsemestre: FormSemestre, etud: Identite): if formsemestre.formation.referentiel_competence is None: - raise ScoException("BUTCursusEtud: pas de référentiel de compétences") + raise ScoNoReferentielCompetences(formation=formsemestre.formation) assert len(etud.formsemestre_inscriptions) > 0 self.formsemestre = formsemestre self.etud = etud diff --git a/app/but/jury_but_pv.py b/app/but/jury_but_pv.py index 2940345e..208d1ef2 100644 --- a/app/but/jury_but_pv.py +++ b/app/but/jury_but_pv.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/but/jury_but_recap.py b/app/but/jury_but_recap.py index e3c5df9c..38657957 100644 --- a/app/but/jury_but_recap.py +++ b/app/but/jury_but_recap.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## @@ -32,7 +32,7 @@ from app.scodoc.sco_codes_parcours import ( from app.scodoc import sco_formsemestre_status from app.scodoc import sco_pvjury from app.scodoc import sco_utils as scu -from app.scodoc.sco_exceptions import ScoValueError +from app.scodoc.sco_exceptions import ScoNoReferentielCompetences def formsemestre_saisie_jury_but( @@ -63,14 +63,7 @@ def formsemestre_saisie_jury_but( # raise ScoValueError("Cette page ne fonctionne que sur les semestres pairs") if formsemestre2.formation.referentiel_competence is None: - raise ScoValueError( - """ -

Pas de référentiel de compétences associé à la formation !

-

Pour associer un référentiel, passer par le menu Semestre / - Voir la formation... et suivre le lien "associer à un référentiel - de compétences" - """ - ) + raise ScoNoReferentielCompetences(formation=formsemestre2.formation) rows, titles, column_ids, jury_stats = get_jury_but_table( formsemestre2, read_only=read_only, mode=mode diff --git a/app/but/jury_but_validation_auto.py b/app/but/jury_but_validation_auto.py index 918bd571..d5e308ab 100644 --- a/app/but/jury_but_validation_auto.py +++ b/app/but/jury_but_validation_auto.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## @@ -15,12 +15,19 @@ from app.scodoc import sco_cache from app.scodoc.sco_exceptions import ScoValueError -def formsemestre_validation_auto_but(formsemestre: FormSemestre, only_adm=True) -> int: +def formsemestre_validation_auto_but( + formsemestre: FormSemestre, only_adm: bool = True, no_overwrite: bool = True +) -> int: """Calcul automatique des décisions de jury sur une année BUT. + Ne modifie jamais de décisions de l'année scolaire précédente, même + si on a des RCUE "à cheval". Normalement, only_adm est True et on n'enregistre que les décisions ADM (de droit). Si only_adm est faux, on enregistre la première décision proposée par ScoDoc (mode à n'utiliser que pour les tests) + Si no_overwrite est vrai (défaut), ne ré-écrit jamais les codes déjà enregistrés + (utiliser faux pour certains tests) + Returns: nombre d'étudiants "admis" """ if not formsemestre.formation.is_apc(): @@ -33,7 +40,7 @@ def formsemestre_validation_auto_but(formsemestre: FormSemestre, only_adm=True) if deca.admis: # année réussie nb_admis += 1 if deca.admis or not only_adm: - deca.record_all() + deca.record_all(no_overwrite=no_overwrite) db.session.commit() return nb_admis diff --git a/app/but/jury_but_view.py b/app/but/jury_but_view.py index 1a19a5b7..3140bd02 100644 --- a/app/but/jury_but_view.py +++ b/app/but/jury_but_view.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## @@ -30,6 +30,7 @@ from app.models import ( Identite, UniteEns, ScolarAutorisationInscription, + ScolarFormSemestreValidation, ) from app.scodoc import html_sco_header from app.scodoc.sco_exceptions import ScoValueError @@ -41,32 +42,21 @@ def show_etud(deca: DecisionsProposeesAnnee, read_only: bool = True) -> str: Si pas read_only, menus sélection codes jury. """ H = [] - if deca.code_valide and not read_only: - erase_span = f"""effacer décisions""" - else: - erase_span = "" H.append("""

""") - if deca.jury_annuel: - H.append( - f""" -
- Décision de jury pour l'année : { - _gen_but_select("code_annee", deca.codes, deca.code_valide, - disabled=True, klass="manual") - } - ({'non ' if deca.code_valide is None else ''}enregistrée) - {erase_span} -
+ H.append( + f""" +
+ Décision de jury pour l'année : { + _gen_but_select("code_annee", deca.codes, deca.code_valide, + disabled=True, klass="manual") + } + ({deca.code_valide or 'non'} enregistrée) +
""" - ) - div_explanation = f"""
{deca.explanation}
""" - else: - H.append("""
Pas de décision annuelle (sem. impair).
""") - div_explanation = "" + ) + div_explanation = f"""
{deca.explanation}
""" + H.append("""
""") formsemestre_1 = deca.formsemestre_impair @@ -119,8 +109,8 @@ def show_etud(deca: DecisionsProposeesAnnee, read_only: bool = True) -> str: if ue.niveau_competence and ue.niveau_competence.id == niveau.id ] ue_pair = ues[0] if ues else None - # Les UEs à afficher, toujours en readonly - # sur le formsemestre de l'année précédente du redoublant + # Les UEs à afficher, + # qui seront toujours en readonly sur le formsemestre de l'année précédente du redoublant ues_ro = [ ( ue_impair, @@ -143,6 +133,7 @@ def show_etud(deca: DecisionsProposeesAnnee, read_only: bool = True) -> str: deca.decisions_ues[ue.id], disabled=read_only or ue_read_only, annee_prec=ue_read_only, + niveau_id=ue.niveau_competence.id, ) ) else: @@ -161,6 +152,7 @@ def _gen_but_select( code_valide: str, disabled: bool = False, klass: str = "", + data: dict = {}, ) -> str: "Le menu html select avec les codes" # if disabled: # mauvaise idée car le disabled est traité en JS @@ -176,8 +168,11 @@ def _gen_but_select( ) return f""" """ @@ -187,6 +182,7 @@ def _gen_but_niveau_ue( dec_ue: DecisionsProposeesUE, disabled: bool = False, annee_prec: bool = False, + niveau_id: int = None, ) -> str: if dec_ue.ue_status and dec_ue.ue_status["is_capitalized"]: moy_ue_str = f"""{ @@ -207,7 +203,14 @@ def _gen_but_niveau_ue( """ else: moy_ue_str = f"""{scu.fmt_note(dec_ue.moy_ue)}""" - scoplement = "" + if dec_ue.code_valide: + scoplement = f"""
+ Code {dec_ue.code_valide} enregistré le {dec_ue.validation.event_date.strftime("%d/%m/%Y")} + à {dec_ue.validation.event_date.strftime("%Hh%M")} +
+ """ + else: + scoplement = "" return f"""
{ _gen_but_select("code_ue_"+str(ue.id), - dec_ue.codes, - dec_ue.code_valide, disabled=disabled + dec_ue.codes, + dec_ue.code_valide, + disabled=disabled, + klass=f"code_ue ue_rcue_{niveau_id}" if not disabled else "" ) }
@@ -245,21 +250,29 @@ def _gen_but_rcue(dec_rcue: DecisionsProposeesRCUE, niveau: ApcNiveau) -> str: else "" ) + # Déjà enregistré ? + niveau_rcue_class = "" + if dec_rcue.code_valide is not None and dec_rcue.codes: + if dec_rcue.code_valide == dec_rcue.codes[0]: + niveau_rcue_class = "recorded" + else: + niveau_rcue_class = "recorded_different" + return f""" -
{scu.fmt_note(dec_rcue.rcue.moy_rcue)}
{scoplement}
-
{_gen_but_select("code_rcue_"+str(niveau.id), + {_gen_but_select("code_rcue_"+str(niveau.id), dec_rcue.codes, dec_rcue.code_valide, - disabled=True, klass="manual" + disabled=True, + klass="manual code_rcue", + data = { "niveau_id" : str(niveau.id)} )} -
""" @@ -278,17 +291,15 @@ def jury_but_semestriel( semestre_terminal = ( formsemestre.semestre_id >= formsemestre.formation.get_parcours().NB_SEM ) + autorisations_passage = ScolarAutorisationInscription.query.filter_by( + etudid=etud.id, + origin_formsemestre_id=formsemestre.id, + ).all() # Par défaut: autorisé à passer dans le semestre suivant si sem. impair, # ou si décision déjà enregistrée: est_autorise_a_passer = (formsemestre.semestre_id % 2) or ( formsemestre.semestre_id + 1 - ) in ( - a.semestre_id - for a in ScolarAutorisationInscription.query.filter_by( - etudid=etud.id, - origin_formsemestre_id=formsemestre.id, - ) - ) + ) in (a.semestre_id for a in autorisations_passage) decisions_ues = { ue.id: DecisionsProposeesUE(etud, formsemestre, ue, inscription_etat) for ue in ues @@ -312,7 +323,9 @@ def jury_but_semestriel( flash("codes enregistrés") if not semestre_terminal: if request.form.get("autorisation_passage"): - if not est_autorise_a_passer: + if not formsemestre.semestre_id + 1 in ( + a.semestre_id for a in autorisations_passage + ): ScolarAutorisationInscription.autorise_etud( etud.id, formsemestre.formation.formation_code, @@ -351,7 +364,7 @@ def jury_but_semestriel( warning = "" H = [ html_sco_header.sco_header( - page_title="Validation BUT", + page_title=f"Validation BUT S{formsemestre.semestre_id}", formsemestre_id=formsemestre.id, etudid=etud.id, cssstyles=("css/jury_but.css",), @@ -372,25 +385,35 @@ def jury_but_semestriel( }">{etud.photo_html(title="fiche de " + etud.nomprenom)} -

Jury sur un semestre BUT isolé

+

Jury sur un semestre BUT isolé (ne concerne que les UEs)

{warning} -
+ """, ] - if (not read_only) and any([dec.code_valide for dec in decisions_ues.values()]): - erase_span = f"""effacer les décisions enregistrées""" - else: - erase_span = "Cet étudiant n'a aucune décision enregistrée pour ce semestre." + + erase_span = "" + if not read_only: + # Requête toutes les validations (pas seulement celles du deca courant), + # au cas où: changement d'architecture, saisie en mode classique, ... + validations = ScolarFormSemestreValidation.query.filter_by( + etudid=etud.id, formsemestre_id=formsemestre.id + ).all() + if validations: + erase_span = f"""effacer les décisions enregistrées""" + else: + erase_span = ( + "Cet étudiant n'a aucune décision enregistrée pour ce semestre." + ) H.append( f"""
- {erase_span}
Unités d'enseignement de S{formsemestre.semestre_id}:
""" @@ -440,6 +463,9 @@ def jury_but_semestriel( autoriser à passer dans le semestre S{formsemestre.semestre_id+1} + {("(autorisations enregistrées: " + ' '.join( + 'S' + str(a.semestre_id or '') for a in autorisations_passage) + ")" + ) if autorisations_passage else ""} """ @@ -447,9 +473,10 @@ def jury_but_semestriel( else: H.append("""
dernier semestre de la formation.
""") H.append( - """ + f"""
- + + {erase_span}
""" ) diff --git a/app/comp/aux_stats.py b/app/comp/aux_stats.py index 3f25a5ad..b0d47924 100644 --- a/app/comp/aux_stats.py +++ b/app/comp/aux_stats.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/comp/bonus_spo.py b/app/comp/bonus_spo.py index 436ace89..2728f336 100644 --- a/app/comp/bonus_spo.py +++ b/app/comp/bonus_spo.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/comp/df_cache.py b/app/comp/df_cache.py index 5b555fec..1dea77c2 100644 --- a/app/comp/df_cache.py +++ b/app/comp/df_cache.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/comp/jury.py b/app/comp/jury.py index cad1b2a5..efee1445 100644 --- a/app/comp/jury.py +++ b/app/comp/jury.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/comp/moy_mat.py b/app/comp/moy_mat.py index 0a752263..a0120f66 100644 --- a/app/comp/moy_mat.py +++ b/app/comp/moy_mat.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/comp/moy_mod.py b/app/comp/moy_mod.py index 963cae78..dfced34c 100644 --- a/app/comp/moy_mod.py +++ b/app/comp/moy_mod.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/comp/moy_sem.py b/app/comp/moy_sem.py index 195c5dc8..089cc68a 100644 --- a/app/comp/moy_sem.py +++ b/app/comp/moy_sem.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/comp/moy_ue.py b/app/comp/moy_ue.py index efca6662..8c1d68da 100644 --- a/app/comp/moy_ue.py +++ b/app/comp/moy_ue.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/comp/res_but.py b/app/comp/res_but.py index ba0f1314..28d8cf72 100644 --- a/app/comp/res_but.py +++ b/app/comp/res_but.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/comp/res_cache.py b/app/comp/res_cache.py index 890526b9..941caad2 100644 --- a/app/comp/res_cache.py +++ b/app/comp/res_cache.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/comp/res_classic.py b/app/comp/res_classic.py index f2393676..077c745c 100644 --- a/app/comp/res_classic.py +++ b/app/comp/res_classic.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/comp/res_common.py b/app/comp/res_common.py index 3c06451f..628db367 100644 --- a/app/comp/res_common.py +++ b/app/comp/res_common.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/comp/res_compat.py b/app/comp/res_compat.py index 1e4c80bc..8a835694 100644 --- a/app/comp/res_compat.py +++ b/app/comp/res_compat.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/comp/res_sem.py b/app/comp/res_sem.py index 0897da01..35affb5c 100644 --- a/app/comp/res_sem.py +++ b/app/comp/res_sem.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/email.py b/app/email.py index ebd3ae0d..241ef079 100644 --- a/app/email.py +++ b/app/email.py @@ -1,7 +1,7 @@ # -*- coding: UTF-8 -* ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/entreprises/app_relations_entreprises.py b/app/entreprises/app_relations_entreprises.py index c989c12f..8d1c9c0c 100644 --- a/app/entreprises/app_relations_entreprises.py +++ b/app/entreprises/app_relations_entreprises.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/entreprises/forms.py b/app/entreprises/forms.py index 44e679f9..fa43c148 100644 --- a/app/entreprises/forms.py +++ b/app/entreprises/forms.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/forms/main/config_apo.py b/app/forms/main/config_apo.py index 946e6ff2..f607822c 100644 --- a/app/forms/main/config_apo.py +++ b/app/forms/main/config_apo.py @@ -5,7 +5,7 @@ # # ScoDoc # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/forms/main/config_logos.py b/app/forms/main/config_logos.py index b35ac34e..2a0051f0 100644 --- a/app/forms/main/config_logos.py +++ b/app/forms/main/config_logos.py @@ -5,7 +5,7 @@ # # ScoDoc # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/forms/main/config_main.py b/app/forms/main/config_main.py index 27eb9329..205c88fa 100644 --- a/app/forms/main/config_main.py +++ b/app/forms/main/config_main.py @@ -5,7 +5,7 @@ # # ScoDoc # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/forms/main/create_dept.py b/app/forms/main/create_dept.py index c0e18eff..1ab7f667 100644 --- a/app/forms/main/create_dept.py +++ b/app/forms/main/create_dept.py @@ -5,7 +5,7 @@ # # ScoDoc # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/models/but_refcomp.py b/app/models/but_refcomp.py index c7330877..647e6ee8 100644 --- a/app/models/but_refcomp.py +++ b/app/models/but_refcomp.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## """ScoDoc 9 models : Référentiel Compétence BUT 2021 @@ -14,7 +14,7 @@ import sqlalchemy from app import db from app.scodoc.sco_utils import ModuleType -from app.scodoc.sco_exceptions import ScoValueError +from app.scodoc.sco_exceptions import ScoNoReferentielCompetences # from https://stackoverflow.com/questions/2537471/method-of-iterating-over-sqlalchemy-models-defined-columns @@ -54,13 +54,15 @@ class ApcReferentielCompetences(db.Model, XMLModel): "Référentiel de compétence d'une spécialité" id = db.Column(db.Integer, primary_key=True) dept_id = db.Column(db.Integer, db.ForeignKey("departement.id"), index=True) - annexe = db.Column(db.Text()) - specialite = db.Column(db.Text()) - specialite_long = db.Column(db.Text()) - type_titre = db.Column(db.Text()) - type_structure = db.Column(db.Text()) + annexe = db.Column(db.Text()) # '1', '22', ... + specialite = db.Column(db.Text()) # 'CJ', 'RT', 'INFO', ... + specialite_long = db.Column( + db.Text() + ) # 'Carrière Juridique', 'Réseaux et télécommunications', ... + type_titre = db.Column(db.Text()) # 'B.U.T.' + type_structure = db.Column(db.Text()) # 'type1', 'type2', ... type_departement = db.Column(db.Text()) # "secondaire", "tertiaire" - version_orebut = db.Column(db.Text()) + version_orebut = db.Column(db.Text()) # '2021-12-11 00:00:00' _xml_attribs = { # Orébut xml attrib : attribute "type": "type_titre", "version": "version_orebut", @@ -322,9 +324,8 @@ class ApcNiveau(db.Model, XMLModel): if annee not in {1, 2, 3}: raise ValueError("annee invalide pour un parcours BUT") if referentiel_competence is None: - raise ScoValueError( - "Pas de référentiel de compétences associé à la formation !" - ) + raise ScoNoReferentielCompetences() + annee_formation = f"BUT{annee}" if parcour is None: return ApcNiveau.query.filter( diff --git a/app/models/but_validations.py b/app/models/but_validations.py index ad191d29..52826003 100644 --- a/app/models/but_validations.py +++ b/app/models/but_validations.py @@ -68,7 +68,8 @@ class ApcValidationRCUE(db.Model): "description en HTML" return f"""Décision sur RCUE {self.ue1.acronyme}/{self.ue2.acronyme}: {self.code} - enregistrée le {self.date.strftime("%d/%m/%Y")}""" + enregistrée le {self.date.strftime("%d/%m/%Y")} + à {self.date.strftime("%Hh%M")}""" def niveau(self) -> ApcNiveau: """Le niveau de compétence associé à cet RCUE.""" @@ -180,8 +181,9 @@ class RegroupementCoherentUE: return self.query_validations().count() > 0 def est_compensable(self): - """Vrai si ce RCUE est validable par compensation - c'est à dire que sa moyenne est > 10 avec une UE < 10 + """Vrai si ce RCUE est validable (uniquement) par compensation + c'est à dire que sa moyenne est > 10 avec une UE < 10. + Note: si ADM, est_compensable est faux. """ return ( (self.moy_rcue is not None) diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index fb84fb02..e985a153 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -1,7 +1,7 @@ # -*- coding: UTF-8 -* ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## @@ -56,9 +56,9 @@ class FormSemestre(db.Model): dept_id = db.Column(db.Integer, db.ForeignKey("departement.id"), index=True) formation_id = db.Column(db.Integer, db.ForeignKey("notes_formations.id")) semestre_id = db.Column(db.Integer, nullable=False, default=1, server_default="1") - titre = db.Column(db.Text()) - date_debut = db.Column(db.Date()) - date_fin = db.Column(db.Date()) + titre = db.Column(db.Text(), nullable=False) + date_debut = db.Column(db.Date(), nullable=False) + date_fin = db.Column(db.Date(), nullable=False) etat = db.Column( db.Boolean(), nullable=False, default=True, server_default="true" ) # False si verrouillé @@ -87,7 +87,10 @@ class FormSemestre(db.Model): ) # couleur fond bulletins HTML: bul_bgcolor = db.Column( - db.String(SHORT_STR_LEN), default="white", server_default="white" + db.String(SHORT_STR_LEN), + default="white", + server_default="white", + nullable=False, ) # autorise resp. a modifier semestre: resp_can_edit = db.Column( @@ -114,6 +117,7 @@ class FormSemestre(db.Model): "ModuleImpl", backref="formsemestre", lazy="dynamic", + cascade="all, delete-orphan", ) etuds = db.relationship( "Identite", diff --git a/app/models/groups.py b/app/models/groups.py index 92e8cc5c..4fd25a94 100644 --- a/app/models/groups.py +++ b/app/models/groups.py @@ -1,7 +1,7 @@ # -*- coding: UTF-8 -* ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/models/moduleimpls.py b/app/models/moduleimpls.py index 5b81fa1b..930909d3 100644 --- a/app/models/moduleimpls.py +++ b/app/models/moduleimpls.py @@ -20,14 +20,12 @@ class ModuleImpl(db.Model): id = db.Column(db.Integer, primary_key=True) moduleimpl_id = db.synonym("id") - module_id = db.Column( - db.Integer, - db.ForeignKey("notes_modules.id"), - ) + module_id = db.Column(db.Integer, db.ForeignKey("notes_modules.id"), nullable=False) formsemestre_id = db.Column( db.Integer, db.ForeignKey("notes_formsemestre.id"), index=True, + nullable=False, ) responsable_id = db.Column("responsable_id", db.Integer, db.ForeignKey("user.id")) # formule de calcul moyenne: diff --git a/app/models/modules.py b/app/models/modules.py index 77c06274..d4c70c03 100644 --- a/app/models/modules.py +++ b/app/models/modules.py @@ -37,7 +37,9 @@ class Module(db.Model): # Type: ModuleType.STANDARD, MALUS, RESSOURCE, SAE (enum) module_type = db.Column(db.Integer, nullable=False, default=0, server_default="0") # Relations: - modimpls = db.relationship("ModuleImpl", backref="module", lazy="dynamic") + modimpls = db.relationship( + "ModuleImpl", backref="module", lazy="dynamic", cascade="all, delete-orphan" + ) ues_apc = db.relationship("UniteEns", secondary="module_ue_coef", viewonly=True) tags = db.relationship( "NotesTag", diff --git a/app/models/validations.py b/app/models/validations.py index 5998ccd7..f0ec9749 100644 --- a/app/models/validations.py +++ b/app/models/validations.py @@ -4,6 +4,7 @@ """ from app import db +from app import log from app.models import SHORT_STR_LEN from app.models import CODE_STR_LEN from app.models.events import Scolog @@ -93,6 +94,10 @@ class ScolarAutorisationInscription(db.Model): db.ForeignKey("notes_formsemestre.id"), ) + def __repr__(self) -> str: + return f"""{self.__class__.__name__}(id={self.id}, etudid={ + self.etudid}, semestre_id={self.semestre_id})""" + def to_dict(self) -> dict: "as a dict" d = dict(self.__dict__) @@ -119,6 +124,7 @@ class ScolarAutorisationInscription(db.Model): Scolog.logdb( "autorise_etud", etudid=etudid, msg=f"Passage vers S{semestre_id}: autorisé" ) + log(f"ScolarAutorisationInscription: recording {autorisation}") @classmethod def delete_autorisation_etud( @@ -132,6 +138,7 @@ class ScolarAutorisationInscription(db.Model): ) for autorisation in autorisations: db.session.delete(autorisation) + log(f"ScolarAutorisationInscription: deleting {autorisation}") Scolog.logdb( "autorise_etud", etudid=etudid, diff --git a/app/pe/pe_avislatex.py b/app/pe/pe_avislatex.py index 5a507738..d68f9509 100644 --- a/app/pe/pe_avislatex.py +++ b/app/pe/pe_avislatex.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/pe/pe_jurype.py b/app/pe/pe_jurype.py index e4541f07..7b0c7997 100644 --- a/app/pe/pe_jurype.py +++ b/app/pe/pe_jurype.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/pe/pe_semestretag.py b/app/pe/pe_semestretag.py index bed33465..eb6b7c46 100644 --- a/app/pe/pe_semestretag.py +++ b/app/pe/pe_semestretag.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/pe/pe_settag.py b/app/pe/pe_settag.py index be5028f3..792175bb 100644 --- a/app/pe/pe_settag.py +++ b/app/pe/pe_settag.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/pe/pe_tagtable.py b/app/pe/pe_tagtable.py index 26cc8e24..e6ddb19c 100644 --- a/app/pe/pe_tagtable.py +++ b/app/pe/pe_tagtable.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/pe/pe_tools.py b/app/pe/pe_tools.py index ad6e2aa2..932a2a00 100644 --- a/app/pe/pe_tools.py +++ b/app/pe/pe_tools.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/pe/pe_view.py b/app/pe/pe_view.py index 06302cd8..bb5f386f 100644 --- a/app/pe/pe_view.py +++ b/app/pe/pe_view.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/__init__.py b/app/scodoc/__init__.py index 54c845c8..1b9a5925 100644 --- a/app/scodoc/__init__.py +++ b/app/scodoc/__init__.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/gen_tables.py b/app/scodoc/gen_tables.py index 0fab06ea..d1553238 100644 --- a/app/scodoc/gen_tables.py +++ b/app/scodoc/gen_tables.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/html_sco_header.py b/app/scodoc/html_sco_header.py index 409e4d13..7098757c 100644 --- a/app/scodoc/html_sco_header.py +++ b/app/scodoc/html_sco_header.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/html_sidebar.py b/app/scodoc/html_sidebar.py index 98acc56c..a487fcf0 100644 --- a/app/scodoc/html_sidebar.py +++ b/app/scodoc/html_sidebar.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/htmlutils.py b/app/scodoc/htmlutils.py index 725a4f4e..51a0b19b 100644 --- a/app/scodoc/htmlutils.py +++ b/app/scodoc/htmlutils.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/notes_table.py b/app/scodoc/notes_table.py index a8322f16..a54a7bda 100644 --- a/app/scodoc/notes_table.py +++ b/app/scodoc/notes_table.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/notes_users.py b/app/scodoc/notes_users.py index 6dd5bf40..33d25a68 100644 --- a/app/scodoc/notes_users.py +++ b/app/scodoc/notes_users.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/safehtml.py b/app/scodoc/safehtml.py index 2988a0f3..5c897cd2 100644 --- a/app/scodoc/safehtml.py +++ b/app/scodoc/safehtml.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_abs.py b/app/scodoc/sco_abs.py index 3cdd17e2..1e56ca87 100644 --- a/app/scodoc/sco_abs.py +++ b/app/scodoc/sco_abs.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_abs_billets.py b/app/scodoc/sco_abs_billets.py index d281b312..5f52e8f7 100644 --- a/app/scodoc/sco_abs_billets.py +++ b/app/scodoc/sco_abs_billets.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_abs_notification.py b/app/scodoc/sco_abs_notification.py index 5f9670f5..fe973a3f 100644 --- a/app/scodoc/sco_abs_notification.py +++ b/app/scodoc/sco_abs_notification.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_abs_views.py b/app/scodoc/sco_abs_views.py index e24f25f4..bb585930 100644 --- a/app/scodoc/sco_abs_views.py +++ b/app/scodoc/sco_abs_views.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_apogee_compare.py b/app/scodoc/sco_apogee_compare.py index b6cb042b..6c967604 100644 --- a/app/scodoc/sco_apogee_compare.py +++ b/app/scodoc/sco_apogee_compare.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_apogee_csv.py b/app/scodoc/sco_apogee_csv.py index 154846bf..212bb133 100644 --- a/app/scodoc/sco_apogee_csv.py +++ b/app/scodoc/sco_apogee_csv.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_archives.py b/app/scodoc/sco_archives.py index 71acd815..71ff5c46 100644 --- a/app/scodoc/sco_archives.py +++ b/app/scodoc/sco_archives.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -28,7 +28,7 @@ """ScoDoc : gestion des archives des PV et bulletins, et des dossiers etudiants (admission) - Archives are plain files, stored in + Archives are plain files, stored in /archives/ (where is usually /opt/scodoc-data, and a departement id (int)) @@ -42,7 +42,7 @@ Les maquettes Apogée pour l'export des notes sont dans /apo_csv//-//.csv - + Un répertoire d'archive contient des fichiers quelconques, et un fichier texte nommé _description.txt qui est une description (humaine, format libre) de l'archive. @@ -105,13 +105,13 @@ class BaseArchiver(object): try: scu.GSL.acquire() if not os.path.isdir(path): - log("creating directory %s" % path) + log(f"creating directory {path}") os.mkdir(path) finally: scu.GSL.release() self.initialized = True - def get_obj_dir(self, oid): + def get_obj_dir(self, oid: int): """ :return: path to directory of archives for this object (eg formsemestre_id or etudid). If directory does not yet exist, create it. @@ -142,7 +142,7 @@ class BaseArchiver(object): dirs = glob.glob(base + "*") return [os.path.split(x)[1] for x in dirs] - def list_obj_archives(self, oid): + def list_obj_archives(self, oid: int): """Returns :return: list of archive identifiers for this object (paths to non empty dirs) """ @@ -157,7 +157,7 @@ class BaseArchiver(object): dirs.sort() return dirs - def delete_archive(self, archive_id): + def delete_archive(self, archive_id: str): """Delete (forever) this archive""" self.initialize() try: @@ -166,7 +166,7 @@ class BaseArchiver(object): finally: scu.GSL.release() - def get_archive_date(self, archive_id): + def get_archive_date(self, archive_id: str): """Returns date (as a DateTime object) of an archive""" return datetime.datetime( *[int(x) for x in os.path.split(archive_id)[1].split("-")] @@ -183,17 +183,17 @@ class BaseArchiver(object): files.sort() return [f for f in files if f and f[0] != "_"] - def get_archive_name(self, archive_id): + def get_archive_name(self, archive_id: str): """name identifying archive, to be used in web URLs""" return os.path.split(archive_id)[1] - def is_valid_archive_name(self, archive_name): + def is_valid_archive_name(self, archive_name: str): """check if name is valid.""" return re.match( "^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}$", archive_name ) - def get_id_from_name(self, oid, archive_name): + def get_id_from_name(self, oid, archive_name: str): """returns archive id (check that name is valid)""" self.initialize() if not self.is_valid_archive_name(archive_name): @@ -206,7 +206,7 @@ class BaseArchiver(object): raise ScoValueError(f"Archive {archive_name} introuvable") return archive_id - def get_archive_description(self, archive_id): + def get_archive_description(self, archive_id: str) -> str: """Return description of archive""" self.initialize() filename = os.path.join(archive_id, "_description.txt") @@ -247,7 +247,7 @@ class BaseArchiver(object): data = data.encode(scu.SCO_ENCODING) self.initialize() filename = scu.sanitize_filename(filename) - log("storing %s (%d bytes) in %s" % (filename, len(data), archive_id)) + log(f"storing {filename} ({len(data)} bytes) in {archive_id}") try: scu.GSL.acquire() fname = os.path.join(archive_id, filename) @@ -261,16 +261,18 @@ class BaseArchiver(object): """Retreive data""" self.initialize() if not scu.is_valid_filename(filename): - log('Archiver.get: invalid filename "%s"' % filename) + log(f"""Archiver.get: invalid filename '{filename}'""") raise ScoValueError("archive introuvable (déjà supprimée ?)") fname = os.path.join(archive_id, filename) - log("reading archive file %s" % fname) + log(f"reading archive file {fname}") with open(fname, "rb") as f: data = f.read() return data def get_archived_file(self, oid, archive_name, filename): - """Recupere donnees du fichier indiqué et envoie au client""" + """Recupère les donnees du fichier indiqué et envoie au client. + Returns: Response + """ archive_id = self.get_id_from_name(oid, archive_name) data = self.get(archive_id, filename) mime = mimetypes.guess_type(filename)[0] diff --git a/app/scodoc/sco_archives_etud.py b/app/scodoc/sco_archives_etud.py index c0a40b14..799024db 100644 --- a/app/scodoc/sco_archives_etud.py +++ b/app/scodoc/sco_archives_etud.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_bac.py b/app/scodoc/sco_bac.py index 6f316a10..7933f351 100644 --- a/app/scodoc/sco_bac.py +++ b/app/scodoc/sco_bac.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_bulletins.py b/app/scodoc/sco_bulletins.py index 410b3972..8e2f2914 100644 --- a/app/scodoc/sco_bulletins.py +++ b/app/scodoc/sco_bulletins.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_bulletins_example.py b/app/scodoc/sco_bulletins_example.py index cf908a1b..83ad85c3 100644 --- a/app/scodoc/sco_bulletins_example.py +++ b/app/scodoc/sco_bulletins_example.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_bulletins_generator.py b/app/scodoc/sco_bulletins_generator.py index 4f40a53e..0d5ad046 100644 --- a/app/scodoc/sco_bulletins_generator.py +++ b/app/scodoc/sco_bulletins_generator.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_bulletins_json.py b/app/scodoc/sco_bulletins_json.py index 675737c6..2d56eeb0 100644 --- a/app/scodoc/sco_bulletins_json.py +++ b/app/scodoc/sco_bulletins_json.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_bulletins_legacy.py b/app/scodoc/sco_bulletins_legacy.py index 314abb0d..37a1b202 100644 --- a/app/scodoc/sco_bulletins_legacy.py +++ b/app/scodoc/sco_bulletins_legacy.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_bulletins_pdf.py b/app/scodoc/sco_bulletins_pdf.py index f063f3a7..369187ca 100644 --- a/app/scodoc/sco_bulletins_pdf.py +++ b/app/scodoc/sco_bulletins_pdf.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_bulletins_signature.py b/app/scodoc/sco_bulletins_signature.py index be93e672..f3a711b5 100644 --- a/app/scodoc/sco_bulletins_signature.py +++ b/app/scodoc/sco_bulletins_signature.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_bulletins_standard.py b/app/scodoc/sco_bulletins_standard.py index b326ef7b..cd535be0 100644 --- a/app/scodoc/sco_bulletins_standard.py +++ b/app/scodoc/sco_bulletins_standard.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -435,7 +435,7 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator): plusminus = pluslink try: ects_txt = str(int(ue["ects"])) - except (ValueError, KeyError): + except (ValueError, KeyError, TypeError): ects_txt = "-" t = { diff --git a/app/scodoc/sco_bulletins_ucac.py b/app/scodoc/sco_bulletins_ucac.py index fc49ffd9..c5a59243 100644 --- a/app/scodoc/sco_bulletins_ucac.py +++ b/app/scodoc/sco_bulletins_ucac.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_bulletins_xml.py b/app/scodoc/sco_bulletins_xml.py index 2f3c2c6b..c67b8ce0 100644 --- a/app/scodoc/sco_bulletins_xml.py +++ b/app/scodoc/sco_bulletins_xml.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_cache.py b/app/scodoc/sco_cache.py index 8cdeed43..f83cc0ec 100644 --- a/app/scodoc/sco_cache.py +++ b/app/scodoc/sco_cache.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_codes_parcours.py b/app/scodoc/sco_codes_parcours.py index a19ab4f4..7d90edbc 100644 --- a/app/scodoc/sco_codes_parcours.py +++ b/app/scodoc/sco_codes_parcours.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -205,6 +205,20 @@ BUT_CODES_PASSAGE = { PAS1NCI, ATJ, } +# les codes, du plus "défavorable" à l'étudiant au plus favorable: +# (valeur par défaut 0) +BUT_CODES_ORDERED = { + "NAR": 0, + "DEF": 0, + "AJ": 10, + "ATJ": 20, + "CMP": 50, + "ADC": 50, + "PASD": 50, + "PAS1NCI": 60, + "ADJ": 100, + "ADM": 100, +} def code_semestre_validant(code: str) -> bool: diff --git a/app/scodoc/sco_compute_moy.py b/app/scodoc/sco_compute_moy.py index b35ca6d4..fb162170 100644 --- a/app/scodoc/sco_compute_moy.py +++ b/app/scodoc/sco_compute_moy.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_config_actions.py b/app/scodoc/sco_config_actions.py index 887b6608..61202781 100644 --- a/app/scodoc/sco_config_actions.py +++ b/app/scodoc/sco_config_actions.py @@ -5,7 +5,7 @@ # # ScoDoc # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_cost_formation.py b/app/scodoc/sco_cost_formation.py index 167ff770..09d1e122 100644 --- a/app/scodoc/sco_cost_formation.py +++ b/app/scodoc/sco_cost_formation.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_cursus.py b/app/scodoc/sco_cursus.py index c492f86b..d4d996de 100644 --- a/app/scodoc/sco_cursus.py +++ b/app/scodoc/sco_cursus.py @@ -4,7 +4,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_cursus_dut.py b/app/scodoc/sco_cursus_dut.py index 32d4796b..3474081f 100644 --- a/app/scodoc/sco_cursus_dut.py +++ b/app/scodoc/sco_cursus_dut.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_debouche.py b/app/scodoc/sco_debouche.py index ea96ff1c..3f83ba75 100644 --- a/app/scodoc/sco_debouche.py +++ b/app/scodoc/sco_debouche.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_dept.py b/app/scodoc/sco_dept.py index e31752a0..6fdf92e4 100644 --- a/app/scodoc/sco_dept.py +++ b/app/scodoc/sco_dept.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_dump_db.py b/app/scodoc/sco_dump_db.py index e1d8eef3..80a0b9c4 100644 --- a/app/scodoc/sco_dump_db.py +++ b/app/scodoc/sco_dump_db.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_edit_apc.py b/app/scodoc/sco_edit_apc.py index 255448ff..1bea3b71 100644 --- a/app/scodoc/sco_edit_apc.py +++ b/app/scodoc/sco_edit_apc.py @@ -2,7 +2,7 @@ # # ScoDoc # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_edit_formation.py b/app/scodoc/sco_edit_formation.py index 4077f633..c64d732e 100644 --- a/app/scodoc/sco_edit_formation.py +++ b/app/scodoc/sco_edit_formation.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_edit_matiere.py b/app/scodoc/sco_edit_matiere.py index a2298fc2..f0f2ce5b 100644 --- a/app/scodoc/sco_edit_matiere.py +++ b/app/scodoc/sco_edit_matiere.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_edit_module.py b/app/scodoc/sco_edit_module.py index 91194ebf..6f5f798e 100644 --- a/app/scodoc/sco_edit_module.py +++ b/app/scodoc/sco_edit_module.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -840,6 +840,8 @@ def module_edit( if selected_ue is None: raise ValueError("UE invalide") tf[2]["semestre_id"] = selected_ue.semestre_idx + if not tf[2].get("code"): + raise ScoValueError("Le code du module doit être spécifié.") # Check unicité code module dans la formation # ??? TODO # diff --git a/app/scodoc/sco_edit_ue.py b/app/scodoc/sco_edit_ue.py index 1ce7c347..4a9af686 100644 --- a/app/scodoc/sco_edit_ue.py +++ b/app/scodoc/sco_edit_ue.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_edt_cal.py b/app/scodoc/sco_edt_cal.py index 39c9541d..06672f95 100644 --- a/app/scodoc/sco_edt_cal.py +++ b/app/scodoc/sco_edt_cal.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_etape_apogee.py b/app/scodoc/sco_etape_apogee.py index 8a14f561..2be38c6d 100644 --- a/app/scodoc/sco_etape_apogee.py +++ b/app/scodoc/sco_etape_apogee.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_etape_apogee_view.py b/app/scodoc/sco_etape_apogee_view.py index 36322c5c..e37b0fd8 100644 --- a/app/scodoc/sco_etape_apogee_view.py +++ b/app/scodoc/sco_etape_apogee_view.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_etape_bilan.py b/app/scodoc/sco_etape_bilan.py index 2c945a7a..a3839fa7 100644 --- a/app/scodoc/sco_etape_bilan.py +++ b/app/scodoc/sco_etape_bilan.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_etud.py b/app/scodoc/sco_etud.py index d4edc89c..6d9bc49f 100644 --- a/app/scodoc/sco_etud.py +++ b/app/scodoc/sco_etud.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_evaluation_check_abs.py b/app/scodoc/sco_evaluation_check_abs.py index 21e10f53..d34137bc 100644 --- a/app/scodoc/sco_evaluation_check_abs.py +++ b/app/scodoc/sco_evaluation_check_abs.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_evaluation_db.py b/app/scodoc/sco_evaluation_db.py index d3e27dae..7d00b208 100644 --- a/app/scodoc/sco_evaluation_db.py +++ b/app/scodoc/sco_evaluation_db.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_evaluation_edit.py b/app/scodoc/sco_evaluation_edit.py index 268fde76..900827d4 100644 --- a/app/scodoc/sco_evaluation_edit.py +++ b/app/scodoc/sco_evaluation_edit.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -132,7 +132,7 @@ def evaluation_create_form( min_note_max_str = "0" # H = [ - f"""

{action} en + f"""

{action} en {scu.MODULE_TYPE_NAMES[mod["module_type"]]} les poids ne sont pas modifiables (voir réglage paramétrage) + """, + }, + ) + ) for ue in sem_ues: coef_ue = ue_coef_dict.get(ue.id, 0.0) form.append( diff --git a/app/scodoc/sco_evaluation_recap.py b/app/scodoc/sco_evaluation_recap.py index 7f1120b7..635d6e8d 100644 --- a/app/scodoc/sco_evaluation_recap.py +++ b/app/scodoc/sco_evaluation_recap.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/scodoc/sco_evaluations.py b/app/scodoc/sco_evaluations.py index 09bf2102..799a0e03 100644 --- a/app/scodoc/sco_evaluations.py +++ b/app/scodoc/sco_evaluations.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_excel.py b/app/scodoc/sco_excel.py index 1ea4cee8..dc875ca9 100644 --- a/app/scodoc/sco_excel.py +++ b/app/scodoc/sco_excel.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_exceptions.py b/app/scodoc/sco_exceptions.py index 856c963f..7896336f 100644 --- a/app/scodoc/sco_exceptions.py +++ b/app/scodoc/sco_exceptions.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -40,8 +40,9 @@ class InvalidNoteValue(ScoException): pass -# Exception qui stoque dest_url class ScoValueError(ScoException): + "Exception avec page d'erreur utilisateur, et qui stoque dest_url" + def __init__(self, msg, dest_url=None): super().__init__(msg) self.dest_url = dest_url @@ -74,7 +75,7 @@ class ScoFormatError(ScoValueError): class ScoInvalidParamError(ScoValueError): """Paramètres requete invalides. - A utilisée lorsqu'une route est appelée avec des paramètres invalides + Utilisée lorsqu'une route est appelée avec des paramètres invalides (id strings, ...) """ @@ -157,6 +158,23 @@ class ScoInvalidIdType(ScoValueError): super().__init__(msg) +class ScoNoReferentielCompetences(ScoValueError): + """Formation APC (BUT) non associée à référentiel de compétences""" + + def __init__(self, msg: str = "", formation: "Formation" = None): + formation_title = ( + f"{formation.titre} version {formation.version}" if formation else "" + ) + msg = f""" +

Pas de référentiel de compétences associé à la formation {formation_title}! +

+

Pour associer un référentiel, passer par le menu Semestre / + Voir la formation... et suivre le lien "associer à un référentiel + de compétences" + """ + super().__init__(msg) + + class ScoGenError(ScoException): "exception avec affichage d'une page explicative ad-hoc" diff --git a/app/scodoc/sco_export_results.py b/app/scodoc/sco_export_results.py index 6c005306..858fc5ae 100644 --- a/app/scodoc/sco_export_results.py +++ b/app/scodoc/sco_export_results.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_find_etud.py b/app/scodoc/sco_find_etud.py index 163daf74..d3fa462e 100644 --- a/app/scodoc/sco_find_etud.py +++ b/app/scodoc/sco_find_etud.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_formation_recap.py b/app/scodoc/sco_formation_recap.py index 9d06dbad..e9ae85a3 100644 --- a/app/scodoc/sco_formation_recap.py +++ b/app/scodoc/sco_formation_recap.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_formations.py b/app/scodoc/sco_formations.py index 150f2af3..099a1614 100644 --- a/app/scodoc/sco_formations.py +++ b/app/scodoc/sco_formations.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_formsemestre.py b/app/scodoc/sco_formsemestre.py index 93161895..2edd3655 100644 --- a/app/scodoc/sco_formsemestre.py +++ b/app/scodoc/sco_formsemestre.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -87,6 +87,8 @@ _formsemestreEditor = ndb.EditableTable( "resp_can_edit": bool, "resp_can_change_ens": bool, "ens_can_edit_eval": bool, + "bul_bgcolor": lambda color: color or "white", + "titre": lambda titre: titre or "sans titre", }, ) diff --git a/app/scodoc/sco_formsemestre_custommenu.py b/app/scodoc/sco_formsemestre_custommenu.py index e879d622..ed39904b 100644 --- a/app/scodoc/sco_formsemestre_custommenu.py +++ b/app/scodoc/sco_formsemestre_custommenu.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_formsemestre_edit.py b/app/scodoc/sco_formsemestre_edit.py index 549bdb28..5a776c35 100644 --- a/app/scodoc/sco_formsemestre_edit.py +++ b/app/scodoc/sco_formsemestre_edit.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -312,6 +312,7 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N le titre: ils seront automatiquement ajoutés """, + "allow_null": False, }, ), ( diff --git a/app/scodoc/sco_formsemestre_exterieurs.py b/app/scodoc/sco_formsemestre_exterieurs.py index c33b7309..c82d176c 100644 --- a/app/scodoc/sco_formsemestre_exterieurs.py +++ b/app/scodoc/sco_formsemestre_exterieurs.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_formsemestre_inscriptions.py b/app/scodoc/sco_formsemestre_inscriptions.py index 3a2bf609..024ccbf9 100644 --- a/app/scodoc/sco_formsemestre_inscriptions.py +++ b/app/scodoc/sco_formsemestre_inscriptions.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py index 7c93ee10..44f14954 100644 --- a/app/scodoc/sco_formsemestre_status.py +++ b/app/scodoc/sco_formsemestre_status.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -1081,10 +1081,26 @@ def formsemestre_status(formsemestre_id=None, check_parcours=True): formsemestre_warning_etuds_sans_note(formsemestre, nt) if can_change_all_notes else "", - """

Tableau de bord: - cliquez sur un module pour saisir des notes -

""", + """

Tableau de bord : """, ] + if formsemestre.est_courant(): + H.append( + """cliquez sur un module pour saisir des notes""" + ) + elif datetime.date.today() > formsemestre.date_fin: + H.append( + """semestre terminé""" + ) + else: + H.append( + """semestre pas encore commencé""" + ) + H.append("

") + + if sco_preferences.get_preference("bul_show_all_evals", formsemestre_id): + H.append( + """
Toutes évaluations (même incomplètes) visibles
""" + ) if nt.expr_diagnostics: H.append(html_expr_diagnostic(nt.expr_diagnostics)) @@ -1361,7 +1377,7 @@ def get_formsemestre_etudids_sans_notes( .count() ) if not nb_notes_sem: - return + return set() etudids_sans_notes = set.intersection( *[ set.intersection(*m_res.evals_etudids_sans_note.values()) diff --git a/app/scodoc/sco_formsemestre_validation.py b/app/scodoc/sco_formsemestre_validation.py index 2a8e226f..2af4b69f 100644 --- a/app/scodoc/sco_formsemestre_validation.py +++ b/app/scodoc/sco_formsemestre_validation.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_formulas.py b/app/scodoc/sco_formulas.py index a9f5d9b7..9a3273a5 100644 --- a/app/scodoc/sco_formulas.py +++ b/app/scodoc/sco_formulas.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py index c741e597..3c350b50 100644 --- a/app/scodoc/sco_groups.py +++ b/app/scodoc/sco_groups.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_groups_edit.py b/app/scodoc/sco_groups_edit.py index 088f74e5..3af2c963 100644 --- a/app/scodoc/sco_groups_edit.py +++ b/app/scodoc/sco_groups_edit.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_groups_exports.py b/app/scodoc/sco_groups_exports.py index b2e95545..d6f63ea0 100644 --- a/app/scodoc/sco_groups_exports.py +++ b/app/scodoc/sco_groups_exports.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_groups_view.py b/app/scodoc/sco_groups_view.py index 667a292b..b31b88e3 100644 --- a/app/scodoc/sco_groups_view.py +++ b/app/scodoc/sco_groups_view.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -364,11 +364,11 @@ class DisplayedGroupsInfos(object): self.formsemestre = sem self.members.extend(group_members) groups_titles.append(group_tit) - if group["partition_name"] == None: + if group["partition_name"] is None: self.tous_les_etuds_du_sem = True else: # liste les partitions explicitement sélectionnés (= des groupes de group_ids) - selected_partitions.add((group["numero"], group["partition_id"])) + selected_partitions.add((group["numero"] or 0, group["partition_id"])) self.selected_partitions = [ x[1] for x in sorted(list(selected_partitions)) diff --git a/app/scodoc/sco_import_etuds.py b/app/scodoc/sco_import_etuds.py index d8b5c3e0..4d88cdb1 100644 --- a/app/scodoc/sco_import_etuds.py +++ b/app/scodoc/sco_import_etuds.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_import_users.py b/app/scodoc/sco_import_users.py index 8683c008..213d44b3 100644 --- a/app/scodoc/sco_import_users.py +++ b/app/scodoc/sco_import_users.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_inscr_passage.py b/app/scodoc/sco_inscr_passage.py index 2d3b644f..22c36b3c 100644 --- a/app/scodoc/sco_inscr_passage.py +++ b/app/scodoc/sco_inscr_passage.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_liste_notes.py b/app/scodoc/sco_liste_notes.py index ba27b1f4..2a63078b 100644 --- a/app/scodoc/sco_liste_notes.py +++ b/app/scodoc/sco_liste_notes.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -76,7 +76,7 @@ def do_evaluation_listenotes( else: raise ValueError("missing argument: evaluation or module") if not evals: - return "

Aucune évaluation !

", f"ScoDoc" + return "

Aucune évaluation !

", "ScoDoc" E = evals[0] # il y a au moins une evaluation modimpl = ModuleImpl.query.get(E["moduleimpl_id"]) @@ -244,7 +244,6 @@ def _make_table_notes( E = evals[0] moduleimpl_id = E["moduleimpl_id"] modimpl = ModuleImpl.query.get_or_404(moduleimpl_id) - modimpl_o = modimpl.to_dict() # TODO temporaire - à refactorer module: Module = modimpl.module formsemestre: FormSemestre = modimpl.formsemestre is_apc = module.formation.get_parcours().APC_SAE diff --git a/app/scodoc/sco_logos.py b/app/scodoc/sco_logos.py index d742eba2..21a84bd0 100644 --- a/app/scodoc/sco_logos.py +++ b/app/scodoc/sco_logos.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_lycee.py b/app/scodoc/sco_lycee.py index 1aba7b89..4d9cf02f 100644 --- a/app/scodoc/sco_lycee.py +++ b/app/scodoc/sco_lycee.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_modalites.py b/app/scodoc/sco_modalites.py index 65b9269b..b12e54e7 100644 --- a/app/scodoc/sco_modalites.py +++ b/app/scodoc/sco_modalites.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_moduleimpl.py b/app/scodoc/sco_moduleimpl.py index cdce8ac4..0e1edbee 100644 --- a/app/scodoc/sco_moduleimpl.py +++ b/app/scodoc/sco_moduleimpl.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_moduleimpl_inscriptions.py b/app/scodoc/sco_moduleimpl_inscriptions.py index 75aad0aa..78cac043 100644 --- a/app/scodoc/sco_moduleimpl_inscriptions.py +++ b/app/scodoc/sco_moduleimpl_inscriptions.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_moduleimpl_status.py b/app/scodoc/sco_moduleimpl_status.py index 67b4a8d9..d0fdd05b 100644 --- a/app/scodoc/sco_moduleimpl_status.py +++ b/app/scodoc/sco_moduleimpl_status.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -629,6 +629,9 @@ def _ligne_evaluation( if etat["evalcomplete"]: etat_txt = """(prise en compte)""" etat_descr = "notes utilisées dans les moyennes" + elif etat["evalattente"] and not evaluation.publish_incomplete: + etat_txt = "(prise en compte, mais notes en attente)" + etat_descr = "il y a des notes en attente" elif evaluation.publish_incomplete: etat_txt = """(prise en compte immédiate)""" etat_descr = ( diff --git a/app/scodoc/sco_page_etud.py b/app/scodoc/sco_page_etud.py index 9d747f45..55bfa0e9 100644 --- a/app/scodoc/sco_page_etud.py +++ b/app/scodoc/sco_page_etud.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_pdf.py b/app/scodoc/sco_pdf.py index 110917e1..e427b717 100755 --- a/app/scodoc/sco_pdf.py +++ b/app/scodoc/sco_pdf.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_photos.py b/app/scodoc/sco_photos.py index 37908d51..e131c94d 100644 --- a/app/scodoc/sco_photos.py +++ b/app/scodoc/sco_photos.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_placement.py b/app/scodoc/sco_placement.py index 898bd31b..0e0ec844 100644 --- a/app/scodoc/sco_placement.py +++ b/app/scodoc/sco_placement.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_portal_apogee.py b/app/scodoc/sco_portal_apogee.py index 435ec4e8..61164c61 100644 --- a/app/scodoc/sco_portal_apogee.py +++ b/app/scodoc/sco_portal_apogee.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_poursuite_dut.py b/app/scodoc/sco_poursuite_dut.py index 36e9ca0e..2c7934dd 100644 --- a/app/scodoc/sco_poursuite_dut.py +++ b/app/scodoc/sco_poursuite_dut.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_preferences.py b/app/scodoc/sco_preferences.py index c18e33e1..1a596333 100644 --- a/app/scodoc/sco_preferences.py +++ b/app/scodoc/sco_preferences.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -2332,7 +2332,9 @@ def doc_preferences(): return "\n".join([" | ".join(x) for x in L]) -def bulletin_option_affichage(formsemestre_id: int, prefs: SemPreferences) -> dict: +def bulletin_option_affichage( + formsemestre: "FormSemestre", prefs: SemPreferences +) -> dict: "dict avec les options d'affichages (préférences) pour ce semestre" fields = ( "bul_show_abs", @@ -2356,4 +2358,8 @@ def bulletin_option_affichage(formsemestre_id: int, prefs: SemPreferences) -> di "bul_show_date_inscr", ) # on enlève le "bul_" de la clé: - return {field[4:]: prefs[field] for field in fields} + fields = {field[4:]: prefs[field] for field in fields} + # Ajoute les réglages du formsemestre qui ne sont pas des préférences: + fields["block_moyenne_generale"] = formsemestre.block_moyenne_generale + fields["bgcolor"] = formsemestre.bul_bgcolor + return fields diff --git a/app/scodoc/sco_prepajury.py b/app/scodoc/sco_prepajury.py index 21e23c70..53c3a9e0 100644 --- a/app/scodoc/sco_prepajury.py +++ b/app/scodoc/sco_prepajury.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_pvjury.py b/app/scodoc/sco_pvjury.py index 3edcf660..e323348d 100644 --- a/app/scodoc/sco_pvjury.py +++ b/app/scodoc/sco_pvjury.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_pvpdf.py b/app/scodoc/sco_pvpdf.py index 70e80c93..d7462c18 100644 --- a/app/scodoc/sco_pvpdf.py +++ b/app/scodoc/sco_pvpdf.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_recapcomplet.py b/app/scodoc/sco_recapcomplet.py index 96378587..64ffe13a 100644 --- a/app/scodoc/sco_recapcomplet.py +++ b/app/scodoc/sco_recapcomplet.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_report.py b/app/scodoc/sco_report.py index 1fe2c17c..f89aa1e1 100644 --- a/app/scodoc/sco_report.py +++ b/app/scodoc/sco_report.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_report_but.py b/app/scodoc/sco_report_but.py index a4fbf3e2..addc782c 100644 --- a/app/scodoc/sco_report_but.py +++ b/app/scodoc/sco_report_but.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_saisie_notes.py b/app/scodoc/sco_saisie_notes.py index ebef0ae7..4583adce 100644 --- a/app/scodoc/sco_saisie_notes.py +++ b/app/scodoc/sco_saisie_notes.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -74,7 +74,7 @@ from app.scodoc import sco_etud def convert_note_from_string( - note, + note: str, note_max, note_min=scu.NOTES_MIN, etudid: int = None, @@ -128,7 +128,8 @@ def _displayNote(val): return val -def _check_notes(notes, evaluation, mod): +def _check_notes(notes: list[(int, float)], evaluation: dict, mod: dict): + # XXX typehint : float or str """notes is a list of tuples (etudid, value) mod is the module (used to ckeck type, for malus) returns list of valid notes (etudid, float value) @@ -426,16 +427,16 @@ def do_evaluation_set_missing( def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False): "suppress all notes in this eval" - E = sco_evaluation_db.do_evaluation_list({"evaluation_id": evaluation_id})[0] + evaluation = Evaluation.query.get_or_404(evaluation_id) if sco_permissions_check.can_edit_notes( - current_user, E["moduleimpl_id"], allow_ens=False + current_user, evaluation.moduleimpl_id, allow_ens=False ): # On a le droit de modifier toutes les notes # recupere les etuds ayant une note notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id) elif sco_permissions_check.can_edit_notes( - current_user, E["moduleimpl_id"], allow_ens=True + current_user, evaluation.moduleimpl_id, allow_ens=True ): # Enseignant associé au module: ne peut supprimer que les notes qu'il a saisi notes_db = sco_evaluation_db.do_evaluation_get_all_notes( @@ -449,7 +450,7 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False): status_url = url_for( "notes.moduleimpl_status", scodoc_dept=g.scodoc_dept, - moduleimpl_id=E["moduleimpl_id"], + moduleimpl_id=evaluation.moduleimpl_id, ) if not dialog_confirmed: @@ -495,13 +496,12 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False): """ ] # news - modimpl = ModuleImpl.query.get(E["moduleimpl_id"]) ScolarNews.add( typ=ScolarNews.NEWS_NOTE, - obj=modimpl.id, + obj=evaluation.moduleimpl.id, text=f"""Suppression des notes d'une évaluation dans
{modimpl.module.titre or 'module sans titre'} + >{evaluation.moduleimpl.module.titre or 'module sans titre'} """, url=status_url, ) @@ -1049,7 +1049,19 @@ def saisie_notes(evaluation_id, group_ids=[]): alone=True, ) ) - H.append("""""") + H.append("""""") + H.append("""""") # Le formulaire de saisie des notes: destination = url_for( diff --git a/app/scodoc/sco_semset.py b/app/scodoc/sco_semset.py index 30ee2ec2..a383f5dd 100644 --- a/app/scodoc/sco_semset.py +++ b/app/scodoc/sco_semset.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_synchro_etuds.py b/app/scodoc/sco_synchro_etuds.py index a4b99b58..4c05d960 100644 --- a/app/scodoc/sco_synchro_etuds.py +++ b/app/scodoc/sco_synchro_etuds.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_tag_module.py b/app/scodoc/sco_tag_module.py index 20c340f1..809facac 100644 --- a/app/scodoc/sco_tag_module.py +++ b/app/scodoc/sco_tag_module.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_trombino.py b/app/scodoc/sco_trombino.py index a5239c78..cf725794 100644 --- a/app/scodoc/sco_trombino.py +++ b/app/scodoc/sco_trombino.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_trombino_doc.py b/app/scodoc/sco_trombino_doc.py index 038832e5..b40da577 100644 --- a/app/scodoc/sco_trombino_doc.py +++ b/app/scodoc/sco_trombino_doc.py @@ -1,6 +1,6 @@ ############################################################################## # ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## diff --git a/app/scodoc/sco_trombino_tours.py b/app/scodoc/sco_trombino_tours.py index 85867b1e..6d017a8f 100644 --- a/app/scodoc/sco_trombino_tours.py +++ b/app/scodoc/sco_trombino_tours.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_ue_external.py b/app/scodoc/sco_ue_external.py index 8a8cff2b..2a863ff5 100644 --- a/app/scodoc/sco_ue_external.py +++ b/app/scodoc/sco_ue_external.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_undo_notes.py b/app/scodoc/sco_undo_notes.py index c70d133a..45307cdd 100644 --- a/app/scodoc/sco_undo_notes.py +++ b/app/scodoc/sco_undo_notes.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_up_to_date.py b/app/scodoc/sco_up_to_date.py index 1ca56b80..f819d76d 100644 --- a/app/scodoc/sco_up_to_date.py +++ b/app/scodoc/sco_up_to_date.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_users.py b/app/scodoc/sco_users.py index a0f08c15..d3db5b8e 100644 --- a/app/scodoc/sco_users.py +++ b/app/scodoc/sco_users.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py index d58fdfbb..c877db59 100644 --- a/app/scodoc/sco_utils.py +++ b/app/scodoc/sco_utils.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_vdi.py b/app/scodoc/sco_vdi.py index f058b650..bb0a48da 100644 --- a/app/scodoc/sco_vdi.py +++ b/app/scodoc/sco_vdi.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/sco_xml.py b/app/scodoc/sco_xml.py index 4e9e6f21..8634b8a4 100644 --- a/app/scodoc/sco_xml.py +++ b/app/scodoc/sco_xml.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/scodoc/scolog.py b/app/scodoc/scolog.py index 0854ec94..a3b07f05 100644 --- a/app/scodoc/scolog.py +++ b/app/scodoc/scolog.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/static/css/jury_but.css b/app/static/css/jury_but.css index 955aed94..4f2538d8 100644 --- a/app/static/css/jury_but.css +++ b/app/static/css/jury_but.css @@ -22,7 +22,7 @@ margin-left: 32px; display: inline-grid; grid-template-columns: repeat(4, auto); - gap: 4px; + gap: 8px; } .but_annee_caption { @@ -143,14 +143,22 @@ div.but_code { div.but_niveau_ue.recorded, div.but_niveau_rcue.recorded { - border-color: rgb(136, 252, 136); - border-width: 2px; + border-color: rgb(0, 169, 0); + border-width: 3px; +} + +div.but_niveau_ue.recorded_different, +div.but_niveau_rcue.recorded_different { + box-shadow: 0 0 0 3px red; + outline: dashed 3px rgb(0, 169, 0); } div.but_niveau_ue.annee_prec { background-color: rgb(167, 167, 0); } +div.but_section_annee.modified, +div.but_niveau_rcue.modified, div.but_niveau_ue.modified { background-color: rgb(255, 214, 254); } @@ -160,6 +168,7 @@ div.but_buttons { } div.but_buttons span { + margin-left: 16px; margin-right: 16px; } diff --git a/app/static/css/partition_editor.css b/app/static/css/partition_editor.css index b0b89fef..3d7140e4 100644 --- a/app/static/css/partition_editor.css +++ b/app/static/css/partition_editor.css @@ -1,3 +1,7 @@ +html { + overflow-x: hidden; +} + .wait { position: fixed; width: 50px; @@ -44,19 +48,43 @@ margin-bottom: 16px; display: inline-block; cursor: pointer; + pointer-events: none; +} + +.loaded .edition { + pointer-events: initial; +} + +.filtres>label { + display: none; +} + +.editionActivated .valider { + display: block; + width: fit-content; + margin-left: auto; + padding: 8px 32px; + background: #90c; + color: #fff; + box-shadow: 0 2px 2px rgb(0, 0, 0, 0.25); + border-radius: 4px; + cursor: pointer; } main { font-family: Verdana, Geneva, Tahoma, sans-serif; display: flex; flex-wrap: wrap; - gap: 32px; - row-gap: 4px; + gap: 8px; margin-right: 16px; + padding: 8px; + border-radius: 12px; + background: #424242; } main h2 { border-bottom: 4px solid #09c; + font-size: 150% !important; } main h2, @@ -64,11 +92,22 @@ main h3 { font-weight: 400; } -body:not(.editionActivated) .editing { - display: none !important; +section { + background: #fff; + padding: 8px; + border-radius: 8px; } -.editionActivated #zoneChoix .etudiants>div { +body:not(.editionActivated) .editing { + display: none; +} + +.nonEditable .editing { + display: none; +} + +.editionActivated #zoneChoix, +.editionActivated #zoneGroupes { pointer-events: none; opacity: 0.2; } @@ -80,7 +119,9 @@ body:not(.editionActivated) .editing { } @keyframes boing { - 100% {transform: translateY(-20px)} + 100% { + transform: translateY(-20px) + } } /****************/ @@ -89,13 +130,19 @@ body:not(.editionActivated) .editing { background: #0c9 !important; padding: 8px 16px !important; cursor: pointer; + color: #fff; + box-shadow: 0 2px 2px rgb(0, 0, 0, 0.25); + border-radius: 4px; + text-align: center; + margin-bottom: 4px; + width: fit-content; } .move, .modif, .suppr { color: #000; - padding: 4px; + padding: 0 4px; cursor: pointer; } @@ -172,10 +219,31 @@ body.editionActivated .filtres>div>div>div>div { .moving { opacity: 0.8; pointer-events: none; - ; } -.grabbing>div:not([data-idgroupe="aucun"]):hover:before { +.grabbing>div[data-idpartition]:not([data-idgroupe]):hover:before { + content: ""; + position: absolute; + left: -4px; + right: -4px; + bottom: calc(100% + 1px); + height: 2px; + width: auto; + background: #c44; + animation: insertPartion 0.2s infinite alternate ease-in-out; +} + +@keyframes insertPartion { + 0% { + transform: translateX(-4px) + } + + 100% { + transform: translateX(4px) + } +} + +.grabbing>*:not([data-idgroupe="aucun"]):hover:before { content: ""; position: absolute; bottom: -4px; @@ -183,10 +251,10 @@ body.editionActivated .filtres>div>div>div>div { right: calc(100% + 1px); width: 2px; background: #c44; - animation: insert 0.2s infinite alternate ease-in-out; + animation: insertGroupe 0.2s infinite alternate ease-in-out; } -@keyframes insert { +@keyframes insertGroupe { 0% { transform: translateY(-4px) } @@ -197,65 +265,116 @@ body.editionActivated .filtres>div>div>div>div { } /*****************************/ -/* Zone Partitions */ +/* Zone Filtres */ /*****************************/ #zonePartitions { width: 100%; } -.filtres { - display: table; +#zonePartitions>div { + width: fit-content; } -.filtres>div { - background: #ddd; +#zonePartitions h3{ + display: flex; +} +#zonePartitions h3 .onoff{ + margin-left: auto; + cursor: pointer; + display: flex; + align-items: center; + gap: 4px; + font-size: 16px; +} + +#zonePartitions .filtres { + width: fit-content; +} + +#zonePartitions .filtres>div { + background: #eee; padding: 8px; border-radius: 8px; margin-bottom: 8px; + position: relative; } -.filtres>div>div>div { +#zonePartitions .filtres .groupes { display: flex; flex-wrap: wrap; gap: 4px; row-gap: 2px; - margin: 8px 0; + margin: 4px 0 0 0; } -.filtres>div>div>div>div { +#zonePartitions .filtres .groupes>div { + position: relative; background: #09c; color: #FFF; border-radius: 4px; padding: 8px 32px; + margin: 0; box-shadow: 0 2px 2px rgba(0, 0, 0, 0.25); } -body:not(.editionActivated) .filtres>div>div>div>div { +body:not(.editionActivated) .filtres .groupes>div { cursor: pointer; } -body:not(.editionActivated) .filtres>div>div>div>div:hover { - box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6); +body:not(.editionActivated) .filtres .groupes>div:hover { + box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6) !important; } -body:not(.editionActivated) .filtres>div>div>div>div:active { - box-shadow: 0 0 0 #000; +body:not(.editionActivated) .filtres .groupes>div:active { + box-shadow: 0 0 0 #000 !important; transform: translateY(2px); } +body.editionActivated .filtres [data-idgroupe=aucun]{ + display: none; +} + +body.editionActivated .filtres .nonEditable .move{ + display: initial; +} .filtres .unselect { - background: rgba(0, 153, 204, 0.5); + background: rgba(0, 153, 204, 0.5) !important; } /*****************************/ /* Zone Etudiants */ /*****************************/ +#zoneChoix>.autoAffectation{ + background: #3c3c3c; + color: #fff; + padding: 4px 8px; + margin-bottom: 16px; + border-radius: 4px; +} +#zoneChoix>.autoAffectation>select{ + border: none; + padding: 4px; + border-radius: 4px; +} +#zoneChoix>.autoAffectation>.affectationGo{ + display: inline-block; + background: #0c9; + padding: 8px 16px; + cursor: pointer; + color: #fff; + box-shadow: 0 2px 2px rgb(0 0 0 / 25%); + border-radius: 4px; + text-align: center; + margin-top: 4px; + margin-bottom: 4px; + width: fit-content; +} #zoneChoix .etudiants>div { background: #FFF; border: 1px solid #aaa; border-radius: 4px; padding: 4px 8px; - margin: 4px 0; + margin: -1px 0; display: flex; flex-wrap: wrap; justify-content: space-between; @@ -266,16 +385,22 @@ body:not(.editionActivated) .filtres>div>div>div>div:active { flex: 1; } -#zoneChoix small { +#zoneChoix .small { color: #444; + font-size: 8px; font-style: italic; } +#zoneChoix .etudiants .grpPartitions { + display: flex; + flex-direction: column; + gap: 2px; +} + #zoneChoix .etudiants .partition { display: flex; flex-wrap: wrap; gap: 4px; - margin-bottom: 4px; } #zoneChoix label { @@ -313,9 +438,12 @@ body:not(.editionActivated) .filtres>div>div>div>div:active { color: #fff; } -.hide { +section:not(#zonePartitions) .hide { display: none !important; } +#zonePartitions .hide{ + opacity: 0.4; +} .saved+span { position: relative; @@ -346,11 +474,8 @@ body:not(.editionActivated) .filtres>div>div>div>div:active { flex: 1; } -#zoneGroupes .groupes { - display: flex; - flex-wrap: wrap; - align-items: flex-start; - gap: 16px; +#zoneGroupes h3 { + width: 100%; } #zoneGroupes .partition { @@ -358,8 +483,9 @@ body:not(.editionActivated) .filtres>div>div>div>div:active { padding: 8px; border-radius: 8px; display: flex; - flex-direction: column; + flex-wrap: wrap; gap: 8px; + margin-bottom: 8px; } h3 { @@ -390,5 +516,6 @@ h3 { } #zoneGroupes [data-idgroupe=aucun] { - background: #b5c2c3 !important; + background: #3c3c3c !important; + color: #fff; } \ No newline at end of file diff --git a/app/static/css/scodoc.css b/app/static/css/scodoc.css index c3e30a19..15f5f738 100644 --- a/app/static/css/scodoc.css +++ b/app/static/css/scodoc.css @@ -1585,7 +1585,7 @@ div.formsemestre_status { color: rgb(215, 90, 0); } -div.formsemestre_status_warning::before { +.formsemestre_status_warning::before { content: "\26a0 \fe0f \00a0"; /* EMO_WARNING, "⚠️" */ } @@ -2884,6 +2884,17 @@ li.tf-msg { vertical-align: -80%; } +.warning-light { + font-style: italic; + color: rgb(166, 50, 159); +} + +.warning-light::before { + content: "\26a0 \fe0f \00a0"; + /* EMO_WARNING, "⚠️" */ +} + + .infop { font-weight: normal; color: rgb(26, 150, 26); diff --git a/app/static/js/jury_but.js b/app/static/js/jury_but.js index c90f95d6..b016f110 100644 --- a/app/static/js/jury_but.js +++ b/app/static/js/jury_but.js @@ -5,17 +5,36 @@ function enable_manual_codes(elt) { $(".jury_but select.manual").prop("disabled", !elt.checked); } -// changement menu code: +// changement d'un menu code: function change_menu_code(elt) { - elt.parentElement.parentElement.classList.remove("recorded"); - // TODO: comparer avec valeur enregistrée (à mettre en data-orig ?) - // et colorer en fonction - elt.parentElement.parentElement.classList.add("modified"); + // Ajuste styles pour visualiser codes enregistrés/modifiés + if (elt.value != elt.dataset.orig_code) { + elt.parentElement.parentElement.classList.add("modified"); + } else { + elt.parentElement.parentElement.classList.remove("modified"); + } + if (elt.value == elt.dataset.orig_recorded) { + elt.parentElement.parentElement.classList.add("recorded"); + } else { + elt.parentElement.parentElement.classList.remove("recorded"); + } + // Si RCUE passant en ADJ, change les menus des UEs associées + if (elt.classList.contains("code_rcue") + && elt.dataset.niveau_id + && elt.value == "ADJ" + && elt.value != elt.dataset.orig_recorded) { + let ue_selects = elt.parentElement.parentElement.parentElement.querySelectorAll( + "select.ue_rcue_" + elt.dataset.niveau_id); + ue_selects.forEach(select => { + select.value = "ADJ"; + change_menu_code(select); // pour changer les styles + }); + } } $(function () { // Recupère la liste ordonnées des etudids - // pour avoir le "suivant" etr le "précédent" + // pour avoir le "suivant" et le "précédent" // (liens de navigation) const url = new URL(document.URL); const frags = url.pathname.split("/"); // .../formsemestre_validation_but/formsemestre_id/etudid @@ -63,6 +82,8 @@ $(function () { // ----- Etat du formulaire jury pour éviter sortie sans enregistrer let FORM_STATE = ""; +let IS_SUBMITTING = false; + // Une chaine décrivant l'état du form function get_form_state() { let codes = []; @@ -73,13 +94,19 @@ function get_form_state() { $('document').ready(function () { FORM_STATE = get_form_state(); + document.querySelector("form#jury_but").addEventListener('submit', jury_form_submit); }); function is_modified() { return FORM_STATE != get_form_state(); } + +function jury_form_submit(event) { + IS_SUBMITTING = true; +} + window.addEventListener("beforeunload", function (e) { - if (is_modified()) { + if ((!IS_SUBMITTING) && is_modified()) { var confirmationMessage = 'Changements non enregistrés !'; (e || window.event).returnValue = confirmationMessage; return confirmationMessage; diff --git a/app/static/js/saisie_notes.js b/app/static/js/saisie_notes.js index 747d09fe..4dc782c9 100644 --- a/app/static/js/saisie_notes.js +++ b/app/static/js/saisie_notes.js @@ -5,6 +5,7 @@ $().ready(function () { $("#formnotes .note").bind("blur", valid_note); $("#formnotes input").bind("paste", paste_text); + $(".btn_masquer_DEM").bind("click", masquer_DEM); }); @@ -92,6 +93,7 @@ function paste_text(e) { var data = clipb.getData('Text'); var list = data.split(/\r\n|\r|\n|\t| /g); var currentInput = event.currentTarget; + var masquerDEM = document.querySelector("body").classList.contains("masquer_DEM"); for (var i = 0; i < list.length; i++) { currentInput.value = list[i]; @@ -99,7 +101,15 @@ function paste_text(e) { evt.initEvent("blur", false, true); currentInput.dispatchEvent(evt); var sibbling = currentInput.parentElement.parentElement.nextElementSibling; - while (sibbling && sibbling.style.display == "none") { + while ( + sibbling && + ( + sibbling.style.display == "none" || + ( + masquerDEM && sibbling.classList.contains("etud_dem") + ) + ) + ) { sibbling = sibbling.nextElementSibling; } if (sibbling) { @@ -112,3 +122,7 @@ function paste_text(e) { } } } + +function masquer_DEM(){ + document.querySelector("body").classList.toggle("masquer_DEM"); +} diff --git a/app/templates/scolar/partition_editor.html b/app/templates/scolar/partition_editor.html index 6faf8409..f98e0987 100644 --- a/app/templates/scolar/partition_editor.html +++ b/app/templates/scolar/partition_editor.html @@ -5,30 +5,30 @@
-

Partitions et groupes

+

Filtres

-
-
-

Afficher les partitions

-
-
-
-

- Afficher les étudiants affectés aux groupes
- Ne s'actualise pas automatiquement lors d'une modification -

-
-
+
+
+
Ajouter une partition
+
+

Etudiants

+
+ Affecter automatiquement les étudiants du groupe
+ + vers le groupe + +
Valider
+
@@ -55,7 +55,9 @@ processDatas(partitions, etudiants); processEvents(); + listeGroupesAutoaffectation(); + document.querySelector("body").classList.add("loaded"); document.querySelector('.wait').style.display = "none"; } @@ -73,22 +75,25 @@ function processDatas(partitions, etudiants) { /* Filtres et groupes */ - let outputPartitions = "
"; - let outputMasques = ""; + let divFiltres = document.querySelector(".filtres"); let outputGroupes = ""; let arrayPartitions = Object.values(partitions).sort((a, b) => { return a.numero - b.numero; }) arrayPartitions.forEach((partition) => { - // Filtres - if (partition.groups_editable) { - outputPartitions += `
||${partition.partition_name}✏️
`; - } else { - outputPartitions += `
${partition.partition_name}
`; - } - outputMasques += `
Non affectés - ${partition.partition_name}
`; + let divPartition = templateFiltres_partition(partition); + divFiltres.appendChild(divPartition); + + let arrayGroups = Object.values(partition.groups).sort((a, b) => { + return a.numero - b.numero; + }) + + arrayGroups.forEach((groupe) => { + let divPlus = divPartition.querySelector(".ajoutGroupe"); + divPlus.parentElement.insertBefore(templateFiltres_groupe(groupe), divPlus); + }) // Groupes outputGroupes += ` @@ -104,35 +109,21 @@ }) let output = ""; arrayGroups.forEach((groupe) => { - /***************/ - if (partition.groups_editable) { - outputMasques += `
||${groupe.group_name}✏️
`; - } else { - outputMasques += `
${groupe.group_name}
`; - } - /***************/ output += templateGroupe_zoneGroupes(groupe.id, groupe.group_name); }) return output; })()}
`; - outputMasques += ` -
+
-
`; }) - document.querySelector(".filtres>.partitions>div").innerHTML = outputPartitions + ` -
+
- `; - document.querySelector(".filtres>.masques>div").innerHTML = outputMasques; - document.querySelector("#zoneGroupes>.groupes").innerHTML = outputGroupes; + document.querySelector("#zoneGroupes>.groupes").innerHTML = outputGroupes; /* Etudiants */ output = ""; etudiants.forEach(etudiant => { output += `
-
${etudiant.nom_disp} ${etudiant.prenom}
${etudiant.bac}
+
${etudiant.nom_disp} ${etudiant.prenom}
${etudiant.bac}
${(() => { let output = "
"; arrayPartitions.forEach((partition) => { @@ -168,6 +159,58 @@ document.querySelector("#zoneChoix>.etudiants").innerHTML = output; } + function templateFiltres_partition(partition) { + let div = document.createElement("div"); + div.dataset.idpartition = partition.id; + if (partition.groups_editable == false) { + div.classList.add("nonEditable"); + } + div.innerHTML = ` + +

+ || + ${partition.partition_name} + ✏️ + + +
Masquer
+

+ + +
+
+ Non affectés +
+
+
+
`; + + div.querySelector(".move").addEventListener("mousedown", moveStart); + div.querySelector(".modif").addEventListener("click", editText); + div.querySelector(".suppr").addEventListener("click", suppr); + div.querySelector(".onoff").addEventListener("click", masquerPartitions); + div.querySelector("[data-idgroupe]").addEventListener("click", filtre); + div.querySelector(".ajoutGroupe").addEventListener("click", addGroupe); + + return div; + } + + function templateFiltres_groupe(groupe) { + let div = document.createElement("div"); + div.dataset.idgroupe = groupe.id; + div.innerHTML = ` + || + ${groupe.group_name} + ✏️ + `; + + div.addEventListener("click", filtre); + div.querySelector(".move").addEventListener("mousedown", moveStart); + div.querySelector(".modif").addEventListener("click", editText); + div.querySelector(".suppr").addEventListener("click", suppr); + + return div; + } + function templateGroupe_zoneGroupes(idGroupe, name) { return `
${name}
@@ -179,6 +222,26 @@ return `
${etudiant.nom_disp} ${etudiant.prenom}
` } + function listeGroupesAutoaffectation() { + let output = ''; + + document.querySelectorAll('#zonePartitions .filtres>div').forEach(partition => { + + output += ` + + `; + partition.querySelectorAll('[data-idgroupe]:not([data-idgroupe="aucun"])').forEach(groupe => { + output += ``; + }) + output += ""; + + }) + + document.querySelector("#affectationFrom").innerHTML = output; + document.querySelector("#affectationTo").innerHTML = output; + + } + /******************************/ /* Gestionnaire d'événements */ /******************************/ @@ -187,11 +250,11 @@ if (!editing) { document.querySelector("body").classList.toggle("editionActivated"); return; - } + } this.checked = true; - if(!editing.classList.contains("highlight")) { + if (!editing.classList.contains("highlight")) { editing.classList.add("highlight"); - setTimeout(()=>{editing.classList.remove("highlight")}, 1000); + setTimeout(() => { editing.classList.remove("highlight") }, 1000); } } function processEvents() { @@ -199,26 +262,25 @@ /* Edition partitions */ /*--------------------*/ document.querySelector(".edition>input").addEventListener("input", setEditMode); - document.querySelectorAll(".ajoutPartition, .ajoutGroupe").forEach(btnPlus => { btnPlus.addEventListener("click", addPartition) }) - document.querySelectorAll(".modif").forEach(btn => { btn.addEventListener("click", editText) }) - document.querySelectorAll(".suppr").forEach(btn => { btn.addEventListener("click", suppr) }) - document.querySelectorAll(".move").forEach(btn => { btn.addEventListener("mousedown", moveStart) }) - - /*---------*/ - /* Filtres */ - /*---------*/ - document.querySelectorAll(".filtres>div>div>div>div:not(.editing)").forEach(btn => { btn.addEventListener("click", filtre) }) + document.querySelectorAll(".ajoutPartition").forEach(btnPlus => { btnPlus.addEventListener("click", addPartition) }) /*--------------------*/ /* Changement groupe */ /*--------------------*/ document.querySelectorAll("label").forEach(btn => { btn.addEventListener("mousedown", (event) => { event.preventDefault() }) }); document.querySelectorAll(".etudiants input").forEach(input => { input.addEventListener("input", assignment) }) + document.querySelector(".affectationGo").addEventListener("click", affectationGo); } /**********************/ /* Filtrage */ /**********************/ + function masquerPartitions() { + let idPartition = this.closest("[data-idpartition]").dataset.idpartition; + document.querySelectorAll(`[data-idpartition="${idPartition}"]`).forEach(e => { + e.classList.toggle("hide"); + }) + } function filtre() { if (document.querySelector("body").classList.contains("editionActivated")) { return; @@ -241,55 +303,68 @@ }) } - if (!this.dataset.idgroupe) { - // Partitions - let groupesSelected = []; - this.parentElement.querySelectorAll(":not(.unselect)").forEach(e => { - groupesSelected.push(e.dataset.idpartition); - }) - document.querySelectorAll(` - .etudiants .partition[data-idpartition], - #zoneGroupes [data-idpartition] - `).forEach(e => { - if (groupesSelected.includes(e.dataset.idpartition)) { - e.classList.remove("hide") - } else { - e.classList.add("hide") - } - }) - } else { - // Groupes - let groupesSelected = {}; + // Groupes + let groupesSelected = {}; - this.parentElement.parentElement.querySelectorAll("[data-idgroupe]:not(.unselect)").forEach(e => { - let idpartition = e.parentElement.dataset.idpartition; - if (!groupesSelected[idpartition]) { - groupesSelected[idpartition] = []; + document.querySelectorAll(".filtres [data-idgroupe]:not(.unselect)").forEach(e => { + let idpartition = e.closest("[data-idpartition]").dataset.idpartition; + if (!groupesSelected[idpartition]) { + groupesSelected[idpartition] = []; + } + groupesSelected[idpartition].push(e.dataset.idgroupe) + }) + document.querySelectorAll("#zoneChoix .etudiants>div").forEach(e => { + let found = true; + Object.entries(groupesSelected).forEach(([idpartition, tabGroupes]) => { + if (!tabGroupes.includes( + e.querySelector(`[data-idpartition="${idpartition}"] input:checked`).value + ) + ) { + found = false } - groupesSelected[idpartition].push(e.dataset.idgroupe) }) - document.querySelectorAll("#zoneChoix .etudiants>div").forEach(e => { - let found = true; - Object.entries(groupesSelected).forEach(([idpartition, tabGroupes]) => { - if (!tabGroupes.includes( - e.querySelector(`[data-idpartition="${idpartition}"] input:checked`).value - ) - ) { - found = false - } - }) - if (found) { - e.classList.remove("hide") - } else { - e.classList.add("hide") - } - }) - } + if (found) { + e.classList.remove("hide") + } else { + e.classList.add("hide") + } + }) } /****************************/ /* Affectation à un groupe */ /****************************/ + function affectationGo(){ + let from = document.querySelector("#affectationFrom").value; + let to = document.querySelector("#affectationTo").value; + + if(!from || !to){ + return; + } + + let elements = []; + + if(from[0] != "n"){ + elements = document.querySelectorAll(`#zoneChoix .etudiants [value="${from}"]:checked`) + } else { + document.querySelectorAll(`#zoneChoix .etudiants [data-idpartition="${from.split("-")[1]}"]`).forEach(element=>{ + if(!element.querySelector('input:not([value="aucun"]):checked')){ + elements.push(element); + } + }) + } + + console.log(elements); + + elements.forEach(groupeSelected=>{ + if(to[0] != "n"){ + groupeSelected.closest(".grpPartitions").querySelector(`[value="${to}"]`).click(); + }else{ + groupeSelected.closest(".grpPartitions").querySelector(".aucun").click(); + } + + }) + } function assignment() { let groupe = this.parentElement.parentElement.parentElement.parentElement; let nom = groupe.children[0].dataset.nom; @@ -346,32 +421,12 @@ /****************************/ function addPartition() { let date = new Date; - if (this.classList.contains("ajoutPartition")) { - // Partition - var name = "Nouvelle " + date.getSeconds(); - let params = (new URL(document.location)).searchParams; - let formsemestre_id = params.get('formsemestre_id'); - var url = "/ScoDoc/{{formsemestre.departement.acronym}}/api/formsemestre/" + formsemestre_id + "/partition/create"; - var payload = { partition_name: name }; - } else { - // Groupe - var name = "Nouveau " + date.getSeconds(); - var url = `/ScoDoc/{{formsemestre.departement.acronym}}/api/partition/${this.parentElement.dataset.idpartition}/group/create`; - var payload = { group_name: name }; - } - var div = document.createElement("div"); - div.innerHTML = ` - || - ${name} - ✏️ - `; - div.querySelector(".modif").addEventListener("click", editText); - div.querySelector(".suppr").addEventListener("click", suppr); - div.querySelector(".move").addEventListener("mousedown", moveStart); - this.parentElement.insertBefore(div, this); - - div.querySelector(".modif").click(); + var name = "Nouvelle " + date.getSeconds(); + let params = (new URL(document.location)).searchParams; + let formsemestre_id = params.get('formsemestre_id'); + var url = "/ScoDoc/{{formsemestre.departement.acronym}}/api/formsemestre/" + formsemestre_id + "/partition/create"; + var payload = { partition_name: name }; // Save fetch(url, @@ -390,76 +445,110 @@ div.remove(); return; } - if (this.classList.contains("ajoutPartition")) { - div.dataset.idpartition = r.id; - // Ajout dans la zone masques - div = document.createElement("div"); - div.dataset.idpartition = r.id; - div.innerHTML = ` -
Non affectés - ${name}
-
+
`; - - div.querySelector("div").addEventListener("click", filtre); - div.querySelector(".ajoutGroupe").addEventListener("click", addPartition); - - document.querySelector("#zonePartitions .masques>div").appendChild(div); - - // Ajout de la zone pour chaque étudiant - let outputGroupes = ""; - - document.querySelectorAll("#zoneChoix .grpPartitions").forEach(e => { - let etudid = e.previousElementSibling.dataset.etudid; - - // Préparation pour la section suivante - let etudiant = { - etudid: etudid, - nom_disp: e.previousElementSibling.dataset.nom, - prenom: e.previousElementSibling.dataset.prenom - } - outputGroupes += templateEtudiant_zoneGroupes(etudiant); - //////////////////////// - - let div = document.createElement("div"); - div.className = "partition"; - div.dataset.idpartition = r.id; - div.innerHTML = ` -
${name}
- - `; - div.querySelector("input").addEventListener("input", assignment); - e.appendChild(div); - }); - - // Ajout de la zone groupes - document.querySelector("#zoneGroupes>.groupes").innerHTML += ` -
-

${name}

-
-
Non affecté(s)
-
${outputGroupes}
-
-
`; - } else { - div.dataset.idgroupe = r.id; - - // Ajout du bouton pour chaque étudiant - let idpartition = this.parentElement.dataset.idpartition; - document.querySelectorAll(`#zoneChoix .etudiants [data-idpartition="${idpartition}"]`).forEach(e => { - let etudid = e.parentElement.parentElement.dataset.etudid; - let label = document.createElement("label"); - label.innerHTML = `${name}`; - label.querySelector("input").addEventListener("input", assignment); - e.appendChild(label); - }) - - // Ajout du groupe dans la zone Groupes - document.querySelector(`#zoneGroupes .partition[data-idpartition="${idpartition}"]`).innerHTML += templateGroupe_zoneGroupes(r.id, name); + // Ajout dans la zone filtres + let partition = { + id: r.id, + partition_name: name } + let divPartition = templateFiltres_partition(partition); + document.querySelector("#zonePartitions .filtres").appendChild(divPartition); + divPartition.querySelector(".modif").click(); + + // Ajout de la zone pour chaque étudiant + let outputGroupes = ""; + + document.querySelectorAll("#zoneChoix .grpPartitions").forEach(e => { + let etudid = e.previousElementSibling.dataset.etudid; + + // Préparation pour la section suivante + let etudiant = { + etudid: etudid, + nom_disp: e.previousElementSibling.dataset.nom, + prenom: e.previousElementSibling.dataset.prenom + } + outputGroupes += templateEtudiant_zoneGroupes(etudiant); + //////////////////////// + + let div = document.createElement("div"); + div.className = "partition"; + div.dataset.idpartition = r.id; + div.innerHTML = ` +
${name}
+ + `; + div.querySelector("input").addEventListener("input", assignment); + e.appendChild(div); + }); + + // Ajout de la zone groupes + document.querySelector("#zoneGroupes>.groupes").innerHTML += ` +
+

${name}

+
+
Non affecté(s)
+
${outputGroupes}
+
+
`; + + listeGroupesAutoaffectation(); + }) + .catch(error => { + document.querySelector("main").innerHTML = "

Une erreur s'est produite lors de la sauvegarde des données.

"; + }) + } + + function addGroupe() { + let date = new Date; + // Groupe + var name = "Nouveau " + date.getSeconds(); + let idPartition = this.parentElement.previousElementSibling.dataset.idpartition; + var url = `/ScoDoc/{{formsemestre.departement.acronym}}/api/partition/${idPartition}/group/create`; + var payload = { group_name: name }; + + fetch(url, + { + method: "POST", + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify(payload) + }) + .then(r => { return r.json() }) + .then(r => { + if (r.message == "invalid partition_name" || r.message == "invalid group_name") { + message("Le nom " + name + " existe déjà"); + return; + } + + let groupe = { + id: r.id, + group_name: name + } + let divGroupe = templateFiltres_groupe(groupe); + this.parentElement.insertBefore(divGroupe, this); + + // Ajout du bouton pour chaque étudiant + document.querySelectorAll(`#zoneChoix .etudiants [data-idpartition="${idPartition}"]`).forEach(e => { + let etudid = e.parentElement.previousElementSibling.dataset.etudid; + let label = document.createElement("label"); + label.innerHTML = `${name}`; + label.querySelector("input").addEventListener("input", assignment); + e.appendChild(label); + }) + + // Ajout du groupe dans la zone Groupes + document.querySelector(`#zoneGroupes .partition[data-idpartition="${idPartition}"]`).innerHTML += templateGroupe_zoneGroupes(r.id, name); + + // Lancement de l'édition du nom + divGroupe.querySelector(".modif").click(); + + listeGroupesAutoaffectation(); }) .catch(error => { document.querySelector("main").innerHTML = "

Une erreur s'est produite lors de la sauvegarde des données.

"; @@ -474,6 +563,7 @@ e.classList.add("editingText"); e.setAttribute("contenteditable", "true"); e.addEventListener("keydown", writing); + e.addEventListener("focusout", () => { saveEditing(this.previousElementSibling) }); // On sélectionne la zone const range = document.createRange(); @@ -506,7 +596,6 @@ var url = `/ScoDoc/{{formsemestre.departement.acronym}}/api/partition/${obj.parentElement.dataset.idpartition}/edit`; var payload = { partition_name: obj.innerText } - document.querySelector(`.masques [data-idpartition="${obj.parentElement.dataset.idpartition}"][data-idgroupe="aucun"]`).innerText = "Non affectés - " + obj.innerText; document.querySelectorAll(`#zoneChoix .etudiants [data-idpartition="${obj.parentElement.dataset.idpartition}"]>div`).forEach(e => { e.innerText = obj.innerText }); document.querySelector(`#zoneGroupes [data-idpartition="${obj.parentElement.dataset.idpartition}"]>h3`).innerText = obj.innerText; } else { @@ -531,6 +620,7 @@ if (!r) { document.querySelector("main").innerHTML = "

Une erreur s'est produite lors de la sauvegarde des données.

"; } + listeGroupesAutoaffectation(); }) .catch(error => { document.querySelector("main").innerHTML = "

Une erreur s'est produite lors de la sauvegarde des données.

"; @@ -586,6 +676,7 @@ if (r.OK != true) { document.querySelector("main").innerHTML = "

Une erreur s'est produite lors de la sauvegarde des données.

"; } + listeGroupesAutoaffectation(); }) } @@ -600,14 +691,21 @@ function moveStart(event) { moveData.x = event.pageX; moveData.y = event.pageY; - moveData.element = this.parentElement; + if (this.parentElement.dataset.idpartition) { + moveData.element = this.parentElement.parentElement; + } else { + moveData.element = this.parentElement; + } moveData.element.classList.add("moving"); moveData.element.parentElement.classList.add('grabbing'); document.body.addEventListener("mousemove", move); - moveData.element.parentElement.querySelectorAll("div:not([data-idgroupe=aucun])").forEach(e => { - e.addEventListener("mouseup", newPosition) - }) document.body.addEventListener("mouseup", moveEnd); + Array.from(moveData.element.parentElement.children).forEach(e => { + if ((e.dataset.idpartition && e.classname != "nonEditable") || + (e.dataset.idgroupe != "aucun")) { + e.addEventListener("mouseup", newPosition) + } + }) } function move(event) { @@ -621,24 +719,28 @@ moveData.element.parentElement.classList.remove('grabbing'); moveData.element.style.transform = ""; moveData.element.classList.remove("moving"); - moveData.element.parentElement.querySelectorAll("div:not([data-idgroupe=aucun])").forEach(e => { - e.removeEventListener("mouseup", newPosition) + Array.from(moveData.element.parentElement.children).forEach(e => { + if ((e.dataset.idpartition && e.classname != "nonEditable") || + (e.dataset.idgroupe && e.dataset.idgroupe != "aucun")) { + e.removeEventListener("mouseup", newPosition) + } }) moveData = {}; } - function newPosition() { + function newPosition(event) { moveData.element.parentElement.insertBefore(moveData.element, this); let positions = []; Array.from(moveData.element.parentElement.children).forEach(e => { - if ((e.dataset.idpartition && e.dataset.idgroupe != "aucun") || (e.dataset.idgroupe && e.dataset.idgroupe != "aucun")) { - positions.push(parseInt(e.dataset.idgroupe || e.dataset.idpartition)) + if ((e.dataset.idpartition && e.classname != "nonEditable") || + (e.dataset.idgroupe && e.dataset.idgroupe != "aucun")) { + positions.push(parseInt(e.dataset.idpartition || e.dataset.idgroupe)) } }) // Save positions - if (this.parentElement.parentElement.parentElement.className == "partitions") { + if (this.dataset.idpartition) { let params = (new URL(document.location)).searchParams; let formsemestre_id = params.get('formsemestre_id'); var url = `/ScoDoc/{{formsemestre.departement.acronym}}/api/formsemestre/${formsemestre_id}/partitions/order`; @@ -659,14 +761,15 @@ }) }) } else { - var url = `/ScoDoc/{{formsemestre.departement.acronym}}/api/partition/${this.parentElement.dataset.idpartition}/groups/order`; + let idPartition = this.closest("[data-idpartition]").dataset.idpartition; + var url = `/ScoDoc/{{formsemestre.departement.acronym}}/api/partition/${idPartition}/groups/order`; - document.querySelectorAll(`#zoneChoix .etudiants .partition[data-idpartition="${this.parentElement.dataset.idpartition}"]`).forEach(partition => { + document.querySelectorAll(`#zoneChoix .etudiants .partition[data-idpartition="${idPartition}"]`).forEach(partition => { positions.forEach(position => { partition.append(partition.querySelector(`[value="${position}"]`).parentElement) }) }) - document.querySelectorAll(`#zoneGroupes .partition[data-idpartition="${this.parentElement.dataset.idpartition}"]`).forEach(partition => { + document.querySelectorAll(`#zoneGroupes .partition[data-idpartition="${idPartition}"]`).forEach(partition => { positions.forEach(position => { partition.append(partition.querySelector(`[data-idgroupe="${position}"]`)) }) @@ -687,11 +790,11 @@ if (!r) { document.querySelector("main").innerHTML = "

Une erreur s'est produite lors de la sauvegarde des données.

"; } + listeGroupesAutoaffectation(); }) .catch(error => { document.querySelector("main").innerHTML = "

Une erreur s'est produite lors de la sauvegarde des données.

"; }) - } /*************************/ diff --git a/app/views/absences.py b/app/views/absences.py index 9e4d5b87..c65a2d49 100644 --- a/app/views/absences.py +++ b/app/views/absences.py @@ -3,7 +3,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -715,7 +715,10 @@ def _gen_form_saisie_groupe( jn = sco_abs.day_names() for d in odates: idx_jour = d.weekday() - noms_jours.append(jn[idx_jour]) + if 0 <= idx_jour < len(jn): + noms_jours.append(jn[idx_jour]) + else: + noms_jours.append("???") # jour non travaillé for jour in noms_jours: H.append( f""" diff --git a/app/views/notes.py b/app/views/notes.py index 7d1d3901..06d62445 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -5,7 +5,7 @@ # # ScoDoc # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -2327,17 +2327,17 @@ def formsemestre_validation_but( # provisoires avec NEXT et PREV try: etudid = int(etudid) - except: + except ValueError: abort(404, "invalid etudid") read_only = not sco_permissions_check.can_validate_sem(formsemestre_id) # --- Navigation - prev = f"""{scu.EMO_PREV_ARROW} précédent """ - next = f"""suivant {scu.EMO_NEXT_ARROW} @@ -2345,7 +2345,7 @@ def formsemestre_validation_but( navigation_div = f"""
""" H = [ html_sco_header.sco_header( - page_title="Validation BUT", + page_title=f"Validation BUT S{formsemestre.semestre_id}", formsemestre_id=formsemestre_id, etudid=etudid, cssstyles=("css/jury_but.css",), javascripts=("js/jury_but.js",), ), - f""" -
+ """
""", ] @@ -2401,7 +2400,6 @@ def formsemestre_validation_but( deca = jury_but.DecisionsProposeesAnnee(etud, formsemestre) if len(deca.rcues_annee) == 0: - # raise ScoValueError("année incomplète: pas de jury BUT annuel possible") return jury_but_view.jury_but_semestriel( formsemestre, etud, read_only, navigation_div=navigation_div ) @@ -2421,7 +2419,7 @@ def formsemestre_validation_but( warning = "" if len(deca.niveaux_competences) != len(deca.decisions_rcue_by_niveau): if deca.a_cheval: - warning += f"""
Attention: regroupements RCUE + warning += """
Attention: regroupements RCUE entre années (redoublement).
""" else: warning += f"""
Attention: {len(deca.niveaux_competences)} @@ -2454,7 +2452,7 @@ def formsemestre_validation_but( {warning}
- + """ ) @@ -2467,6 +2465,10 @@ def formsemestre_validation_but( Les champs entourés en vert sont enregistrés.
""" ) else: + erase_span = f"""effacer décisions""" H.append( f"""
@@ -2477,7 +2479,8 @@ def formsemestre_validation_but(
- + + {erase_span}
""" ) @@ -2790,7 +2793,9 @@ def formsemestre_jury_but_erase( explanation=f"""Les validations d'UE et autorisations de passage du semestre S{formsemestre.semestre_id} seront effacées.""" if only_one_sem - else """Les validations de toutes les UE, RCUE (compétences) et année seront effacées.""", + else """Les validations de toutes les UE, RCUE (compétences) et année seront effacées. + Les décisions de l'année scolaire précédente ne seront pas modifiées. + """, cancel_url=dest_url, ) diff --git a/app/views/pn_modules.py b/app/views/pn_modules.py index feee40a7..c67e79a3 100644 --- a/app/views/pn_modules.py +++ b/app/views/pn_modules.py @@ -5,7 +5,7 @@ # # ScoDoc # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/views/scodoc.py b/app/views/scodoc.py index 494cf3b9..184fcdd1 100644 --- a/app/views/scodoc.py +++ b/app/views/scodoc.py @@ -5,7 +5,7 @@ # # ScoDoc # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/views/scolar.py b/app/views/scolar.py index b82b082a..8a25b34b 100644 --- a/app/views/scolar.py +++ b/app/views/scolar.py @@ -3,7 +3,7 @@ # # ScoDoc # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/app/views/users.py b/app/views/users.py index 8c726f1a..4ad30aeb 100644 --- a/app/views/users.py +++ b/app/views/users.py @@ -5,7 +5,7 @@ # # ScoDoc # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/config.py b/config.py index 57c0fb1f..740860a6 100755 --- a/config.py +++ b/config.py @@ -72,7 +72,7 @@ class DevConfig(Config): class TestConfig(DevConfig): "Pour les tests unitaires" TESTING = True - DEBUG = True + DEBUG = False SQLALCHEMY_DATABASE_URI = ( os.environ.get("SCODOC_TEST_DATABASE_URI") or "postgresql:///SCODOC_TEST" ) diff --git a/migrations/versions/554c13cea377_formsemestre_not_nulls.py b/migrations/versions/554c13cea377_formsemestre_not_nulls.py new file mode 100644 index 00000000..a7cdc9e5 --- /dev/null +++ b/migrations/versions/554c13cea377_formsemestre_not_nulls.py @@ -0,0 +1,66 @@ +"""formsemestre_not_nulls + +Revision ID: 554c13cea377 +Revises: 3c12f5850cff +Create Date: 2023-01-09 08:02:53.637488 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "554c13cea377" +down_revision = "3c12f5850cff" +branch_labels = None +depends_on = None + + +def upgrade(): + # + op.execute("UPDATE notes_formsemestre SET titre = '' WHERE titre is NULL;") + op.alter_column( + "notes_formsemestre", "titre", existing_type=sa.TEXT(), nullable=False + ) + op.execute( + "UPDATE notes_formsemestre SET date_debut = now() WHERE date_debut is NULL;" + ) + op.alter_column( + "notes_formsemestre", "date_debut", existing_type=sa.DATE(), nullable=False + ) + op.execute("UPDATE notes_formsemestre SET date_fin = now() WHERE date_fin is NULL;") + op.alter_column( + "notes_formsemestre", "date_fin", existing_type=sa.DATE(), nullable=False + ) + op.execute( + "UPDATE notes_formsemestre SET bul_bgcolor = 'white' WHERE bul_bgcolor is NULL;" + ) + op.alter_column( + "notes_formsemestre", + "bul_bgcolor", + existing_type=sa.VARCHAR(length=32), + nullable=False, + existing_server_default=sa.text("'white'::character varying"), + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column( + "notes_formsemestre", + "bul_bgcolor", + existing_type=sa.VARCHAR(length=32), + nullable=True, + existing_server_default=sa.text("'white'::character varying"), + ) + op.alter_column( + "notes_formsemestre", "date_fin", existing_type=sa.DATE(), nullable=True + ) + op.alter_column( + "notes_formsemestre", "date_debut", existing_type=sa.DATE(), nullable=True + ) + op.alter_column( + "notes_formsemestre", "titre", existing_type=sa.TEXT(), nullable=True + ) + # ### end Alembic commands ### diff --git a/migrations/versions/7e5b519a27e1_cascade_modimpls.py b/migrations/versions/7e5b519a27e1_cascade_modimpls.py new file mode 100644 index 00000000..42870d01 --- /dev/null +++ b/migrations/versions/7e5b519a27e1_cascade_modimpls.py @@ -0,0 +1,43 @@ +"""cascade_modimpls + +Revision ID: 7e5b519a27e1 +Revises: 554c13cea377 +Create Date: 2023-01-12 08:49:01.744120 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "7e5b519a27e1" +down_revision = "554c13cea377" +branch_labels = None +depends_on = None + + +def upgrade(): + # + op.execute("DELETE FROM notes_moduleimpl WHERE module_id is NULL;") + op.alter_column( + "notes_moduleimpl", "module_id", existing_type=sa.INTEGER(), nullable=False + ) + op.execute("DELETE FROM notes_moduleimpl WHERE formsemestre_id is NULL;") + op.alter_column( + "notes_moduleimpl", + "formsemestre_id", + existing_type=sa.INTEGER(), + nullable=False, + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column( + "notes_moduleimpl", "formsemestre_id", existing_type=sa.INTEGER(), nullable=True + ) + op.alter_column( + "notes_moduleimpl", "module_id", existing_type=sa.INTEGER(), nullable=True + ) + # ### end Alembic commands ### diff --git a/misc/csv2rules.py b/misc/csv2rules.py index 4eb01c9d..df4ca162 100755 --- a/misc/csv2rules.py +++ b/misc/csv2rules.py @@ -6,7 +6,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/misc/geolocalize_lycees.py b/misc/geolocalize_lycees.py index fc4e63dd..ab16cd2d 100644 --- a/misc/geolocalize_lycees.py +++ b/misc/geolocalize_lycees.py @@ -5,7 +5,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/pytest.ini b/pytest.ini index a73c5d60..e6563b88 100644 --- a/pytest.ini +++ b/pytest.ini @@ -2,3 +2,5 @@ markers = slow: marks tests as slow (deselect with '-m "not slow"') lemans + lyon + diff --git a/sco_version.py b/sco_version.py index 98f0f59b..96130c74 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.4.20" +SCOVERSION = "9.4.26" SCONAME = "ScoDoc" diff --git a/tests/ressources/formations/scodoc_formation_BUT_GEII_lyon_v1.xml b/tests/ressources/formations/scodoc_formation_BUT_GEII_lyon_v1.xml new file mode 100644 index 00000000..81cdb8e5 --- /dev/null +++ b/tests/ressources/formations/scodoc_formation_BUT_GEII_lyon_v1.xml @@ -0,0 +1,504 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/unit/cursus_but_geii_lyon.yaml b/tests/unit/cursus_but_geii_lyon.yaml new file mode 100644 index 00000000..1230273c --- /dev/null +++ b/tests/unit/cursus_but_geii_lyon.yaml @@ -0,0 +1,278 @@ +# Tests unitaires jury BUT - IUT Lyon GEII +# Essais avec un BUT GEII, 2 UE en BUT1 / 4 UE en BUT2-BUT3 et 3 parcours +# Contrib Pascal B. + +ReferentielCompetences: + filename: but-GEII-05012022-081639.xml + specialite: GEII + +Formation: + filename: scodoc_formation_BUT_GEII_lyon_v1.xml + # Association des UE aux compétences: + ues: + # S1 : Tronc commun GEII + 'UE11': + annee: BUT1 + competence: Concevoir + 'UE12': + annee: BUT1 + competence: Vérifier + + # S2 : Tronc commun GEII + 'UE21': + annee: BUT1 + competence: Concevoir + 'UE22': + annee: BUT1 + competence: Vérifier + + # S3 : Tronc commun GEII + 'UE31': + annee: BUT2 + competence: Concevoir + 'UE32': + annee: BUT2 + competence: Vérifier + 'UE33': + annee: BUT2 + competence: Maintenir + # S3 : Parcours EME + 'UE34EME': + annee: BUT2 + competence: Installer + parcours: EME + # S3 : Parcours ESE + 'UE34ESE': + annee: BUT2 + competence: Implanter + parcours: ESE + # S3 : Parcours AII + 'UE34AII': + annee: BUT2 + competence: Intégrer + parcours: AII + + # S4 : Tronc commun GEII + 'UE41': + annee: BUT2 + competence: Concevoir + 'UE42': + annee: BUT2 + competence: Vérifier + 'UE43': + annee: BUT2 + competence: Maintenir + # S4 : Parcours EME + 'UE44EME': + annee: BUT2 + competence: Installer + parcours: EME + # S4 : Parcours ESE + 'UE44ESE': + annee: BUT2 + competence: Implanter + parcours: ESE + # S4 : Parcours AII + 'UE44AII': + annee: BUT2 + competence: Intégrer + parcours: AII + + modules_parcours: + # cette section permet d'associer des modules à des parcours + # les codes modules peuvent être des regexp + EME: [ .*EME.* ] + ESE: [ .*ESE.* ] + AII: [ .*AII.* ] + +FormSemestres: + # S1 et S2 : + S1: + idx: 1 + date_debut: 2021-09-01 + date_fin: 2022-01-15 + S2: + idx: 2 + date_debut: 2022-01-16 + date_fin: 2022-06-30 + # S3 avec les trois parcours réunis: + # S3: + # idx: 3 + # date_debut: 2022-09-01 + # date_fin: 2023-01-13 + # codes_parcours: ['AII', 'EME', 'ESE'] + # Un S1 pour les redoublants + S1-red: + idx: 1 + date_debut: 2022-09-02 + date_fin: 2023-01-12 + +Etudiants: + geii8: + prenom: etugeii8 + civilite: M + formsemestres: + S1: + notes_modules: # on joue avec les SAE seulement car elles sont "diagonales" + "S1.1": 7.00 + "S1.2": 9.00 + attendu: # les codes jury que l'on doit vérifier + deca: + passage_de_droit: False + nb_competences: 2 + nb_rcue_annee: 0 + decisions_ues: + "UE11": + codes: [ "AJ", "..." ] + code_valide: AJ + decision_jury: AJ + moy_ue: 7.00 + "UE12": + codes: [ "AJ", "..." ] + code_valide: AJ + decision_jury: AJ + moy_ue: 9.00 + S2: + notes_modules: # on joue avec les SAE seulement car elles sont "diagonales" + "S2.1": 12.00 + "S2.2": 12.00 + attendu: # les codes jury que l'on doit vérifier + deca: + passage_de_droit: False + nb_competences: 2 + nb_rcue_annee: 2 + valide_moitie_rcue: False + codes: [ "RED", "..." ] + decisions_ues: + "UE21": + codes: [ "ADM", "..." ] + code_valide: ADM + decision_jury: ADM + moy_ue: 12.00 + "UE22": + codes: [ "ADM", "..." ] + code_valide: ADM + decision_jury: ADM + moy_ue: 12.00 + decisions_rcues: # on repère ici les RCUE par l'acronyme de leur 1ere UE (donc du S1) + "UE11": + code_valide: AJ + decision_jury: AJ + rcue: + moy_rcue: 9.50 + est_compensable: False + "UE12": + code_valide: CMP + decision_jury: CMP + rcue: + moy_rcue: 10.50 + est_compensable: True + decision_annee: RED + S1-red: + notes_modules: # on joue avec les SAE seulement car elles sont "diagonales" + "S1.1": 9.50 + "S1.2": 7.00 + attendu: # les codes jury que l'on doit vérifier + deca: + passage_de_droit: False + nb_competences: 2 + nb_rcue_annee: 2 + decisions_ues: + "UE11": + codes: [ "CMP", "..." ] + code_valide: CMP + decision_jury: CMP + moy_ue: 9.50 + "UE12": + codes: [ "AJ", "..." ] + code_valide: AJ + decision_jury: AJ + moy_ue: 7.00 + decisions_rcues: # on repère ici les RCUE par l'acronyme de leur 1ere UE (donc du S1) + "UE11": + code_valide: CMP + rcue: + moy_rcue: 10.75 + est_compensable: True + "UE12": + code_valide: CMP # car validé en fin de S2 + rcue: + moy_rcue: 9.50 # la moyenne courante (et non enregistrée), donc pas 10.5 + est_compensable: False + decision_annee: ADM + geii43: + prenom: etugeii43 + civilite: M + formsemestres: + S1: + notes_modules: # on joue avec les SAE seulement car elles sont "diagonales" + "S1.1": 9.00 + "S1.2": 9.00 + attendu: # les codes jury que l'on doit vérifier + deca: + passage_de_droit: False + nb_competences: 2 + nb_rcue_annee: 0 + decisions_ues: + "UE11": + codes: [ "AJ", "..." ] + code_valide: AJ + decision_jury: AJ + moy_ue: 9.00 + "UE12": + codes: [ "AJ", "..." ] + code_valide: AJ + decision_jury: AJ + moy_ue: 9.00 + S2: + notes_modules: # on joue avec les SAE seulement car elles sont "diagonales" + "S2.1": 9.00 + "S2.2": 9.00 + attendu: # les codes jury que l'on doit vérifier + deca: + passage_de_droit: False + nb_competences: 2 + nb_rcue_annee: 2 + valide_moitie_rcue: False + codes: [ "RED", "..." ] + decisions_ues: + "UE21": + codes: [ "AJ", "..." ] + code_valide: AJ + moy_ue: 9.00 + "UE22": + codes: [ "AJ", "..." ] + code_valide: AJ # va basculer en ADJ car RCUE en ADJ (mais le test est AVANT !) + moy_ue: 9.00 + decisions_rcues: # on repère ici les RCUE par l'acronyme de leur 1ere UE (donc du S1) + "UE11": + code_valide: AJ + decision_jury: AJ # inutile de la préciser mais on peut + rcue: + moy_rcue: 9.00 + est_compensable: False + "UE12": + code_valide: AJ # code par défaut proposé + decision_jury: ADJ # code donné par le jury de S2 + rcue: + moy_rcue: 9.00 + est_compensable: False + decision_annee: RED + S1-red: + notes_modules: # on joue avec les SAE seulement car elles sont "diagonales" + "S1.1": 11.00 + "S1.2": 7.00 + attendu: # les codes jury que l'on doit vérifier + deca: + passage_de_droit: false + nb_competences: 2 + nb_rcue_annee: 0 + decisions_ues: + "UE11": + codes: [ "ADM", "..." ] + code_valide: ADM + moy_ue: 11.00 + "UE12": + code_valide: AJ + moy_ue: 7.00 + decision_annee: AJ diff --git a/tests/unit/cursus_but_gmp_iutlm.yaml b/tests/unit/cursus_but_gmp_iutlm.yaml index 47e0cbf8..4d3da7a6 100644 --- a/tests/unit/cursus_but_gmp_iutlm.yaml +++ b/tests/unit/cursus_but_gmp_iutlm.yaml @@ -1,6 +1,9 @@ # Tests unitaires jury BUT - IUTLM GMP # Essais avec un BUT GMP, 4 UE + 1 bonus et deux parcours sur S3 S4 # Contrib Martin M. +# +# Pour ne jouer que ce scénario: +# pytest -m lemans tests/unit/test_but_jury.py ReferentielCompetences: filename: but-GMP-05012022-081650.xml @@ -115,7 +118,7 @@ FormSemestres: date_fin: 2024-01-12 Etudiants: - gmp01: + gmp01: # cursus S1, S2, S3 prenom: etugmp01 civilite: M formsemestres: @@ -231,7 +234,7 @@ Etudiants: code_valide: ADM moy_ue: 12.5 - gmp02: + gmp02: # cursus S1, S2, S3 prenom: etugmp02 civilite: F formsemestres: @@ -346,7 +349,7 @@ Etudiants: # code_valide: ADM # moy_ue: 14 - gmp03: + gmp03: # cursus S1, S2, S1-red prenom: etugmp03 civilite: X formsemestres: @@ -431,6 +434,7 @@ Etudiants: notes_modules: # on ne note ici que les UE à refaire "SAE1.1": 14. # il améliore son UE 1 "SAE1.2": 12. # et cette fois reussi les autres + "SAE1.3": EXC # pour que l'éval soit complete "SAE1.4": 13. attendu: nb_competences: 4 @@ -447,3 +451,19 @@ Etudiants: "UE1.4-C4": code_valide: ADM moy_ue: 13 + + gmp04: # cursus S1-red (primo-entrant) + prenom: Primo + civilite: M + formsemestres: + S1-red: + notes_modules: + "SAE1.1": 11. + "SAE1.2": 12. + "SAE1.3": 13. + "SAE1.4": 9. + attendu: # les codes jury que l'on doit vérifier + deca: + "UE1.4-C4": + code_valide: "AJ" + moy_ue: 9. diff --git a/tests/unit/sco_fake_gen.py b/tests/unit/sco_fake_gen.py index 0c07a27e..e70c434c 100644 --- a/tests/unit/sco_fake_gen.py +++ b/tests/unit/sco_fake_gen.py @@ -232,7 +232,7 @@ class ScoFake(object): self, formation_id=None, semestre_id=None, - titre=None, + titre="", date_debut=None, date_fin=None, etat=None, @@ -253,6 +253,7 @@ class ScoFake(object): ) -> int: if responsables is None: responsables = (self.default_user.id,) + titre = titre or "sans titre" oid = sco_formsemestre.do_formsemestre_create(locals()) oids = sco_formsemestre.do_formsemestre_list( args={"formsemestre_id": oid} diff --git a/tests/unit/test_but_jury.py b/tests/unit/test_but_jury.py index 15a59342..29cf7182 100644 --- a/tests/unit/test_but_jury.py +++ b/tests/unit/test_but_jury.py @@ -1,19 +1,29 @@ +############################################################################## +# ScoDoc +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. +# See LICENSE +############################################################################## + """ Test jury BUT avec parcours + +Ces tests sont généralement lents (construction de la base), +et donc marqués par `@pytest.mark.slow`. + +Certains sont aussi marqués par @pytest.mark.lemans ou @pytest.mark.lyon +pour lancer certains tests spécifiques seulement. + +Exemple utilisation spécifique: +# test sur "Lyon" seulement: +pytest --pdb -m lyon tests/unit/test_but_jury.py + """ import pytest from tests.unit import yaml_setup import app -from app.but.jury_but import DecisionsProposeesAnnee from app.but.jury_but_validation_auto import formsemestre_validation_auto_but -from app.models import ( - Formation, - FormSemestre, - Identite, - UniteEns, -) -from app.scodoc import sco_utils as scu +from app.models import FormSemestre from config import TestConfig DEPT = TestConfig.DEPT_TEST @@ -35,7 +45,7 @@ def test_but_jury_GB(test_client): # Vérifie les deca de tous les semestres: for formsemestre in FormSemestre.query: - _check_deca(formsemestre) + yaml_setup.check_deca_fields(formsemestre) # Saisie de toutes les décisions de jury for formsemestre in FormSemestre.query.order_by(FormSemestre.semestre_id): @@ -43,18 +53,18 @@ def test_but_jury_GB(test_client): # Vérifie résultats attendus: S1: FormSemestre = FormSemestre.query.filter_by(titre="S1_SEE").first() - _test_but_jury(S1, doc) + yaml_setup.test_but_jury(S1, doc) S2: FormSemestre = FormSemestre.query.filter_by(titre="S2_SEE").first() - _test_but_jury(S2, doc) + yaml_setup.test_but_jury(S2, doc) S3: FormSemestre = FormSemestre.query.filter_by(titre="S3").first() - _test_but_jury(S3, doc) + yaml_setup.test_but_jury(S3, doc) # _test_but_jury(S1_redoublant, doc) @pytest.mark.slow @pytest.mark.lemans def test_but_jury_GMP_lm(test_client): - """Tests sur un cursus GMP fournit par Le Mans""" + """Tests sur un cursus GMP fourni par Le Mans""" app.set_sco_dept(DEPT) # Construit la base de test GB une seule fois # puis lance les tests de jury @@ -66,7 +76,7 @@ def test_but_jury_GMP_lm(test_client): # Vérifie les deca de tous les semestres: for formsemestre in formsemestres: - _check_deca(formsemestre) + yaml_setup.check_deca_fields(formsemestre) # Saisie de toutes les décisions de jury qui ne le seraient pas déjà for formsemestre in formsemestres: @@ -74,61 +84,30 @@ def test_but_jury_GMP_lm(test_client): # Vérifie résultats attendus: for formsemestre in formsemestres: - _test_but_jury(formsemestre, doc) + yaml_setup.test_but_jury(formsemestre, doc) -def _check_deca(formsemestre: FormSemestre, etud: Identite = None): - """vérifie les champs principaux de l'instance de DecisionsProposeesAnnee""" - etud = etud or formsemestre.etuds.first() - assert etud # il faut au moins un étudiant dans le semestre - deca = DecisionsProposeesAnnee(etud, formsemestre) - assert deca.validation is None # pas encore de validation enregistrée - assert False is deca.recorded - assert deca.code_valide is None - if formsemestre.semestre_id % 2: - assert deca.formsemestre_impair == formsemestre - assert formsemestre.query_ues_parcours_etud(etud.id).all() == deca.ues_impair - else: - assert deca.formsemestre_pair == formsemestre - assert formsemestre.query_ues_parcours_etud(etud.id).all() == deca.ues_pair - assert deca.inscription_etat == scu.INSCRIT - assert deca.inscription_etat_impair == scu.INSCRIT - assert (deca.parcour is None) or ( - deca.parcour.id in {p.id for p in formsemestre.parcours} - ) +@pytest.mark.slow +@pytest.mark.lyon +def test_but_jury_GEII_lyon(test_client): + """Tests sur un cursus GEII fourni par Lyon""" + app.set_sco_dept(DEPT) + # Construit la base de test GB une seule fois + # puis lance les tests de jury + doc = yaml_setup.setup_from_yaml("tests/unit/cursus_but_geii_lyon.yaml") - nb_ues = ( - len(deca.formsemestre_pair.query_ues_parcours_etud(etud.id).all()) - if deca.formsemestre_pair - else 0 - ) - nb_ues += ( - len(deca.formsemestre_impair.query_ues_parcours_etud(etud.id).all()) - if deca.formsemestre_impair - else 0 - ) - assert len(deca.decisions_ues) == nb_ues + formsemestres = FormSemestre.query.order_by( + FormSemestre.date_debut, FormSemestre.semestre_id + ).all() - nb_ues_un_sem = ( - len(deca.formsemestre_impair.query_ues_parcours_etud(etud.id).all()) - if deca.formsemestre_impair - else len(deca.formsemestre_pair.query_ues_parcours_etud(etud.id).all()) - ) - assert len(deca.niveaux_competences) == nb_ues_un_sem - assert deca.nb_competences == nb_ues_un_sem + # Vérifie les champs de DecisionsProposeesAnnee de tous les semestres: + for formsemestre in formsemestres: + yaml_setup.check_deca_fields(formsemestre) - -def _test_but_jury(formsemestre: FormSemestre, doc: dict): - """Test jurys - Vérifie les champs de DecisionsProposeesAnnee et UEs - """ - for etud in formsemestre.etuds: - deca = DecisionsProposeesAnnee(etud, formsemestre) - doc_formsemestre = doc["Etudiants"][etud.nom]["formsemestres"][ - formsemestre.titre - ] - assert doc_formsemestre - if "attendu" in doc_formsemestre: - if "deca" in doc_formsemestre["attendu"]: - deca_att = doc_formsemestre["attendu"]["deca"] - yaml_setup.compare_decisions_annee(deca, deca_att) + # Saisie de toutes les décisions de jury "automatiques" + # et vérification des résultats attendus: + for formsemestre in formsemestres: + formsemestre_validation_auto_but( + formsemestre, only_adm=False, no_overwrite=False + ) + yaml_setup.test_but_jury(formsemestre, doc) diff --git a/tests/unit/yaml_setup.py b/tests/unit/yaml_setup.py index 535fcdc7..12e9d0ce 100644 --- a/tests/unit/yaml_setup.py +++ b/tests/unit/yaml_setup.py @@ -1,7 +1,45 @@ +############################################################################## +# ScoDoc +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. +# See LICENSE +############################################################################## + """ -Met en place une base pour les tests, à partir d'une description YAML -qui peut donner la formation, son ref. compétences, les formsemestres, -les étudiants et leurs notes. +Met en place une base pour les tests unitaires, à partir d'une description +YAML qui peut donner la formation, son ref. compétences, les formsemestres, +les étudiants et leurs notes et décisions de jury. + +Le traitement est effectué dans l'ordre suivant: + +setup_from_yaml() + + - setup_but_formation(): + - import de la formation (le test utilise une seule formation) + - associe_ues_et_parcours(): + - crée les associations formation <-> référentiel de compétence + - setup_formsemestres() + - crée les formsemestres décrits dans le YAML + avec tous les modules du semestre ou des parcours si indiqués + et une évaluation dans chaque moduleimpl. + - inscrit_les_etudiants() + - inscrit et place dans les groupes de parcours + - note_les_modules() + - saisie de toutes les notes indiquées dans le YAML + +check_deca_fields() + - vérifie les champs du deca (nb UE, compétences, ...) mais pas les décisions de jury. + +formsemestre_validation_auto_but(only_adm=False) + - enregistre toutes les décisions "par défaut" proposées (pas seulement les ADM) + +test_but_jury() + - compare décisions attendues indiquées dans le YAML avec celles de ScoDoc + et enregistre immédiatement APRES la décision manuelle indiquée par `decision_jury` + dans le YAML. + + +Les tests unitaires associés sont généralement lents (construction de la base), +et donc marqués par `@pytest.mark.slow`. """ import os @@ -157,7 +195,7 @@ def _un_semestre( date_debut: str, date_fin: str, ) -> FormSemestre: - "Création d'un formsemetre" + "Création d'un formsemestre" formsemestre = FormSemestre( formation=formation, parcours=parcours, @@ -237,10 +275,21 @@ def note_les_modules(doc: dict): raise ValueError(f"module de code '{code_module}' introuvable") for evaluation in modimpl.evaluations: # s'il y a plusieurs evals, affecte la même note à chacune + note_value, invalid = sco_saisie_notes.convert_note_from_string( + str(note), + 20.0, + note_min=scu.NOTES_MIN, + etudid=etud.id, + absents=[], + tosuppress=[], + invalids=[], + ) + assert not invalid # valeur note invalide + assert isinstance(note_value, float) sco_saisie_notes.notes_add( a_user, evaluation.id, - [(etud.id, float(note))], + [(etud.id, note_value)], comment="note_les_modules", ) @@ -341,7 +390,7 @@ def _check_decisions_ues( decisions_ues: dict[int, DecisionsProposeesUE], decisions_ues_att: dict[str:dict] ): """Vérifie les décisions d'UE - pui enregistre décision manuelle si indiquée. + puis enregistre décision manuelle si indiquée dans le YAML. """ for acronyme, dec_ue_att in decisions_ues_att.items(): # retrouve l'UE @@ -374,7 +423,7 @@ def _check_decisions_rcues( ): "Vérifie les décisions d'RCUEs" for acronyme, dec_rcue_att in decisions_rcues_att.items(): - # retrouve la décision RCUE à partir de l'acronyme de la 1er UE + # retrouve la décision RCUE à partir de l'acronyme de la 1ère UE rcues_d = [ dec_rcue for dec_rcue in decisions_rcues @@ -399,11 +448,18 @@ def _check_decisions_rcues( dec_rcue.rcue.est_compensable() == dec_rcue_att["rcue"]["est_compensable"] ) + # Force décision de jury: + code_manuel = dec_rcue_att.get("decision_jury") + if code_manuel is not None: + assert code_manuel in dec_rcue.codes + dec_rcue.record(code_manuel) def compare_decisions_annee(deca: DecisionsProposeesAnnee, deca_att: dict): - """Vérifie que les résultats de jury calculés sont ceux attendus, + """Vérifie que les résultats de jury calculés pour l'année, les RCUEs et les UEs + sont ceux attendus, puis enregistre les décisions manuelles indiquées dans le YAML. + deca est le résultat calculé par ScoDoc deca_att est un dict lu du YAML """ @@ -424,3 +480,70 @@ def compare_decisions_annee(deca: DecisionsProposeesAnnee, deca_att: dict): _check_decisions_rcues( deca.decisions_rcue_by_niveau.values(), deca_att["decisions_rcues"] ) + # Force décision de jury: + code_manuel = deca_att.get("decision_jury") + if code_manuel is not None: + assert code_manuel in deca.codes + deca.record(code_manuel) + assert deca.recorded + + +def check_deca_fields(formsemestre: FormSemestre, etud: Identite = None): + """Vérifie les champs principaux (inscription, nb UE, nb compétences) + de l'instance de DecisionsProposeesAnnee. + Ne vérifie pas les décisions de jury proprement dites. + Si etud n'est pas spécifié, prend le premier inscrit trouvé dans le semestre. + """ + etud = etud or formsemestre.etuds.first() + assert etud # il faut au moins un étudiant dans le semestre + deca = DecisionsProposeesAnnee(etud, formsemestre) + assert deca.validation is None # pas encore de validation enregistrée + assert False is deca.recorded + assert deca.code_valide is None + if formsemestre.semestre_id % 2: + assert deca.formsemestre_impair == formsemestre + assert formsemestre.query_ues_parcours_etud(etud.id).all() == deca.ues_impair + else: + assert deca.formsemestre_pair == formsemestre + assert formsemestre.query_ues_parcours_etud(etud.id).all() == deca.ues_pair + assert deca.inscription_etat == scu.INSCRIT + assert deca.inscription_etat_impair == scu.INSCRIT + assert (deca.parcour is None) or ( + deca.parcour.id in {p.id for p in formsemestre.parcours} + ) + + nb_ues = ( + len(deca.formsemestre_pair.query_ues_parcours_etud(etud.id).all()) + if deca.formsemestre_pair + else 0 + ) + nb_ues += ( + len(deca.formsemestre_impair.query_ues_parcours_etud(etud.id).all()) + if deca.formsemestre_impair + else 0 + ) + assert len(deca.decisions_ues) == nb_ues + + nb_ues_un_sem = ( + len(deca.formsemestre_impair.query_ues_parcours_etud(etud.id).all()) + if deca.formsemestre_impair + else len(deca.formsemestre_pair.query_ues_parcours_etud(etud.id).all()) + ) + assert len(deca.niveaux_competences) == nb_ues_un_sem + assert deca.nb_competences == nb_ues_un_sem + + +def test_but_jury(formsemestre: FormSemestre, doc: dict): + """Test jurys BUT + Vérifie les champs de DecisionsProposeesAnnee et UEs + """ + for etud in formsemestre.etuds: + deca = DecisionsProposeesAnnee(etud, formsemestre) + doc_formsemestre = doc["Etudiants"][etud.nom]["formsemestres"][ + formsemestre.titre + ] + assert doc_formsemestre + if "attendu" in doc_formsemestre: + if "deca" in doc_formsemestre["attendu"]: + deca_att = doc_formsemestre["attendu"]["deca"] + compare_decisions_annee(deca, deca_att) diff --git a/tools/anonymize_db.py b/tools/anonymize_db.py index 34543fe4..a6825098 100755 --- a/tools/anonymize_db.py +++ b/tools/anonymize_db.py @@ -6,7 +6,7 @@ # # Gestion scolarite IUT # -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/tools/upgrade.sh b/tools/upgrade.sh index 7a7d9b46..551ca1a8 100755 --- a/tools/upgrade.sh +++ b/tools/upgrade.sh @@ -19,6 +19,7 @@ source "$SCRIPT_DIR/utils.sh" cd "$SCODOC_DIR" || { echo "Invalid directory"; exit 1; } +export DEBIAN_FRONTEND=noninteractive check_uid_root "$0"