From 34fe649d515265e191f35d1594b430e9c09bb0bb Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 8 Jul 2024 23:13:45 +0200 Subject: [PATCH] Log etud: code & check --- app/api/jury.py | 3 +- app/api/justificatifs.py | 8 ++- app/models/events.py | 22 ++++++- app/scodoc/sco_cursus_dut.py | 29 ++++----- app/scodoc/sco_debouche.py | 9 ++- app/scodoc/sco_etud.py | 19 +----- app/scodoc/sco_formsemestre_inscriptions.py | 11 ++-- app/scodoc/sco_formsemestre_validation.py | 10 ++- app/scodoc/sco_groups.py | 6 +- app/scodoc/sco_moduleimpl.py | 7 +-- app/scodoc/sco_moduleimpl_inscriptions.py | 7 +-- app/scodoc/sco_page_etud.py | 2 +- app/scodoc/sco_photos.py | 7 +-- app/scodoc/scolog.py | 68 --------------------- app/views/notes.py | 8 ++- app/views/scolar.py | 41 ++++++------- 16 files changed, 91 insertions(+), 166 deletions(-) delete mode 100644 app/scodoc/scolog.py diff --git a/app/api/jury.py b/app/api/jury.py index e2e29b7d..4fce0bfa 100644 --- a/app/api/jury.py +++ b/app/api/jury.py @@ -290,13 +290,12 @@ def validation_rcue_record(etudid: int): db.session.add(validation) # invalider bulletins (les autres résultats ne dépendent pas des RCUEs): sco_cache.invalidate_formsemestre_etud(etud) - db.session.commit() Scolog.logdb( method="validation_rcue_record", etudid=etudid, msg=f"Enregistrement {validation}", - commit=True, ) + db.session.commit() log(f"{operation} {validation}") return validation.to_dict() diff --git a/app/api/justificatifs.py b/app/api/justificatifs.py index 28063d05..79c05d8e 100644 --- a/app/api/justificatifs.py +++ b/app/api/justificatifs.py @@ -533,13 +533,12 @@ def justif_edit(justif_id: int): # Mise à jour du justificatif justificatif_unique.dejustifier_assiduites() db.session.add(justificatif_unique) - db.session.commit() - Scolog.logdb( method="edit_justificatif", etudid=justificatif_unique.etudiant.id, msg=f"justificatif modif: {justificatif_unique}", ) + db.session.commit() # Génération du dictionnaire de retour # La couverture correspond @@ -627,6 +626,11 @@ def _delete_one(justif_id: int) -> tuple[int, str]: scass.simple_invalidate_cache(justificatif_unique.to_dict()) # On actualise les assiduités justifiées de l'étudiant concerné justificatif_unique.dejustifier_assiduites() + Scolog.logdb( + method="justificatif/delete", + etudid=justificatif_unique.etudiant.id, + msg="suppression justificatif", + ) # On supprime le justificatif db.session.delete(justificatif_unique) diff --git a/app/models/events.py b/app/models/events.py index 659bf260..3e6c2bf7 100644 --- a/app/models/events.py +++ b/app/models/events.py @@ -12,12 +12,12 @@ from app import db from app import email from app import log from app.auth.models import User -from app.models import SHORT_STR_LEN +from app.models import ScoDocModel, SHORT_STR_LEN import app.scodoc.sco_utils as scu from app.scodoc import sco_preferences -class Scolog(db.Model): +class Scolog(ScoDocModel): """Log des actions (journal modif etudiants)""" __tablename__ = "scolog" @@ -34,7 +34,8 @@ class Scolog(db.Model): def logdb( cls, method: str = None, etudid: int = None, msg: str = None, commit=False ): - """Add entry in student's log (replacement for old scolog.logdb)""" + """Add entry in student's log (replacement for old scolog.logdb). + Par défaut ne commite pas.""" entry = Scolog( method=method, msg=msg, @@ -45,6 +46,21 @@ class Scolog(db.Model): if commit: db.session.commit() + def to_dict(self, convert_date=False) -> dict: + "convert to dict" + return { + "etudid": self.etudid, + "date": ( + (self.date.strftime(scu.DATETIME_FMT) if convert_date else self.date) + if self.date + else "" + ), + "_date_order": self.date.isoformat() if self.date else "", + "authenticated_user": self.authenticated_user or "", + "msg": self.msg or "", + "method": self.method or "", + } + class ScolarNews(db.Model): """Nouvelles pour page d'accueil""" diff --git a/app/scodoc/sco_cursus_dut.py b/app/scodoc/sco_cursus_dut.py index 7cb14eb1..06c64b21 100644 --- a/app/scodoc/sco_cursus_dut.py +++ b/app/scodoc/sco_cursus_dut.py @@ -31,13 +31,18 @@ from app import db from app.comp import res_sem from app.comp.res_compat import NotesTableCompat -from app.models import FormSemestre, Identite, ScolarAutorisationInscription, UniteEns +from app.models import ( + FormSemestre, + Identite, + ScolarAutorisationInscription, + Scolog, + UniteEns, +) import app.scodoc.sco_utils as scu import app.scodoc.notesdb as ndb from app import log -from app.scodoc.scolog import logdb -from app.scodoc import sco_cache, sco_etud +from app.scodoc import sco_cache from app.scodoc import sco_formsemestre from app.scodoc.codes_cursus import ( CMP, @@ -586,13 +591,11 @@ class SituationEtudCursusClassic(SituationEtudCursus): decision.assiduite, decision.formsemestre_id_utilise_pour_compenser, ) - logdb( - cnx, + Scolog.logdb( method="validate_sem", etudid=self.etudid, commit=False, - msg="formsemestre_id=%s code=%s" - % (self.formsemestre_id, decision.code_etat), + msg=f"formsemestre_id={self.formsemestre_id} code={decision.code_etat}", ) # -- decisions UEs formsemestre_validate_ues( @@ -616,13 +619,11 @@ class SituationEtudCursusClassic(SituationEtudCursus): assidu=True, formsemestre_id_utilise_pour_compenser=fsid, ) - logdb( - cnx, + Scolog.logdb( method="validate_sem", etudid=self.etudid, commit=False, - msg="formsemestre_id=%s code=%s" - % (self.prev_formsemestre.id, decision.new_code_prev), + msg=f"formsemestre_id={self.prev_formsemestre.id} code={decision.new_code_prev}", ) # modifs des codes d'UE (pourraient passer de ADM a CMP, meme sans modif des notes) formsemestre_validate_ues( @@ -937,13 +938,13 @@ def formsemestre_validate_ues(formsemestre_id, etudid, code_etat_sem, assiduite) cnx, nt, formsemestre_id, etudid, ue_id, code_ue ) - logdb( - cnx, + Scolog.logdb( method="validate_ue", etudid=etudid, - msg="ue_id=%s code=%s" % (ue_id, code_ue), + msg=f"ue_id={ue_id} code={code_ue}", commit=False, ) + db.session.commit() cnx.commit() diff --git a/app/scodoc/sco_debouche.py b/app/scodoc/sco_debouche.py index 8c0eb287..18b62e89 100644 --- a/app/scodoc/sco_debouche.py +++ b/app/scodoc/sco_debouche.py @@ -34,11 +34,10 @@ from flask import url_for, g, request from app import log from app.comp import res_sem from app.comp.res_compat import NotesTableCompat -from app.models import FormSemestre +from app.models import FormSemestre, Scolog import app.scodoc.sco_utils as scu import app.scodoc.notesdb as ndb from app.scodoc.sco_exceptions import AccessDenied, ScoValueError -from app.scodoc.scolog import logdb from app.scodoc.gen_tables import GenTable from app.scodoc import safehtml from app.scodoc import html_sco_header @@ -291,8 +290,8 @@ def itemsuivi_suppress(itemsuivi_id): item = itemsuivi_get(cnx, itemsuivi_id, ignore_errors=True) if item: _itemsuivi_delete(cnx, itemsuivi_id) - logdb(cnx, method="itemsuivi_suppress", etudid=item["etudid"]) - log("suppressed itemsuivi %s" % (itemsuivi_id,)) + Scolog.logdb(method="itemsuivi_suppress", etudid=item["etudid"], commit=True) + log(f"suppressed itemsuivi {itemsuivi_id}") return ("", 204) @@ -304,7 +303,7 @@ def itemsuivi_create(etudid, item_date=None, situation="", fmt=None): itemsuivi_id = _itemsuivi_create( cnx, args={"etudid": etudid, "item_date": item_date, "situation": situation} ) - logdb(cnx, method="itemsuivi_create", etudid=etudid) + Scolog.logdb(method="itemsuivi_create", etudid=etudid, commit=True) log("created itemsuivi %s for %s" % (itemsuivi_id, etudid)) item = itemsuivi_get(cnx, itemsuivi_id) if fmt == "json": diff --git a/app/scodoc/sco_etud.py b/app/scodoc/sco_etud.py index 5ad69068..fde98370 100644 --- a/app/scodoc/sco_etud.py +++ b/app/scodoc/sco_etud.py @@ -37,7 +37,7 @@ from flask import url_for, g from app import db, email from app import log -from app.models import Admission, Identite +from app.models import Admission, Identite, Scolog from app.models.etudiants import ( check_etud_duplicate_code, input_civilite, @@ -53,10 +53,9 @@ from app.scodoc.sco_utils import ( format_prenom, ) import app.scodoc.notesdb as ndb -from app.scodoc.sco_exceptions import ScoGenError, ScoValueError +from app.scodoc.sco_exceptions import ScoValueError from app.scodoc import safehtml from app.scodoc import sco_preferences -from app.scodoc.scolog import logdb def format_etud_ident(etud: dict): @@ -511,8 +510,7 @@ def create_etud(cnx, args: dict = None): etudid = etud.id # log - logdb( - cnx, + Scolog.logdb( method="etudident_edit_form", etudid=etudid, msg="creation initiale", @@ -681,17 +679,6 @@ o.close() """ -def list_scolog(etudid): - "liste des operations effectuees sur cet etudiant" - cnx = ndb.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) - cursor.execute( - "SELECT * FROM scolog WHERE etudid=%(etudid)s ORDER BY DATE DESC", - {"etudid": etudid}, - ) - return cursor.dictfetchall() - - def fill_etuds_info(etuds: list[dict], add_admission=True): """etuds est une liste d'etudiants (mappings) Pour chaque etudiant, ajoute ou formatte les champs diff --git a/app/scodoc/sco_formsemestre_inscriptions.py b/app/scodoc/sco_formsemestre_inscriptions.py index 15995e11..217a096e 100644 --- a/app/scodoc/sco_formsemestre_inscriptions.py +++ b/app/scodoc/sco_formsemestre_inscriptions.py @@ -42,7 +42,6 @@ from app.models.groups import Partition, GroupDescr from app.models.scolar_event import ScolarEvent import app.scodoc.sco_utils as scu from app import log -from app.scodoc.scolog import logdb from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.codes_cursus import UE_STANDARD, UE_SPORT, UE_TYPE_NAME import app.scodoc.notesdb as ndb @@ -112,12 +111,11 @@ def do_formsemestre_inscription_create(args, method=None): }, ) # Log etudiant - logdb( - cnx, + Scolog.logdb( method=method, etudid=args["etudid"], msg=f"inscription en semestre {args['formsemestre_id']}", - commit=False, + commit=True, ) # sco_cache.invalidate_formsemestre(formsemestre_id=args["formsemestre_id"]) @@ -265,12 +263,11 @@ def do_formsemestre_desinscription( db.session.commit() flash(f"Semestre extérieur supprimé: {formsemestre.titre_annee()}") - logdb( - cnx, + Scolog.logdb( method="formsemestre_desinscription", etudid=etudid, msg=f"desinscription semestre {formsemestre_id}", - commit=False, + commit=True, ) diff --git a/app/scodoc/sco_formsemestre_validation.py b/app/scodoc/sco_formsemestre_validation.py index 3b3fa3b5..1598abc5 100644 --- a/app/scodoc/sco_formsemestre_validation.py +++ b/app/scodoc/sco_formsemestre_validation.py @@ -41,7 +41,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, ScolarNews +from app.models import Formation, FormSemestre, UniteEns, ScolarNews, Scolog from app.models.notes import etud_has_notes_attente from app.models.validations import ( ScolarAutorisationInscription, @@ -49,7 +49,6 @@ from app.models.validations import ( ) from app.models.but_validations import ApcValidationRCUE, ApcValidationAnnee from app.scodoc.sco_exceptions import ScoValueError -from app.scodoc.scolog import logdb from app.scodoc.codes_cursus import * from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message @@ -1077,7 +1076,7 @@ def do_formsemestre_validation_auto(formsemestre_id): url_for('notes.formsemestre_validation_etud_form', scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id, etudid=etud.id, check=1) - }">{etud_d["nomprenom"]}""" + }">{etud.nom_prenom()}""" ) H.append("") H.append( @@ -1392,12 +1391,11 @@ def do_formsemestre_validate_previous_ue( is_external=True, ) - logdb( - cnx, + Scolog.logdb( method="formsemestre_validate_previous_ue", etudid=etudid, msg=f"Validation UE prec. {ue_id} {ue.acronyme}: {code}", - commit=False, + commit=True, ) _invalidate_etud_formation_caches(etudid, formsemestre.formation_id) cnx.commit() diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py index 8609c623..6faf1cea 100644 --- a/app/scodoc/sco_groups.py +++ b/app/scodoc/sco_groups.py @@ -44,9 +44,7 @@ from app.comp.res_compat import NotesTableCompat from app.models import FormSemestre, Identite, Scolog from app.models import SHORT_STR_LEN from app.models.groups import GroupDescr, Partition -import app.scodoc.sco_utils as scu import app.scodoc.notesdb as ndb -from app.scodoc.scolog import logdb from app.scodoc import html_sco_header from app.scodoc import sco_cache from app.scodoc import codes_cursus @@ -769,12 +767,12 @@ groupsToDelete={groupsToDelete} {"etudid": etudid, "group_id": group_id}, cursor=cursor, ) - logdb( - cnx, + Scolog.logdb( method="removeFromGroup", etudid=etudid, msg=f"""formsemestre_id={partition.formsemestre.id},partition_name={ partition.partition_name}, group_name={group.group_name}""", + commit=True, ) # Supprime les groupes indiqués comme supprimés: diff --git a/app/scodoc/sco_moduleimpl.py b/app/scodoc/sco_moduleimpl.py index 86c4dfe9..c315477c 100644 --- a/app/scodoc/sco_moduleimpl.py +++ b/app/scodoc/sco_moduleimpl.py @@ -30,7 +30,7 @@ import psycopg2 -from app.scodoc import scolog +from app.models import Scolog from app.scodoc import sco_cache import app.scodoc.notesdb as ndb from app.scodoc.sco_exceptions import ScoValueError @@ -182,12 +182,11 @@ def do_moduleimpl_inscription_create(args, formsemestre_id=None, cnx=None): sco_cache.invalidate_formsemestre( formsemestre_id=formsemestre_id ) # > moduleimpl_inscription - scolog.logdb( - cnx, + Scolog.logdb( method="moduleimpl_inscription", etudid=args["etudid"], msg=f"inscription module {args['moduleimpl_id']}", - commit=False, + commit=True, ) return r diff --git a/app/scodoc/sco_moduleimpl_inscriptions.py b/app/scodoc/sco_moduleimpl_inscriptions.py index 7b5eaa2e..2e5968b6 100644 --- a/app/scodoc/sco_moduleimpl_inscriptions.py +++ b/app/scodoc/sco_moduleimpl_inscriptions.py @@ -44,8 +44,8 @@ from app.models import ( Partition, ScolarFormSemestreValidation, UniteEns, + Scolog, ) -from app.scodoc.scolog import logdb from app.scodoc import html_sco_header from app.scodoc import htmlutils from app.scodoc import sco_cache @@ -774,12 +774,11 @@ def do_etud_desinscrit_ue_classic(etudid, formsemestre_id, ue_id): """, {"etudid": etudid, "formsemestre_id": formsemestre_id, "ue_id": ue_id}, ) - logdb( - cnx, + Scolog.logdb( method="etud_desinscrit_ue", etudid=etudid, msg=f"desinscription UE {ue_id}", - commit=False, + commit=True, ) sco_cache.invalidate_formsemestre( formsemestre_id=formsemestre_id diff --git a/app/scodoc/sco_page_etud.py b/app/scodoc/sco_page_etud.py index 0177cf48..a04c845d 100644 --- a/app/scodoc/sco_page_etud.py +++ b/app/scodoc/sco_page_etud.py @@ -800,7 +800,7 @@ def menus_etud(etudid): }, { "title": "Voir le journal...", - "endpoint": "scolar.showEtudLog", + "endpoint": "scolar.show_etud_log", "args": {"etudid": etud["etudid"]}, "enabled": True, }, diff --git a/app/scodoc/sco_photos.py b/app/scodoc/sco_photos.py index 35b2ea12..0e69214b 100755 --- a/app/scodoc/sco_photos.py +++ b/app/scodoc/sco_photos.py @@ -64,8 +64,6 @@ from app.scodoc import sco_etud from app.scodoc import sco_portal_apogee from app.scodoc import sco_preferences from app.scodoc.sco_exceptions import ScoValueError -from app.scodoc.scolog import logdb -import app.scodoc.notesdb as ndb import app.scodoc.sco_utils as scu from config import Config @@ -304,10 +302,9 @@ def suppress_photo(etud: Identite) -> None: for filename in filenames: log(f"removing file {filename}") os.remove(filename) - db.session.commit() # 3- log - cnx = ndb.GetDBConnexion() - logdb(cnx, method="changePhoto", msg="suppression", etudid=etud.id) + Scolog.logdb(method="changePhoto", msg="suppression", etudid=etud.id) + db.session.commit() # --------------------------------------------------------------------------- diff --git a/app/scodoc/scolog.py b/app/scodoc/scolog.py deleted file mode 100644 index f972d3b4..00000000 --- a/app/scodoc/scolog.py +++ /dev/null @@ -1,68 +0,0 @@ -# -*- mode: python -*- -# -*- coding: utf-8 -*- - -############################################################################## -# -# Gestion scolarite IUT -# -# Copyright (c) 1999 - 2024 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 -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# -# Emmanuel Viennet emmanuel.viennet@viennet.net -# -############################################################################## - -"""Logging des opérations en base de données -""" - -from flask import request -from flask_login import current_user -import app.scodoc.notesdb as ndb - - -def logdb(cnx=None, method=None, etudid=None, msg=None, commit=True): - "Add entry" - if not cnx: - raise ValueError("logdb: cnx is None") - - args = { - "authenticated_user": current_user.user_name, - } - - args.update({"method": method, "etudid": etudid, "msg": msg}) - ndb.quote_dict(args) - cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) - cursor.execute( - """INSERT INTO scolog - (authenticated_user,method,etudid,msg) - VALUES - (%(authenticated_user)s,%(method)s,%(etudid)s,%(msg)s)""", - args, - ) - if commit: - cnx.commit() - - -def loglist(cnx, method=None, authenticated_user=None): - """List of events logged for these method and user""" - cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) - cursor.execute( - """SELECT * FROM scolog - WHERE method=%(method)s - AND authenticated_user=%(authenticated_user)s""", - {"method": method, "authenticated_user": authenticated_user}, - ) - return cursor.dictfetchall() diff --git a/app/views/notes.py b/app/views/notes.py index 9157af76..482b6c48 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -2909,7 +2909,7 @@ def formsemestre_saisie_jury(formsemestre_id: int, selected_etudid: int = None): @scodoc @permission_required(Permission.ScoView) def formsemestre_jury_erase(formsemestre_id: int, etudid: int = None): - """Supprime la décision de jury (classique ou BUT) pour cette année. + """Supprime toutes les décisions de jury (classique ou BUT) pour cette année. Si l'étudiant n'est pas spécifié, efface les décisions de tous les inscrits. En BUT, si only_one_sem n'efface que pour le formsemestre indiqué, pas les deux de l'année. En classique, n'affecte que les décisions issues de ce formsemestre. @@ -2992,8 +2992,10 @@ def formsemestre_jury_erase(formsemestre_id: int, etudid: int = None): """ + """

