############################################################################## # ScoDoc # Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## """ScoDoc 9 API : Assiduités """ from datetime import datetime from flask import g, request from flask_json import as_json from flask_login import current_user, login_required from app import db, log import app.scodoc.sco_assiduites as scass import app.scodoc.sco_utils as scu from app.api import api_bp as bp from app.api import api_web_bp, get_model_api_object, tools from app.decorators import permission_required, scodoc from app.models import ( Assiduite, FormSemestre, Identite, ModuleImpl, Scolog, Justificatif, ) from app.models.assiduites import get_assiduites_justif from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_permissions import Permission from app.scodoc.sco_utils import json_error @bp.route("/assiduite/<int:assiduite_id>") @api_web_bp.route("/assiduite/<int:assiduite_id>") @scodoc @permission_required(Permission.ScoView) def assiduite(assiduite_id: int = None): """Retourne un objet assiduité à partir de son id Exemple de résultat: { "assiduite_id": 1, "etudid": 2, "moduleimpl_id": 3, "date_debut": "2022-10-31T08:00+01:00", "date_fin": "2022-10-31T10:00+01:00", "etat": "retard", "desc": "une description", "user_id: 1 or null, "est_just": False or True, } """ return get_model_api_object(Assiduite, assiduite_id, Identite) @bp.route("/assiduite/<int:assiduite_id>/justificatifs", defaults={"long": False}) @api_web_bp.route( "/assiduite/<int:assiduite_id>/justificatifs", defaults={"long": False} ) @bp.route("/assiduite/<int:assiduite_id>/justificatifs/long", defaults={"long": True}) @api_web_bp.route( "/assiduite/<int:assiduite_id>/justificatifs/long", defaults={"long": True} ) @scodoc @permission_required(Permission.ScoView) @as_json def assiduite_justificatifs(assiduite_id: int = None, long: bool = False): """Retourne la liste des justificatifs qui justifie cette assiduitée Exemple de résultat: [ 1, 2, 3, ... ] """ return get_assiduites_justif(assiduite_id, True) # etudid @bp.route("/assiduites/<etudid>/count", defaults={"with_query": False}) @api_web_bp.route("/assiduites/<etudid>/count", defaults={"with_query": False}) @bp.route("/assiduites/<etudid>/count/query", defaults={"with_query": True}) @api_web_bp.route("/assiduites/<etudid>/count/query", defaults={"with_query": True}) @bp.route("/assiduites/etudid/<etudid>/count", defaults={"with_query": False}) @api_web_bp.route("/assiduites/etudid/<etudid>/count", defaults={"with_query": False}) @bp.route("/assiduites/etudid/<etudid>/count/query", defaults={"with_query": True}) @api_web_bp.route( "/assiduites/etudid/<etudid>/count/query", defaults={"with_query": True} ) # nip @bp.route("/assiduites/nip/<nip>/count", defaults={"with_query": False}) @api_web_bp.route("/assiduites/nip/<nip>/count", defaults={"with_query": False}) @bp.route("/assiduites/nip/<nip>/count/query", defaults={"with_query": True}) @api_web_bp.route("/assiduites/nip/<nip>/count/query", defaults={"with_query": True}) # ine @bp.route("/assiduites/ine/<ine>/count", defaults={"with_query": False}) @api_web_bp.route("/assiduites/ine/<ine>/count", defaults={"with_query": False}) @bp.route("/assiduites/ine/<ine>/count/query", defaults={"with_query": True}) @api_web_bp.route("/assiduites/ine/<ine>/count/query", defaults={"with_query": True}) # @login_required @scodoc @as_json @permission_required(Permission.ScoView) def count_assiduites( etudid: int = None, nip: str = None, ine: str = None, with_query: bool = False ): """ Retourne le nombre d'assiduités d'un étudiant chemin : /assiduites/<int:etudid>/count Un filtrage peut être donné avec une query chemin : /assiduites/<int:etudid>/count/query? Les différents filtres : Type (type de comptage -> journee, demi, heure, nombre d'assiduite): query?type=(journee, demi, heure) -> une seule valeur parmis les trois ex: .../query?type=heure Comportement par défaut : compte le nombre d'assiduité enregistrée Etat (etat de l'étudiant -> absent, present ou retard): query?etat=[- liste des états séparé par une virgule -] ex: .../query?etat=present,retard Date debut (date de début de l'assiduité, sont affichés les assiduités dont la date de début est supérieur ou égale à la valeur donnée): query?date_debut=[- date au format iso -] ex: query?date_debut=2022-11-03T08:00+01:00 Date fin (date de fin de l'assiduité, sont affichés les assiduités dont la date de fin est inférieure ou égale à la valeur donnée): query?date_fin=[- date au format iso -] ex: query?date_fin=2022-11-03T10:00+01:00 Moduleimpl_id (l'id du module concerné par l'assiduité): query?moduleimpl_id=[- int ou vide -] ex: query?moduleimpl_id=1234 query?moduleimpl_od= Formsemstre_id (l'id du formsemestre concerné par l'assiduité) query?formsemestre_id=[int] ex query?formsemestre_id=3 user_id (l'id de l'auteur de l'assiduité) query?user_id=[int] ex query?user_id=3 est_just (si l'assiduité est justifié (fait aussi filtre par abs/retard)) query?est_just=[bool] query?est_just=f query?est_just=t """ # query = Identite.query.filter_by(id=etudid) # if g.scodoc_dept: # query = query.filter_by(dept_id=g.scodoc_dept_id) # etud: Identite = query.first_or_404(etudid) etud: Identite = tools.get_etud(etudid, nip, ine) if etud is None: return json_error( 404, message="étudiant inconnu", ) filtered: dict[str, object] = {} metric: str = "all" if with_query: metric, filtered = _count_manager(request) return scass.get_assiduites_stats( assiduites=etud.assiduites, metric=metric, filtered=filtered ) # etudid @bp.route("/assiduites/<etudid>", defaults={"with_query": False}) @api_web_bp.route("/assiduites/<etudid>", defaults={"with_query": False}) @bp.route("/assiduites/<etudid>/query", defaults={"with_query": True}) @api_web_bp.route("/assiduites/<etudid>/query", defaults={"with_query": True}) @bp.route("/assiduites/etudid/<etudid>", defaults={"with_query": False}) @api_web_bp.route("/assiduites/etudid/<etudid>", defaults={"with_query": False}) @bp.route("/assiduites/etudid/<etudid>/query", defaults={"with_query": True}) @api_web_bp.route("/assiduites/etudid/<etudid>/query", defaults={"with_query": True}) # nip @bp.route("/assiduites/nip/<nip>", defaults={"with_query": False}) @api_web_bp.route("/assiduites/nip/<nip>", defaults={"with_query": False}) @bp.route("/assiduites/nip/<nip>/query", defaults={"with_query": True}) @api_web_bp.route("/assiduites/nip/<nip>/query", defaults={"with_query": True}) # ine @bp.route("/assiduites/ine/<ine>", defaults={"with_query": False}) @api_web_bp.route("/assiduites/ine/<ine>", defaults={"with_query": False}) @bp.route("/assiduites/ine/<ine>/query", defaults={"with_query": True}) @api_web_bp.route("/assiduites/ine/<ine>/query", defaults={"with_query": True}) # @login_required @scodoc @as_json @permission_required(Permission.ScoView) def assiduites(etudid: int = None, nip=None, ine=None, with_query: bool = False): """ Retourne toutes les assiduités d'un étudiant chemin : /assiduites/<int:etudid> Un filtrage peut être donné avec une query chemin : /assiduites/<int:etudid>/query? Les différents filtres : Etat (etat de l'étudiant -> absent, present ou retard): query?etat=[- liste des états séparé par une virgule -] ex: .../query?etat=present,retard Date debut (date de début de l'assiduité, sont affichés les assiduités dont la date de début est supérieur ou égale à la valeur donnée): query?date_debut=[- date au format iso -] ex: query?date_debut=2022-11-03T08:00+01:00 Date fin (date de fin de l'assiduité, sont affichés les assiduités dont la date de fin est inférieure ou égale à la valeur donnée): query?date_fin=[- date au format iso -] ex: query?date_fin=2022-11-03T10:00+01:00 Moduleimpl_id (l'id du module concerné par l'assiduité): query?moduleimpl_id=[- int ou vide -] ex: query?moduleimpl_id=1234 query?moduleimpl_od= Formsemstre_id (l'id du formsemestre concerné par l'assiduité) query?formsemstre_id=[int] ex query?formsemestre_id=3 user_id (l'id de l'auteur de l'assiduité) query?user_id=[int] ex query?user_id=3 est_just (si l'assiduité est justifié (fait aussi filtre par abs/retard)) query?est_just=[bool] query?est_just=f query?est_just=t """ # query = Identite.query.filter_by(id=etudid) # if g.scodoc_dept: # query = query.filter_by(dept_id=g.scodoc_dept_id) # etud: Identite = query.first_or_404(etudid) etud: Identite = tools.get_etud(etudid, nip, ine) if etud is None: return json_error( 404, message="étudiant inconnu", ) assiduites_query = etud.assiduites if with_query: assiduites_query = _filter_manager(request, assiduites_query) data_set: list[dict] = [] for ass in assiduites_query.all(): data = ass.to_dict(format_api=True) data = _with_justifs(data) data_set.append(data) return data_set @bp.route("/assiduites/group/query", defaults={"with_query": True}) @api_web_bp.route("/assiduites/group/query", defaults={"with_query": True}) @login_required @scodoc @as_json @permission_required(Permission.ScoView) def assiduites_group(with_query: bool = False): """ Retourne toutes les assiduités d'un groupe d'étudiants chemin : /assiduites/group/query?etudids=1,2,3 Un filtrage peut être donné avec une query chemin : /assiduites/group/query?etudids=1,2,3 Les différents filtres : Etat (etat de l'étudiant -> absent, present ou retard): query?etat=[- liste des états séparé par une virgule -] ex: .../query?etat=present,retard Date debut (date de début de l'assiduité, sont affichés les assiduités dont la date de début est supérieur ou égale à la valeur donnée): query?date_debut=[- date au format iso -] ex: query?date_debut=2022-11-03T08:00+01:00 Date fin (date de fin de l'assiduité, sont affichés les assiduités dont la date de fin est inférieure ou égale à la valeur donnée): query?date_fin=[- date au format iso -] ex: query?date_fin=2022-11-03T10:00+01:00 Moduleimpl_id (l'id du module concerné par l'assiduité): query?moduleimpl_id=[- int ou vide -] ex: query?moduleimpl_id=1234 query?moduleimpl_od= Formsemstre_id (l'id du formsemestre concerné par l'assiduité) query?formsemstre_id=[int] ex query?formsemestre_id=3 user_id (l'id de l'auteur de l'assiduité) query?user_id=[int] ex query?user_id=3 est_just (si l'assiduité est justifié (fait aussi filtre par abs/retard)) query?est_just=[bool] query?est_just=f query?est_just=t """ etuds = request.args.get("etudids", "") etuds = etuds.split(",") try: etuds = [int(etu) for etu in etuds] except ValueError: return json_error(404, "Le champs etudids n'est pas correctement formé") query = Identite.query.filter(Identite.id.in_(etuds)) if g.scodoc_dept: query = query.filter_by(dept_id=g.scodoc_dept_id) if len(etuds) != query.count() or len(etuds) == 0: return json_error( 404, "Tous les étudiants ne sont pas dans le même département et/ou n'existe pas.", ) assiduites_query = Assiduite.query.filter(Assiduite.etudid.in_(etuds)) if with_query: assiduites_query = _filter_manager(request, assiduites_query) data_set: dict[list[dict]] = {str(key): [] for key in etuds} for ass in assiduites_query.all(): data = ass.to_dict(format_api=True) data = _with_justifs(data) data_set.get(str(data["etudid"])).append(data) return data_set @bp.route( "/assiduites/formsemestre/<int:formsemestre_id>", defaults={"with_query": False} ) @api_web_bp.route( "/assiduites/formsemestre/<int:formsemestre_id>", defaults={"with_query": False} ) @bp.route( "/assiduites/formsemestre/<int:formsemestre_id>/query", defaults={"with_query": True}, ) @api_web_bp.route( "/assiduites/formsemestre/<int:formsemestre_id>/query", defaults={"with_query": True}, ) @login_required @scodoc @as_json @permission_required(Permission.ScoView) def assiduites_formsemestre(formsemestre_id: int, with_query: bool = False): """Retourne toutes les assiduités du formsemestre""" formsemestre: FormSemestre = None formsemestre_id = int(formsemestre_id) formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first() if formsemestre is None: return json_error(404, "le paramètre 'formsemestre_id' n'existe pas") assiduites_query = scass.filter_by_formsemestre(Assiduite.query,Assiduite, formsemestre) if with_query: assiduites_query = _filter_manager(request, assiduites_query) data_set: list[dict] = [] for ass in assiduites_query.all(): data = ass.to_dict(format_api=True) data = _with_justifs(data) data_set.append(data) return data_set @bp.route( "/assiduites/formsemestre/<int:formsemestre_id>/count", defaults={"with_query": False}, ) @api_web_bp.route( "/assiduites/formsemestre/<int:formsemestre_id>/count", defaults={"with_query": False}, ) @bp.route( "/assiduites/formsemestre/<int:formsemestre_id>/count/query", defaults={"with_query": True}, ) @api_web_bp.route( "/assiduites/formsemestre/<int:formsemestre_id>/count/query", defaults={"with_query": True}, ) @login_required @scodoc @as_json @permission_required(Permission.ScoView) def count_assiduites_formsemestre( formsemestre_id: int = None, with_query: bool = False ): """Comptage des assiduités du formsemestre""" formsemestre: FormSemestre = None formsemestre_id = int(formsemestre_id) formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first() if formsemestre is None: return json_error(404, "le paramètre 'formsemestre_id' n'existe pas") etuds = formsemestre.etuds.all() etuds_id = [etud.id for etud in etuds] assiduites_query = Assiduite.query.filter(Assiduite.etudid.in_(etuds_id)) assiduites_query = scass.filter_by_formsemestre( assiduites_query, Assiduite, formsemestre ) metric: str = "all" filtered: dict = {} if with_query: metric, filtered = _count_manager(request) return scass.get_assiduites_stats(assiduites_query, metric, filtered) # etudid @bp.route("/assiduite/<etudid>/create", methods=["POST"]) @api_web_bp.route("/assiduite/<etudid>/create", methods=["POST"]) @bp.route("/assiduite/etudid/<etudid>/create", methods=["POST"]) @api_web_bp.route("/assiduite/etudid/<etudid>/create", methods=["POST"]) # nip @bp.route("/assiduite/nip/<nip>/create", methods=["POST"]) @api_web_bp.route("/assiduite/nip/<nip>/create", methods=["POST"]) # ine @bp.route("/assiduite/ine/<ine>/create", methods=["POST"]) @api_web_bp.route("/assiduite/ine/<ine>/create", methods=["POST"]) # @scodoc @as_json @login_required @permission_required(Permission.ScoAbsChange) def assiduite_create(etudid: int = None, nip=None, ine=None): """ Création d'une assiduité pour l'étudiant (etudid) La requête doit avoir un content type "application/json": [ { "date_debut": str, "date_fin": str, "etat": str, }, { "date_debut": str, "date_fin": str, "etat": str, "moduleimpl_id": int, "desc":str, } ... ] """ etud: Identite = tools.get_etud(etudid, nip, ine) if etud is None: return json_error( 404, message="étudiant inconnu", ) create_list: list[object] = request.get_json(force=True) if not isinstance(create_list, list): return json_error(404, "Le contenu envoyé n'est pas une liste") errors: list = [] success: list = [] for i, data in enumerate(create_list): code, obj = _create_singular(data, etud) if code == 404: errors.append({"indice": i, "message": obj}) else: success.append({"indice": i, "message": obj}) scass.simple_invalidate_cache(data, etud.id) db.session.commit() return {"errors": errors, "success": success} @bp.route("/assiduites/create", methods=["POST"]) @api_web_bp.route("/assiduites/create", methods=["POST"]) @scodoc @as_json @login_required @permission_required(Permission.ScoAbsChange) def assiduites_create(): """ Création d'une assiduité ou plusieurs assiduites La requête doit avoir un content type "application/json": [ { "date_debut": str, "date_fin": str, "etat": str, "etudid":int, }, { "date_debut": str, "date_fin": str, "etat": str, "etudid":int, "moduleimpl_id": int, "desc":str, } ... ] """ create_list: list[object] = request.get_json(force=True) if not isinstance(create_list, list): return json_error(404, "Le contenu envoyé n'est pas une liste") errors: list = [] success: list = [] for i, data in enumerate(create_list): etud: Identite = Identite.query.filter_by(id=data["etudid"]).first() if etud is None: errors.append({"indice": i, "message": "Cet étudiant n'existe pas."}) continue code, obj = _create_singular(data, etud) if code == 404: errors.append({"indice": i, "message": obj}) else: success.append({"indice": i, "message": obj}) scass.simple_invalidate_cache(data) return {"errors": errors, "success": success} def _create_singular( data: dict, etud: Identite, ) -> tuple[int, object]: errors: list[str] = [] # -- vérifications de l'objet json -- # cas 1 : ETAT etat = data.get("etat", None) if etat is None: errors.append("param 'etat': manquant") elif not scu.EtatAssiduite.contains(etat): errors.append("param 'etat': invalide") etat = scu.EtatAssiduite.get(etat) # cas 2 : date_debut date_debut = data.get("date_debut", None) if date_debut is None: errors.append("param 'date_debut': manquant") deb = scu.is_iso_formated(date_debut, convert=True) if deb is None: errors.append("param 'date_debut': format invalide") # cas 3 : date_fin date_fin = data.get("date_fin", None) if date_fin is None: errors.append("param 'date_fin': manquant") fin = scu.is_iso_formated(date_fin, convert=True) if fin is None: errors.append("param 'date_fin': format invalide") # cas 4 : moduleimpl_id moduleimpl_id = data.get("moduleimpl_id", False) moduleimpl: ModuleImpl = None if moduleimpl_id not in [False, None]: moduleimpl = ModuleImpl.query.filter_by(id=int(moduleimpl_id)).first() if moduleimpl is None: errors.append("param 'moduleimpl_id': invalide") # cas 5 : desc desc: str = data.get("desc", None) external_data = data.get("external_data", False) if external_data is not False: if not isinstance(external_data, dict): errors.append("param 'external_data' : n'est pas un objet JSON") if errors: err: str = ", ".join(errors) return (404, err) # TOUT EST OK try: nouv_assiduite: Assiduite = Assiduite.create_assiduite( date_debut=deb, date_fin=fin, etat=etat, etud=etud, moduleimpl=moduleimpl, description=desc, user_id=current_user.id, external_data=external_data, ) db.session.add(nouv_assiduite) db.session.commit() return (200, {"assiduite_id": nouv_assiduite.id}) except ScoValueError as excp: return ( 404, excp.args[0], ) @bp.route("/assiduite/delete", methods=["POST"]) @api_web_bp.route("/assiduite/delete", methods=["POST"]) @login_required @scodoc @as_json @permission_required(Permission.ScoAbsChange) def assiduite_delete(): """ Suppression d'une assiduité à partir de son id Forme des données envoyées : [ <assiduite_id:int>, ... ] """ assiduites_list: list[int] = request.get_json(force=True) if not isinstance(assiduites_list, list): return json_error(404, "Le contenu envoyé n'est pas une liste") output = {"errors": [], "success": []} for i, ass in enumerate(assiduites_list): code, msg = _delete_singular(ass, db) if code == 404: output["errors"].append({"indice": i, "message": msg}) else: output["success"].append({"indice": i, "message": "OK"}) db.session.commit() return output def _delete_singular(assiduite_id: int, database): assiduite_unique: Assiduite = Assiduite.query.filter_by(id=assiduite_id).first() if assiduite_unique is None: return (404, "Assiduite non existante") ass_dict = assiduite_unique.to_dict() log(f"delete_assiduite: {assiduite_unique.etudiant.id} {assiduite_unique}") Scolog.logdb( method="delete_assiduite", etudid=assiduite_unique.etudiant.id, msg=f"assiduité: {assiduite_unique}", ) database.session.delete(assiduite_unique) scass.simple_invalidate_cache(ass_dict) return (200, "OK") @bp.route("/assiduite/<int:assiduite_id>/edit", methods=["POST"]) @api_web_bp.route("/assiduite/<int:assiduite_id>/edit", methods=["POST"]) @login_required @scodoc @as_json @permission_required(Permission.ScoAbsChange) def assiduite_edit(assiduite_id: int): """ Edition d'une assiduité à partir de son id La requête doit avoir un content type "application/json": { "etat"?: str, "moduleimpl_id"?: int "desc"?: str "est_just"?: bool } """ assiduite_unique: Assiduite = Assiduite.query.filter_by( id=assiduite_id ).first_or_404() errors: list[str] = [] data = request.get_json(force=True) # Vérifications de data # Cas 1 : Etat if data.get("etat") is not None: etat = scu.EtatAssiduite.get(data.get("etat")) if etat is None: errors.append("param 'etat': invalide") else: assiduite_unique.etat = etat # Cas 2 : Moduleimpl_id moduleimpl_id = data.get("moduleimpl_id", False) moduleimpl: ModuleImpl = None if moduleimpl_id is not False: if moduleimpl_id is not None and moduleimpl_id != "": moduleimpl = ModuleImpl.query.filter_by(id=int(moduleimpl_id)).first() if moduleimpl is None: errors.append("param 'moduleimpl_id': invalide") else: if not moduleimpl.est_inscrit( Identite.query.filter_by(id=assiduite_unique.etudid).first() ): errors.append("param 'moduleimpl_id': etud non inscrit") else: assiduite_unique.moduleimpl_id = moduleimpl_id else: assiduite_unique.moduleimpl_id = None # Cas 3 : desc desc = data.get("desc", False) if desc is not False: assiduite_unique.description = desc # Cas 4 : est_just est_just = data.get("est_just") if est_just is not None: if not isinstance(est_just, bool): errors.append("param 'est_just' : booléen non reconnu") else: assiduite_unique.est_just = est_just external_data = data.get("external_data") if external_data is not None: if not isinstance(external_data, dict): errors.append("param 'external_data' : n'est pas un objet JSON") else: assiduite_unique.external_data = external_data if errors: err: str = ", ".join(errors) return json_error(404, err) log(f"assiduite_edit: {assiduite_unique.etudiant.id} {assiduite_unique}") Scolog.logdb( "assiduite_edit", assiduite_unique.etudiant.id, msg=f"assiduite: modif {assiduite_unique}", ) db.session.add(assiduite_unique) db.session.commit() scass.simple_invalidate_cache(assiduite_unique.to_dict()) return {"OK": True} @bp.route("/assiduites/edit", methods=["POST"]) @api_web_bp.route("/assiduites/edit", methods=["POST"]) @login_required @scodoc @as_json @permission_required(Permission.ScoAbsChange) def assiduites_edit(): """ Edition de plusieurs assiduités La requête doit avoir un content type "application/json": [ { "assiduite_id" : int, "etat"?: str, "moduleimpl_id"?: int "desc"?: str "est_just"?: bool } ] """ edit_list: list[object] = request.get_json(force=True) if not isinstance(edit_list, list): return json_error(404, "Le contenu envoyé n'est pas une liste") errors: list[dict] = [] success: list[dict] = [] for i, data in enumerate(edit_list): assi: Identite = Assiduite.query.filter_by(id=data["assiduite_id"]).first() if assi is None: errors.append( { "indice": i, "message": f"assiduité {data['assiduite_id']} n'existe pas.", } ) continue code, obj = _edit_singular(assi, data) obj_retour = { "indice": i, "message": obj, } if code == 404: errors.append(obj_retour) else: success.append(obj_retour) db.session.commit() return {"errors": errors, "success": success} def _edit_singular(assiduite_unique, data): errors: list[str] = [] # Vérifications de data # Cas 1 : Etat if data.get("etat") is not None: etat = scu.EtatAssiduite.get(data.get("etat")) if etat is None: errors.append("param 'etat': invalide") else: assiduite_unique.etat = etat # Cas 2 : Moduleimpl_id moduleimpl_id = data.get("moduleimpl_id", False) moduleimpl: ModuleImpl = None if moduleimpl_id is not False: if moduleimpl_id is not None: moduleimpl = ModuleImpl.query.filter_by(id=int(moduleimpl_id)).first() if moduleimpl is None: errors.append("param 'moduleimpl_id': invalide") else: if not moduleimpl.est_inscrit( Identite.query.filter_by(id=assiduite_unique.etudid).first() ): errors.append("param 'moduleimpl_id': etud non inscrit") else: assiduite_unique.moduleimpl_id = moduleimpl_id else: assiduite_unique.moduleimpl_id = moduleimpl_id # Cas 3 : desc desc = data.get("desc", False) if desc is not False: assiduite_unique.desc = desc # Cas 4 : est_just est_just = data.get("est_just") if est_just is not None: if not isinstance(est_just, bool): errors.append("param 'est_just' : booléen non reconnu") else: assiduite_unique.est_just = est_just if errors: err: str = ", ".join(errors) return (404, err) log(f"_edit_singular: {assiduite_unique.etudiant.id} {assiduite_unique}") Scolog.logdb( "assiduite_edit", assiduite_unique.etudiant.id, msg=f"assiduite: modif {assiduite_unique}", ) db.session.add(assiduite_unique) scass.simple_invalidate_cache(assiduite_unique.to_dict()) return (200, "OK") # -- Utils -- def _count_manager(requested) -> tuple[str, dict]: """ Retourne la/les métriques à utiliser ainsi que le filtre donnés en query de la requête """ filtered: dict = {} # cas 1 : etat assiduite etat = requested.args.get("etat") if etat is not None: filtered["etat"] = etat # cas 2 : date de début deb = requested.args.get("date_debut", "").replace(" ", "+") deb: datetime = scu.is_iso_formated(deb, True) if deb is not None: filtered["date_debut"] = deb # cas 3 : date de fin fin = requested.args.get("date_fin", "").replace(" ", "+") fin = scu.is_iso_formated(fin, True) if fin is not None: filtered["date_fin"] = fin # cas 4 : moduleimpl_id module = requested.args.get("moduleimpl_id", False) try: if module is False: raise ValueError if module != "": module = int(module) else: module = None except ValueError: module = False if module is not False: filtered["moduleimpl_id"] = module # cas 5 : formsemestre_id formsemestre_id = requested.args.get("formsemestre_id") if formsemestre_id is not None: formsemestre: FormSemestre = None formsemestre_id = int(formsemestre_id) formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first() filtered["formsemestre"] = formsemestre # cas 6 : type metric = requested.args.get("metric", "all") # cas 7 : est_just est_just: str = requested.args.get("est_just") if est_just is not None: trues: tuple[str] = ("v", "t", "vrai", "true") falses: tuple[str] = ("f", "faux", "false") if est_just.lower() in trues: filtered["est_just"] = True elif est_just.lower() in falses: filtered["est_just"] = False # cas 8 : user_id user_id = requested.args.get("user_id", False) if user_id is not False: filtered["user_id"] = user_id return (metric, filtered) def _filter_manager(requested, assiduites_query: Assiduite): """ Retourne les assiduites entrées filtrées en fonction de la request """ # cas 1 : etat assiduite etat = requested.args.get("etat") if etat is not None: assiduites_query = scass.filter_assiduites_by_etat(assiduites_query, etat) # cas 2 : date de début deb = requested.args.get("date_debut", "").replace(" ", "+") deb: datetime = scu.is_iso_formated(deb, True) # cas 3 : date de fin fin = requested.args.get("date_fin", "").replace(" ", "+") fin = scu.is_iso_formated(fin, True) if (deb, fin) != (None, None): assiduites_query: Assiduite = scass.filter_by_date( assiduites_query, Assiduite, deb, fin ) # cas 4 : moduleimpl_id module = requested.args.get("moduleimpl_id", False) try: if module is False: raise ValueError if module != "": module = int(module) else: module = None except ValueError: module = False if module is not False: assiduites_query = scass.filter_by_module_impl(assiduites_query, module) # cas 5 : formsemestre_id formsemestre_id = requested.args.get("formsemestre_id") if formsemestre_id is not None: formsemestre: FormSemestre = None formsemestre_id = int(formsemestre_id) formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first() assiduites_query = scass.filter_by_formsemestre( assiduites_query, Assiduite, formsemestre ) # cas 6 : est_just est_just: str = requested.args.get("est_just") if est_just is not None: trues: tuple[str] = ("v", "t", "vrai", "true") falses: tuple[str] = ("f", "faux", "false") if est_just.lower() in trues: assiduites_query: Assiduite = scass.filter_assiduites_by_est_just( assiduites_query, True ) elif est_just.lower() in falses: assiduites_query: Assiduite = scass.filter_assiduites_by_est_just( assiduites_query, False ) # cas 8 : user_id user_id = requested.args.get("user_id", False) if user_id is not False: assiduites_query: Assiduite = scass.filter_by_user_id(assiduites_query, user_id) return assiduites_query def _with_justifs(assi): if request.args.get("with_justifs") is None: return assi assi["justificatifs"] = get_assiduites_justif(assi["assiduite_id"], True) return assi