2023-04-17 15:35:42 +02:00
|
|
|
##############################################################################
|
|
|
|
# ScoDoc
|
2023-12-31 23:04:06 +01:00
|
|
|
# Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved.
|
2023-04-17 15:35:42 +02:00
|
|
|
# See LICENSE
|
|
|
|
##############################################################################
|
2024-04-22 09:20:05 +02:00
|
|
|
"""ScoDoc 9 API : Assiduités"""
|
|
|
|
|
2023-04-17 15:35:42 +02:00
|
|
|
from datetime import datetime
|
2023-07-26 13:27:57 +02:00
|
|
|
|
2023-05-17 21:16:23 +02:00
|
|
|
from flask import g, request
|
2023-07-26 13:27:57 +02:00
|
|
|
from flask_json import as_json
|
|
|
|
from flask_login import current_user, login_required
|
2023-12-08 13:42:24 +01:00
|
|
|
from flask_sqlalchemy.query import Query
|
2024-04-22 09:20:05 +02:00
|
|
|
from sqlalchemy.orm.exc import ObjectDeletedError
|
2024-06-26 11:20:28 +02:00
|
|
|
from werkzeug.exceptions import HTTPException
|
2023-04-17 15:35:42 +02:00
|
|
|
|
2023-10-10 22:52:00 +02:00
|
|
|
from app import db, log, set_sco_dept
|
2023-04-17 15:35:42 +02:00
|
|
|
import app.scodoc.sco_assiduites as scass
|
|
|
|
import app.scodoc.sco_utils as scu
|
|
|
|
from app.api import api_bp as bp
|
2023-07-27 14:58:57 +02:00
|
|
|
from app.api import api_web_bp, get_model_api_object, tools
|
2023-04-17 15:35:42 +02:00
|
|
|
from app.decorators import permission_required, scodoc
|
2023-07-30 16:34:05 +02:00
|
|
|
from app.models import (
|
|
|
|
Assiduite,
|
2024-06-26 11:20:28 +02:00
|
|
|
Evaluation,
|
2023-07-30 16:34:05 +02:00
|
|
|
FormSemestre,
|
|
|
|
Identite,
|
|
|
|
ModuleImpl,
|
|
|
|
Scolog,
|
|
|
|
)
|
2023-10-26 15:52:53 +02:00
|
|
|
from app.models.assiduites import (
|
|
|
|
get_assiduites_justif,
|
|
|
|
get_justifs_from_date,
|
|
|
|
get_formsemestre_from_data,
|
|
|
|
)
|
2023-04-17 15:35:42 +02:00
|
|
|
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)
|
2024-01-05 13:42:55 +01:00
|
|
|
@as_json
|
2023-04-17 15:35:42 +02:00
|
|
|
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,
|
2023-10-06 16:32:06 +02:00
|
|
|
"user_name" : login scodoc or null
|
|
|
|
"user_nom_complet": "Marie Dupont"
|
2023-04-17 15:35:42 +02:00
|
|
|
"est_just": False or True,
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
|
|
|
return get_model_api_object(Assiduite, assiduite_id, Identite)
|
|
|
|
|
|
|
|
|
2023-07-30 17:09:21 +02:00
|
|
|
@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}
|
|
|
|
)
|
2023-07-30 16:34:05 +02:00
|
|
|
@scodoc
|
|
|
|
@permission_required(Permission.ScoView)
|
|
|
|
@as_json
|
2023-07-30 17:09:21 +02:00
|
|
|
def assiduite_justificatifs(assiduite_id: int = None, long: bool = False):
|
2023-07-30 16:34:05 +02:00
|
|
|
"""Retourne la liste des justificatifs qui justifie cette assiduitée
|
|
|
|
|
|
|
|
Exemple de résultat:
|
|
|
|
[
|
|
|
|
1,
|
|
|
|
2,
|
|
|
|
3,
|
|
|
|
...
|
|
|
|
]
|
|
|
|
"""
|
|
|
|
|
2023-12-08 13:42:24 +01:00
|
|
|
return get_assiduites_justif(assiduite_id, long)
|
2023-07-30 16:34:05 +02:00
|
|
|
|
|
|
|
|
2023-07-27 14:58:57 +02:00
|
|
|
# etudid
|
2023-10-11 16:37:13 +02:00
|
|
|
@bp.route("/assiduites/<int:etudid>/count", defaults={"with_query": False})
|
|
|
|
@api_web_bp.route("/assiduites/<int:etudid>/count", defaults={"with_query": False})
|
|
|
|
@bp.route("/assiduites/<int:etudid>/count/query", defaults={"with_query": True})
|
|
|
|
@api_web_bp.route("/assiduites/<int:etudid>/count/query", defaults={"with_query": True})
|
|
|
|
@bp.route("/assiduites/etudid/<int:etudid>/count", defaults={"with_query": False})
|
2023-07-27 14:58:57 +02:00
|
|
|
@api_web_bp.route(
|
2023-10-11 16:37:13 +02:00
|
|
|
"/assiduites/etudid/<int:etudid>/count", defaults={"with_query": False}
|
|
|
|
)
|
|
|
|
@bp.route("/assiduites/etudid/<int:etudid>/count/query", defaults={"with_query": True})
|
|
|
|
@api_web_bp.route(
|
|
|
|
"/assiduites/etudid/<int:etudid>/count/query", defaults={"with_query": True}
|
2023-07-27 14:58:57 +02:00
|
|
|
)
|
|
|
|
# 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})
|
|
|
|
#
|
2023-04-17 15:35:42 +02:00
|
|
|
@login_required
|
|
|
|
@scodoc
|
2023-05-17 21:16:23 +02:00
|
|
|
@as_json
|
2023-04-17 15:35:42 +02:00
|
|
|
@permission_required(Permission.ScoView)
|
2024-07-17 14:58:49 +02:00
|
|
|
def assiduites_count(
|
2023-07-27 14:58:57 +02:00
|
|
|
etudid: int = None, nip: str = None, ine: str = None, with_query: bool = False
|
|
|
|
):
|
2023-04-17 15:35:42 +02:00
|
|
|
"""
|
|
|
|
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
|
|
|
|
|
2024-06-20 17:56:12 +02:00
|
|
|
QUERY
|
|
|
|
-----
|
|
|
|
user_id:<int:user_id>
|
|
|
|
est_just:<bool:est_just>
|
|
|
|
moduleimpl_id:<int:moduleimpl_id>
|
|
|
|
date_debut:<string:date_debut_iso>
|
|
|
|
date_fin:<string:date_fin_iso>
|
|
|
|
etat:<array[string]:etat>
|
|
|
|
formsemestre_id:<int:formsemestre_id>
|
|
|
|
metric:<array[string]:metric>
|
|
|
|
split:<bool:split>
|
2023-07-30 17:09:21 +02:00
|
|
|
|
2023-04-17 15:35:42 +02:00
|
|
|
"""
|
2023-07-27 14:58:57 +02:00
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Récupération de l'étudiant
|
2023-07-27 14:58:57 +02:00
|
|
|
etud: Identite = tools.get_etud(etudid, nip, ine)
|
2023-10-27 16:05:40 +02:00
|
|
|
# Vérification que l'étudiant existe
|
2023-07-27 14:58:57 +02:00
|
|
|
if etud is None:
|
|
|
|
return json_error(
|
|
|
|
404,
|
|
|
|
message="étudiant inconnu",
|
|
|
|
)
|
2024-01-05 23:20:32 +01:00
|
|
|
set_sco_dept(etud.departement.acronym)
|
2023-04-17 15:35:42 +02:00
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Les filtres qui seront appliqués au comptage (type, date, etudid...)
|
2023-04-17 15:35:42 +02:00
|
|
|
filtered: dict[str, object] = {}
|
2023-10-27 16:05:40 +02:00
|
|
|
# la métrique du comptage (all, demi, heure, journee)
|
2023-04-17 15:35:42 +02:00
|
|
|
metric: str = "all"
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Si la requête a des paramètres
|
2023-04-17 15:35:42 +02:00
|
|
|
if with_query:
|
|
|
|
metric, filtered = _count_manager(request)
|
|
|
|
|
2023-05-17 21:16:23 +02:00
|
|
|
return scass.get_assiduites_stats(
|
|
|
|
assiduites=etud.assiduites, metric=metric, filtered=filtered
|
2023-04-17 15:35:42 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2023-07-27 14:58:57 +02:00
|
|
|
# etudid
|
2023-10-11 16:37:13 +02:00
|
|
|
@bp.route("/assiduites/<int:etudid>", defaults={"with_query": False})
|
|
|
|
@api_web_bp.route("/assiduites/<int:etudid>", defaults={"with_query": False})
|
|
|
|
@bp.route("/assiduites/<int:etudid>/query", defaults={"with_query": True})
|
|
|
|
@api_web_bp.route("/assiduites/<int:etudid>/query", defaults={"with_query": True})
|
|
|
|
@bp.route("/assiduites/etudid/<int:etudid>", defaults={"with_query": False})
|
|
|
|
@api_web_bp.route("/assiduites/etudid/<int:etudid>", defaults={"with_query": False})
|
|
|
|
@bp.route("/assiduites/etudid/<int:etudid>/query", defaults={"with_query": True})
|
|
|
|
@api_web_bp.route(
|
|
|
|
"/assiduites/etudid/<int:etudid>/query", defaults={"with_query": True}
|
|
|
|
)
|
2023-07-27 14:58:57 +02:00
|
|
|
# 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})
|
|
|
|
#
|
2023-04-17 15:35:42 +02:00
|
|
|
@login_required
|
|
|
|
@scodoc
|
2023-05-17 21:16:23 +02:00
|
|
|
@as_json
|
2023-04-17 15:35:42 +02:00
|
|
|
@permission_required(Permission.ScoView)
|
2023-07-27 14:58:57 +02:00
|
|
|
def assiduites(etudid: int = None, nip=None, ine=None, with_query: bool = False):
|
2023-04-17 15:35:42 +02:00
|
|
|
"""
|
|
|
|
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
|
|
|
|
|
2024-06-20 17:56:12 +02:00
|
|
|
QUERY
|
|
|
|
-----
|
|
|
|
user_id:<int:user_id>
|
|
|
|
est_just:<bool:est_just>
|
|
|
|
moduleimpl_id:<int:moduleimpl_id>
|
|
|
|
date_debut:<string:date_debut_iso>
|
|
|
|
date_fin:<string:date_fin_iso>
|
|
|
|
etat:<array[string]:etat>
|
|
|
|
formsemestre_id:<int:formsemestre_id>
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
"""
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Récupération de l'étudiant
|
2023-07-27 14:58:57 +02:00
|
|
|
etud: Identite = tools.get_etud(etudid, nip, ine)
|
2023-04-17 15:35:42 +02:00
|
|
|
|
2023-07-27 14:58:57 +02:00
|
|
|
if etud is None:
|
|
|
|
return json_error(
|
|
|
|
404,
|
|
|
|
message="étudiant inconnu",
|
|
|
|
)
|
2024-06-26 11:20:28 +02:00
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Récupération des assiduités de l'étudiant
|
2023-08-14 01:08:04 +02:00
|
|
|
assiduites_query: Query = etud.assiduites
|
2023-04-17 15:35:42 +02:00
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Filtrage des assiduités en fonction des paramètres de la requête
|
2023-04-17 15:35:42 +02:00
|
|
|
if with_query:
|
|
|
|
assiduites_query = _filter_manager(request, assiduites_query)
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Préparation de la réponse json
|
|
|
|
|
2023-04-17 15:35:42 +02:00
|
|
|
data_set: list[dict] = []
|
2023-10-27 16:05:40 +02:00
|
|
|
|
2023-04-17 15:35:42 +02:00
|
|
|
for ass in assiduites_query.all():
|
2023-10-27 16:05:40 +02:00
|
|
|
# conversion Assiduite -> Dict
|
2023-04-17 15:35:42 +02:00
|
|
|
data = ass.to_dict(format_api=True)
|
2023-10-27 16:05:40 +02:00
|
|
|
# Ajout des justificatifs (ou non dépendamment de la requête)
|
2023-07-30 17:09:21 +02:00
|
|
|
data = _with_justifs(data)
|
2023-10-27 16:05:40 +02:00
|
|
|
# Ajout de l'assiduité dans la liste de retour
|
2023-04-17 15:35:42 +02:00
|
|
|
data_set.append(data)
|
|
|
|
|
2023-05-17 21:16:23 +02:00
|
|
|
return data_set
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
|
2024-06-26 11:20:28 +02:00
|
|
|
@bp.route("/assiduites/<int:etudid>/evaluations")
|
|
|
|
@api_web_bp.route("/assiduites/<int:etudid>/evaluations")
|
|
|
|
# etudid
|
|
|
|
@bp.route("/assiduites/etudid/<int:etudid>/evaluations")
|
|
|
|
@api_web_bp.route("/assiduites/etudid/<int:etudid>/evaluations")
|
|
|
|
# ine
|
|
|
|
@bp.route("/assiduites/ine/<ine>/evaluations")
|
|
|
|
@api_web_bp.route("/assiduites/ine/<ine>/evaluations")
|
|
|
|
# nip
|
|
|
|
@bp.route("/assiduites/nip/<nip>/evaluations")
|
|
|
|
@api_web_bp.route("/assiduites/nip/<nip>/evaluations")
|
|
|
|
@login_required
|
|
|
|
@scodoc
|
|
|
|
@as_json
|
|
|
|
@permission_required(Permission.ScoView)
|
|
|
|
def assiduites_evaluations(etudid: int = None, nip=None, ine=None):
|
|
|
|
"""
|
|
|
|
Retourne la liste de toutes les évaluations de l'étudiant
|
|
|
|
Pour chaque évaluation, retourne la liste des objets assiduités
|
|
|
|
sur la plage de l'évaluation
|
|
|
|
|
|
|
|
Présentation du retour :
|
|
|
|
[
|
|
|
|
{
|
|
|
|
"evaluation_id": 1234,
|
|
|
|
"assiduites": [
|
|
|
|
{
|
|
|
|
"assiduite_id":1234,
|
|
|
|
...
|
|
|
|
},
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
|
|
|
|
"""
|
|
|
|
# Récupération de l'étudiant
|
|
|
|
etud: Identite = tools.get_etud(etudid, nip, ine)
|
|
|
|
if etud is None:
|
|
|
|
return json_error(
|
|
|
|
404,
|
|
|
|
message="étudiant inconnu",
|
|
|
|
)
|
|
|
|
|
|
|
|
# Récupération des évaluations et des assidiutés
|
|
|
|
etud_evaluations_assiduites: list[dict] = scass.get_etud_evaluations_assiduites(
|
|
|
|
etud
|
|
|
|
)
|
|
|
|
|
|
|
|
return etud_evaluations_assiduites
|
|
|
|
|
|
|
|
|
|
|
|
@api_web_bp.route("/evaluation/<int:evaluation_id>/assiduites")
|
|
|
|
@bp.route("/evaluation/<int:evaluation_id>/assiduites")
|
|
|
|
@login_required
|
|
|
|
@scodoc
|
|
|
|
@as_json
|
|
|
|
@permission_required(Permission.ScoView)
|
|
|
|
def evaluation_assiduites(evaluation_id):
|
|
|
|
"""
|
|
|
|
Retourne les objets assiduités de chaque étudiant sur la plage de l'évaluation
|
|
|
|
Présentation du retour :
|
|
|
|
{
|
|
|
|
"<etudid>" : [
|
|
|
|
{
|
|
|
|
"assiduite_id":1234,
|
|
|
|
...
|
|
|
|
},
|
|
|
|
]
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
# Récupération de l'évaluation
|
|
|
|
try:
|
|
|
|
evaluation: Evaluation = Evaluation.get_evaluation(evaluation_id)
|
|
|
|
except HTTPException:
|
|
|
|
return json_error(404, "L'évaluation n'existe pas")
|
|
|
|
|
|
|
|
evaluation_assiduites_par_etudid: dict[int, list[Assiduite]] = {}
|
|
|
|
for assi in scass.get_evaluation_assiduites(evaluation):
|
|
|
|
etudid: str = str(assi.etudid)
|
|
|
|
etud_assiduites = evaluation_assiduites_par_etudid.get(etudid, [])
|
|
|
|
etud_assiduites.append(assi.to_dict(format_api=True))
|
|
|
|
evaluation_assiduites_par_etudid[etudid] = etud_assiduites
|
|
|
|
|
|
|
|
return evaluation_assiduites_par_etudid
|
|
|
|
|
|
|
|
|
2023-04-17 15:35:42 +02:00
|
|
|
@bp.route("/assiduites/group/query", defaults={"with_query": True})
|
|
|
|
@api_web_bp.route("/assiduites/group/query", defaults={"with_query": True})
|
|
|
|
@login_required
|
|
|
|
@scodoc
|
2023-05-17 21:16:23 +02:00
|
|
|
@as_json
|
2023-04-17 15:35:42 +02:00
|
|
|
@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
|
|
|
|
|
2024-06-20 17:56:12 +02:00
|
|
|
QUERY
|
|
|
|
-----
|
|
|
|
user_id:<int:user_id>
|
|
|
|
est_just:<bool:est_just>
|
|
|
|
moduleimpl_id:<int:moduleimpl_id>
|
|
|
|
date_debut:<string:date_debut_iso>
|
|
|
|
date_fin:<string:date_fin_iso>
|
|
|
|
etat:<array[string]:etat>
|
|
|
|
etudids:<array[int]:etudids
|
|
|
|
formsemestre_id:<int:formsemestre_id>
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
"""
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Récupération des étudiants dans la requête
|
2023-04-17 15:35:42 +02:00
|
|
|
etuds = request.args.get("etudids", "")
|
|
|
|
etuds = etuds.split(",")
|
|
|
|
try:
|
|
|
|
etuds = [int(etu) for etu in etuds]
|
|
|
|
except ValueError:
|
2024-02-12 10:12:46 +01:00
|
|
|
return json_error(404, "Le champ etudids n'est pas correctement formé")
|
2023-04-17 15:35:42 +02:00
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Vérification que tous les étudiants sont du même département
|
2023-04-17 15:35:42 +02:00
|
|
|
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.",
|
|
|
|
)
|
2023-10-27 16:05:40 +02:00
|
|
|
|
|
|
|
# Récupération de toutes les assiduités liés aux étudiants
|
2023-04-17 15:35:42 +02:00
|
|
|
assiduites_query = Assiduite.query.filter(Assiduite.etudid.in_(etuds))
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Filtrage des assiduités en fonction des filtres passés dans la requête
|
2023-04-17 15:35:42 +02:00
|
|
|
if with_query:
|
|
|
|
assiduites_query = _filter_manager(request, assiduites_query)
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Préparation de retour json
|
|
|
|
# Dict représentant chaque étudiant avec sa liste d'assiduité
|
2023-05-29 11:32:46 +02:00
|
|
|
data_set: dict[list[dict]] = {str(key): [] for key in etuds}
|
2023-04-17 15:35:42 +02:00
|
|
|
for ass in assiduites_query.all():
|
|
|
|
data = ass.to_dict(format_api=True)
|
2023-07-30 17:09:21 +02:00
|
|
|
data = _with_justifs(data)
|
2023-10-27 16:05:40 +02:00
|
|
|
# Ajout de l'assiduité dans la liste du bon étudiant
|
2023-05-29 11:32:46 +02:00
|
|
|
data_set.get(str(data["etudid"])).append(data)
|
2023-05-17 21:16:23 +02:00
|
|
|
return data_set
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
@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
|
2023-05-17 21:16:23 +02:00
|
|
|
@as_json
|
2023-04-17 15:35:42 +02:00
|
|
|
@permission_required(Permission.ScoView)
|
|
|
|
def assiduites_formsemestre(formsemestre_id: int, with_query: bool = False):
|
2024-06-20 17:56:12 +02:00
|
|
|
"""Retourne toutes les assiduités du formsemestre
|
|
|
|
QUERY
|
|
|
|
-----
|
|
|
|
user_id:<int:user_id>
|
|
|
|
est_just:<bool:est_just>
|
|
|
|
moduleimpl_id:<int:moduleimpl_id>
|
|
|
|
date_debut:<string:date_debut_iso>
|
|
|
|
date_fin:<string:date_fin_iso>
|
|
|
|
etat:<array[string]:etat>
|
|
|
|
"""
|
2023-10-27 16:05:40 +02:00
|
|
|
|
|
|
|
# Récupération du formsemestre à partir du formsemestre_id
|
2023-04-17 15:35:42 +02:00
|
|
|
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")
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Récupération des assiduités du formsemestre
|
2023-08-11 16:14:29 +02:00
|
|
|
assiduites_query = scass.filter_by_formsemestre(
|
|
|
|
Assiduite.query, Assiduite, formsemestre
|
|
|
|
)
|
2023-10-27 16:05:40 +02:00
|
|
|
# Filtrage en fonction des paramètres de la requête
|
2023-04-17 15:35:42 +02:00
|
|
|
if with_query:
|
|
|
|
assiduites_query = _filter_manager(request, assiduites_query)
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Préparation du retour JSON
|
2023-04-17 15:35:42 +02:00
|
|
|
data_set: list[dict] = []
|
|
|
|
for ass in assiduites_query.all():
|
|
|
|
data = ass.to_dict(format_api=True)
|
2023-07-30 17:09:21 +02:00
|
|
|
data = _with_justifs(data)
|
2023-04-17 15:35:42 +02:00
|
|
|
data_set.append(data)
|
|
|
|
|
2023-05-17 21:16:23 +02:00
|
|
|
return data_set
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
@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
|
2023-05-17 21:16:23 +02:00
|
|
|
@as_json
|
2023-04-17 15:35:42 +02:00
|
|
|
@permission_required(Permission.ScoView)
|
2024-07-17 14:58:49 +02:00
|
|
|
def assiduites_formsemestre_count(
|
2023-04-17 15:35:42 +02:00
|
|
|
formsemestre_id: int = None, with_query: bool = False
|
|
|
|
):
|
2024-06-20 17:56:12 +02:00
|
|
|
"""Comptage des assiduités du formsemestre
|
|
|
|
|
|
|
|
QUERY
|
|
|
|
-----
|
|
|
|
user_id:<int:user_id>
|
|
|
|
est_just:<bool:est_just>
|
|
|
|
moduleimpl_id:<int:moduleimpl_id>
|
|
|
|
date_debut:<string:date_debut_iso>
|
|
|
|
date_fin:<string:date_fin_iso>
|
|
|
|
etat:<array[string]:etat>
|
|
|
|
formsemestre_id:<int:formsemestre_id>
|
|
|
|
metric:<array[string]:metric>
|
|
|
|
split:<bool:split>
|
|
|
|
"""
|
2023-10-27 16:05:40 +02:00
|
|
|
|
|
|
|
# Récupération du formsemestre à partir du formsemestre_id
|
2023-04-17 15:35:42 +02:00
|
|
|
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")
|
|
|
|
|
2024-01-05 23:20:32 +01:00
|
|
|
set_sco_dept(formsemestre.departement.acronym)
|
2024-01-05 13:42:55 +01:00
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Récupération des étudiants du formsemestre
|
2023-04-17 15:35:42 +02:00
|
|
|
etuds = formsemestre.etuds.all()
|
|
|
|
etuds_id = [etud.id for etud in etuds]
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Récupération des assiduités des étudiants du semestre
|
2023-04-17 15:35:42 +02:00
|
|
|
assiduites_query = Assiduite.query.filter(Assiduite.etudid.in_(etuds_id))
|
2023-10-27 16:05:40 +02:00
|
|
|
# Filtrage des assiduités en fonction des dates du semestre
|
2023-08-09 09:57:47 +02:00
|
|
|
assiduites_query = scass.filter_by_formsemestre(
|
|
|
|
assiduites_query, Assiduite, formsemestre
|
|
|
|
)
|
2023-10-27 16:05:40 +02:00
|
|
|
|
|
|
|
# Gestion de la métrique de comptage (all,demi,heure,journee)
|
2023-04-17 15:35:42 +02:00
|
|
|
metric: str = "all"
|
2023-10-27 16:05:40 +02:00
|
|
|
# Gestion du filtre (en fonction des paramètres de la requête)
|
2023-04-17 15:35:42 +02:00
|
|
|
filtered: dict = {}
|
|
|
|
if with_query:
|
|
|
|
metric, filtered = _count_manager(request)
|
|
|
|
|
2023-05-17 21:16:23 +02:00
|
|
|
return scass.get_assiduites_stats(assiduites_query, metric, filtered)
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
|
2023-07-27 14:58:57 +02:00
|
|
|
# etudid
|
2023-10-11 16:37:13 +02:00
|
|
|
@bp.route("/assiduite/<int:etudid>/create", methods=["POST"])
|
|
|
|
@api_web_bp.route("/assiduite/<int:etudid>/create", methods=["POST"])
|
|
|
|
@bp.route("/assiduite/etudid/<int:etudid>/create", methods=["POST"])
|
|
|
|
@api_web_bp.route("/assiduite/etudid/<int:etudid>/create", methods=["POST"])
|
2023-07-27 14:58:57 +02:00
|
|
|
# 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"])
|
|
|
|
#
|
2023-04-17 15:35:42 +02:00
|
|
|
@scodoc
|
2023-05-17 21:16:23 +02:00
|
|
|
@as_json
|
2023-04-17 15:35:42 +02:00
|
|
|
@login_required
|
2023-09-29 21:17:31 +02:00
|
|
|
@permission_required(Permission.AbsChange)
|
2023-07-27 14:58:57 +02:00
|
|
|
def assiduite_create(etudid: int = None, nip=None, ine=None):
|
2023-04-17 15:35:42 +02:00
|
|
|
"""
|
2023-10-11 14:45:06 +02:00
|
|
|
Enregistrement d'assiduités pour un étudiant (etudid)
|
2023-04-17 15:35:42 +02:00
|
|
|
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,
|
|
|
|
}
|
|
|
|
...
|
|
|
|
]
|
|
|
|
|
|
|
|
"""
|
2023-10-27 16:05:40 +02:00
|
|
|
# Récupération de l'étudiant
|
2023-07-27 14:58:57 +02:00
|
|
|
etud: Identite = tools.get_etud(etudid, nip, ine)
|
|
|
|
if etud is None:
|
|
|
|
return json_error(
|
|
|
|
404,
|
|
|
|
message="étudiant inconnu",
|
|
|
|
)
|
2023-10-27 16:05:40 +02:00
|
|
|
# Mise à jour du "g.scodoc_dept" si route sans dept
|
2023-10-11 14:45:06 +02:00
|
|
|
if g.scodoc_dept is None and etud.dept_id is not None:
|
2023-10-10 22:52:00 +02:00
|
|
|
# route sans département
|
|
|
|
set_sco_dept(etud.departement.acronym)
|
2023-10-27 16:05:40 +02:00
|
|
|
|
|
|
|
# Récupération de la liste des assiduités à créer
|
2023-04-17 15:35:42 +02:00
|
|
|
create_list: list[object] = request.get_json(force=True)
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Vérification que c'est bien une liste
|
2023-04-17 15:35:42 +02:00
|
|
|
if not isinstance(create_list, list):
|
|
|
|
return json_error(404, "Le contenu envoyé n'est pas une liste")
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Préparation du retour
|
|
|
|
|
|
|
|
errors: list[dict[str, object]] = []
|
|
|
|
success: list[dict[str, object]] = []
|
|
|
|
|
|
|
|
# Pour chaque objet de la liste,
|
|
|
|
# on récupère son indice et l'objet
|
2023-04-17 15:35:42 +02:00
|
|
|
for i, data in enumerate(create_list):
|
2023-10-27 16:05:40 +02:00
|
|
|
# On créé l'assiduité
|
|
|
|
# 200 + obj si réussi
|
|
|
|
# 404 + message d'erreur si non réussi
|
2023-12-06 02:04:10 +01:00
|
|
|
code, obj = _create_one(data, etud)
|
2023-04-17 15:35:42 +02:00
|
|
|
if code == 404:
|
2023-07-27 18:00:40 +02:00
|
|
|
errors.append({"indice": i, "message": obj})
|
2023-04-17 15:35:42 +02:00
|
|
|
else:
|
2023-07-27 18:00:40 +02:00
|
|
|
success.append({"indice": i, "message": obj})
|
2023-06-30 15:34:50 +02:00
|
|
|
scass.simple_invalidate_cache(data, etud.id)
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
db.session.commit()
|
|
|
|
|
2023-05-17 21:16:23 +02:00
|
|
|
return {"errors": errors, "success": success}
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
@bp.route("/assiduites/create", methods=["POST"])
|
|
|
|
@api_web_bp.route("/assiduites/create", methods=["POST"])
|
|
|
|
@scodoc
|
2023-05-17 21:16:23 +02:00
|
|
|
@as_json
|
2023-04-17 15:35:42 +02:00
|
|
|
@login_required
|
2023-09-29 21:17:31 +02:00
|
|
|
@permission_required(Permission.AbsChange)
|
2023-04-17 15:35:42 +02:00
|
|
|
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")
|
|
|
|
|
2023-07-27 18:00:40 +02:00
|
|
|
errors: list = []
|
|
|
|
success: list = []
|
2023-04-17 15:35:42 +02:00
|
|
|
for i, data in enumerate(create_list):
|
|
|
|
etud: Identite = Identite.query.filter_by(id=data["etudid"]).first()
|
|
|
|
if etud is None:
|
2023-07-27 18:00:40 +02:00
|
|
|
errors.append({"indice": i, "message": "Cet étudiant n'existe pas."})
|
2023-04-17 15:35:42 +02:00
|
|
|
continue
|
2023-10-11 14:45:06 +02:00
|
|
|
if g.scodoc_dept is None and etud.dept_id is not None:
|
2023-10-10 22:52:00 +02:00
|
|
|
# route sans département
|
|
|
|
set_sco_dept(etud.departement.acronym)
|
2023-04-17 15:35:42 +02:00
|
|
|
|
2023-12-06 02:04:10 +01:00
|
|
|
code, obj = _create_one(data, etud)
|
2023-04-17 15:35:42 +02:00
|
|
|
if code == 404:
|
2023-07-27 18:00:40 +02:00
|
|
|
errors.append({"indice": i, "message": obj})
|
2023-04-17 15:35:42 +02:00
|
|
|
else:
|
2023-07-27 18:00:40 +02:00
|
|
|
success.append({"indice": i, "message": obj})
|
2023-06-30 15:34:50 +02:00
|
|
|
scass.simple_invalidate_cache(data)
|
2023-04-17 15:35:42 +02:00
|
|
|
|
2023-05-17 21:16:23 +02:00
|
|
|
return {"errors": errors, "success": success}
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
|
2023-12-06 02:04:10 +01:00
|
|
|
def _create_one(
|
2023-04-17 15:35:42 +02:00
|
|
|
data: dict,
|
|
|
|
etud: Identite,
|
|
|
|
) -> tuple[int, object]:
|
2023-10-27 16:05:40 +02:00
|
|
|
"""
|
2023-12-06 02:04:10 +01:00
|
|
|
Création d'une assiduité à partir d'un dict
|
2023-10-27 16:05:40 +02:00
|
|
|
|
2023-12-06 02:04:10 +01:00
|
|
|
Cette fonction vérifie les données du dict (qui vient du JSON API)
|
2023-10-27 16:05:40 +02:00
|
|
|
|
|
|
|
Puis crée l'assiduité si la représentation est valide.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
data (dict): représentation json d'une assiduité
|
|
|
|
etud (Identite): l'étudiant concerné par l'assiduité
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
tuple[int, object]: code, objet
|
|
|
|
code : 200 si réussi 404 sinon
|
|
|
|
objet : dict{assiduite_id:?} si réussi str"message d'erreur" sinon
|
|
|
|
"""
|
|
|
|
|
2023-04-17 15:35:42 +02:00
|
|
|
errors: list[str] = []
|
|
|
|
|
|
|
|
# -- vérifications de l'objet json --
|
|
|
|
# cas 1 : ETAT
|
2023-10-27 16:05:40 +02:00
|
|
|
etat: str = data.get("etat", None)
|
2023-04-17 15:35:42 +02:00
|
|
|
if etat is None:
|
|
|
|
errors.append("param 'etat': manquant")
|
|
|
|
elif not scu.EtatAssiduite.contains(etat):
|
|
|
|
errors.append("param 'etat': invalide")
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
etat: scu.EtatAssiduite = scu.EtatAssiduite.get(etat)
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
# cas 2 : date_debut
|
2023-10-27 16:05:40 +02:00
|
|
|
date_debut: str = data.get("date_debut", None)
|
2023-04-17 15:35:42 +02:00
|
|
|
if date_debut is None:
|
|
|
|
errors.append("param 'date_debut': manquant")
|
2023-10-27 16:05:40 +02:00
|
|
|
# Conversion de la chaine de caractère en datetime (tz ou non)
|
|
|
|
deb: datetime = scu.is_iso_formated(date_debut, convert=True)
|
|
|
|
# si chaine invalide
|
2023-04-17 15:35:42 +02:00
|
|
|
if deb is None:
|
|
|
|
errors.append("param 'date_debut': format invalide")
|
2023-10-27 16:05:40 +02:00
|
|
|
# Si datetime sans timezone
|
2023-10-11 14:45:06 +02:00
|
|
|
elif deb.tzinfo is None:
|
2023-10-27 16:05:40 +02:00
|
|
|
# Mise à jour de la timezone avec celle du serveur
|
|
|
|
deb: datetime = scu.localize_datetime(deb)
|
2023-04-17 15:35:42 +02:00
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# cas 3 : date_fin (Même fonctionnement ^ )
|
|
|
|
date_fin: str = data.get("date_fin", None)
|
2023-04-17 15:35:42 +02:00
|
|
|
if date_fin is None:
|
|
|
|
errors.append("param 'date_fin': manquant")
|
2023-10-27 16:05:40 +02:00
|
|
|
fin: datetime = scu.is_iso_formated(date_fin, convert=True)
|
2023-04-17 15:35:42 +02:00
|
|
|
if fin is None:
|
|
|
|
errors.append("param 'date_fin': format invalide")
|
2023-10-11 14:45:06 +02:00
|
|
|
elif fin.tzinfo is None:
|
2023-10-27 16:05:40 +02:00
|
|
|
fin: datetime = scu.localize_datetime(fin)
|
2023-04-17 15:35:42 +02:00
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# cas 4 : desc
|
2023-04-17 15:35:42 +02:00
|
|
|
desc: str = data.get("desc", None)
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# cas 5 : external data
|
|
|
|
external_data: dict = data.get("external_data", None)
|
2023-08-11 16:14:29 +02:00
|
|
|
if external_data is not None:
|
2023-07-31 09:41:32 +02:00
|
|
|
if not isinstance(external_data, dict):
|
|
|
|
errors.append("param 'external_data' : n'est pas un objet JSON")
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# cas 6 : moduleimpl_id
|
2023-09-04 15:05:17 +02:00
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# On récupère le moduleimpl
|
2023-09-04 15:05:17 +02:00
|
|
|
moduleimpl_id = data.get("moduleimpl_id", False)
|
|
|
|
moduleimpl: ModuleImpl = None
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# On vérifie si le moduleimpl existe (uniquement s'il a été renseigné)
|
2023-09-05 09:25:51 +02:00
|
|
|
if moduleimpl_id not in [False, None, "", "-1"]:
|
2023-10-27 16:05:40 +02:00
|
|
|
# Si le moduleimpl n'est pas "autre" alors on vérifie si l'id est valide
|
2023-09-04 15:05:17 +02:00
|
|
|
if moduleimpl_id != "autre":
|
2023-09-05 09:25:51 +02:00
|
|
|
try:
|
|
|
|
moduleimpl = ModuleImpl.query.filter_by(id=int(moduleimpl_id)).first()
|
|
|
|
except ValueError:
|
|
|
|
moduleimpl = None
|
2023-09-04 15:05:17 +02:00
|
|
|
if moduleimpl is None:
|
|
|
|
errors.append("param 'moduleimpl_id': invalide")
|
|
|
|
else:
|
2023-10-27 16:05:40 +02:00
|
|
|
# Sinon on met à none le moduleimpl
|
|
|
|
# et on ajoute dans external data
|
|
|
|
# le module "autre"
|
2023-09-04 15:05:17 +02:00
|
|
|
moduleimpl_id = None
|
2023-10-27 16:05:40 +02:00
|
|
|
external_data: dict = external_data if external_data is not None else {}
|
2023-09-04 15:05:17 +02:00
|
|
|
external_data["module"] = "Autre"
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Si il y a des erreurs alors on ne crée pas l'assiduité et on renvoie les erreurs
|
2023-04-17 15:35:42 +02:00
|
|
|
if errors:
|
2023-10-27 16:05:40 +02:00
|
|
|
# Construit une chaine de caractère avec les erreurs séparées par `,`
|
2023-04-17 15:35:42 +02:00
|
|
|
err: str = ", ".join(errors)
|
2023-10-27 16:05:40 +02:00
|
|
|
# 404 représente le code d'erreur et err la chaine nouvellement créée
|
2023-04-17 15:35:42 +02:00
|
|
|
return (404, err)
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# SI TOUT EST OK
|
2023-04-17 15:35:42 +02:00
|
|
|
try:
|
2023-10-27 16:05:40 +02:00
|
|
|
# On essaye de créer l'assiduité
|
2023-04-17 15:35:42 +02:00
|
|
|
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,
|
2023-07-31 09:41:32 +02:00
|
|
|
external_data=external_data,
|
2023-10-09 23:01:19 +02:00
|
|
|
notify_mail=True,
|
2023-04-17 15:35:42 +02:00
|
|
|
)
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# create_assiduite générera des ScoValueError si jamais il y a un autre problème
|
|
|
|
# - Etudiant non inscrit dans le module
|
|
|
|
# - module obligatoire
|
|
|
|
# - Assiduité conflictuelles
|
|
|
|
|
|
|
|
# Si tout s'est bien passé on ajoute l'assiduité à la session
|
|
|
|
# et on retourne un code 200 avec un objet possèdant l'assiduite_id
|
2023-04-17 15:35:42 +02:00
|
|
|
db.session.add(nouv_assiduite)
|
|
|
|
db.session.commit()
|
|
|
|
return (200, {"assiduite_id": nouv_assiduite.id})
|
|
|
|
except ScoValueError as excp:
|
2023-10-26 17:28:36 +02:00
|
|
|
# ici on utilise pas json_error car on doit renvoyer status, message
|
2023-10-27 16:05:40 +02:00
|
|
|
# Ici json_error ne peut être utilisé car il terminerai le processus de création
|
|
|
|
# Cela voudrait dire qu'une seule erreur dans une assiduité imposerait de
|
|
|
|
# tout refaire à partir de l'erreur.
|
|
|
|
|
|
|
|
# renvoit un code 404 et le message d'erreur de la ScoValueError
|
2023-10-26 17:28:36 +02:00
|
|
|
return 404, excp.args[0]
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
@bp.route("/assiduite/delete", methods=["POST"])
|
|
|
|
@api_web_bp.route("/assiduite/delete", methods=["POST"])
|
|
|
|
@login_required
|
|
|
|
@scodoc
|
2023-05-17 21:16:23 +02:00
|
|
|
@as_json
|
2023-09-29 21:17:31 +02:00
|
|
|
@permission_required(Permission.AbsChange)
|
2023-04-17 15:35:42 +02:00
|
|
|
def assiduite_delete():
|
|
|
|
"""
|
|
|
|
Suppression d'une assiduité à partir de son id
|
|
|
|
|
|
|
|
Forme des données envoyées :
|
|
|
|
|
|
|
|
[
|
|
|
|
<assiduite_id:int>,
|
|
|
|
...
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
"""
|
2023-10-27 16:05:40 +02:00
|
|
|
# Récupération des ids envoyés dans la liste
|
2023-04-17 15:35:42 +02:00
|
|
|
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")
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Préparation du retour json
|
2023-07-27 18:00:40 +02:00
|
|
|
output = {"errors": [], "success": []}
|
2023-04-17 15:35:42 +02:00
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Pour chaque assiduite_id on essaye de supprimer l'assiduité
|
|
|
|
for i, assiduite_id in enumerate(assiduites_list):
|
2023-12-06 02:04:10 +01:00
|
|
|
# De la même façon que "_create_one"
|
2023-10-27 16:05:40 +02:00
|
|
|
# Ici le code est soit 200 si réussi ou 404 si raté
|
|
|
|
# Le message est le message d'erreur si erreur
|
|
|
|
code, msg = _delete_one(assiduite_id)
|
2023-04-17 15:35:42 +02:00
|
|
|
if code == 404:
|
2023-07-27 18:00:40 +02:00
|
|
|
output["errors"].append({"indice": i, "message": msg})
|
2023-04-17 15:35:42 +02:00
|
|
|
else:
|
2023-07-27 18:00:40 +02:00
|
|
|
output["success"].append({"indice": i, "message": "OK"})
|
2023-06-30 15:34:50 +02:00
|
|
|
|
2023-04-17 15:35:42 +02:00
|
|
|
db.session.commit()
|
2023-05-17 21:16:23 +02:00
|
|
|
return output
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
def _delete_one(assiduite_id: int) -> tuple[int, str]:
|
|
|
|
"""
|
|
|
|
_delete_singular Supprime une assiduité à partir de son id
|
|
|
|
|
|
|
|
Args:
|
|
|
|
assiduite_id (int): l'identifiant de l'assiduité
|
|
|
|
Returns:
|
|
|
|
tuple[int, str]: code, message
|
|
|
|
code : 200 si réussi, 404 sinon
|
|
|
|
message : OK si réussi, le message d'erreur sinon
|
|
|
|
"""
|
2023-04-17 15:35:42 +02:00
|
|
|
assiduite_unique: Assiduite = Assiduite.query.filter_by(id=assiduite_id).first()
|
|
|
|
if assiduite_unique is None:
|
2023-10-27 16:05:40 +02:00
|
|
|
# Ici json_error ne peut être utilisé car il terminerai le processus de création
|
|
|
|
# Cela voudrait dire qu'une seule erreur d'id imposerait de
|
|
|
|
# tout refaire à partir de l'erreur.
|
2023-10-26 17:28:36 +02:00
|
|
|
return 404, "Assiduite non existante"
|
2023-10-27 16:05:40 +02:00
|
|
|
|
|
|
|
# Mise à jour du g.scodoc_dept si la route est sans département
|
2023-10-26 15:52:53 +02:00
|
|
|
if g.scodoc_dept is None and assiduite_unique.etudiant.dept_id is not None:
|
|
|
|
# route sans département
|
|
|
|
set_sco_dept(assiduite_unique.etudiant.departement.acronym)
|
2023-10-27 16:05:40 +02:00
|
|
|
|
|
|
|
# Récupération de la version dict de l'assiduité
|
|
|
|
# Pour invalider le cache
|
|
|
|
assi_dict = assiduite_unique.to_dict()
|
|
|
|
|
|
|
|
# Suppression de l'assiduité et LOG
|
2023-07-26 16:00:23 +02:00
|
|
|
log(f"delete_assiduite: {assiduite_unique.etudiant.id} {assiduite_unique}")
|
2023-07-26 13:27:57 +02:00
|
|
|
Scolog.logdb(
|
|
|
|
method="delete_assiduite",
|
|
|
|
etudid=assiduite_unique.etudiant.id,
|
|
|
|
msg=f"assiduité: {assiduite_unique}",
|
|
|
|
)
|
2023-10-27 16:05:40 +02:00
|
|
|
db.session.delete(assiduite_unique)
|
|
|
|
# Invalidation du cache
|
|
|
|
scass.simple_invalidate_cache(assi_dict)
|
2023-10-26 17:28:36 +02:00
|
|
|
return 200, "OK"
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
@bp.route("/assiduite/<int:assiduite_id>/edit", methods=["POST"])
|
|
|
|
@api_web_bp.route("/assiduite/<int:assiduite_id>/edit", methods=["POST"])
|
|
|
|
@login_required
|
|
|
|
@scodoc
|
2023-05-17 21:16:23 +02:00
|
|
|
@as_json
|
2023-09-29 21:17:31 +02:00
|
|
|
@permission_required(Permission.AbsChange)
|
2023-04-17 15:35:42 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
"""
|
2023-10-27 16:05:40 +02:00
|
|
|
|
|
|
|
# Récupération de l'assiduité à modifier
|
2024-01-05 13:42:55 +01:00
|
|
|
assiduite_unique: Assiduite = Assiduite.query.filter_by(id=assiduite_id).first()
|
|
|
|
if assiduite_unique is None:
|
|
|
|
return json_error(404, "Assiduité non existante")
|
2023-10-27 16:05:40 +02:00
|
|
|
# Récupération des valeurs à modifier
|
2023-04-17 15:35:42 +02:00
|
|
|
data = request.get_json(force=True)
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Code 200 si modification réussie
|
|
|
|
# Code 404 si raté + message d'erreur
|
|
|
|
code, obj = _edit_one(assiduite_unique, data)
|
2023-04-17 15:35:42 +02:00
|
|
|
|
2023-09-04 15:05:17 +02:00
|
|
|
if code == 404:
|
|
|
|
return json_error(404, obj)
|
2023-04-17 15:35:42 +02:00
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Mise à jour de l'assiduité et LOG
|
2023-07-26 16:00:23 +02:00
|
|
|
log(f"assiduite_edit: {assiduite_unique.etudiant.id} {assiduite_unique}")
|
2023-07-26 13:27:57 +02:00
|
|
|
Scolog.logdb(
|
|
|
|
"assiduite_edit",
|
|
|
|
assiduite_unique.etudiant.id,
|
|
|
|
msg=f"assiduite: modif {assiduite_unique}",
|
|
|
|
)
|
2023-04-17 15:35:42 +02:00
|
|
|
db.session.commit()
|
2024-04-22 09:20:05 +02:00
|
|
|
try:
|
|
|
|
scass.simple_invalidate_cache(assiduite_unique.to_dict())
|
|
|
|
except ObjectDeletedError:
|
|
|
|
return json_error(404, "Assiduité supprimée / inexistante")
|
2023-06-30 15:34:50 +02:00
|
|
|
|
2023-05-17 21:16:23 +02:00
|
|
|
return {"OK": True}
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
@bp.route("/assiduites/edit", methods=["POST"])
|
|
|
|
@api_web_bp.route("/assiduites/edit", methods=["POST"])
|
|
|
|
@login_required
|
|
|
|
@scodoc
|
2023-05-17 21:16:23 +02:00
|
|
|
@as_json
|
2023-09-29 21:17:31 +02:00
|
|
|
@permission_required(Permission.AbsChange)
|
2023-04-17 15:35:42 +02:00
|
|
|
def assiduites_edit():
|
|
|
|
"""
|
2023-07-26 13:27:57 +02:00
|
|
|
Edition de plusieurs assiduités
|
2023-04-17 15:35:42 +02:00
|
|
|
La requête doit avoir un content type "application/json":
|
2023-07-26 13:27:57 +02:00
|
|
|
[
|
|
|
|
{
|
|
|
|
"assiduite_id" : int,
|
|
|
|
"etat"?: str,
|
|
|
|
"moduleimpl_id"?: int
|
|
|
|
"desc"?: str
|
|
|
|
"est_just"?: bool
|
|
|
|
}
|
|
|
|
]
|
2023-04-17 15:35:42 +02:00
|
|
|
"""
|
|
|
|
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")
|
|
|
|
|
2023-07-27 18:00:40 +02:00
|
|
|
errors: list[dict] = []
|
|
|
|
success: list[dict] = []
|
2023-04-17 15:35:42 +02:00
|
|
|
for i, data in enumerate(edit_list):
|
|
|
|
assi: Identite = Assiduite.query.filter_by(id=data["assiduite_id"]).first()
|
|
|
|
if assi is None:
|
2023-07-27 18:00:40 +02:00
|
|
|
errors.append(
|
|
|
|
{
|
|
|
|
"indice": i,
|
|
|
|
"message": f"assiduité {data['assiduite_id']} n'existe pas.",
|
|
|
|
}
|
|
|
|
)
|
2023-04-17 15:35:42 +02:00
|
|
|
continue
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
code, obj = _edit_one(assi, data)
|
2023-07-27 18:00:40 +02:00
|
|
|
obj_retour = {
|
|
|
|
"indice": i,
|
|
|
|
"message": obj,
|
|
|
|
}
|
2023-04-17 15:35:42 +02:00
|
|
|
if code == 404:
|
2023-07-27 18:00:40 +02:00
|
|
|
errors.append(obj_retour)
|
2023-04-17 15:35:42 +02:00
|
|
|
else:
|
2023-07-27 18:00:40 +02:00
|
|
|
success.append(obj_retour)
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
db.session.commit()
|
|
|
|
|
2023-05-17 21:16:23 +02:00
|
|
|
return {"errors": errors, "success": success}
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
def _edit_one(assiduite_unique: Assiduite, data: dict) -> tuple[int, str]:
|
|
|
|
"""
|
|
|
|
_edit_singular Modifie une assiduité à partir de données JSON
|
|
|
|
|
|
|
|
Args:
|
|
|
|
assiduite_unique (Assiduite): l'assiduité à modifier
|
|
|
|
data (dict): les nouvelles données
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
tuple[int,str]: code, message
|
|
|
|
code : 200 si réussi, 404 sinon
|
|
|
|
message : OK si réussi, message d'erreur sinon
|
|
|
|
"""
|
|
|
|
|
|
|
|
# Mise à jour du g.scodoc_dept en cas de route sans département
|
2023-10-26 15:52:53 +02:00
|
|
|
if g.scodoc_dept is None and assiduite_unique.etudiant.dept_id is not None:
|
|
|
|
# route sans département
|
|
|
|
set_sco_dept(assiduite_unique.etudiant.departement.acronym)
|
2023-10-27 16:05:40 +02:00
|
|
|
|
2023-04-17 15:35:42 +02:00
|
|
|
errors: list[str] = []
|
|
|
|
|
|
|
|
# Vérifications de data
|
|
|
|
|
|
|
|
# Cas 1 : Etat
|
|
|
|
if data.get("etat") is not None:
|
2023-10-27 16:05:40 +02:00
|
|
|
etat: scu.EtatAssiduite = scu.EtatAssiduite.get(data.get("etat"))
|
2023-04-17 15:35:42 +02:00
|
|
|
if etat is None:
|
|
|
|
errors.append("param 'etat': invalide")
|
|
|
|
else:
|
2023-10-27 16:05:40 +02:00
|
|
|
# Mise à jour de l'état
|
2023-04-17 15:35:42 +02:00
|
|
|
assiduite_unique.etat = etat
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Cas 2 : external_data
|
|
|
|
external_data: dict = data.get("external_data")
|
2023-09-04 15:05:17 +02:00
|
|
|
if external_data is not None:
|
|
|
|
if not isinstance(external_data, dict):
|
|
|
|
errors.append("param 'external_data' : n'est pas un objet JSON")
|
|
|
|
else:
|
2023-10-27 16:05:40 +02:00
|
|
|
# Mise à jour de l'external data
|
2023-09-04 15:05:17 +02:00
|
|
|
assiduite_unique.external_data = external_data
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Cas 3 : Moduleimpl_id
|
2023-04-17 15:35:42 +02:00
|
|
|
moduleimpl_id = data.get("moduleimpl_id", False)
|
|
|
|
moduleimpl: ModuleImpl = None
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# False si on modifie pas le moduleimpl
|
2023-04-17 15:35:42 +02:00
|
|
|
if moduleimpl_id is not False:
|
2023-10-27 16:05:40 +02:00
|
|
|
# Si le module n'est pas nul
|
2023-09-05 09:25:51 +02:00
|
|
|
if moduleimpl_id not in [None, "", "-1"]:
|
2023-10-27 16:05:40 +02:00
|
|
|
# Gestion du module Autre
|
2023-09-04 15:05:17 +02:00
|
|
|
if moduleimpl_id == "autre":
|
2023-10-27 16:05:40 +02:00
|
|
|
# module autre = moduleimpl_id:None + external_data["module"]:"Autre"
|
2023-09-04 15:05:17 +02:00
|
|
|
assiduite_unique.moduleimpl_id = None
|
2023-10-27 16:05:40 +02:00
|
|
|
external_data: dict = (
|
2023-09-04 15:05:17 +02:00
|
|
|
external_data
|
|
|
|
if external_data is not None and isinstance(external_data, dict)
|
|
|
|
else assiduite_unique.external_data
|
|
|
|
)
|
2023-10-27 16:05:40 +02:00
|
|
|
external_data: dict = external_data if external_data is not None else {}
|
2023-09-04 15:05:17 +02:00
|
|
|
external_data["module"] = "Autre"
|
|
|
|
assiduite_unique.external_data = external_data
|
2023-04-17 15:35:42 +02:00
|
|
|
else:
|
2023-10-27 16:05:40 +02:00
|
|
|
# Vérification de l'id et récupération de l'objet ModuleImpl
|
2023-09-05 09:25:51 +02:00
|
|
|
try:
|
|
|
|
moduleimpl = ModuleImpl.query.filter_by(
|
|
|
|
id=int(moduleimpl_id)
|
|
|
|
).first()
|
|
|
|
except ValueError:
|
|
|
|
moduleimpl = None
|
|
|
|
|
2023-09-04 15:05:17 +02:00
|
|
|
if moduleimpl is None:
|
|
|
|
errors.append("param 'moduleimpl_id': invalide")
|
2023-04-17 15:35:42 +02:00
|
|
|
else:
|
2023-12-08 13:42:24 +01:00
|
|
|
if not moduleimpl.est_inscrit(assiduite_unique.etudiant):
|
2023-09-04 15:05:17 +02:00
|
|
|
errors.append("param 'moduleimpl_id': etud non inscrit")
|
|
|
|
else:
|
2023-10-27 16:05:40 +02:00
|
|
|
# Mise à jour du moduleimpl
|
2023-09-04 15:05:17 +02:00
|
|
|
assiduite_unique.moduleimpl_id = moduleimpl_id
|
2023-04-17 15:35:42 +02:00
|
|
|
else:
|
2023-10-27 16:05:40 +02:00
|
|
|
# Vérification du force module en cas de modification du moduleimpl en moduleimpl nul
|
|
|
|
# Récupération du formsemestre lié à l'assiduité
|
2023-10-26 15:52:53 +02:00
|
|
|
formsemestre: FormSemestre = get_formsemestre_from_data(
|
|
|
|
assiduite_unique.to_dict()
|
|
|
|
)
|
|
|
|
force: bool
|
|
|
|
|
|
|
|
if formsemestre:
|
|
|
|
force = scu.is_assiduites_module_forced(formsemestre_id=formsemestre.id)
|
|
|
|
else:
|
2023-12-08 13:42:24 +01:00
|
|
|
force = scu.is_assiduites_module_forced(
|
|
|
|
dept_id=assiduite_unique.etudiant.dept_id
|
|
|
|
)
|
2023-10-26 15:52:53 +02:00
|
|
|
|
2023-10-27 10:33:37 +02:00
|
|
|
external_data = (
|
|
|
|
external_data
|
|
|
|
if external_data is not None and isinstance(external_data, dict)
|
|
|
|
else assiduite_unique.external_data
|
|
|
|
)
|
|
|
|
|
2023-12-05 21:04:38 +01:00
|
|
|
if force and not (
|
|
|
|
external_data is not None and external_data.get("module", False) != ""
|
|
|
|
):
|
2023-10-26 15:52:53 +02:00
|
|
|
errors.append(
|
|
|
|
"param 'moduleimpl_id' : le moduleimpl_id ne peut pas être nul"
|
|
|
|
)
|
2023-04-17 15:35:42 +02:00
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Cas 4 : desc
|
|
|
|
desc: str = data.get("desc", False)
|
2023-04-17 15:35:42 +02:00
|
|
|
if desc is not False:
|
2023-09-11 09:16:50 +02:00
|
|
|
assiduite_unique.description = desc
|
2023-04-17 15:35:42 +02:00
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Cas 5 : est_just
|
2023-09-11 09:05:29 +02:00
|
|
|
if assiduite_unique.etat == scu.EtatAssiduite.PRESENT:
|
|
|
|
assiduite_unique.est_just = False
|
|
|
|
else:
|
|
|
|
assiduite_unique.est_just = (
|
|
|
|
len(
|
|
|
|
get_justifs_from_date(
|
|
|
|
assiduite_unique.etudiant.id,
|
|
|
|
assiduite_unique.date_debut,
|
|
|
|
assiduite_unique.date_fin,
|
|
|
|
valid=True,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
> 0
|
|
|
|
)
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
if errors:
|
2023-10-27 16:05:40 +02:00
|
|
|
# Retour des erreurs en une seule chaîne séparée par des `,`
|
2023-04-17 15:35:42 +02:00
|
|
|
err: str = ", ".join(errors)
|
|
|
|
return (404, err)
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Mise à jour de l'assiduité et LOG
|
2023-07-26 16:00:23 +02:00
|
|
|
log(f"_edit_singular: {assiduite_unique.etudiant.id} {assiduite_unique}")
|
2023-07-26 13:27:57 +02:00
|
|
|
Scolog.logdb(
|
|
|
|
"assiduite_edit",
|
|
|
|
assiduite_unique.etudiant.id,
|
|
|
|
msg=f"assiduite: modif {assiduite_unique}",
|
|
|
|
)
|
2023-04-17 15:35:42 +02:00
|
|
|
db.session.add(assiduite_unique)
|
2023-06-30 15:34:50 +02:00
|
|
|
scass.simple_invalidate_cache(assiduite_unique.to_dict())
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
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:
|
2023-09-04 09:08:40 +02:00
|
|
|
trues: tuple[str] = ("v", "t", "vrai", "true", "1")
|
|
|
|
falses: tuple[str] = ("f", "faux", "false", "0")
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2023-09-04 09:08:40 +02:00
|
|
|
# cas 9 : split
|
|
|
|
|
|
|
|
split = requested.args.get("split", False)
|
|
|
|
if split is not False:
|
|
|
|
filtered["split"] = True
|
|
|
|
|
2023-04-17 15:35:42 +02:00
|
|
|
return (metric, filtered)
|
|
|
|
|
|
|
|
|
2023-08-14 01:08:04 +02:00
|
|
|
def _filter_manager(requested, assiduites_query: Query) -> Query:
|
2023-04-17 15:35:42 +02:00
|
|
|
"""
|
2023-10-27 16:05:40 +02:00
|
|
|
_filter_manager Retourne les assiduites entrées filtrées en fonction de la request
|
|
|
|
|
|
|
|
Args:
|
|
|
|
requested (request): La requête http
|
|
|
|
assiduites_query (Query): la query d'assiduités à filtrer
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Query: La query filtrée
|
2023-04-17 15:35:42 +02:00
|
|
|
"""
|
|
|
|
# cas 1 : etat assiduite
|
2023-10-27 16:05:40 +02:00
|
|
|
etat: str = requested.args.get("etat")
|
2023-04-17 15:35:42 +02:00
|
|
|
if etat is not None:
|
|
|
|
assiduites_query = scass.filter_assiduites_by_etat(assiduites_query, etat)
|
|
|
|
|
|
|
|
# cas 2 : date de début
|
2023-10-27 16:05:40 +02:00
|
|
|
deb: str = requested.args.get("date_debut", "").replace(" ", "+")
|
|
|
|
deb: datetime = scu.is_iso_formated(
|
|
|
|
deb, True
|
|
|
|
) # transformation de la chaine en datetime
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
# cas 3 : date de fin
|
2023-10-27 16:05:40 +02:00
|
|
|
fin: str = requested.args.get("date_fin", "").replace(" ", "+")
|
|
|
|
fin: datetime = scu.is_iso_formated(
|
|
|
|
fin, True
|
|
|
|
) # transformation de la chaine en datetime
|
2023-04-17 15:35:42 +02:00
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# Pour filtrer les dates il faut forcement avoir les deux bornes
|
|
|
|
# [date_debut : date_fin]
|
2023-04-17 15:35:42 +02:00
|
|
|
if (deb, fin) != (None, None):
|
2023-08-14 01:08:04 +02:00
|
|
|
assiduites_query: Query = scass.filter_by_date(
|
2023-04-17 15:35:42 +02:00
|
|
|
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()
|
2023-08-09 09:57:47 +02:00
|
|
|
assiduites_query = scass.filter_by_formsemestre(
|
|
|
|
assiduites_query, Assiduite, formsemestre
|
|
|
|
)
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
# cas 6 : est_just
|
|
|
|
|
|
|
|
est_just: str = requested.args.get("est_just")
|
|
|
|
if est_just is not None:
|
2023-09-04 09:08:40 +02:00
|
|
|
trues: tuple[str] = ("v", "t", "vrai", "true", "1")
|
|
|
|
falses: tuple[str] = ("f", "faux", "false", "0")
|
2023-04-17 15:35:42 +02:00
|
|
|
|
|
|
|
if est_just.lower() in trues:
|
2023-08-14 01:08:04 +02:00
|
|
|
assiduites_query: Query = scass.filter_assiduites_by_est_just(
|
2023-04-17 15:35:42 +02:00
|
|
|
assiduites_query, True
|
|
|
|
)
|
|
|
|
elif est_just.lower() in falses:
|
2023-08-14 01:08:04 +02:00
|
|
|
assiduites_query: Query = scass.filter_assiduites_by_est_just(
|
2023-04-17 15:35:42 +02:00
|
|
|
assiduites_query, False
|
|
|
|
)
|
|
|
|
|
|
|
|
# cas 8 : user_id
|
|
|
|
|
|
|
|
user_id = requested.args.get("user_id", False)
|
|
|
|
if user_id is not False:
|
2023-08-14 01:08:04 +02:00
|
|
|
assiduites_query: Query = scass.filter_by_user_id(assiduites_query, user_id)
|
2023-04-17 15:35:42 +02:00
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# cas 9 : order (renvoie la query ordonnée en "date début Décroissante")
|
2023-09-11 08:31:09 +02:00
|
|
|
order = requested.args.get("order", None)
|
|
|
|
if order is not None:
|
|
|
|
assiduites_query: Query = assiduites_query.order_by(Assiduite.date_debut.desc())
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
# cas 10 : courant (Ne renvoie que les assiduités de l'année courante)
|
2023-09-12 09:37:03 +02:00
|
|
|
courant = requested.args.get("courant", None)
|
|
|
|
if courant is not None:
|
|
|
|
annee: int = scu.annee_scolaire()
|
|
|
|
|
|
|
|
assiduites_query: Query = assiduites_query.filter(
|
2023-12-29 13:57:44 +01:00
|
|
|
Assiduite.date_debut >= scu.date_debut_annee_scolaire(annee),
|
|
|
|
Assiduite.date_fin <= scu.date_fin_annee_scolaire(annee),
|
2023-09-12 09:37:03 +02:00
|
|
|
)
|
|
|
|
|
2023-04-17 15:35:42 +02:00
|
|
|
return assiduites_query
|
2023-07-30 17:09:21 +02:00
|
|
|
|
|
|
|
|
2023-10-27 16:05:40 +02:00
|
|
|
def _with_justifs(assi: dict):
|
|
|
|
"""
|
|
|
|
_with_justifs ajoute la liste des justificatifs à l'assiduité
|
|
|
|
|
|
|
|
Condition : `with_justifs` doit se trouver dans les paramètres de la requête
|
|
|
|
Args:
|
|
|
|
assi (dict): un dictionnaire représentant une assiduité
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
dict: l'assiduité avec les justificatifs ajoutés
|
|
|
|
"""
|
2023-07-30 17:09:21 +02:00
|
|
|
if request.args.get("with_justifs") is None:
|
|
|
|
return assi
|
2023-08-09 09:57:47 +02:00
|
|
|
assi["justificatifs"] = get_assiduites_justif(assi["assiduite_id"], True)
|
2023-07-30 17:09:21 +02:00
|
|
|
return assi
|