From c9babcd8c266abc3e1ed3b7c8debc4c09edc4d41 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 23 Jun 2023 10:37:32 +0200 Subject: [PATCH] =?UTF-8?q?news=20pour=20op=C3=A9rations=20jury.=20Impleme?= =?UTF-8?q?nts=20#668?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/jury.py | 22 +++++++++++++++++- app/but/jury_but.py | 5 +--- app/but/jury_but_validation_auto.py | 14 ++++++++++-- app/but/jury_but_view.py | 11 +++++++++ app/models/events.py | 12 +++++++--- app/scodoc/sco_edit_formation.py | 2 ++ app/scodoc/sco_edit_matiere.py | 2 -- app/scodoc/sco_edit_module.py | 2 -- app/scodoc/sco_edit_ue.py | 2 -- app/scodoc/sco_etud.py | 1 + app/scodoc/sco_formations.py | 1 + app/scodoc/sco_formsemestre.py | 1 + app/scodoc/sco_formsemestre_edit.py | 1 + app/scodoc/sco_formsemestre_validation.py | 28 +++++++++++++++-------- app/scodoc/sco_import_etuds.py | 1 + app/scodoc/sco_synchro_etuds.py | 1 - app/static/css/scodoc.css | 2 +- app/views/notes.py | 18 ++++++++++----- sco_version.py | 2 +- 19 files changed, 94 insertions(+), 34 deletions(-) diff --git a/app/api/jury.py b/app/api/jury.py index 2800c77a..7de6cdef 100644 --- a/app/api/jury.py +++ b/app/api/jury.py @@ -5,9 +5,10 @@ ############################################################################## """ - ScoDoc 9 API : jury WIP + ScoDoc 9 API : jury WIP à compléter avec enregistrement décisions """ +from flask import g, url_for from flask_json import as_json from flask_login import login_required @@ -24,6 +25,7 @@ from app.models import ( Identite, ScolarAutorisationInscription, ScolarFormSemestreValidation, + ScolarNews, ) from app.scodoc import sco_cache from app.scodoc.sco_permissions import Permission @@ -47,6 +49,20 @@ def decisions_jury(formsemestre_id: int): raise ScoException("non implemente") +def _news_delete_jury_etud(etud: Identite): + "génère news sur effacement décision" + # n'utilise pas g.scodoc_dept, pas toujours dispo en mode API + url = url_for( + "scolar.ficheEtud", scodoc_dept=etud.departement.acronym, etudid=etud.id + ) + ScolarNews.add( + typ=ScolarNews.NEWS_JURY, + obj=etud.id, + text=f"""Suppression décision jury pour {etud.nomprenom}""", + url=url, + ) + + @bp.route( "/etudiant//jury/validation_ue//delete", methods=["POST"], @@ -94,6 +110,7 @@ def _validation_ue_delete(etudid: int, validation_id: int): db.session.delete(validation) sco_cache.invalidate_formsemestre_etud(etud) db.session.commit() + _news_delete_jury_etud(etud) return "ok" @@ -121,6 +138,7 @@ def autorisation_inscription_delete(etudid: int, validation_id: int): db.session.delete(validation) sco_cache.invalidate_formsemestre_etud(etud) db.session.commit() + _news_delete_jury_etud(etud) return "ok" @@ -148,6 +166,7 @@ def validation_rcue_delete(etudid: int, validation_id: int): db.session.delete(validation) sco_cache.invalidate_formsemestre_etud(etud) db.session.commit() + _news_delete_jury_etud(etud) return "ok" @@ -175,4 +194,5 @@ def validation_annee_but_delete(etudid: int, validation_id: int): db.session.delete(validation) sco_cache.invalidate_formsemestre_etud(etud) db.session.commit() + _news_delete_jury_etud(etud) return "ok" diff --git a/app/but/jury_but.py b/app/but/jury_but.py index d9f8031a..1a5c1a4c 100644 --- a/app/but/jury_but.py +++ b/app/but/jury_but.py @@ -459,10 +459,7 @@ class DecisionsProposeesAnnee(DecisionsProposees): """informations, for debugging purpose.""" text = f"""DecisionsProposeesAnnee
    -
  • Etudiant: {self.etud.nomprenom} -
  • +
  • Étudiant: {self.etud.html_link_fiche()}
  • """ for formsemestre, title in ( (self.formsemestre_impair, "formsemestre_impair"), diff --git a/app/but/jury_but_validation_auto.py b/app/but/jury_but_validation_auto.py index e698b5dd..a061b42f 100644 --- a/app/but/jury_but_validation_auto.py +++ b/app/but/jury_but_validation_auto.py @@ -6,11 +6,11 @@ """Jury BUT: calcul des décisions de jury annuelles "automatiques" """ +from flask import g, url_for from app import db from app.but import jury_but -from app.models.etudiants import Identite -from app.models.formsemestre import FormSemestre +from app.models import Identite, FormSemestre, ScolarNews from app.scodoc import sco_cache from app.scodoc.sco_exceptions import ScoValueError @@ -39,4 +39,14 @@ def formsemestre_validation_auto_but( nb_etud_modif += deca.record_all(only_validantes=only_adm) db.session.commit() + ScolarNews.add( + typ=ScolarNews.NEWS_JURY, + obj=formsemestre.id, + text=f"""Calcul jury automatique du semestre {formsemestre.html_link_status()}""", + url=url_for( + "notes.formsemestre_status", + scodoc_dept=g.scodoc_dept, + formsemestre_id=formsemestre.id, + ), + ) return nb_etud_modif diff --git a/app/but/jury_but_view.py b/app/but/jury_but_view.py index 87321684..275d93b1 100644 --- a/app/but/jury_but_view.py +++ b/app/but/jury_but_view.py @@ -31,6 +31,7 @@ from app.models import ( UniteEns, ScolarAutorisationInscription, ScolarFormSemestreValidation, + ScolarNews, ) from app.models.config import ScoDocSiteConfig from app.scodoc import html_sco_header @@ -369,6 +370,16 @@ def jury_but_semestriel( flash( f"autorisation de passage en S{formsemestre.semestre_id + 1} annulée" ) + ScolarNews.add( + typ=ScolarNews.NEWS_JURY, + obj=formsemestre.id, + text=f"""Saisie décision jury dans {formsemestre.html_link_status()}""", + url=url_for( + "notes.formsemestre_status", + scodoc_dept=g.scodoc_dept, + formsemestre_id=formsemestre.id, + ), + ) return flask.redirect( url_for( "notes.formsemestre_validation_but", diff --git a/app/models/events.py b/app/models/events.py index 6555948d..d3e77097 100644 --- a/app/models/events.py +++ b/app/models/events.py @@ -54,14 +54,17 @@ class ScolarNews(db.Model): NEWS_APO = "APO" # changements de codes APO NEWS_FORM = "FORM" # modification formation (object=formation_id) NEWS_INSCR = "INSCR" # inscription d'étudiants (object=None ou formsemestre_id) + NEWS_JURY = "JURY" # saisie jury NEWS_MISC = "MISC" # unused NEWS_NOTE = "NOTES" # saisie note (object=moduleimpl_id) NEWS_SEM = "SEM" # creation semestre (object=None) + NEWS_MAP = { NEWS_ABS: "saisie absence", NEWS_APO: "modif. code Apogée", NEWS_FORM: "modification formation", NEWS_INSCR: "inscription d'étudiants", + NEWS_JURY: "saisie jury", NEWS_MISC: "opération", # unused NEWS_NOTE: "saisie note", NEWS_SEM: "création semestre", @@ -130,10 +133,10 @@ class ScolarNews(db.Model): return query.order_by(cls.date.desc()).limit(n).all() @classmethod - def add(cls, typ, obj=None, text="", url=None, max_frequency=0): + def add(cls, typ, obj=None, text="", url=None, max_frequency=600): """Enregistre une nouvelle Si max_frequency, ne génère pas 2 nouvelles "identiques" - à moins de max_frequency secondes d'intervalle. + à moins de max_frequency secondes d'intervalle (10 minutes par défaut). Deux nouvelles sont considérées comme "identiques" si elles ont même (obj, typ, user). La nouvelle enregistrée est aussi envoyée par mail. @@ -153,7 +156,10 @@ class ScolarNews(db.Model): if last_news: now = datetime.datetime.now(tz=last_news.date.tzinfo) if (now - last_news.date) < datetime.timedelta(seconds=max_frequency): - # on n'enregistre pas + # pas de nouvel event, mais met à jour l'heure + last_news.date = datetime.datetime.now() + db.session.add(last_news) + db.session.commit() return news = ScolarNews( diff --git a/app/scodoc/sco_edit_formation.py b/app/scodoc/sco_edit_formation.py index 384ec065..a3ce002a 100644 --- a/app/scodoc/sco_edit_formation.py +++ b/app/scodoc/sco_edit_formation.py @@ -132,6 +132,7 @@ def do_formation_delete(formation_id): typ=ScolarNews.NEWS_FORM, obj=formation_id, text=f"Suppression de la formation {acronyme}", + max_frequency=0, ) @@ -329,6 +330,7 @@ def do_formation_create(args: dict) -> Formation: typ=ScolarNews.NEWS_FORM, text=f"""Création de la formation { formation.titre} ({formation.acronyme}) version {formation.version}""", + max_frequency=0, ) return formation diff --git a/app/scodoc/sco_edit_matiere.py b/app/scodoc/sco_edit_matiere.py index 312f01ec..ef425b49 100644 --- a/app/scodoc/sco_edit_matiere.py +++ b/app/scodoc/sco_edit_matiere.py @@ -93,7 +93,6 @@ def do_matiere_create(args): typ=ScolarNews.NEWS_FORM, obj=ue["formation_id"], text=f"Modification de la formation {formation.acronyme}", - max_frequency=10 * 60, ) formation.invalidate_cached_sems() return r @@ -199,7 +198,6 @@ def do_matiere_delete(oid): typ=ScolarNews.NEWS_FORM, obj=ue["formation_id"], text=f"Modification de la formation {formation.acronyme}", - max_frequency=10 * 60, ) formation.invalidate_cached_sems() diff --git a/app/scodoc/sco_edit_module.py b/app/scodoc/sco_edit_module.py index 4d963c6e..139afa84 100644 --- a/app/scodoc/sco_edit_module.py +++ b/app/scodoc/sco_edit_module.py @@ -114,7 +114,6 @@ def do_module_create(args) -> int: typ=ScolarNews.NEWS_FORM, obj=formation.id, text=f"Modification de la formation {formation.acronyme}", - max_frequency=10 * 60, ) formation.invalidate_cached_sems() return module_id @@ -186,7 +185,6 @@ def do_module_delete(oid): typ=ScolarNews.NEWS_FORM, obj=mod["formation_id"], text=f"Modification de la formation {formation.acronyme}", - max_frequency=10 * 60, ) formation.invalidate_cached_sems() diff --git a/app/scodoc/sco_edit_ue.py b/app/scodoc/sco_edit_ue.py index ba6cb918..fbe8e2be 100644 --- a/app/scodoc/sco_edit_ue.py +++ b/app/scodoc/sco_edit_ue.py @@ -145,7 +145,6 @@ def do_ue_create(args): typ=ScolarNews.NEWS_FORM, obj=args["formation_id"], text=f"Modification de la formation {formation.acronyme}", - max_frequency=10 * 60, ) formation.invalidate_cached_sems() return ue_id @@ -230,7 +229,6 @@ def do_ue_delete(ue: UniteEns, delete_validations=False, force=False): typ=ScolarNews.NEWS_FORM, obj=formation.id, text=f"Modification de la formation {formation.acronyme}", - max_frequency=10 * 60, ) # if not force: diff --git a/app/scodoc/sco_etud.py b/app/scodoc/sco_etud.py index 7bbf12b5..827363c8 100644 --- a/app/scodoc/sco_etud.py +++ b/app/scodoc/sco_etud.py @@ -671,6 +671,7 @@ def create_etud(cnx, args: dict = None): typ=ScolarNews.NEWS_INSCR, text='Nouvel étudiant %(nomprenom)s' % etud, url=etud["url"], + max_frequency=0, ) return etud diff --git a/app/scodoc/sco_formations.py b/app/scodoc/sco_formations.py index 981908c9..398ed7d4 100644 --- a/app/scodoc/sco_formations.py +++ b/app/scodoc/sco_formations.py @@ -638,6 +638,7 @@ def formation_create_new_version(formation_id, redirect=True): typ=ScolarNews.NEWS_FORM, obj=new_id, text=f"Nouvelle version de la formation {formation.acronyme}", + max_frequency=0, ) if redirect: flash("Nouvelle version !") diff --git a/app/scodoc/sco_formsemestre.py b/app/scodoc/sco_formsemestre.py index 3d46b1de..bac3352a 100644 --- a/app/scodoc/sco_formsemestre.py +++ b/app/scodoc/sco_formsemestre.py @@ -261,6 +261,7 @@ def do_formsemestre_create(args, silent=False): typ=ScolarNews.NEWS_SEM, text='Création du semestre %(titre)s' % args, url=args["url"], + max_frequency=0, ) return formsemestre_id diff --git a/app/scodoc/sco_formsemestre_edit.py b/app/scodoc/sco_formsemestre_edit.py index 4961e8a2..4a94f0bd 100644 --- a/app/scodoc/sco_formsemestre_edit.py +++ b/app/scodoc/sco_formsemestre_edit.py @@ -1521,6 +1521,7 @@ def do_formsemestre_delete(formsemestre_id): typ=ScolarNews.NEWS_SEM, obj=formsemestre_id, text="Suppression du semestre %(titre)s" % sem, + max_frequency=0, ) diff --git a/app/scodoc/sco_formsemestre_validation.py b/app/scodoc/sco_formsemestre_validation.py index 3a173f03..5712d3eb 100644 --- a/app/scodoc/sco_formsemestre_validation.py +++ b/app/scodoc/sco_formsemestre_validation.py @@ -39,7 +39,7 @@ from app import db, log from app.comp import res_sem from app.comp.res_compat import NotesTableCompat -from app.models import Formation, FormSemestre, UniteEns +from app.models import Formation, FormSemestre, UniteEns, ScolarNews from app.models.notes import etud_has_notes_attente from app.models.validations import ( ScolarAutorisationInscription, @@ -992,16 +992,26 @@ def do_formsemestre_validation_auto(formsemestre_id): ) nb_valid += 1 log( - "do_formsemestre_validation_auto: %d validations, %d conflicts" - % (nb_valid, len(conflicts)) + f"do_formsemestre_validation_auto: {nb_valid} validations, {len(conflicts)} conflicts" ) - H = [html_sco_header.sco_header(page_title="Saisie automatique")] - H.append( - """

    Saisie automatique des décisions du semestre %s

    + ScolarNews.add( + typ=ScolarNews.NEWS_JURY, + obj=formsemestre.id, + text=f"""Calcul jury automatique du semestre {formsemestre.html_link_status() + } ({nb_valid} décisions)""", + url=url_for( + "notes.formsemestre_status", + scodoc_dept=g.scodoc_dept, + formsemestre_id=formsemestre.id, + ), + ) + H = [ + f"""{html_sco_header.sco_header(page_title="Saisie automatique")} +

    Saisie automatique des décisions du semestre {formsemestre.titre_annee()}

    Opération effectuée.

    -

    %d étudiants validés (sur %s)

    """ - % (sem["titreannee"], nb_valid, len(etudids)) - ) +

    {nb_valid} étudiants validés sur {len(etudids)}

    + """ + ] if conflicts: H.append( f"""

    Attention: {len(conflicts)} étudiants non modifiés diff --git a/app/scodoc/sco_import_etuds.py b/app/scodoc/sco_import_etuds.py index 736d6021..e7eb8dce 100644 --- a/app/scodoc/sco_import_etuds.py +++ b/app/scodoc/sco_import_etuds.py @@ -480,6 +480,7 @@ def scolars_import_excel_file( text="Inscription de %d étudiants" # peuvent avoir ete inscrits a des semestres differents % len(created_etudids), obj=formsemestre_id, + max_frequency=0, ) log("scolars_import_excel_file: completing transaction") diff --git a/app/scodoc/sco_synchro_etuds.py b/app/scodoc/sco_synchro_etuds.py index 6685909c..764d889f 100644 --- a/app/scodoc/sco_synchro_etuds.py +++ b/app/scodoc/sco_synchro_etuds.py @@ -704,7 +704,6 @@ def do_import_etuds_from_portal(sem, a_importer, etudsapo_ident): typ=ScolarNews.NEWS_INSCR, text=f"Import Apogée de {len(created_etudids)} étudiants en ", obj=sem["formsemestre_id"], - max_frequency=10 * 60, # 10' ) diff --git a/app/static/css/scodoc.css b/app/static/css/scodoc.css index 0173a14e..ca1b1f12 100644 --- a/app/static/css/scodoc.css +++ b/app/static/css/scodoc.css @@ -629,7 +629,7 @@ div.news { border-radius: 8px; } -div.news a { +div.news a, div.news a.stdlink { color: black; text-decoration: none; } diff --git a/app/views/notes.py b/app/views/notes.py index c5157194..1ef70693 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -2410,6 +2410,16 @@ def formsemestre_validation_but( if request.method == "POST": if not read_only: deca.record_form(request.form) + ScolarNews.add( + typ=ScolarNews.NEWS_JURY, + obj=formsemestre.id, + text=f"""Saisie décision jury dans {formsemestre.html_link_status()}""", + url=url_for( + "notes.formsemestre_status", + scodoc_dept=g.scodoc_dept, + formsemestre_id=formsemestre.id, + ), + ) flash("codes enregistrés") return flask.redirect( url_for( @@ -3059,7 +3069,6 @@ def formsemestre_set_apo_etapes(): ScolarNews.add( typ=ScolarNews.NEWS_APO, text=f"Modification code Apogée du semestre {formsemestre.titre_annee()})", - max_frequency=10 * 60, ) return ("", 204) @@ -3081,7 +3090,6 @@ def formsemestre_set_elt_annee_apo(): ScolarNews.add( typ=ScolarNews.NEWS_APO, text=f"Modification code Apogée du semestre {formsemestre.titre_annee()})", - max_frequency=10 * 60, ) return ("", 204) @@ -3103,7 +3111,6 @@ def formsemestre_set_elt_sem_apo(): ScolarNews.add( typ=ScolarNews.NEWS_APO, text=f"Modification code Apogée du semestre {formsemestre.titre_annee()})", - max_frequency=10 * 60, ) return ("", 204) @@ -3125,7 +3132,6 @@ def ue_set_apo(): ScolarNews.add( typ=ScolarNews.NEWS_FORM, text=f"Modification code Apogée d'UE dans la formation {ue.formation.titre} ({ue.formation.acronyme})", - max_frequency=10 * 60, ) return ("", 204) @@ -3146,8 +3152,8 @@ def module_set_apo(): db.session.commit() ScolarNews.add( typ=ScolarNews.NEWS_FORM, - text=f"Modification code Apogée d'UE dans la formation {mod.formation.titre} ({mod.formation.acronyme})", - max_frequency=10 * 60, + text=f"""Modification code Apogée d'UE dans la formation { + mod.formation.titre} ({mod.formation.acronyme})""", ) return ("", 204) diff --git a/sco_version.py b/sco_version.py index 7be8d568..bd238565 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.4.90" +SCOVERSION = "9.4.91" SCONAME = "ScoDoc"