Efface aussi toutes les validations concernant l'année BUT de ce semestre, - même si elles ont été acquises ailleurs. -

""" + même si elles ont été acquises ailleurs, ainsi que les validations de DUT en 120 ECTS + obtenues après BUT1/BUT2. +

+ """ if is_apc else "" + """ diff --git a/app/views/scolar.py b/app/views/scolar.py index 8355fcef..161ac486 100644 --- a/app/views/scolar.py +++ b/app/views/scolar.py @@ -70,7 +70,6 @@ from app.views import ScoData import app.scodoc.sco_utils as scu import app.scodoc.notesdb as ndb -from app.scodoc.scolog import logdb from app.scodoc.sco_permissions import Permission from app.scodoc.sco_exceptions import ( AccessDenied, @@ -78,7 +77,7 @@ from app.scodoc.sco_exceptions import ( ScoValueError, ) -from app.scodoc.TrivialFormulator import DMY_REGEXP, TrivialFormulator, tf_error_message +from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message from app.scodoc.gen_tables import GenTable from app.scodoc import ( codes_cursus, @@ -302,39 +301,37 @@ class DeptLogosConfigurationForm(FlaskForm): # -------------------------------------------------------------------- -@bp.route("/showEtudLog") +@bp.route("/show_etud_log") @scodoc @permission_required(Permission.ScoView) @scodoc7func -def showEtudLog(etudid, fmt="html"): +def show_etud_log(etudid, fmt="html"): """Display log of operations on this student""" - etud = sco_etud.get_etud_info(filled=True)[0] - - ops = sco_etud.list_scolog(etudid) + etud = Identite.get_etud(etudid) + operations = Scolog.query.filter_by(etudid=etud.id).order_by(Scolog.date.desc()) tab = GenTable( titles={ "date": "Date", "authenticated_user": "Utilisateur", - "remote_addr": "IP", "method": "Opération", "msg": "Message", }, - columns_ids=("date", "authenticated_user", "remote_addr", "method", "msg"), - rows=ops, + columns_ids=("date", "authenticated_user", "method", "msg"), + rows=[op.to_dict(convert_date=True) for op in operations], html_sortable=True, html_class="table_leftalign", - base_url="%s?etudid=%s" % (request.base_url, etudid), - page_title="Opérations sur %(nomprenom)s" % etud, - html_title="

Opérations effectuées sur l'étudiant %(nomprenom)s

" % etud, - filename="log_" + scu.make_filename(etud["nomprenom"]), + base_url="%s?etudid=%s" % (request.base_url, etud.id), + page_title=f"Opérations sur {etud.nom_prenom()}", + html_title=f"""

Opérations effectuées sur l'étudiant{etud.e} { + etud.html_link_fiche()}

""", + filename="log_" + scu.make_filename(etud.nom_prenom()), html_next_section=f""" -