From aee4f14b816415e31087943411de7122fc438013 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sun, 21 Jan 2024 18:07:56 +0100 Subject: [PATCH] =?UTF-8?q?Acc=C3=A8s=20au=20d=C3=A9tail=20d'un=20justific?= =?UTF-8?q?atif=20avec=20AbsJustifView:=20closes=20#824?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/__init__.py | 17 ++- app/api/justificatifs.py | 22 ++-- app/models/assiduites.py | 18 +-- app/scodoc/html_sco_header.py | 4 +- app/scodoc/html_sidebar.py | 60 ++++++++- app/scodoc/sco_formsemestre_status.py | 52 +------- app/scodoc/sco_page_etud.py | 4 +- app/scodoc/sco_permissions.py | 8 +- app/tables/liste_assiduites.py | 22 +++- .../pages/ajout_justificatif_etud.j2 | 33 ++++- .../pages/tableau_assiduite_actions.j2 | 6 +- .../widgets/tableau_actions/details.j2 | 116 +++++++++++------- .../widgets/tableau_actions/modifier.j2 | 10 +- app/templates/sidebar_dept.j2 | 9 +- app/views/__init__.py | 5 +- app/views/assiduites.py | 47 +++---- sco_version.py | 2 +- 17 files changed, 274 insertions(+), 161 deletions(-) diff --git a/app/api/__init__.py b/app/api/__init__.py index a6f2b680b..fb994bfdd 100644 --- a/app/api/__init__.py +++ b/app/api/__init__.py @@ -3,9 +3,11 @@ from flask_json import as_json from flask import Blueprint from flask import request, g +from flask_login import current_user from app import db from app.scodoc import sco_utils as scu from app.scodoc.sco_exceptions import AccessDenied, ScoException +from app.scodoc.sco_permissions import Permission api_bp = Blueprint("api", __name__) api_web_bp = Blueprint("apiweb", __name__) @@ -48,13 +50,21 @@ def requested_format(default_format="json", allowed_formats=None): @as_json -def get_model_api_object(model_cls: db.Model, model_id: int, join_cls: db.Model = None): +def get_model_api_object( + model_cls: db.Model, + model_id: int, + join_cls: db.Model = None, + restrict: bool | None = None, +): """ Retourne une réponse contenant la représentation api de l'objet "Model[model_id]" Filtrage du département en fonction d'une classe de jointure (eg: Identite, Formsemestre) -> join_cls exemple d'utilisation : fonction "justificatif()" -> app/api/justificatifs.py + + L'agument restrict est passé to_dict, est signale que l'on veut une version restreinte + (sans données personnelles, ou sans informations sur le justificatif d'absence) """ query = model_cls.query.filter_by(id=model_id) if g.scodoc_dept and join_cls is not None: @@ -66,8 +76,9 @@ def get_model_api_object(model_cls: db.Model, model_id: int, join_cls: db.Model 404, message=f"{model_cls.__name__} inexistant(e)", ) - - return unique.to_dict(format_api=True) + if restrict is None: + return unique.to_dict(format_api=True) + return unique.to_dict(format_api=True, restrict=restrict) from app.api import tokens diff --git a/app/api/justificatifs.py b/app/api/justificatifs.py index 9fd61fdc5..a24b0f273 100644 --- a/app/api/justificatifs.py +++ b/app/api/justificatifs.py @@ -53,14 +53,19 @@ def justificatif(justif_id: int = None): "date_fin": "2022-10-31T10:00+01:00", "etat": "valide", "fichier": "archive_id", - "raison": "une raison", + "raison": "une raison", // VIDE si pas le droit "entry_date": "2022-10-31T08:00+01:00", "user_id": 1 or null, } """ - return get_model_api_object(Justificatif, justif_id, Identite) + return get_model_api_object( + Justificatif, + justif_id, + Identite, + restrict=not current_user.has_permission(Permission.AbsJustifView), + ) # etudid @@ -133,8 +138,9 @@ def justificatifs(etudid: int = None, nip=None, ine=None, with_query: bool = Fal # Mise en forme des données puis retour en JSON data_set: list[dict] = [] + restrict = not current_user.has_permission(Permission.AbsJustifView) for just in justificatifs_query.all(): - data = just.to_dict(format_api=True) + data = just.to_dict(format_api=True, restrict=restrict) data_set.append(data) return data_set @@ -172,14 +178,15 @@ def justificatifs_dept(dept_id: int = None, with_query: bool = False): justificatifs_query: Query = _filter_manager(request, justificatifs_query) # Mise en forme des données et retour JSON + restrict = not current_user.has_permission(Permission.AbsJustifView) data_set: list[dict] = [] for just in justificatifs_query: - data_set.append(_set_sems(just)) + data_set.append(_set_sems(just, restrict=restrict)) return data_set -def _set_sems(justi: Justificatif) -> dict: +def _set_sems(justi: Justificatif, restrict: bool) -> dict: """ _set_sems Ajoute le formsemestre associé au justificatif s'il existe @@ -192,7 +199,7 @@ def _set_sems(justi: Justificatif) -> dict: dict: La représentation de l'assiduité en dictionnaire """ # Conversion du justificatif en dictionnaire - data = justi.to_dict(format_api=True) + data = justi.to_dict(format_api=True, restrict=restrict) # Récupération du formsemestre de l'assiduité formsemestre: FormSemestre = get_formsemestre_from_data(justi.to_dict()) @@ -246,9 +253,10 @@ def justificatifs_formsemestre(formsemestre_id: int, with_query: bool = False): justificatifs_query: Query = _filter_manager(request, justificatifs_query) # Retour des justificatifs en JSON + restrict = not current_user.has_permission(Permission.AbsJustifView) data_set: list[dict] = [] for justi in justificatifs_query.all(): - data = justi.to_dict(format_api=True) + data = justi.to_dict(format_api=True, restrict=restrict) data_set.append(data) return data_set diff --git a/app/models/assiduites.py b/app/models/assiduites.py index c7cf8fa3d..b4087e4ee 100644 --- a/app/models/assiduites.py +++ b/app/models/assiduites.py @@ -88,8 +88,10 @@ class Assiduite(ScoDocModel): lazy="select", ) - def to_dict(self, format_api=True) -> dict: - """Retourne la représentation json de l'assiduité""" + def to_dict(self, format_api=True, restrict: bool | None = None) -> dict: + """Retourne la représentation json de l'assiduité + restrict n'est pas utilisé ici. + """ etat = self.etat user: User | None = None if format_api: @@ -453,8 +455,10 @@ class Justificatif(ScoDocModel): query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id) return query.first_or_404() - def to_dict(self, format_api: bool = False) -> dict: - """transformation de l'objet en dictionnaire sérialisable""" + def to_dict(self, format_api: bool = False, restrict: bool = False) -> dict: + """L'objet en dictionnaire sérialisable. + Si restrict, ne donne par la raison et les fichiers et external_data + """ etat = self.etat user: User = self.user if self.user_id is not None else None @@ -469,13 +473,13 @@ class Justificatif(ScoDocModel): "date_debut": self.date_debut, "date_fin": self.date_fin, "etat": etat, - "raison": self.raison, - "fichier": self.fichier, + "raison": None if restrict else self.raison, + "fichier": None if restrict else self.fichier, "entry_date": self.entry_date, "user_id": None if user is None else user.id, # l'uid "user_name": None if user is None else user.user_name, # le login "user_nom_complet": None if user is None else user.get_nomcomplet(), - "external_data": self.external_data, + "external_data": None if restrict else self.external_data, } return data diff --git a/app/scodoc/html_sco_header.py b/app/scodoc/html_sco_header.py index 2e06370af..b76a8e77d 100644 --- a/app/scodoc/html_sco_header.py +++ b/app/scodoc/html_sco_header.py @@ -145,7 +145,9 @@ def sco_header( etudid=None, formsemestre_id=None, ): - "Main HTML page header for ScoDoc" + """Main HTML page header for ScoDoc + Utilisé dans les anciennes pages. Les nouvelles pages utilisent le template Jinja. + """ from app.scodoc.sco_formsemestre_status import formsemestre_page_title if etudid is not None: diff --git a/app/scodoc/html_sidebar.py b/app/scodoc/html_sidebar.py index dce59627f..c0c732ce9 100755 --- a/app/scodoc/html_sidebar.py +++ b/app/scodoc/html_sidebar.py @@ -32,12 +32,66 @@ from flask import render_template, url_for from flask import g, request from flask_login import current_user +from app import db +from app.models import Evaluation, GroupDescr, ModuleImpl, Partition import app.scodoc.sco_utils as scu from app.scodoc import sco_preferences from app.scodoc.sco_permissions import Permission from sco_version import SCOVERSION +def retreive_formsemestre_from_request() -> int: + """Cherche si on a de quoi déduire le semestre affiché à partir des + arguments de la requête: + formsemestre_id ou moduleimpl ou evaluation ou group_id ou partition_id + Returns None si pas défini. + """ + if request.method == "GET": + args = request.args + elif request.method == "POST": + args = request.form + else: + return None + formsemestre_id = None + # Search formsemestre + group_ids = args.get("group_ids", []) + if "formsemestre_id" in args: + formsemestre_id = args["formsemestre_id"] + elif "moduleimpl_id" in args and args["moduleimpl_id"]: + modimpl = db.session.get(ModuleImpl, args["moduleimpl_id"]) + if not modimpl: + return None # suppressed ? + formsemestre_id = modimpl.formsemestre_id + elif "evaluation_id" in args: + evaluation = db.session.get(Evaluation, args["evaluation_id"]) + if not evaluation: + return None # evaluation suppressed ? + formsemestre_id = evaluation.moduleimpl.formsemestre_id + elif "group_id" in args: + group = db.session.get(GroupDescr, args["group_id"]) + if not group: + return None + formsemestre_id = group.partition.formsemestre_id + elif group_ids: + if isinstance(group_ids, str): + group_ids = group_ids.split(",") + group_id = group_ids[0] + group = db.session.get(GroupDescr, group_id) + if not group: + return None + formsemestre_id = group.partition.formsemestre_id + elif "partition_id" in args: + partition = db.session.get(Partition, args["partition_id"]) + if not partition: + return None + formsemestre_id = partition.formsemestre_id + + if formsemestre_id is None: + return None # no current formsemestre + + return int(formsemestre_id) + + def sidebar_common(): "partie commune à toutes les sidebar" home_link = url_for("scodoc.index", scodoc_dept=g.scodoc_dept) @@ -129,13 +183,17 @@ def sidebar(etudid: int = None): ) H.append("