forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -163,23 +163,22 @@ def count_assiduites(
|
|||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# 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)
|
|
||||||
|
|
||||||
|
# Récupération de l'étudiant
|
||||||
etud: Identite = tools.get_etud(etudid, nip, ine)
|
etud: Identite = tools.get_etud(etudid, nip, ine)
|
||||||
|
# Vérification que l'étudiant existe
|
||||||
if etud is None:
|
if etud is None:
|
||||||
return json_error(
|
return json_error(
|
||||||
404,
|
404,
|
||||||
message="étudiant inconnu",
|
message="étudiant inconnu",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Les filtres qui seront appliqués au comptage (type, date, etudid...)
|
||||||
filtered: dict[str, object] = {}
|
filtered: dict[str, object] = {}
|
||||||
|
# la métrique du comptage (all, demi, heure, journee)
|
||||||
metric: str = "all"
|
metric: str = "all"
|
||||||
|
|
||||||
|
# Si la requête a des paramètres
|
||||||
if with_query:
|
if with_query:
|
||||||
metric, filtered = _count_manager(request)
|
metric, filtered = _count_manager(request)
|
||||||
|
|
||||||
@ -254,11 +253,7 @@ def assiduites(etudid: int = None, nip=None, ine=None, with_query: bool = False)
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# query = Identite.query.filter_by(id=etudid)
|
# Récupération de l'étudiant
|
||||||
# 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)
|
etud: Identite = tools.get_etud(etudid, nip, ine)
|
||||||
|
|
||||||
if etud is None:
|
if etud is None:
|
||||||
@ -266,15 +261,23 @@ def assiduites(etudid: int = None, nip=None, ine=None, with_query: bool = False)
|
|||||||
404,
|
404,
|
||||||
message="étudiant inconnu",
|
message="étudiant inconnu",
|
||||||
)
|
)
|
||||||
|
# Récupération des assiduités de l'étudiant
|
||||||
assiduites_query: Query = etud.assiduites
|
assiduites_query: Query = etud.assiduites
|
||||||
|
|
||||||
|
# Filtrage des assiduités en fonction des paramètres de la requête
|
||||||
if with_query:
|
if with_query:
|
||||||
assiduites_query = _filter_manager(request, assiduites_query)
|
assiduites_query = _filter_manager(request, assiduites_query)
|
||||||
|
|
||||||
|
# Préparation de la réponse json
|
||||||
|
|
||||||
data_set: list[dict] = []
|
data_set: list[dict] = []
|
||||||
|
|
||||||
for ass in assiduites_query.all():
|
for ass in assiduites_query.all():
|
||||||
|
# conversion Assiduite -> Dict
|
||||||
data = ass.to_dict(format_api=True)
|
data = ass.to_dict(format_api=True)
|
||||||
|
# Ajout des justificatifs (ou non dépendamment de la requête)
|
||||||
data = _with_justifs(data)
|
data = _with_justifs(data)
|
||||||
|
# Ajout de l'assiduité dans la liste de retour
|
||||||
data_set.append(data)
|
data_set.append(data)
|
||||||
|
|
||||||
return data_set
|
return data_set
|
||||||
@ -326,6 +329,7 @@ def assiduites_group(with_query: bool = False):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Récupération des étudiants dans la requête
|
||||||
etuds = request.args.get("etudids", "")
|
etuds = request.args.get("etudids", "")
|
||||||
etuds = etuds.split(",")
|
etuds = etuds.split(",")
|
||||||
try:
|
try:
|
||||||
@ -333,6 +337,7 @@ def assiduites_group(with_query: bool = False):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
return json_error(404, "Le champs etudids n'est pas correctement formé")
|
return json_error(404, "Le champs etudids n'est pas correctement formé")
|
||||||
|
|
||||||
|
# Vérification que tous les étudiants sont du même département
|
||||||
query = Identite.query.filter(Identite.id.in_(etuds))
|
query = Identite.query.filter(Identite.id.in_(etuds))
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
query = query.filter_by(dept_id=g.scodoc_dept_id)
|
query = query.filter_by(dept_id=g.scodoc_dept_id)
|
||||||
@ -342,15 +347,21 @@ def assiduites_group(with_query: bool = False):
|
|||||||
404,
|
404,
|
||||||
"Tous les étudiants ne sont pas dans le même département et/ou n'existe pas.",
|
"Tous les étudiants ne sont pas dans le même département et/ou n'existe pas.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Récupération de toutes les assiduités liés aux étudiants
|
||||||
assiduites_query = Assiduite.query.filter(Assiduite.etudid.in_(etuds))
|
assiduites_query = Assiduite.query.filter(Assiduite.etudid.in_(etuds))
|
||||||
|
|
||||||
|
# Filtrage des assiduités en fonction des filtres passés dans la requête
|
||||||
if with_query:
|
if with_query:
|
||||||
assiduites_query = _filter_manager(request, assiduites_query)
|
assiduites_query = _filter_manager(request, assiduites_query)
|
||||||
|
|
||||||
|
# Préparation de retour json
|
||||||
|
# Dict représentant chaque étudiant avec sa liste d'assiduité
|
||||||
data_set: dict[list[dict]] = {str(key): [] for key in etuds}
|
data_set: dict[list[dict]] = {str(key): [] for key in etuds}
|
||||||
for ass in assiduites_query.all():
|
for ass in assiduites_query.all():
|
||||||
data = ass.to_dict(format_api=True)
|
data = ass.to_dict(format_api=True)
|
||||||
data = _with_justifs(data)
|
data = _with_justifs(data)
|
||||||
|
# Ajout de l'assiduité dans la liste du bon étudiant
|
||||||
data_set.get(str(data["etudid"])).append(data)
|
data_set.get(str(data["etudid"])).append(data)
|
||||||
return data_set
|
return data_set
|
||||||
|
|
||||||
@ -375,20 +386,23 @@ def assiduites_group(with_query: bool = False):
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
def assiduites_formsemestre(formsemestre_id: int, with_query: bool = False):
|
def assiduites_formsemestre(formsemestre_id: int, with_query: bool = False):
|
||||||
"""Retourne toutes les assiduités du formsemestre"""
|
"""Retourne toutes les assiduités du formsemestre"""
|
||||||
|
|
||||||
|
# Récupération du formsemestre à partir du formsemestre_id
|
||||||
formsemestre: FormSemestre = None
|
formsemestre: FormSemestre = None
|
||||||
formsemestre_id = int(formsemestre_id)
|
formsemestre_id = int(formsemestre_id)
|
||||||
formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first()
|
formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first()
|
||||||
|
|
||||||
if formsemestre is None:
|
if formsemestre is None:
|
||||||
return json_error(404, "le paramètre 'formsemestre_id' n'existe pas")
|
return json_error(404, "le paramètre 'formsemestre_id' n'existe pas")
|
||||||
|
|
||||||
|
# Récupération des assiduités du formsemestre
|
||||||
assiduites_query = scass.filter_by_formsemestre(
|
assiduites_query = scass.filter_by_formsemestre(
|
||||||
Assiduite.query, Assiduite, formsemestre
|
Assiduite.query, Assiduite, formsemestre
|
||||||
)
|
)
|
||||||
|
# Filtrage en fonction des paramètres de la requête
|
||||||
if with_query:
|
if with_query:
|
||||||
assiduites_query = _filter_manager(request, assiduites_query)
|
assiduites_query = _filter_manager(request, assiduites_query)
|
||||||
|
|
||||||
|
# Préparation du retour JSON
|
||||||
data_set: list[dict] = []
|
data_set: list[dict] = []
|
||||||
for ass in assiduites_query.all():
|
for ass in assiduites_query.all():
|
||||||
data = ass.to_dict(format_api=True)
|
data = ass.to_dict(format_api=True)
|
||||||
@ -422,21 +436,28 @@ def count_assiduites_formsemestre(
|
|||||||
formsemestre_id: int = None, with_query: bool = False
|
formsemestre_id: int = None, with_query: bool = False
|
||||||
):
|
):
|
||||||
"""Comptage des assiduités du formsemestre"""
|
"""Comptage des assiduités du formsemestre"""
|
||||||
|
|
||||||
|
# Récupération du formsemestre à partir du formsemestre_id
|
||||||
formsemestre: FormSemestre = None
|
formsemestre: FormSemestre = None
|
||||||
formsemestre_id = int(formsemestre_id)
|
formsemestre_id = int(formsemestre_id)
|
||||||
formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first()
|
formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first()
|
||||||
|
|
||||||
if formsemestre is None:
|
if formsemestre is None:
|
||||||
return json_error(404, "le paramètre 'formsemestre_id' n'existe pas")
|
return json_error(404, "le paramètre 'formsemestre_id' n'existe pas")
|
||||||
|
|
||||||
|
# Récupération des étudiants du formsemestre
|
||||||
etuds = formsemestre.etuds.all()
|
etuds = formsemestre.etuds.all()
|
||||||
etuds_id = [etud.id for etud in etuds]
|
etuds_id = [etud.id for etud in etuds]
|
||||||
|
|
||||||
|
# Récupération des assiduités des étudiants du semestre
|
||||||
assiduites_query = Assiduite.query.filter(Assiduite.etudid.in_(etuds_id))
|
assiduites_query = Assiduite.query.filter(Assiduite.etudid.in_(etuds_id))
|
||||||
|
# Filtrage des assiduités en fonction des dates du semestre
|
||||||
assiduites_query = scass.filter_by_formsemestre(
|
assiduites_query = scass.filter_by_formsemestre(
|
||||||
assiduites_query, Assiduite, formsemestre
|
assiduites_query, Assiduite, formsemestre
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Gestion de la métrique de comptage (all,demi,heure,journee)
|
||||||
metric: str = "all"
|
metric: str = "all"
|
||||||
|
# Gestion du filtre (en fonction des paramètres de la requête)
|
||||||
filtered: dict = {}
|
filtered: dict = {}
|
||||||
if with_query:
|
if with_query:
|
||||||
metric, filtered = _count_manager(request)
|
metric, filtered = _count_manager(request)
|
||||||
@ -481,23 +502,36 @@ def assiduite_create(etudid: int = None, nip=None, ine=None):
|
|||||||
]
|
]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
# Récupération de l'étudiant
|
||||||
etud: Identite = tools.get_etud(etudid, nip, ine)
|
etud: Identite = tools.get_etud(etudid, nip, ine)
|
||||||
if etud is None:
|
if etud is None:
|
||||||
return json_error(
|
return json_error(
|
||||||
404,
|
404,
|
||||||
message="étudiant inconnu",
|
message="étudiant inconnu",
|
||||||
)
|
)
|
||||||
|
# Mise à jour du "g.scodoc_dept" si route sans dept
|
||||||
if g.scodoc_dept is None and etud.dept_id is not None:
|
if g.scodoc_dept is None and etud.dept_id is not None:
|
||||||
# route sans département
|
# route sans département
|
||||||
set_sco_dept(etud.departement.acronym)
|
set_sco_dept(etud.departement.acronym)
|
||||||
|
|
||||||
|
# Récupération de la liste des assiduités à créer
|
||||||
create_list: list[object] = request.get_json(force=True)
|
create_list: list[object] = request.get_json(force=True)
|
||||||
|
|
||||||
|
# Vérification que c'est bien une liste
|
||||||
if not isinstance(create_list, list):
|
if not isinstance(create_list, list):
|
||||||
return json_error(404, "Le contenu envoyé n'est pas une liste")
|
return json_error(404, "Le contenu envoyé n'est pas une liste")
|
||||||
|
|
||||||
errors: list = []
|
# Préparation du retour
|
||||||
success: list = []
|
|
||||||
|
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
|
||||||
for i, data in enumerate(create_list):
|
for i, data in enumerate(create_list):
|
||||||
|
# On créé l'assiduité
|
||||||
|
# 200 + obj si réussi
|
||||||
|
# 404 + message d'erreur si non réussi
|
||||||
code, obj = _create_one(data, etud)
|
code, obj = _create_one(data, etud)
|
||||||
if code == 404:
|
if code == 404:
|
||||||
errors.append({"indice": i, "message": obj})
|
errors.append({"indice": i, "message": obj})
|
||||||
@ -570,53 +604,77 @@ def _create_one(
|
|||||||
data: dict,
|
data: dict,
|
||||||
etud: Identite,
|
etud: Identite,
|
||||||
) -> tuple[int, object]:
|
) -> tuple[int, object]:
|
||||||
"""TODO @iziram: documenter"""
|
"""
|
||||||
|
_create_one Création d'une assiduité à partir d'une représentation JSON
|
||||||
|
|
||||||
|
Cette fonction vérifie la représentation JSON
|
||||||
|
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
|
||||||
errors: list[str] = []
|
errors: list[str] = []
|
||||||
|
|
||||||
# -- vérifications de l'objet json --
|
# -- vérifications de l'objet json --
|
||||||
# cas 1 : ETAT
|
# cas 1 : ETAT
|
||||||
etat = data.get("etat", None)
|
etat: str = data.get("etat", None)
|
||||||
if etat is None:
|
if etat is None:
|
||||||
errors.append("param 'etat': manquant")
|
errors.append("param 'etat': manquant")
|
||||||
elif not scu.EtatAssiduite.contains(etat):
|
elif not scu.EtatAssiduite.contains(etat):
|
||||||
errors.append("param 'etat': invalide")
|
errors.append("param 'etat': invalide")
|
||||||
|
|
||||||
etat = scu.EtatAssiduite.get(etat)
|
etat: scu.EtatAssiduite = scu.EtatAssiduite.get(etat)
|
||||||
|
|
||||||
# cas 2 : date_debut
|
# cas 2 : date_debut
|
||||||
date_debut = data.get("date_debut", None)
|
date_debut: str = data.get("date_debut", None)
|
||||||
if date_debut is None:
|
if date_debut is None:
|
||||||
errors.append("param 'date_debut': manquant")
|
errors.append("param 'date_debut': manquant")
|
||||||
deb = scu.is_iso_formated(date_debut, convert=True)
|
# 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
|
||||||
if deb is None:
|
if deb is None:
|
||||||
errors.append("param 'date_debut': format invalide")
|
errors.append("param 'date_debut': format invalide")
|
||||||
|
# Si datetime sans timezone
|
||||||
elif deb.tzinfo is None:
|
elif deb.tzinfo is None:
|
||||||
deb = scu.localize_datetime(deb)
|
# Mise à jour de la timezone avec celle du serveur
|
||||||
|
deb: datetime = scu.localize_datetime(deb)
|
||||||
|
|
||||||
# cas 3 : date_fin
|
# cas 3 : date_fin (Même fonctionnement ^ )
|
||||||
date_fin = data.get("date_fin", None)
|
date_fin: str = data.get("date_fin", None)
|
||||||
if date_fin is None:
|
if date_fin is None:
|
||||||
errors.append("param 'date_fin': manquant")
|
errors.append("param 'date_fin': manquant")
|
||||||
fin = scu.is_iso_formated(date_fin, convert=True)
|
fin: datetime = scu.is_iso_formated(date_fin, convert=True)
|
||||||
if fin is None:
|
if fin is None:
|
||||||
errors.append("param 'date_fin': format invalide")
|
errors.append("param 'date_fin': format invalide")
|
||||||
elif fin.tzinfo is None:
|
elif fin.tzinfo is None:
|
||||||
fin = scu.localize_datetime(fin)
|
fin: datetime = scu.localize_datetime(fin)
|
||||||
|
|
||||||
# cas 5 : desc
|
# cas 4 : desc
|
||||||
desc: str = data.get("desc", None)
|
desc: str = data.get("desc", None)
|
||||||
|
|
||||||
external_data = data.get("external_data", None)
|
# cas 5 : external data
|
||||||
|
external_data: dict = data.get("external_data", None)
|
||||||
if external_data is not None:
|
if external_data is not None:
|
||||||
if not isinstance(external_data, dict):
|
if not isinstance(external_data, dict):
|
||||||
errors.append("param 'external_data' : n'est pas un objet JSON")
|
errors.append("param 'external_data' : n'est pas un objet JSON")
|
||||||
|
|
||||||
# cas 4 : moduleimpl_id
|
# cas 6 : moduleimpl_id
|
||||||
|
|
||||||
|
# On récupère le moduleimpl
|
||||||
moduleimpl_id = data.get("moduleimpl_id", False)
|
moduleimpl_id = data.get("moduleimpl_id", False)
|
||||||
moduleimpl: ModuleImpl = None
|
moduleimpl: ModuleImpl = None
|
||||||
|
|
||||||
|
# On vérifie si le moduleimpl existe (uniquement s'il a été renseigné)
|
||||||
if moduleimpl_id not in [False, None, "", "-1"]:
|
if moduleimpl_id not in [False, None, "", "-1"]:
|
||||||
|
# Si le moduleimpl n'est pas "autre" alors on vérifie si l'id est valide
|
||||||
if moduleimpl_id != "autre":
|
if moduleimpl_id != "autre":
|
||||||
try:
|
try:
|
||||||
moduleimpl = ModuleImpl.query.filter_by(id=int(moduleimpl_id)).first()
|
moduleimpl = ModuleImpl.query.filter_by(id=int(moduleimpl_id)).first()
|
||||||
@ -625,16 +683,23 @@ def _create_one(
|
|||||||
if moduleimpl is None:
|
if moduleimpl is None:
|
||||||
errors.append("param 'moduleimpl_id': invalide")
|
errors.append("param 'moduleimpl_id': invalide")
|
||||||
else:
|
else:
|
||||||
|
# Sinon on met à none le moduleimpl
|
||||||
|
# et on ajoute dans external data
|
||||||
|
# le module "autre"
|
||||||
moduleimpl_id = None
|
moduleimpl_id = None
|
||||||
external_data = external_data if external_data is not None else {}
|
external_data: dict = external_data if external_data is not None else {}
|
||||||
external_data["module"] = "Autre"
|
external_data["module"] = "Autre"
|
||||||
|
|
||||||
|
# Si il y a des erreurs alors on ne crée pas l'assiduité et on renvoie les erreurs
|
||||||
if errors:
|
if errors:
|
||||||
|
# Construit une chaine de caractère avec les erreurs séparées par `,`
|
||||||
err: str = ", ".join(errors)
|
err: str = ", ".join(errors)
|
||||||
|
# 404 représente le code d'erreur et err la chaine nouvellement créée
|
||||||
return (404, err)
|
return (404, err)
|
||||||
|
|
||||||
# TOUT EST OK
|
# SI TOUT EST OK
|
||||||
try:
|
try:
|
||||||
|
# On essaye de créer l'assiduité
|
||||||
nouv_assiduite: Assiduite = Assiduite.create_assiduite(
|
nouv_assiduite: Assiduite = Assiduite.create_assiduite(
|
||||||
date_debut=deb,
|
date_debut=deb,
|
||||||
date_fin=fin,
|
date_fin=fin,
|
||||||
@ -647,12 +712,23 @@ def _create_one(
|
|||||||
notify_mail=True,
|
notify_mail=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 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
|
||||||
db.session.add(nouv_assiduite)
|
db.session.add(nouv_assiduite)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return (200, {"assiduite_id": nouv_assiduite.id})
|
return (200, {"assiduite_id": nouv_assiduite.id})
|
||||||
except ScoValueError as excp:
|
except ScoValueError as excp:
|
||||||
# ici on utilise pas json_error car on doit renvoyer status, message
|
# ici on utilise pas json_error car on doit renvoyer status, message
|
||||||
|
# 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
|
||||||
return 404, excp.args[0]
|
return 404, excp.args[0]
|
||||||
|
|
||||||
|
|
||||||
@ -675,14 +751,20 @@ def assiduite_delete():
|
|||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
# Récupération des ids envoyés dans la liste
|
||||||
assiduites_list: list[int] = request.get_json(force=True)
|
assiduites_list: list[int] = request.get_json(force=True)
|
||||||
if not isinstance(assiduites_list, list):
|
if not isinstance(assiduites_list, list):
|
||||||
return json_error(404, "Le contenu envoyé n'est pas une liste")
|
return json_error(404, "Le contenu envoyé n'est pas une liste")
|
||||||
|
|
||||||
|
# Préparation du retour json
|
||||||
output = {"errors": [], "success": []}
|
output = {"errors": [], "success": []}
|
||||||
|
|
||||||
for i, ass in enumerate(assiduites_list):
|
# Pour chaque assiduite_id on essaye de supprimer l'assiduité
|
||||||
code, msg = _delete_singular(ass, db)
|
for i, assiduite_id in enumerate(assiduites_list):
|
||||||
|
# De la même façon que "_create_one"
|
||||||
|
# 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)
|
||||||
if code == 404:
|
if code == 404:
|
||||||
output["errors"].append({"indice": i, "message": msg})
|
output["errors"].append({"indice": i, "message": msg})
|
||||||
else:
|
else:
|
||||||
@ -692,24 +774,43 @@ def assiduite_delete():
|
|||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
def _delete_singular(assiduite_id: int, database) -> tuple[int, str]:
|
def _delete_one(assiduite_id: int) -> tuple[int, str]:
|
||||||
"""@iziram PLEASE COMMENT THIS F*CKING CODE"""
|
"""
|
||||||
|
_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
|
||||||
|
"""
|
||||||
assiduite_unique: Assiduite = Assiduite.query.filter_by(id=assiduite_id).first()
|
assiduite_unique: Assiduite = Assiduite.query.filter_by(id=assiduite_id).first()
|
||||||
if assiduite_unique is None:
|
if assiduite_unique is None:
|
||||||
# on ne peut pas utiliser json_error ici car on est déclaré (int, str)
|
# 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.
|
||||||
return 404, "Assiduite non existante"
|
return 404, "Assiduite non existante"
|
||||||
|
|
||||||
|
# Mise à jour du g.scodoc_dept si la route est sans département
|
||||||
if g.scodoc_dept is None and assiduite_unique.etudiant.dept_id is not None:
|
if g.scodoc_dept is None and assiduite_unique.etudiant.dept_id is not None:
|
||||||
# route sans département
|
# route sans département
|
||||||
set_sco_dept(assiduite_unique.etudiant.departement.acronym)
|
set_sco_dept(assiduite_unique.etudiant.departement.acronym)
|
||||||
ass_dict = assiduite_unique.to_dict()
|
|
||||||
|
# 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
|
||||||
log(f"delete_assiduite: {assiduite_unique.etudiant.id} {assiduite_unique}")
|
log(f"delete_assiduite: {assiduite_unique.etudiant.id} {assiduite_unique}")
|
||||||
Scolog.logdb(
|
Scolog.logdb(
|
||||||
method="delete_assiduite",
|
method="delete_assiduite",
|
||||||
etudid=assiduite_unique.etudiant.id,
|
etudid=assiduite_unique.etudiant.id,
|
||||||
msg=f"assiduité: {assiduite_unique}",
|
msg=f"assiduité: {assiduite_unique}",
|
||||||
)
|
)
|
||||||
database.session.delete(assiduite_unique)
|
db.session.delete(assiduite_unique)
|
||||||
scass.simple_invalidate_cache(ass_dict)
|
# Invalidation du cache
|
||||||
|
scass.simple_invalidate_cache(assi_dict)
|
||||||
return 200, "OK"
|
return 200, "OK"
|
||||||
|
|
||||||
|
|
||||||
@ -730,17 +831,25 @@ def assiduite_edit(assiduite_id: int):
|
|||||||
"est_just"?: bool
|
"est_just"?: bool
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Récupération de l'assiduité à modifier
|
||||||
assiduite_unique: Assiduite = Assiduite.query.filter_by(
|
assiduite_unique: Assiduite = Assiduite.query.filter_by(
|
||||||
id=assiduite_id
|
id=assiduite_id
|
||||||
).first_or_404()
|
).first_or_404()
|
||||||
errors: list[str] = []
|
# Récupération des valeurs à modifier
|
||||||
data = request.get_json(force=True)
|
data = request.get_json(force=True)
|
||||||
|
|
||||||
code, obj = _edit_singular(assiduite_unique, data)
|
# Préparation du retour
|
||||||
|
errors: list[str] = []
|
||||||
|
|
||||||
|
# Code 200 si modification réussie
|
||||||
|
# Code 404 si raté + message d'erreur
|
||||||
|
code, obj = _edit_one(assiduite_unique, data)
|
||||||
|
|
||||||
if code == 404:
|
if code == 404:
|
||||||
return json_error(404, obj)
|
return json_error(404, obj)
|
||||||
|
|
||||||
|
# Mise à jour de l'assiduité et LOG
|
||||||
log(f"assiduite_edit: {assiduite_unique.etudiant.id} {assiduite_unique}")
|
log(f"assiduite_edit: {assiduite_unique.etudiant.id} {assiduite_unique}")
|
||||||
Scolog.logdb(
|
Scolog.logdb(
|
||||||
"assiduite_edit",
|
"assiduite_edit",
|
||||||
@ -791,7 +900,7 @@ def assiduites_edit():
|
|||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
code, obj = _edit_singular(assi, data)
|
code, obj = _edit_one(assi, data)
|
||||||
obj_retour = {
|
obj_retour = {
|
||||||
"indice": i,
|
"indice": i,
|
||||||
"message": obj,
|
"message": obj,
|
||||||
@ -806,46 +915,69 @@ def assiduites_edit():
|
|||||||
return {"errors": errors, "success": success}
|
return {"errors": errors, "success": success}
|
||||||
|
|
||||||
|
|
||||||
def _edit_singular(assiduite_unique, data):
|
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
|
||||||
if g.scodoc_dept is None and assiduite_unique.etudiant.dept_id is not None:
|
if g.scodoc_dept is None and assiduite_unique.etudiant.dept_id is not None:
|
||||||
# route sans département
|
# route sans département
|
||||||
set_sco_dept(assiduite_unique.etudiant.departement.acronym)
|
set_sco_dept(assiduite_unique.etudiant.departement.acronym)
|
||||||
|
|
||||||
errors: list[str] = []
|
errors: list[str] = []
|
||||||
|
|
||||||
# Vérifications de data
|
# Vérifications de data
|
||||||
|
|
||||||
# Cas 1 : Etat
|
# Cas 1 : Etat
|
||||||
if data.get("etat") is not None:
|
if data.get("etat") is not None:
|
||||||
etat = scu.EtatAssiduite.get(data.get("etat"))
|
etat: scu.EtatAssiduite = scu.EtatAssiduite.get(data.get("etat"))
|
||||||
if etat is None:
|
if etat is None:
|
||||||
errors.append("param 'etat': invalide")
|
errors.append("param 'etat': invalide")
|
||||||
else:
|
else:
|
||||||
|
# Mise à jour de l'état
|
||||||
assiduite_unique.etat = etat
|
assiduite_unique.etat = etat
|
||||||
|
|
||||||
external_data = data.get("external_data")
|
# Cas 2 : external_data
|
||||||
|
external_data: dict = data.get("external_data")
|
||||||
if external_data is not None:
|
if external_data is not None:
|
||||||
if not isinstance(external_data, dict):
|
if not isinstance(external_data, dict):
|
||||||
errors.append("param 'external_data' : n'est pas un objet JSON")
|
errors.append("param 'external_data' : n'est pas un objet JSON")
|
||||||
else:
|
else:
|
||||||
|
# Mise à jour de l'external data
|
||||||
assiduite_unique.external_data = external_data
|
assiduite_unique.external_data = external_data
|
||||||
|
|
||||||
# Cas 2 : Moduleimpl_id
|
# Cas 3 : Moduleimpl_id
|
||||||
moduleimpl_id = data.get("moduleimpl_id", False)
|
moduleimpl_id = data.get("moduleimpl_id", False)
|
||||||
moduleimpl: ModuleImpl = None
|
moduleimpl: ModuleImpl = None
|
||||||
|
|
||||||
|
# False si on modifie pas le moduleimpl
|
||||||
if moduleimpl_id is not False:
|
if moduleimpl_id is not False:
|
||||||
|
# Si le module n'est pas nul
|
||||||
if moduleimpl_id not in [None, "", "-1"]:
|
if moduleimpl_id not in [None, "", "-1"]:
|
||||||
|
# Gestion du module Autre
|
||||||
if moduleimpl_id == "autre":
|
if moduleimpl_id == "autre":
|
||||||
|
# module autre = moduleimpl_id:None + external_data["module"]:"Autre"
|
||||||
assiduite_unique.moduleimpl_id = None
|
assiduite_unique.moduleimpl_id = None
|
||||||
external_data = (
|
external_data: dict = (
|
||||||
external_data
|
external_data
|
||||||
if external_data is not None and isinstance(external_data, dict)
|
if external_data is not None and isinstance(external_data, dict)
|
||||||
else assiduite_unique.external_data
|
else assiduite_unique.external_data
|
||||||
)
|
)
|
||||||
external_data = external_data if external_data is not None else {}
|
external_data: dict = external_data if external_data is not None else {}
|
||||||
external_data["module"] = "Autre"
|
external_data["module"] = "Autre"
|
||||||
assiduite_unique.external_data = external_data
|
assiduite_unique.external_data = external_data
|
||||||
else:
|
else:
|
||||||
|
# Vérification de l'id et récupération de l'objet ModuleImpl
|
||||||
try:
|
try:
|
||||||
moduleimpl = ModuleImpl.query.filter_by(
|
moduleimpl = ModuleImpl.query.filter_by(
|
||||||
id=int(moduleimpl_id)
|
id=int(moduleimpl_id)
|
||||||
@ -861,8 +993,11 @@ def _edit_singular(assiduite_unique, data):
|
|||||||
):
|
):
|
||||||
errors.append("param 'moduleimpl_id': etud non inscrit")
|
errors.append("param 'moduleimpl_id': etud non inscrit")
|
||||||
else:
|
else:
|
||||||
|
# Mise à jour du moduleimpl
|
||||||
assiduite_unique.moduleimpl_id = moduleimpl_id
|
assiduite_unique.moduleimpl_id = moduleimpl_id
|
||||||
else:
|
else:
|
||||||
|
# Vérification du force module en cas de modification du moduleimpl en moduleimpl nul
|
||||||
|
# Récupération du formsemestre lié à l'assiduité
|
||||||
formsemestre: FormSemestre = get_formsemestre_from_data(
|
formsemestre: FormSemestre = get_formsemestre_from_data(
|
||||||
assiduite_unique.to_dict()
|
assiduite_unique.to_dict()
|
||||||
)
|
)
|
||||||
@ -873,17 +1008,23 @@ def _edit_singular(assiduite_unique, data):
|
|||||||
else:
|
else:
|
||||||
force = scu.is_assiduites_module_forced(dept_id=etud.dept_id)
|
force = scu.is_assiduites_module_forced(dept_id=etud.dept_id)
|
||||||
|
|
||||||
if force:
|
external_data = (
|
||||||
|
external_data
|
||||||
|
if external_data is not None and isinstance(external_data, dict)
|
||||||
|
else assiduite_unique.external_data
|
||||||
|
)
|
||||||
|
|
||||||
|
if force and not external_data.get("module", False):
|
||||||
errors.append(
|
errors.append(
|
||||||
"param 'moduleimpl_id' : le moduleimpl_id ne peut pas être nul"
|
"param 'moduleimpl_id' : le moduleimpl_id ne peut pas être nul"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Cas 3 : desc
|
# Cas 4 : desc
|
||||||
desc = data.get("desc", False)
|
desc: str = data.get("desc", False)
|
||||||
if desc is not False:
|
if desc is not False:
|
||||||
assiduite_unique.description = desc
|
assiduite_unique.description = desc
|
||||||
|
|
||||||
# Cas 4 : est_just
|
# Cas 5 : est_just
|
||||||
if assiduite_unique.etat == scu.EtatAssiduite.PRESENT:
|
if assiduite_unique.etat == scu.EtatAssiduite.PRESENT:
|
||||||
assiduite_unique.est_just = False
|
assiduite_unique.est_just = False
|
||||||
else:
|
else:
|
||||||
@ -900,9 +1041,11 @@ def _edit_singular(assiduite_unique, data):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if errors:
|
if errors:
|
||||||
|
# Retour des erreurs en une seule chaîne séparée par des `,`
|
||||||
err: str = ", ".join(errors)
|
err: str = ", ".join(errors)
|
||||||
return (404, err)
|
return (404, err)
|
||||||
|
|
||||||
|
# Mise à jour de l'assiduité et LOG
|
||||||
log(f"_edit_singular: {assiduite_unique.etudiant.id} {assiduite_unique}")
|
log(f"_edit_singular: {assiduite_unique.etudiant.id} {assiduite_unique}")
|
||||||
Scolog.logdb(
|
Scolog.logdb(
|
||||||
"assiduite_edit",
|
"assiduite_edit",
|
||||||
@ -997,21 +1140,34 @@ def _count_manager(requested) -> tuple[str, dict]:
|
|||||||
|
|
||||||
def _filter_manager(requested, assiduites_query: Query) -> Query:
|
def _filter_manager(requested, assiduites_query: Query) -> Query:
|
||||||
"""
|
"""
|
||||||
Retourne les assiduites entrées filtrées en fonction de la request
|
_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
|
||||||
"""
|
"""
|
||||||
# cas 1 : etat assiduite
|
# cas 1 : etat assiduite
|
||||||
etat = requested.args.get("etat")
|
etat: str = requested.args.get("etat")
|
||||||
if etat is not None:
|
if etat is not None:
|
||||||
assiduites_query = scass.filter_assiduites_by_etat(assiduites_query, etat)
|
assiduites_query = scass.filter_assiduites_by_etat(assiduites_query, etat)
|
||||||
|
|
||||||
# cas 2 : date de début
|
# cas 2 : date de début
|
||||||
deb = requested.args.get("date_debut", "").replace(" ", "+")
|
deb: str = requested.args.get("date_debut", "").replace(" ", "+")
|
||||||
deb: datetime = scu.is_iso_formated(deb, True)
|
deb: datetime = scu.is_iso_formated(
|
||||||
|
deb, True
|
||||||
|
) # transformation de la chaine en datetime
|
||||||
|
|
||||||
# cas 3 : date de fin
|
# cas 3 : date de fin
|
||||||
fin = requested.args.get("date_fin", "").replace(" ", "+")
|
fin: str = requested.args.get("date_fin", "").replace(" ", "+")
|
||||||
fin = scu.is_iso_formated(fin, True)
|
fin: datetime = scu.is_iso_formated(
|
||||||
|
fin, True
|
||||||
|
) # transformation de la chaine en datetime
|
||||||
|
|
||||||
|
# Pour filtrer les dates il faut forcement avoir les deux bornes
|
||||||
|
# [date_debut : date_fin]
|
||||||
if (deb, fin) != (None, None):
|
if (deb, fin) != (None, None):
|
||||||
assiduites_query: Query = scass.filter_by_date(
|
assiduites_query: Query = scass.filter_by_date(
|
||||||
assiduites_query, Assiduite, deb, fin
|
assiduites_query, Assiduite, deb, fin
|
||||||
@ -1065,10 +1221,12 @@ def _filter_manager(requested, assiduites_query: Query) -> Query:
|
|||||||
if user_id is not False:
|
if user_id is not False:
|
||||||
assiduites_query: Query = scass.filter_by_user_id(assiduites_query, user_id)
|
assiduites_query: Query = scass.filter_by_user_id(assiduites_query, user_id)
|
||||||
|
|
||||||
|
# cas 9 : order (renvoie la query ordonnée en "date début Décroissante")
|
||||||
order = requested.args.get("order", None)
|
order = requested.args.get("order", None)
|
||||||
if order is not None:
|
if order is not None:
|
||||||
assiduites_query: Query = assiduites_query.order_by(Assiduite.date_debut.desc())
|
assiduites_query: Query = assiduites_query.order_by(Assiduite.date_debut.desc())
|
||||||
|
|
||||||
|
# cas 10 : courant (Ne renvoie que les assiduités de l'année courante)
|
||||||
courant = requested.args.get("courant", None)
|
courant = requested.args.get("courant", None)
|
||||||
if courant is not None:
|
if courant is not None:
|
||||||
annee: int = scu.annee_scolaire()
|
annee: int = scu.annee_scolaire()
|
||||||
@ -1081,7 +1239,17 @@ def _filter_manager(requested, assiduites_query: Query) -> Query:
|
|||||||
return assiduites_query
|
return assiduites_query
|
||||||
|
|
||||||
|
|
||||||
def _with_justifs(assi):
|
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
|
||||||
|
"""
|
||||||
if request.args.get("with_justifs") is None:
|
if request.args.get("with_justifs") is None:
|
||||||
return assi
|
return assi
|
||||||
assi["justificatifs"] = get_assiduites_justif(assi["assiduite_id"], True)
|
assi["justificatifs"] = get_assiduites_justif(assi["assiduite_id"], True)
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
|
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
|
||||||
# See LICENSE
|
# See LICENSE
|
||||||
##############################################################################
|
##############################################################################
|
||||||
"""ScoDoc 9 API : Assiduités
|
"""ScoDoc 9 API : Justificatifs
|
||||||
"""
|
"""
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
@ -28,6 +28,7 @@ from app.models import (
|
|||||||
)
|
)
|
||||||
from app.models.assiduites import (
|
from app.models.assiduites import (
|
||||||
compute_assiduites_justified,
|
compute_assiduites_justified,
|
||||||
|
get_formsemestre_from_data,
|
||||||
)
|
)
|
||||||
from app.scodoc.sco_archives_justificatifs import JustificatifArchiver
|
from app.scodoc.sco_archives_justificatifs import JustificatifArchiver
|
||||||
from app.scodoc.sco_exceptions import ScoValueError
|
from app.scodoc.sco_exceptions import ScoValueError
|
||||||
@ -114,7 +115,7 @@ def justificatifs(etudid: int = None, nip=None, ine=None, with_query: bool = Fal
|
|||||||
query?user_id=[int]
|
query?user_id=[int]
|
||||||
ex query?user_id=3
|
ex query?user_id=3
|
||||||
"""
|
"""
|
||||||
|
# Récupération de l'étudiant
|
||||||
etud: Identite = tools.get_etud(etudid, nip, ine)
|
etud: Identite = tools.get_etud(etudid, nip, ine)
|
||||||
|
|
||||||
if etud is None:
|
if etud is None:
|
||||||
@ -122,11 +123,15 @@ def justificatifs(etudid: int = None, nip=None, ine=None, with_query: bool = Fal
|
|||||||
404,
|
404,
|
||||||
message="étudiant inconnu",
|
message="étudiant inconnu",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Récupération des justificatifs de l'étudiant
|
||||||
justificatifs_query = etud.justificatifs
|
justificatifs_query = etud.justificatifs
|
||||||
|
|
||||||
|
# Filtrage des justificatifs en fonction de la requête
|
||||||
if with_query:
|
if with_query:
|
||||||
justificatifs_query = _filter_manager(request, justificatifs_query)
|
justificatifs_query = _filter_manager(request, justificatifs_query)
|
||||||
|
|
||||||
|
# Mise en forme des données puis retour en JSON
|
||||||
data_set: list[dict] = []
|
data_set: list[dict] = []
|
||||||
for just in justificatifs_query.all():
|
for just in justificatifs_query.all():
|
||||||
data = just.to_dict(format_api=True)
|
data = just.to_dict(format_api=True)
|
||||||
@ -147,44 +152,51 @@ def justificatifs(etudid: int = None, nip=None, ine=None, with_query: bool = Fal
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
def justificatifs_dept(dept_id: int = None, with_query: bool = False):
|
def justificatifs_dept(dept_id: int = None, with_query: bool = False):
|
||||||
""" """
|
""" """
|
||||||
dept = Departement.query.get_or_404(dept_id)
|
|
||||||
etuds = [etud.id for etud in dept.etudiants]
|
|
||||||
|
|
||||||
justificatifs_query = Justificatif.query.filter(Justificatif.etudid.in_(etuds))
|
# Récupération du département et des étudiants du département
|
||||||
|
dept: Departement = Departement.query.get_or_404(dept_id)
|
||||||
|
etuds: list[int] = [etud.id for etud in dept.etudiants]
|
||||||
|
|
||||||
|
# Récupération des justificatifs des étudiants du département
|
||||||
|
justificatifs_query: Query = Justificatif.query.filter(
|
||||||
|
Justificatif.etudid.in_(etuds)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Filtrage des justificatifs
|
||||||
if with_query:
|
if with_query:
|
||||||
justificatifs_query = _filter_manager(request, justificatifs_query)
|
justificatifs_query: Query = _filter_manager(request, justificatifs_query)
|
||||||
|
|
||||||
|
# Mise en forme des données et retour JSON
|
||||||
data_set: list[dict] = []
|
data_set: list[dict] = []
|
||||||
for just in justificatifs_query:
|
for just in justificatifs_query:
|
||||||
data_set.append(_set_sems_and_groupe(just))
|
data_set.append(_set_sems(just))
|
||||||
|
|
||||||
return data_set
|
return data_set
|
||||||
|
|
||||||
|
|
||||||
def _set_sems_and_groupe(justi: Justificatif) -> dict:
|
def _set_sems(justi: Justificatif) -> dict:
|
||||||
from app.scodoc.sco_groups import get_etud_groups
|
"""
|
||||||
|
_set_sems Ajoute le formsemestre associé au justificatif s'il existe
|
||||||
|
|
||||||
|
Si le formsemestre n'existe pas, renvoie la simple représentation du justificatif
|
||||||
|
|
||||||
|
Args:
|
||||||
|
justi (Justificatif): Le justificatif
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
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)
|
||||||
|
|
||||||
formsemestre: FormSemestre = (
|
# Récupération du formsemestre de l'assiduité
|
||||||
FormSemestre.query.join(
|
formsemestre: FormSemestre = get_formsemestre_from_data(justi.to_dict())
|
||||||
FormSemestreInscription,
|
# Si le formsemestre existe on l'ajoute au dictionnaire
|
||||||
FormSemestre.id == FormSemestreInscription.formsemestre_id,
|
|
||||||
)
|
|
||||||
.filter(
|
|
||||||
justi.date_debut <= FormSemestre.date_fin,
|
|
||||||
justi.date_fin >= FormSemestre.date_debut,
|
|
||||||
FormSemestreInscription.etudid == justi.etudid,
|
|
||||||
)
|
|
||||||
.first()
|
|
||||||
)
|
|
||||||
if formsemestre:
|
if formsemestre:
|
||||||
data["formsemestre"] = {
|
data["formsemestre"] = {
|
||||||
"id": formsemestre.id,
|
"id": formsemestre.id,
|
||||||
"title": formsemestre.session_id(),
|
"title": formsemestre.session_id(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
@ -208,20 +220,27 @@ def _set_sems_and_groupe(justi: Justificatif) -> dict:
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
def justificatifs_formsemestre(formsemestre_id: int, with_query: bool = False):
|
def justificatifs_formsemestre(formsemestre_id: int, with_query: bool = False):
|
||||||
"""Retourne tous les justificatifs du formsemestre"""
|
"""Retourne tous les justificatifs du formsemestre"""
|
||||||
|
|
||||||
|
# Récupération du formsemestre
|
||||||
formsemestre: FormSemestre = None
|
formsemestre: FormSemestre = None
|
||||||
formsemestre_id = int(formsemestre_id)
|
formsemestre_id = int(formsemestre_id)
|
||||||
formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first()
|
formsemestre: FormSemestre = FormSemestre.query.filter_by(
|
||||||
|
id=formsemestre_id
|
||||||
|
).first()
|
||||||
|
|
||||||
if formsemestre is None:
|
if formsemestre is None:
|
||||||
return json_error(404, "le paramètre 'formsemestre_id' n'existe pas")
|
return json_error(404, "le paramètre 'formsemestre_id' n'existe pas")
|
||||||
|
|
||||||
justificatifs_query = scass.filter_by_formsemestre(
|
# Récupération des justificatifs du semestre
|
||||||
|
justificatifs_query: Query = scass.filter_by_formsemestre(
|
||||||
Justificatif.query, Justificatif, formsemestre
|
Justificatif.query, Justificatif, formsemestre
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Filtrage des justificatifs
|
||||||
if with_query:
|
if with_query:
|
||||||
justificatifs_query = _filter_manager(request, justificatifs_query)
|
justificatifs_query: Query = _filter_manager(request, justificatifs_query)
|
||||||
|
|
||||||
|
# Retour des justificatifs en JSON
|
||||||
data_set: list[dict] = []
|
data_set: list[dict] = []
|
||||||
for justi in justificatifs_query.all():
|
for justi in justificatifs_query.all():
|
||||||
data = justi.to_dict(format_api=True)
|
data = justi.to_dict(format_api=True)
|
||||||
@ -264,6 +283,8 @@ def justif_create(etudid: int = None, nip=None, ine=None):
|
|||||||
]
|
]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Récupération de l'étudiant
|
||||||
etud: Identite = tools.get_etud(etudid, nip, ine)
|
etud: Identite = tools.get_etud(etudid, nip, ine)
|
||||||
|
|
||||||
if etud is None:
|
if etud is None:
|
||||||
@ -272,16 +293,22 @@ def justif_create(etudid: int = None, nip=None, ine=None):
|
|||||||
message="étudiant inconnu",
|
message="étudiant inconnu",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Récupération des justificatifs à créer
|
||||||
create_list: list[object] = request.get_json(force=True)
|
create_list: list[object] = request.get_json(force=True)
|
||||||
|
|
||||||
if not isinstance(create_list, list):
|
if not isinstance(create_list, list):
|
||||||
return json_error(404, "Le contenu envoyé n'est pas une liste")
|
return json_error(404, "Le contenu envoyé n'est pas une liste")
|
||||||
|
|
||||||
errors: list = []
|
errors: list[dict] = []
|
||||||
success: list = []
|
success: list[dict] = []
|
||||||
justifs: list = []
|
justifs: list[Justificatif] = []
|
||||||
|
|
||||||
|
# énumération des justificatifs
|
||||||
for i, data in enumerate(create_list):
|
for i, data in enumerate(create_list):
|
||||||
code, obj, justi = _create_one(data, etud)
|
code, obj, justi = _create_one(data, etud)
|
||||||
|
code: int
|
||||||
|
obj: str | dict
|
||||||
|
justi: Justificatif | None
|
||||||
if code == 404:
|
if code == 404:
|
||||||
errors.append({"indice": i, "message": obj})
|
errors.append({"indice": i, "message": obj})
|
||||||
else:
|
else:
|
||||||
@ -289,6 +316,7 @@ def justif_create(etudid: int = None, nip=None, ine=None):
|
|||||||
justifs.append(justi)
|
justifs.append(justi)
|
||||||
scass.simple_invalidate_cache(data, etud.id)
|
scass.simple_invalidate_cache(data, etud.id)
|
||||||
|
|
||||||
|
# Actualisation des assiduités justifiées en fonction de tous les nouveaux justificatifs
|
||||||
compute_assiduites_justified(etud.etudid, justifs)
|
compute_assiduites_justified(etud.etudid, justifs)
|
||||||
return {"errors": errors, "success": success}
|
return {"errors": errors, "success": success}
|
||||||
|
|
||||||
@ -296,32 +324,32 @@ def justif_create(etudid: int = None, nip=None, ine=None):
|
|||||||
def _create_one(
|
def _create_one(
|
||||||
data: dict,
|
data: dict,
|
||||||
etud: Identite,
|
etud: Identite,
|
||||||
) -> tuple[int, object]:
|
) -> tuple[int, object, Justificatif]:
|
||||||
errors: list[str] = []
|
errors: list[str] = []
|
||||||
|
|
||||||
# -- vérifications de l'objet json --
|
# -- vérifications de l'objet json --
|
||||||
# cas 1 : ETAT
|
# cas 1 : ETAT
|
||||||
etat = data.get("etat", None)
|
etat: str = data.get("etat", None)
|
||||||
if etat is None:
|
if etat is None:
|
||||||
errors.append("param 'etat': manquant")
|
errors.append("param 'etat': manquant")
|
||||||
elif not scu.EtatJustificatif.contains(etat):
|
elif not scu.EtatJustificatif.contains(etat):
|
||||||
errors.append("param 'etat': invalide")
|
errors.append("param 'etat': invalide")
|
||||||
|
|
||||||
etat = scu.EtatJustificatif.get(etat)
|
etat: scu.EtatJustificatif = scu.EtatJustificatif.get(etat)
|
||||||
|
|
||||||
# cas 2 : date_debut
|
# cas 2 : date_debut
|
||||||
date_debut = data.get("date_debut", None)
|
date_debut: str = data.get("date_debut", None)
|
||||||
if date_debut is None:
|
if date_debut is None:
|
||||||
errors.append("param 'date_debut': manquant")
|
errors.append("param 'date_debut': manquant")
|
||||||
deb = scu.is_iso_formated(date_debut, convert=True)
|
deb: datetime = scu.is_iso_formated(date_debut, convert=True)
|
||||||
if deb is None:
|
if deb is None:
|
||||||
errors.append("param 'date_debut': format invalide")
|
errors.append("param 'date_debut': format invalide")
|
||||||
|
|
||||||
# cas 3 : date_fin
|
# cas 3 : date_fin
|
||||||
date_fin = data.get("date_fin", None)
|
date_fin: str = data.get("date_fin", None)
|
||||||
if date_fin is None:
|
if date_fin is None:
|
||||||
errors.append("param 'date_fin': manquant")
|
errors.append("param 'date_fin': manquant")
|
||||||
fin = scu.is_iso_formated(date_fin, convert=True)
|
fin: datetime = scu.is_iso_formated(date_fin, convert=True)
|
||||||
if fin is None:
|
if fin is None:
|
||||||
errors.append("param 'date_fin': format invalide")
|
errors.append("param 'date_fin': format invalide")
|
||||||
|
|
||||||
@ -329,7 +357,7 @@ def _create_one(
|
|||||||
|
|
||||||
raison: str = data.get("raison", None)
|
raison: str = data.get("raison", None)
|
||||||
|
|
||||||
external_data = data.get("external_data")
|
external_data: dict = data.get("external_data")
|
||||||
if external_data is not None:
|
if external_data is not None:
|
||||||
if not isinstance(external_data, dict):
|
if not isinstance(external_data, dict):
|
||||||
errors.append("param 'external_data' : n'est pas un objet JSON")
|
errors.append("param 'external_data' : n'est pas un objet JSON")
|
||||||
@ -341,6 +369,7 @@ def _create_one(
|
|||||||
# TOUT EST OK
|
# TOUT EST OK
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# On essaye de créer le justificatif
|
||||||
nouv_justificatif: Query = Justificatif.create_justificatif(
|
nouv_justificatif: Query = Justificatif.create_justificatif(
|
||||||
date_debut=deb,
|
date_debut=deb,
|
||||||
date_fin=fin,
|
date_fin=fin,
|
||||||
@ -351,6 +380,11 @@ def _create_one(
|
|||||||
external_data=external_data,
|
external_data=external_data,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Si tout s'est bien passé on ajoute l'assiduité à la session
|
||||||
|
# et on retourne un code 200 avec un objet possèdant le justif_id
|
||||||
|
# ainsi que les assiduités justifiées par le dit justificatif
|
||||||
|
|
||||||
|
# On renvoie aussi le justificatif créé pour pour le calcul total de fin
|
||||||
db.session.add(nouv_justificatif)
|
db.session.add(nouv_justificatif)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
@ -363,10 +397,7 @@ def _create_one(
|
|||||||
nouv_justificatif,
|
nouv_justificatif,
|
||||||
)
|
)
|
||||||
except ScoValueError as excp:
|
except ScoValueError as excp:
|
||||||
return (
|
return (404, excp.args[0], None)
|
||||||
404,
|
|
||||||
excp.args[0],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/justificatif/<int:justif_id>/edit", methods=["POST"])
|
@bp.route("/justificatif/<int:justif_id>/edit", methods=["POST"])
|
||||||
@ -387,53 +418,58 @@ def justif_edit(justif_id: int):
|
|||||||
"date_fin"?: str
|
"date_fin"?: str
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Récupération du justificatif à modifier
|
||||||
justificatif_unique: Query = Justificatif.query.filter_by(
|
justificatif_unique: Query = Justificatif.query.filter_by(
|
||||||
id=justif_id
|
id=justif_id
|
||||||
).first_or_404()
|
).first_or_404()
|
||||||
|
|
||||||
errors: list[str] = []
|
errors: list[str] = []
|
||||||
data = request.get_json(force=True)
|
data = request.get_json(force=True)
|
||||||
|
|
||||||
|
# Récupération des assiduités (id) précédemment justifiée par le justificatif
|
||||||
avant_ids: list[int] = scass.justifies(justificatif_unique)
|
avant_ids: list[int] = scass.justifies(justificatif_unique)
|
||||||
# Vérifications de data
|
# Vérifications de data
|
||||||
|
|
||||||
# Cas 1 : Etat
|
# Cas 1 : Etat
|
||||||
if data.get("etat") is not None:
|
if data.get("etat") is not None:
|
||||||
etat = scu.EtatJustificatif.get(data.get("etat"))
|
etat: scu.EtatJustificatif = scu.EtatJustificatif.get(data.get("etat"))
|
||||||
if etat is None:
|
if etat is None:
|
||||||
errors.append("param 'etat': invalide")
|
errors.append("param 'etat': invalide")
|
||||||
else:
|
else:
|
||||||
justificatif_unique.etat = etat
|
justificatif_unique.etat = etat
|
||||||
|
|
||||||
# Cas 2 : raison
|
# Cas 2 : raison
|
||||||
raison = data.get("raison", False)
|
raison: str = data.get("raison", False)
|
||||||
if raison is not False:
|
if raison is not False:
|
||||||
justificatif_unique.raison = raison
|
justificatif_unique.raison = raison
|
||||||
|
|
||||||
deb, fin = None, None
|
deb, fin = None, None
|
||||||
|
|
||||||
# cas 3 : date_debut
|
# cas 3 : date_debut
|
||||||
date_debut = data.get("date_debut", False)
|
date_debut: str = data.get("date_debut", False)
|
||||||
if date_debut is not False:
|
if date_debut is not False:
|
||||||
if date_debut is None:
|
if date_debut is None:
|
||||||
errors.append("param 'date_debut': manquant")
|
errors.append("param 'date_debut': manquant")
|
||||||
deb = scu.is_iso_formated(date_debut.replace(" ", "+"), convert=True)
|
deb: datetime = scu.is_iso_formated(date_debut.replace(" ", "+"), convert=True)
|
||||||
if deb is None:
|
if deb is None:
|
||||||
errors.append("param 'date_debut': format invalide")
|
errors.append("param 'date_debut': format invalide")
|
||||||
|
|
||||||
# cas 4 : date_fin
|
# cas 4 : date_fin
|
||||||
date_fin = data.get("date_fin", False)
|
date_fin: str = data.get("date_fin", False)
|
||||||
if date_fin is not False:
|
if date_fin is not False:
|
||||||
if date_fin is None:
|
if date_fin is None:
|
||||||
errors.append("param 'date_fin': manquant")
|
errors.append("param 'date_fin': manquant")
|
||||||
fin = scu.is_iso_formated(date_fin.replace(" ", "+"), convert=True)
|
fin: datetime = scu.is_iso_formated(date_fin.replace(" ", "+"), convert=True)
|
||||||
if fin is None:
|
if fin is None:
|
||||||
errors.append("param 'date_fin': format invalide")
|
errors.append("param 'date_fin': format invalide")
|
||||||
|
|
||||||
# Mise à jour des dates
|
# Récupération des dates précédentes si deb ou fin est None
|
||||||
deb = deb if deb is not None else justificatif_unique.date_debut
|
deb = deb if deb is not None else justificatif_unique.date_debut
|
||||||
fin = fin if fin is not None else justificatif_unique.date_fin
|
fin = fin if fin is not None else justificatif_unique.date_fin
|
||||||
|
|
||||||
external_data = data.get("external_data")
|
# Mise à jour de l'external data
|
||||||
|
external_data: dict = data.get("external_data")
|
||||||
if external_data is not None:
|
if external_data is not None:
|
||||||
if not isinstance(external_data, dict):
|
if not isinstance(external_data, dict):
|
||||||
errors.append("param 'external_data' : n'est pas un objet JSON")
|
errors.append("param 'external_data' : n'est pas un objet JSON")
|
||||||
@ -443,6 +479,7 @@ def justif_edit(justif_id: int):
|
|||||||
if fin <= deb:
|
if fin <= deb:
|
||||||
errors.append("param 'dates' : Date de début après date de fin")
|
errors.append("param 'dates' : Date de début après date de fin")
|
||||||
|
|
||||||
|
# Mise à jour des dates du justificatif
|
||||||
justificatif_unique.date_debut = deb
|
justificatif_unique.date_debut = deb
|
||||||
justificatif_unique.date_fin = fin
|
justificatif_unique.date_fin = fin
|
||||||
|
|
||||||
@ -450,9 +487,14 @@ def justif_edit(justif_id: int):
|
|||||||
err: str = ", ".join(errors)
|
err: str = ", ".join(errors)
|
||||||
return json_error(404, err)
|
return json_error(404, err)
|
||||||
|
|
||||||
|
# Mise à jour du justificatif
|
||||||
db.session.add(justificatif_unique)
|
db.session.add(justificatif_unique)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
# Génération du dictionnaire de retour
|
||||||
|
# La couverture correspond
|
||||||
|
# - aux assiduités précédemment justifiées par le justificatif
|
||||||
|
# - aux assiduités qui sont justifiées par le justificatif modifié
|
||||||
retour = {
|
retour = {
|
||||||
"couverture": {
|
"couverture": {
|
||||||
"avant": avant_ids,
|
"avant": avant_ids,
|
||||||
@ -463,7 +505,7 @@ def justif_edit(justif_id: int):
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
# Invalide le cache
|
||||||
scass.simple_invalidate_cache(justificatif_unique.to_dict())
|
scass.simple_invalidate_cache(justificatif_unique.to_dict())
|
||||||
return retour
|
return retour
|
||||||
|
|
||||||
@ -487,6 +529,8 @@ def justif_delete():
|
|||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Récupération des justif_ids
|
||||||
justificatifs_list: list[int] = request.get_json(force=True)
|
justificatifs_list: list[int] = request.get_json(force=True)
|
||||||
if not isinstance(justificatifs_list, list):
|
if not isinstance(justificatifs_list, list):
|
||||||
return json_error(404, "Le contenu envoyé n'est pas une liste")
|
return json_error(404, "Le contenu envoyé n'est pas une liste")
|
||||||
@ -494,7 +538,7 @@ def justif_delete():
|
|||||||
output = {"errors": [], "success": []}
|
output = {"errors": [], "success": []}
|
||||||
|
|
||||||
for i, ass in enumerate(justificatifs_list):
|
for i, ass in enumerate(justificatifs_list):
|
||||||
code, msg = _delete_singular(ass, db)
|
code, msg = _delete_one(ass)
|
||||||
if code == 404:
|
if code == 404:
|
||||||
output["errors"].append({"indice": i, "message": msg})
|
output["errors"].append({"indice": i, "message": msg})
|
||||||
else:
|
else:
|
||||||
@ -505,22 +549,41 @@ def justif_delete():
|
|||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
def _delete_singular(justif_id: int, database):
|
def _delete_one(justif_id: int) -> tuple[int, str]:
|
||||||
justificatif_unique: Query = Justificatif.query.filter_by(id=justif_id).first()
|
"""
|
||||||
|
_delete_one Supprime un justificatif
|
||||||
|
|
||||||
|
Args:
|
||||||
|
justif_id (int): l'identifiant du justificatif
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple[int, str]: code, message
|
||||||
|
code : 200 si réussi, 404 sinon
|
||||||
|
message : OK si réussi, message d'erreur sinon
|
||||||
|
"""
|
||||||
|
# Récupération du justificatif à supprimer
|
||||||
|
justificatif_unique: Justificatif = Justificatif.query.filter_by(
|
||||||
|
id=justif_id
|
||||||
|
).first()
|
||||||
if justificatif_unique is None:
|
if justificatif_unique is None:
|
||||||
return (404, "Justificatif non existant")
|
return (404, "Justificatif non existant")
|
||||||
|
|
||||||
|
# Récupération de l'archive du justificatif
|
||||||
archive_name: str = justificatif_unique.fichier
|
archive_name: str = justificatif_unique.fichier
|
||||||
|
|
||||||
if archive_name is not None:
|
if archive_name is not None:
|
||||||
|
# Si elle existe : on essaye de la supprimer
|
||||||
archiver: JustificatifArchiver = JustificatifArchiver()
|
archiver: JustificatifArchiver = JustificatifArchiver()
|
||||||
try:
|
try:
|
||||||
archiver.delete_justificatif(justificatif_unique.etudiant, archive_name)
|
archiver.delete_justificatif(justificatif_unique.etudiant, archive_name)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# On invalide le cache
|
||||||
scass.simple_invalidate_cache(justificatif_unique.to_dict())
|
scass.simple_invalidate_cache(justificatif_unique.to_dict())
|
||||||
database.session.delete(justificatif_unique)
|
# On supprime le justificatif
|
||||||
|
db.session.delete(justificatif_unique)
|
||||||
|
# On actualise les assiduités justifiées de l'étudiant concerné
|
||||||
compute_assiduites_justified(
|
compute_assiduites_justified(
|
||||||
justificatif_unique.etudid,
|
justificatif_unique.etudid,
|
||||||
Justificatif.query.filter_by(etudid=justificatif_unique.etudid).all(),
|
Justificatif.query.filter_by(etudid=justificatif_unique.etudid).all(),
|
||||||
@ -541,23 +604,27 @@ def justif_import(justif_id: int = None):
|
|||||||
"""
|
"""
|
||||||
Importation d'un fichier (création d'archive)
|
Importation d'un fichier (création d'archive)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# On vérifie qu'un fichier a bien été envoyé
|
||||||
if len(request.files) == 0:
|
if len(request.files) == 0:
|
||||||
return json_error(404, "Il n'y a pas de fichier joint")
|
return json_error(404, "Il n'y a pas de fichier joint")
|
||||||
|
|
||||||
file = list(request.files.values())[0]
|
file = list(request.files.values())[0]
|
||||||
if file.filename == "":
|
if file.filename == "":
|
||||||
return json_error(404, "Il n'y a pas de fichier joint")
|
return json_error(404, "Il n'y a pas de fichier joint")
|
||||||
|
|
||||||
|
# On récupère le justificatif auquel on va importer le fichier
|
||||||
query: Query = Justificatif.query.filter_by(id=justif_id)
|
query: Query = Justificatif.query.filter_by(id=justif_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id)
|
query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id)
|
||||||
|
|
||||||
justificatif_unique: Justificatif = query.first_or_404()
|
justificatif_unique: Justificatif = query.first_or_404()
|
||||||
|
|
||||||
|
# Récupération de l'archive si elle existe
|
||||||
archive_name: str = justificatif_unique.fichier
|
archive_name: str = justificatif_unique.fichier
|
||||||
|
|
||||||
|
# Utilisation de l'archiver de justificatifs
|
||||||
archiver: JustificatifArchiver = JustificatifArchiver()
|
archiver: JustificatifArchiver = JustificatifArchiver()
|
||||||
try:
|
try:
|
||||||
|
# On essaye de sauvegarder le fichier
|
||||||
fname: str
|
fname: str
|
||||||
archive_name, fname = archiver.save_justificatif(
|
archive_name, fname = archiver.save_justificatif(
|
||||||
justificatif_unique.etudiant,
|
justificatif_unique.etudiant,
|
||||||
@ -567,6 +634,7 @@ def justif_import(justif_id: int = None):
|
|||||||
user_id=current_user.id,
|
user_id=current_user.id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# On actualise l'archive du justificatif
|
||||||
justificatif_unique.fichier = archive_name
|
justificatif_unique.fichier = archive_name
|
||||||
|
|
||||||
db.session.add(justificatif_unique)
|
db.session.add(justificatif_unique)
|
||||||
@ -574,6 +642,7 @@ def justif_import(justif_id: int = None):
|
|||||||
|
|
||||||
return {"filename": fname}
|
return {"filename": fname}
|
||||||
except ScoValueError as err:
|
except ScoValueError as err:
|
||||||
|
# Si cela ne fonctionne pas on renvoie une erreur
|
||||||
return json_error(404, err.args[0])
|
return json_error(404, err.args[0])
|
||||||
|
|
||||||
|
|
||||||
@ -587,23 +656,26 @@ def justif_export(justif_id: int = None, filename: str = None):
|
|||||||
Retourne un fichier d'une archive d'un justificatif
|
Retourne un fichier d'une archive d'un justificatif
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# On récupère le justificatif concerné
|
||||||
query: Query = Justificatif.query.filter_by(id=justif_id)
|
query: Query = Justificatif.query.filter_by(id=justif_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id)
|
query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id)
|
||||||
|
|
||||||
justificatif_unique: Justificatif = query.first_or_404()
|
justificatif_unique: Justificatif = query.first_or_404()
|
||||||
|
|
||||||
|
# On récupère l'archive concernée
|
||||||
archive_name: str = justificatif_unique.fichier
|
archive_name: str = justificatif_unique.fichier
|
||||||
if archive_name is None:
|
if archive_name is None:
|
||||||
|
# On retourne une erreur si le justificatif n'a pas de fichiers
|
||||||
return json_error(404, "le justificatif ne possède pas de fichier")
|
return json_error(404, "le justificatif ne possède pas de fichier")
|
||||||
|
|
||||||
|
# On récupère le fichier et le renvoie en une réponse déjà formée
|
||||||
archiver: JustificatifArchiver = JustificatifArchiver()
|
archiver: JustificatifArchiver = JustificatifArchiver()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return archiver.get_justificatif_file(
|
return archiver.get_justificatif_file(
|
||||||
archive_name, justificatif_unique.etudiant, filename
|
archive_name, justificatif_unique.etudiant, filename
|
||||||
)
|
)
|
||||||
except ScoValueError as err:
|
except ScoValueError as err:
|
||||||
|
# On retourne une erreur json si jamais il y a un problème
|
||||||
return json_error(404, err.args[0])
|
return json_error(404, err.args[0])
|
||||||
|
|
||||||
|
|
||||||
@ -616,7 +688,6 @@ def justif_export(justif_id: int = None, filename: str = None):
|
|||||||
def justif_remove(justif_id: int = None):
|
def justif_remove(justif_id: int = None):
|
||||||
"""
|
"""
|
||||||
Supression d'un fichier ou d'une archive
|
Supression d'un fichier ou d'une archive
|
||||||
# TOTALK: Doc, expliquer les noms coté server
|
|
||||||
{
|
{
|
||||||
"remove": <"all"/"list">
|
"remove": <"all"/"list">
|
||||||
|
|
||||||
@ -627,31 +698,41 @@ def justif_remove(justif_id: int = None):
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# On récupère le dictionnaire
|
||||||
data: dict = request.get_json(force=True)
|
data: dict = request.get_json(force=True)
|
||||||
|
|
||||||
|
# On récupère le justificatif concerné
|
||||||
query: Query = Justificatif.query.filter_by(id=justif_id)
|
query: Query = Justificatif.query.filter_by(id=justif_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id)
|
query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id)
|
||||||
|
|
||||||
justificatif_unique: Justificatif = query.first_or_404()
|
justificatif_unique: Justificatif = query.first_or_404()
|
||||||
|
|
||||||
|
# On récupère l'archive
|
||||||
archive_name: str = justificatif_unique.fichier
|
archive_name: str = justificatif_unique.fichier
|
||||||
if archive_name is None:
|
if archive_name is None:
|
||||||
|
# On retourne une erreur si le justificatif n'a pas de fichiers
|
||||||
return json_error(404, "le justificatif ne possède pas de fichier")
|
return json_error(404, "le justificatif ne possède pas de fichier")
|
||||||
|
|
||||||
|
# On regarde le type de suppression (all ou list)
|
||||||
|
# Si all : on supprime tous les fichiers
|
||||||
|
# Si list : on supprime les fichiers dont le nom est dans la liste
|
||||||
remove: str = data.get("remove")
|
remove: str = data.get("remove")
|
||||||
if remove is None or remove not in ("all", "list"):
|
if remove is None or remove not in ("all", "list"):
|
||||||
return json_error(404, "param 'remove': Valeur invalide")
|
return json_error(404, "param 'remove': Valeur invalide")
|
||||||
|
|
||||||
|
# On récupère l'archiver et l'étudiant
|
||||||
archiver: JustificatifArchiver = JustificatifArchiver()
|
archiver: JustificatifArchiver = JustificatifArchiver()
|
||||||
etud = justificatif_unique.etudiant
|
etud = justificatif_unique.etudiant
|
||||||
try:
|
try:
|
||||||
if remove == "all":
|
if remove == "all":
|
||||||
|
# Suppression de toute l'archive du justificatif
|
||||||
archiver.delete_justificatif(etud, archive_name=archive_name)
|
archiver.delete_justificatif(etud, archive_name=archive_name)
|
||||||
justificatif_unique.fichier = None
|
justificatif_unique.fichier = None
|
||||||
db.session.add(justificatif_unique)
|
db.session.add(justificatif_unique)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
# Suppression des fichiers dont le nom se trouve dans la liste "filenames"
|
||||||
for fname in data.get("filenames", []):
|
for fname in data.get("filenames", []):
|
||||||
archiver.delete_justificatif(
|
archiver.delete_justificatif(
|
||||||
etud,
|
etud,
|
||||||
@ -659,6 +740,7 @@ def justif_remove(justif_id: int = None):
|
|||||||
filename=fname,
|
filename=fname,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Si il n'y a plus de fichiers dans l'archive, on la supprime
|
||||||
if len(archiver.list_justificatifs(archive_name, etud)) == 0:
|
if len(archiver.list_justificatifs(archive_name, etud)) == 0:
|
||||||
archiver.delete_justificatif(etud, archive_name)
|
archiver.delete_justificatif(etud, archive_name)
|
||||||
justificatif_unique.fichier = None
|
justificatif_unique.fichier = None
|
||||||
@ -666,8 +748,10 @@ def justif_remove(justif_id: int = None):
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
except ScoValueError as err:
|
except ScoValueError as err:
|
||||||
|
# On retourne une erreur json si jamais il y a eu un problème
|
||||||
return json_error(404, err.args[0])
|
return json_error(404, err.args[0])
|
||||||
|
|
||||||
|
# On retourne une réponse "removed" si tout s'est bien passé
|
||||||
return {"response": "removed"}
|
return {"response": "removed"}
|
||||||
|
|
||||||
|
|
||||||
@ -682,29 +766,36 @@ def justif_list(justif_id: int = None):
|
|||||||
Liste les fichiers du justificatif
|
Liste les fichiers du justificatif
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Récupération du justificatif concerné
|
||||||
query: Query = Justificatif.query.filter_by(id=justif_id)
|
query: Query = Justificatif.query.filter_by(id=justif_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id)
|
query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id)
|
||||||
|
|
||||||
justificatif_unique: Justificatif = query.first_or_404()
|
justificatif_unique: Justificatif = query.first_or_404()
|
||||||
|
|
||||||
|
# Récupération de l'archive avec l'archiver
|
||||||
archive_name: str = justificatif_unique.fichier
|
archive_name: str = justificatif_unique.fichier
|
||||||
|
|
||||||
filenames: list[str] = []
|
filenames: list[str] = []
|
||||||
|
|
||||||
archiver: JustificatifArchiver = JustificatifArchiver()
|
archiver: JustificatifArchiver = JustificatifArchiver()
|
||||||
if archive_name is not None:
|
if archive_name is not None:
|
||||||
filenames = archiver.list_justificatifs(
|
filenames = archiver.list_justificatifs(
|
||||||
archive_name, justificatif_unique.etudiant
|
archive_name, justificatif_unique.etudiant
|
||||||
)
|
)
|
||||||
|
# Préparation du retour
|
||||||
|
# - total : le nombre total de fichier du justificatif
|
||||||
|
# - filenames : le nom des fichiers visible par l'utilisateur
|
||||||
retour = {"total": len(filenames), "filenames": []}
|
retour = {"total": len(filenames), "filenames": []}
|
||||||
|
|
||||||
|
# Pour chaque nom de fichier on vérifie
|
||||||
|
# - Si l'utilisateur qui a importé le fichier est le même que
|
||||||
|
# l'utilisateur qui a demandé la liste des fichiers
|
||||||
|
# - Ou si l'utilisateur qui a demandé la liste possède la permission AbsJustifView
|
||||||
|
# Si c'est le cas alors on ajoute à la liste des fichiers visibles
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
if int(filename[1]) == current_user.id or current_user.has_permission(
|
if int(filename[1]) == current_user.id or current_user.has_permission(
|
||||||
Permission.AbsJustifView
|
Permission.AbsJustifView
|
||||||
):
|
):
|
||||||
retour["filenames"].append(filename[0])
|
retour["filenames"].append(filename[0])
|
||||||
|
# On renvoie le total et la liste des fichiers visibles
|
||||||
return retour
|
return retour
|
||||||
|
|
||||||
|
|
||||||
@ -720,44 +811,45 @@ def justif_justifies(justif_id: int = None):
|
|||||||
Liste assiduite_id justifiées par le justificatif
|
Liste assiduite_id justifiées par le justificatif
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# On récupère le justificatif concerné
|
||||||
query: Query = Justificatif.query.filter_by(id=justif_id)
|
query: Query = Justificatif.query.filter_by(id=justif_id)
|
||||||
if g.scodoc_dept:
|
if g.scodoc_dept:
|
||||||
query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id)
|
query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id)
|
||||||
|
|
||||||
justificatif_unique: Justificatif = query.first_or_404()
|
justificatif_unique: Justificatif = query.first_or_404()
|
||||||
|
|
||||||
|
# On récupère la liste des assiduités justifiées par le justificatif
|
||||||
assiduites_list: list[int] = scass.justifies(justificatif_unique)
|
assiduites_list: list[int] = scass.justifies(justificatif_unique)
|
||||||
|
# On la renvoie
|
||||||
return assiduites_list
|
return assiduites_list
|
||||||
|
|
||||||
|
|
||||||
# -- Utils --
|
# -- Utils --
|
||||||
|
|
||||||
|
|
||||||
def _filter_manager(requested, justificatifs_query):
|
def _filter_manager(requested, justificatifs_query: Query):
|
||||||
"""
|
"""
|
||||||
Retourne les justificatifs entrés filtrés en fonction de la request
|
Retourne les justificatifs entrés filtrés en fonction de la request
|
||||||
"""
|
"""
|
||||||
# cas 1 : etat justificatif
|
# cas 1 : etat justificatif
|
||||||
etat = requested.args.get("etat")
|
etat: str = requested.args.get("etat")
|
||||||
if etat is not None:
|
if etat is not None:
|
||||||
justificatifs_query = scass.filter_justificatifs_by_etat(
|
justificatifs_query: Query = scass.filter_justificatifs_by_etat(
|
||||||
justificatifs_query, etat
|
justificatifs_query, etat
|
||||||
)
|
)
|
||||||
|
|
||||||
# cas 2 : date de début
|
# cas 2 : date de début
|
||||||
deb = requested.args.get("date_debut", "").replace(" ", "+")
|
deb: str = requested.args.get("date_debut", "").replace(" ", "+")
|
||||||
deb: datetime = scu.is_iso_formated(deb, True)
|
deb: datetime = scu.is_iso_formated(deb, True)
|
||||||
|
|
||||||
# cas 3 : date de fin
|
# cas 3 : date de fin
|
||||||
fin = requested.args.get("date_fin", "").replace(" ", "+")
|
fin: str = requested.args.get("date_fin", "").replace(" ", "+")
|
||||||
fin = scu.is_iso_formated(fin, True)
|
fin: datetime = scu.is_iso_formated(fin, True)
|
||||||
|
|
||||||
if (deb, fin) != (None, None):
|
if (deb, fin) != (None, None):
|
||||||
justificatifs_query: Query = scass.filter_by_date(
|
justificatifs_query: Query = scass.filter_by_date(
|
||||||
justificatifs_query, Justificatif, deb, fin
|
justificatifs_query, Justificatif, deb, fin
|
||||||
)
|
)
|
||||||
|
# cas 4 : user_id
|
||||||
user_id = requested.args.get("user_id", False)
|
user_id = requested.args.get("user_id", False)
|
||||||
if user_id is not False:
|
if user_id is not False:
|
||||||
justificatifs_query: Query = scass.filter_by_user_id(
|
justificatifs_query: Query = scass.filter_by_user_id(
|
||||||
@ -778,12 +870,13 @@ def _filter_manager(requested, justificatifs_query):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
formsemestre = None
|
formsemestre = None
|
||||||
|
|
||||||
|
# cas 6 : order (retourne les justificatifs par ordre décroissant de date_debut)
|
||||||
order = requested.args.get("order", None)
|
order = requested.args.get("order", None)
|
||||||
if order is not None:
|
if order is not None:
|
||||||
justificatifs_query: Query = justificatifs_query.order_by(
|
justificatifs_query: Query = justificatifs_query.order_by(
|
||||||
Justificatif.date_debut.desc()
|
Justificatif.date_debut.desc()
|
||||||
)
|
)
|
||||||
|
# cas 7 : courant (retourne uniquement les justificatifs de l'année scolaire courante)
|
||||||
courant = requested.args.get("courant", None)
|
courant = requested.args.get("courant", None)
|
||||||
if courant is not None:
|
if courant is not None:
|
||||||
annee: int = scu.annee_scolaire()
|
annee: int = scu.annee_scolaire()
|
||||||
@ -793,6 +886,7 @@ def _filter_manager(requested, justificatifs_query):
|
|||||||
Justificatif.date_fin <= scu.date_fin_anne_scolaire(annee),
|
Justificatif.date_fin <= scu.date_fin_anne_scolaire(annee),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# cas 8 : group_id filtre les justificatifs d'un groupe d'étudiant
|
||||||
group_id = requested.args.get("group_id", None)
|
group_id = requested.args.get("group_id", None)
|
||||||
if group_id is not None:
|
if group_id is not None:
|
||||||
try:
|
try:
|
||||||
|
@ -37,7 +37,9 @@ import datetime
|
|||||||
|
|
||||||
|
|
||||||
class TimeField(StringField):
|
class TimeField(StringField):
|
||||||
"""HTML5 time input."""
|
"""HTML5 time input.
|
||||||
|
tiré de : https://gist.github.com/tachyondecay/6016d32f65a996d0d94f
|
||||||
|
"""
|
||||||
|
|
||||||
widget = TimeInput()
|
widget = TimeInput()
|
||||||
|
|
||||||
|
@ -82,6 +82,7 @@ class Assiduite(db.Model):
|
|||||||
etat = self.etat
|
etat = self.etat
|
||||||
user: User = None
|
user: User = None
|
||||||
if format_api:
|
if format_api:
|
||||||
|
# format api utilise les noms "present,absent,retard" au lieu des int
|
||||||
etat = EtatAssiduite.inverse().get(self.etat).name
|
etat = EtatAssiduite.inverse().get(self.etat).name
|
||||||
if self.user_id is not None:
|
if self.user_id is not None:
|
||||||
user = db.session.get(User, self.user_id)
|
user = db.session.get(User, self.user_id)
|
||||||
@ -345,15 +346,10 @@ def is_period_conflicting(
|
|||||||
avec les justificatifs ou assiduites déjà présentes
|
avec les justificatifs ou assiduites déjà présentes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# On s'assure que les dates soient avec TimeZone
|
||||||
date_debut = localize_datetime(date_debut)
|
date_debut = localize_datetime(date_debut)
|
||||||
date_fin = localize_datetime(date_fin)
|
date_fin = localize_datetime(date_fin)
|
||||||
|
|
||||||
if (
|
|
||||||
collection.filter_by(date_debut=date_debut, date_fin=date_fin).first()
|
|
||||||
is not None
|
|
||||||
):
|
|
||||||
return True
|
|
||||||
|
|
||||||
count: int = collection.filter(
|
count: int = collection.filter(
|
||||||
collection_cls.date_debut < date_fin, collection_cls.date_fin > date_debut
|
collection_cls.date_debut < date_fin, collection_cls.date_fin > date_debut
|
||||||
).count()
|
).count()
|
||||||
@ -375,19 +371,26 @@ def compute_assiduites_justified(
|
|||||||
Returns:
|
Returns:
|
||||||
list[int]: la liste des assiduités qui ont été justifiées.
|
list[int]: la liste des assiduités qui ont été justifiées.
|
||||||
"""
|
"""
|
||||||
|
# Si on ne donne pas de justificatifs on prendra par défaut tous les justificatifs de l'étudiant
|
||||||
if justificatifs is None:
|
if justificatifs is None:
|
||||||
justificatifs: Justificatif = Justificatif.query.filter_by(etudid=etudid).all()
|
justificatifs: list[Justificatif] = Justificatif.query.filter_by(
|
||||||
|
etudid=etudid
|
||||||
|
).all()
|
||||||
|
|
||||||
|
# On ne prend que les justificatifs valides
|
||||||
justificatifs = [j for j in justificatifs if j.etat == EtatJustificatif.VALIDE]
|
justificatifs = [j for j in justificatifs if j.etat == EtatJustificatif.VALIDE]
|
||||||
|
|
||||||
|
# On récupère les assiduités de l'étudiant
|
||||||
assiduites: Assiduite = Assiduite.query.filter_by(etudid=etudid)
|
assiduites: Assiduite = Assiduite.query.filter_by(etudid=etudid)
|
||||||
|
|
||||||
assiduites_justifiees: list[int] = []
|
assiduites_justifiees: list[int] = []
|
||||||
|
|
||||||
for assi in assiduites:
|
for assi in assiduites:
|
||||||
|
# On ne justifie pas les Présences
|
||||||
if assi.etat == EtatAssiduite.PRESENT:
|
if assi.etat == EtatAssiduite.PRESENT:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# On récupère les justificatifs qui justifient l'assiduité `assi`
|
||||||
assi_justificatifs = Justificatif.query.filter(
|
assi_justificatifs = Justificatif.query.filter(
|
||||||
Justificatif.etudid == assi.etudid,
|
Justificatif.etudid == assi.etudid,
|
||||||
Justificatif.date_debut <= assi.date_debut,
|
Justificatif.date_debut <= assi.date_debut,
|
||||||
@ -395,21 +398,39 @@ def compute_assiduites_justified(
|
|||||||
Justificatif.etat == EtatJustificatif.VALIDE,
|
Justificatif.etat == EtatJustificatif.VALIDE,
|
||||||
).all()
|
).all()
|
||||||
|
|
||||||
|
# Si au moins un justificatif possède une période qui couvre l'assiduité
|
||||||
if any(
|
if any(
|
||||||
assi.date_debut >= j.date_debut and assi.date_fin <= j.date_fin
|
assi.date_debut >= j.date_debut and assi.date_fin <= j.date_fin
|
||||||
for j in justificatifs + assi_justificatifs
|
for j in justificatifs + assi_justificatifs
|
||||||
):
|
):
|
||||||
|
# On justifie l'assiduité
|
||||||
|
# On ajoute l'id de l'assiduité à la liste des assiduités justifiées
|
||||||
assi.est_just = True
|
assi.est_just = True
|
||||||
assiduites_justifiees.append(assi.assiduite_id)
|
assiduites_justifiees.append(assi.assiduite_id)
|
||||||
db.session.add(assi)
|
db.session.add(assi)
|
||||||
elif reset:
|
elif reset:
|
||||||
|
# Si le paramètre reset est Vrai alors les assiduités non justifiées
|
||||||
|
# sont remise en "non justifiée"
|
||||||
assi.est_just = False
|
assi.est_just = False
|
||||||
db.session.add(assi)
|
db.session.add(assi)
|
||||||
|
# On valide la session
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
# On renvoie la liste des assiduite_id des assiduités justifiées
|
||||||
return assiduites_justifiees
|
return assiduites_justifiees
|
||||||
|
|
||||||
|
|
||||||
def get_assiduites_justif(assiduite_id: int, long: bool):
|
def get_assiduites_justif(assiduite_id: int, long: bool) -> list[int | dict]:
|
||||||
|
"""
|
||||||
|
get_assiduites_justif Récupération des justificatifs d'une assiduité
|
||||||
|
|
||||||
|
Args:
|
||||||
|
assiduite_id (int): l'identifiant de l'assiduité
|
||||||
|
long (bool): Retourner des dictionnaires à la place
|
||||||
|
des identifiants des justificatifs
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[int | dict]: La liste des justificatifs (par défaut uniquement les identifiants, sinon les Dict si long est vrai)
|
||||||
|
"""
|
||||||
assi: Assiduite = Assiduite.query.get_or_404(assiduite_id)
|
assi: Assiduite = Assiduite.query.get_or_404(assiduite_id)
|
||||||
return get_justifs_from_date(assi.etudid, assi.date_debut, assi.date_fin, long)
|
return get_justifs_from_date(assi.etudid, assi.date_debut, assi.date_fin, long)
|
||||||
|
|
||||||
@ -420,20 +441,57 @@ def get_justifs_from_date(
|
|||||||
date_fin: datetime,
|
date_fin: datetime,
|
||||||
long: bool = False,
|
long: bool = False,
|
||||||
valid: bool = False,
|
valid: bool = False,
|
||||||
):
|
) -> list[int | dict]:
|
||||||
|
"""
|
||||||
|
get_justifs_from_date Récupération des justificatifs couvrant une période pour un étudiant donné
|
||||||
|
|
||||||
|
Args:
|
||||||
|
etudid (int): l'identifiant de l'étudiant
|
||||||
|
date_debut (datetime): la date de début (datetime avec timezone)
|
||||||
|
date_fin (datetime): la date de fin (datetime avec timezone)
|
||||||
|
long (bool, optional): Définition de la sortie.
|
||||||
|
Vrai pour avoir les dictionnaires des justificatifs.
|
||||||
|
Faux pour avoir uniquement les identifiants
|
||||||
|
Defaults to False.
|
||||||
|
valid (bool, optional): Filtre pour n'avoir que les justificatifs valide.
|
||||||
|
Si vrai : le retour ne contiendra que des justificatifs valides
|
||||||
|
Sinon le retour contiendra tout type de justificatifs
|
||||||
|
Defaults to False.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[int | dict]: La liste des justificatifs (par défaut uniquement les identifiants, sinon les Dict si long est vrai)
|
||||||
|
"""
|
||||||
|
# On récupère les justificatifs d'un étudiant couvrant la période donnée
|
||||||
justifs: Query = Justificatif.query.filter(
|
justifs: Query = Justificatif.query.filter(
|
||||||
Justificatif.etudid == etudid,
|
Justificatif.etudid == etudid,
|
||||||
Justificatif.date_debut <= date_debut,
|
Justificatif.date_debut <= date_debut,
|
||||||
Justificatif.date_fin >= date_fin,
|
Justificatif.date_fin >= date_fin,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# si valide est vrai alors on filtre pour n'avoir que les justificatifs valide
|
||||||
if valid:
|
if valid:
|
||||||
justifs = justifs.filter(Justificatif.etat == EtatJustificatif.VALIDE)
|
justifs = justifs.filter(Justificatif.etat == EtatJustificatif.VALIDE)
|
||||||
|
|
||||||
|
# On renvoie la liste des id des justificatifs si long est Faux, sinon on renvoie les dicts des justificatifs
|
||||||
return [j.justif_id if not long else j.to_dict(True) for j in justifs]
|
return [j.justif_id if not long else j.to_dict(True) for j in justifs]
|
||||||
|
|
||||||
|
|
||||||
def get_formsemestre_from_data(data: dict[str, datetime | int]) -> FormSemestre:
|
def get_formsemestre_from_data(data: dict[str, datetime | int]) -> FormSemestre:
|
||||||
|
"""
|
||||||
|
get_formsemestre_from_data récupère un formsemestre en fonction des données passées
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data (dict[str, datetime | int]): Une réprésentation simplifiée d'une assiduité ou d'un justificatif
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"etudid" : int,
|
||||||
|
"date_debut": datetime (tz),
|
||||||
|
"date_fin": datetime (tz),
|
||||||
|
}
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
FormSemestre: Le formsemestre trouvé ou None
|
||||||
|
"""
|
||||||
return (
|
return (
|
||||||
FormSemestre.query.join(
|
FormSemestre.query.join(
|
||||||
FormSemestreInscription,
|
FormSemestreInscription,
|
||||||
|
@ -755,9 +755,7 @@ function isConflictSameAsPeriod(conflict, period = undefined) {
|
|||||||
* @returns {Date} la date sélectionnée
|
* @returns {Date} la date sélectionnée
|
||||||
*/
|
*/
|
||||||
function getDate() {
|
function getDate() {
|
||||||
const date = new Date(
|
const date = new Date(document.querySelector("#tl_date").value);
|
||||||
document.querySelector("#tl_date").getAttribute("value")
|
|
||||||
);
|
|
||||||
date.setHours(0, 0, 0, 0);
|
date.setHours(0, 0, 0, 0);
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
@ -1652,9 +1650,6 @@ function fastJustify(assiduite) {
|
|||||||
fin: new moment.tz(assiduite.date_fin, TIMEZONE),
|
fin: new moment.tz(assiduite.date_fin, TIMEZONE),
|
||||||
};
|
};
|
||||||
const action = (justifs) => {
|
const action = (justifs) => {
|
||||||
if (justifs.length > 0) {
|
|
||||||
justifyAssiduite(assiduite.assiduite_id, !assiduite.est_just);
|
|
||||||
} else {
|
|
||||||
//créer un nouveau justificatif
|
//créer un nouveau justificatif
|
||||||
// Afficher prompt -> demander raison et état
|
// Afficher prompt -> demander raison et état
|
||||||
|
|
||||||
@ -1701,7 +1696,6 @@ function fastJustify(assiduite) {
|
|||||||
() => {},
|
() => {},
|
||||||
"#7059FF"
|
"#7059FF"
|
||||||
);
|
);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
if (assiduite.etudid) {
|
if (assiduite.etudid) {
|
||||||
getJustificatifFromPeriod(period, assiduite.etudid, action);
|
getJustificatifFromPeriod(period, assiduite.etudid, action);
|
||||||
@ -1804,7 +1798,9 @@ function getModuleImpl(assiduite) {
|
|||||||
assiduite.external_data != null &&
|
assiduite.external_data != null &&
|
||||||
assiduite.external_data.hasOwnProperty("module")
|
assiduite.external_data.hasOwnProperty("module")
|
||||||
) {
|
) {
|
||||||
return assiduite.external_data.module;
|
return assiduite.external_data.module == "Autre"
|
||||||
|
? "Tout module"
|
||||||
|
: assiduite.external_data.module;
|
||||||
} else {
|
} else {
|
||||||
return "Pas de module";
|
return "Pas de module";
|
||||||
}
|
}
|
||||||
|
@ -345,8 +345,8 @@
|
|||||||
let assi = Object.values(assiduites).flat().filter((a) => { return a.assiduite_id == obj_id })[0]
|
let assi = Object.values(assiduites).flat().filter((a) => { return a.assiduite_id == obj_id })[0]
|
||||||
li.addEventListener('click', () => {
|
li.addEventListener('click', () => {
|
||||||
|
|
||||||
if (assiduite && !assiduite[0].est_just && assiduite[0].etat != "PRESENT") {
|
if (assi && !assi.est_just && assi.etat != "PRESENT") {
|
||||||
fastJustify(assiduite[0])
|
fastJustify(assi)
|
||||||
} else {
|
} else {
|
||||||
openAlertModal("Erreur", document.createTextNode("L'assiduité est déjà justifiée."))
|
openAlertModal("Erreur", document.createTextNode("L'assiduité est déjà justifiée."))
|
||||||
}
|
}
|
||||||
|
@ -160,6 +160,8 @@ class HTMLBuilder:
|
|||||||
@permission_required(Permission.AbsChange)
|
@permission_required(Permission.AbsChange)
|
||||||
def bilan_dept():
|
def bilan_dept():
|
||||||
"""Gestionnaire assiduités, page principale"""
|
"""Gestionnaire assiduités, page principale"""
|
||||||
|
|
||||||
|
# Préparation de la page
|
||||||
H = [
|
H = [
|
||||||
html_sco_header.sco_header(
|
html_sco_header.sco_header(
|
||||||
page_title="Saisie de l'assiduité",
|
page_title="Saisie de l'assiduité",
|
||||||
@ -183,7 +185,10 @@ def bilan_dept():
|
|||||||
"""<p class="help">Pour signaler, annuler ou justifier l'assiduité d'un seul étudiant,
|
"""<p class="help">Pour signaler, annuler ou justifier l'assiduité d'un seul étudiant,
|
||||||
choisissez d'abord la personne concernée :</p>"""
|
choisissez d'abord la personne concernée :</p>"""
|
||||||
)
|
)
|
||||||
|
# Ajout de la barre de recherche d'étudiant (redirection vers bilan etud)
|
||||||
H.append(sco_find_etud.form_search_etud(dest_url="assiduites.bilan_etud"))
|
H.append(sco_find_etud.form_search_etud(dest_url="assiduites.bilan_etud"))
|
||||||
|
|
||||||
|
# Gestion des billets d'absences
|
||||||
if current_user.has_permission(
|
if current_user.has_permission(
|
||||||
Permission.AbsChange
|
Permission.AbsChange
|
||||||
) and sco_preferences.get_preference("handle_billets_abs"):
|
) and sco_preferences.get_preference("handle_billets_abs"):
|
||||||
@ -195,19 +200,23 @@ def bilan_dept():
|
|||||||
</li></ul>
|
</li></ul>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Récupération des années d'étude du département
|
||||||
|
# (afin de sélectionner une année)
|
||||||
dept: Departement = Departement.query.filter_by(id=g.scodoc_dept_id).first()
|
dept: Departement = Departement.query.filter_by(id=g.scodoc_dept_id).first()
|
||||||
annees: list[int] = sorted(
|
annees: list[int] = sorted(
|
||||||
[f.date_debut.year for f in dept.formsemestres],
|
[f.date_debut.year for f in dept.formsemestres],
|
||||||
reverse=True,
|
reverse=True,
|
||||||
)
|
)
|
||||||
|
annee = scu.annee_scolaire() # Année courante, sera utilisée par défaut
|
||||||
annee = scu.annee_scolaire()
|
# Génération d'une liste "json" d'années
|
||||||
|
|
||||||
annees_str: str = "["
|
annees_str: str = "["
|
||||||
for ann in annees:
|
for ann in annees:
|
||||||
annees_str += f"{ann},"
|
annees_str += f"{ann},"
|
||||||
annees_str += "]"
|
annees_str += "]"
|
||||||
|
|
||||||
|
# Récupération d'un formsemestre
|
||||||
|
# (pour n'afficher que les assiduites/justificatifs liés au formsemestre)
|
||||||
formsemestre_id = request.args.get("formsemestre_id", "")
|
formsemestre_id = request.args.get("formsemestre_id", "")
|
||||||
if formsemestre_id:
|
if formsemestre_id:
|
||||||
try:
|
try:
|
||||||
@ -216,6 +225,7 @@ def bilan_dept():
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
formsemestre_id = ""
|
formsemestre_id = ""
|
||||||
|
|
||||||
|
# Peuplement du template jinja
|
||||||
H.append(
|
H.append(
|
||||||
render_template(
|
render_template(
|
||||||
"assiduites/pages/bilan_dept.j2",
|
"assiduites/pages/bilan_dept.j2",
|
||||||
@ -230,49 +240,6 @@ def bilan_dept():
|
|||||||
return "\n".join(H)
|
return "\n".join(H)
|
||||||
|
|
||||||
|
|
||||||
# @bp.route("/ListeSemestre")
|
|
||||||
# @scodoc
|
|
||||||
# @permission_required(Permission.ScoView)
|
|
||||||
# def liste_assiduites_formsemestre():
|
|
||||||
# """
|
|
||||||
# liste_assiduites_etud Affichage de toutes les assiduites et justificatifs d'un etudiant
|
|
||||||
# Args:
|
|
||||||
# etudid (int): l'identifiant de l'étudiant
|
|
||||||
|
|
||||||
# Returns:
|
|
||||||
# str: l'html généré
|
|
||||||
# """
|
|
||||||
|
|
||||||
# formsemestre_id = request.args.get("formsemestre_id", -1)
|
|
||||||
# formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
|
||||||
# if formsemestre.dept_id != g.scodoc_dept_id:
|
|
||||||
# abort(404, "FormSemestre inexistant dans ce département")
|
|
||||||
|
|
||||||
# header: str = html_sco_header.sco_header(
|
|
||||||
# page_title="Liste des assiduités du semestre",
|
|
||||||
# init_qtip=True,
|
|
||||||
# javascripts=[
|
|
||||||
# "js/assiduites.js",
|
|
||||||
# "libjs/moment-2.29.4.min.js",
|
|
||||||
# "libjs/moment-timezone.js",
|
|
||||||
# ],
|
|
||||||
# cssstyles=CSSSTYLES
|
|
||||||
# + [
|
|
||||||
# "css/assiduites.css",
|
|
||||||
# ],
|
|
||||||
# )
|
|
||||||
|
|
||||||
# return HTMLBuilder(
|
|
||||||
# header,
|
|
||||||
# render_template(
|
|
||||||
# "assiduites/pages/liste_semestre.j2",
|
|
||||||
# sco=ScoData(formsemestre=formsemestre),
|
|
||||||
# sem=formsemestre.titre_annee(),
|
|
||||||
# formsemestre_id=formsemestre.id,
|
|
||||||
# ),
|
|
||||||
# ).build()
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/SignaleAssiduiteEtud")
|
@bp.route("/SignaleAssiduiteEtud")
|
||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.AbsChange)
|
@permission_required(Permission.AbsChange)
|
||||||
@ -287,22 +254,22 @@ def signal_assiduites_etud():
|
|||||||
str: l'html généré
|
str: l'html généré
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Récupération de l'étudiant concerné
|
||||||
etudid = request.args.get("etudid", -1)
|
etudid = request.args.get("etudid", -1)
|
||||||
etud: Identite = Identite.query.get_or_404(etudid)
|
etud: Identite = Identite.query.get_or_404(etudid)
|
||||||
if etud.dept_id != g.scodoc_dept_id:
|
if etud.dept_id != g.scodoc_dept_id:
|
||||||
abort(404, "étudiant inexistant dans ce département")
|
abort(404, "étudiant inexistant dans ce département")
|
||||||
|
|
||||||
|
# Récupération de la date (par défaut la date du jour)
|
||||||
date = request.args.get("date", datetime.date.today().isoformat())
|
date = request.args.get("date", datetime.date.today().isoformat())
|
||||||
|
|
||||||
# gestion évaluations
|
# gestion évaluations (Appel à la page depuis les évaluations)
|
||||||
|
|
||||||
saisie_eval: bool = request.args.get("saisie_eval") is not None
|
saisie_eval: bool = request.args.get("saisie_eval") is not None
|
||||||
|
|
||||||
date_deb: str = request.args.get("date_deb")
|
date_deb: str = request.args.get("date_deb")
|
||||||
date_fin: str = request.args.get("date_fin")
|
date_fin: str = request.args.get("date_fin")
|
||||||
moduleimpl_id: int = request.args.get("moduleimpl_id", "")
|
moduleimpl_id: int = request.args.get("moduleimpl_id", "")
|
||||||
evaluation_id: int = request.args.get("evaluation_id")
|
evaluation_id: int = request.args.get("evaluation_id")
|
||||||
|
|
||||||
redirect_url: str = (
|
redirect_url: str = (
|
||||||
"#"
|
"#"
|
||||||
if not saisie_eval
|
if not saisie_eval
|
||||||
@ -313,6 +280,7 @@ def signal_assiduites_etud():
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Préparation de la page (Header)
|
||||||
header: str = html_sco_header.sco_header(
|
header: str = html_sco_header.sco_header(
|
||||||
page_title="Saisie assiduité",
|
page_title="Saisie assiduité",
|
||||||
init_qtip=True,
|
init_qtip=True,
|
||||||
@ -335,11 +303,14 @@ def signal_assiduites_etud():
|
|||||||
"assi_afternoon_time", "18:00:00"
|
"assi_afternoon_time", "18:00:00"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Gestion du selecteur de moduleimpl (pour le tableau différé)
|
||||||
select = f"""
|
select = f"""
|
||||||
<select class="dynaSelect">
|
<select class="dynaSelect">
|
||||||
{render_template("assiduites/widgets/simplemoduleimpl_select.j2")}
|
{render_template("assiduites/widgets/simplemoduleimpl_select.j2")}
|
||||||
</select>
|
</select>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Génération de la page
|
||||||
return HTMLBuilder(
|
return HTMLBuilder(
|
||||||
header,
|
header,
|
||||||
_mini_timeline(),
|
_mini_timeline(),
|
||||||
@ -381,13 +352,16 @@ def liste_assiduites_etud():
|
|||||||
str: l'html généré
|
str: l'html généré
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Récupération de l'étudiant concerné
|
||||||
etudid = request.args.get("etudid", -1)
|
etudid = request.args.get("etudid", -1)
|
||||||
etud: Identite = Identite.query.get_or_404(etudid)
|
etud: Identite = Identite.query.get_or_404(etudid)
|
||||||
if etud.dept_id != g.scodoc_dept_id:
|
if etud.dept_id != g.scodoc_dept_id:
|
||||||
abort(404, "étudiant inexistant dans ce département")
|
abort(404, "étudiant inexistant dans ce département")
|
||||||
|
|
||||||
|
# Gestion d'une assiduité unique (redirigé depuis le calendrier)
|
||||||
assiduite_id: int = request.args.get("assiduite_id", -1)
|
assiduite_id: int = request.args.get("assiduite_id", -1)
|
||||||
|
|
||||||
|
# Préparation de la page
|
||||||
header: str = html_sco_header.sco_header(
|
header: str = html_sco_header.sco_header(
|
||||||
page_title=f"Assiduité de {etud.nomprenom}",
|
page_title=f"Assiduité de {etud.nomprenom}",
|
||||||
init_qtip=True,
|
init_qtip=True,
|
||||||
@ -401,7 +375,7 @@ def liste_assiduites_etud():
|
|||||||
"css/assiduites.css",
|
"css/assiduites.css",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
# Peuplement du template jinja
|
||||||
return HTMLBuilder(
|
return HTMLBuilder(
|
||||||
header,
|
header,
|
||||||
render_template(
|
render_template(
|
||||||
@ -429,12 +403,13 @@ def bilan_etud():
|
|||||||
Returns:
|
Returns:
|
||||||
str: l'html généré
|
str: l'html généré
|
||||||
"""
|
"""
|
||||||
|
# Récupération de l'étudiant
|
||||||
etudid = request.args.get("etudid", -1)
|
etudid = request.args.get("etudid", -1)
|
||||||
etud: Identite = Identite.query.get_or_404(etudid)
|
etud: Identite = Identite.query.get_or_404(etudid)
|
||||||
if etud.dept_id != g.scodoc_dept_id:
|
if etud.dept_id != g.scodoc_dept_id:
|
||||||
abort(404, "étudiant inexistant dans ce département")
|
abort(404, "étudiant inexistant dans ce département")
|
||||||
|
|
||||||
|
# Préparation de la page (header)
|
||||||
header: str = html_sco_header.sco_header(
|
header: str = html_sco_header.sco_header(
|
||||||
page_title=f"Bilan de l'assiduité de {etud.nomprenom}",
|
page_title=f"Bilan de l'assiduité de {etud.nomprenom}",
|
||||||
init_qtip=True,
|
init_qtip=True,
|
||||||
@ -449,13 +424,16 @@ def bilan_etud():
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Gestion des dates du bilan (par défaut l'année scolaire)
|
||||||
date_debut: str = f"{scu.annee_scolaire()}-09-01"
|
date_debut: str = f"{scu.annee_scolaire()}-09-01"
|
||||||
date_fin: str = f"{scu.annee_scolaire()+1}-06-30"
|
date_fin: str = f"{scu.annee_scolaire()+1}-06-30"
|
||||||
|
|
||||||
|
# Récupération de la métrique d'assiduité
|
||||||
assi_metric = scu.translate_assiduites_metric(
|
assi_metric = scu.translate_assiduites_metric(
|
||||||
sco_preferences.get_preference("assi_metrique", dept_id=g.scodoc_dept_id),
|
sco_preferences.get_preference("assi_metrique", dept_id=g.scodoc_dept_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Génération de la page
|
||||||
return HTMLBuilder(
|
return HTMLBuilder(
|
||||||
header,
|
header,
|
||||||
render_template(
|
render_template(
|
||||||
@ -486,11 +464,13 @@ def ajout_justificatif_etud():
|
|||||||
str: l'html généré
|
str: l'html généré
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Récupération de l'étudiant concerné
|
||||||
etudid = request.args.get("etudid", -1)
|
etudid = request.args.get("etudid", -1)
|
||||||
etud: Identite = Identite.query.get_or_404(etudid)
|
etud: Identite = Identite.query.get_or_404(etudid)
|
||||||
if etud.dept_id != g.scodoc_dept_id:
|
if etud.dept_id != g.scodoc_dept_id:
|
||||||
abort(404, "étudiant inexistant dans ce département")
|
abort(404, "étudiant inexistant dans ce département")
|
||||||
|
|
||||||
|
# Préparation de la page (header)
|
||||||
header: str = html_sco_header.sco_header(
|
header: str = html_sco_header.sco_header(
|
||||||
page_title="Justificatifs",
|
page_title="Justificatifs",
|
||||||
init_qtip=True,
|
init_qtip=True,
|
||||||
@ -505,6 +485,7 @@ def ajout_justificatif_etud():
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Peuplement du template jinja
|
||||||
return HTMLBuilder(
|
return HTMLBuilder(
|
||||||
header,
|
header,
|
||||||
render_template(
|
render_template(
|
||||||
@ -533,11 +514,13 @@ def calendrier_etud():
|
|||||||
str: l'html généré
|
str: l'html généré
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Récupération de l'étudiant
|
||||||
etudid = request.args.get("etudid", -1)
|
etudid = request.args.get("etudid", -1)
|
||||||
etud: Identite = Identite.query.get_or_404(etudid)
|
etud: Identite = Identite.query.get_or_404(etudid)
|
||||||
if etud.dept_id != g.scodoc_dept_id:
|
if etud.dept_id != g.scodoc_dept_id:
|
||||||
abort(404, "étudiant inexistant dans ce département")
|
abort(404, "étudiant inexistant dans ce département")
|
||||||
|
|
||||||
|
# Préparation de la page
|
||||||
header: str = html_sco_header.sco_header(
|
header: str = html_sco_header.sco_header(
|
||||||
page_title="Calendrier de l'assiduité",
|
page_title="Calendrier de l'assiduité",
|
||||||
init_qtip=True,
|
init_qtip=True,
|
||||||
@ -552,16 +535,20 @@ def calendrier_etud():
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Récupération des années d'étude de l'étudiant
|
||||||
annees: list[int] = sorted(
|
annees: list[int] = sorted(
|
||||||
[ins.formsemestre.date_debut.year for ins in etud.formsemestre_inscriptions],
|
[ins.formsemestre.date_debut.year for ins in etud.formsemestre_inscriptions],
|
||||||
reverse=True,
|
reverse=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Transformation en une liste "json"
|
||||||
|
# (sera utilisé pour générer le selecteur d'année)
|
||||||
annees_str: str = "["
|
annees_str: str = "["
|
||||||
for ann in annees:
|
for ann in annees:
|
||||||
annees_str += f"{ann},"
|
annees_str += f"{ann},"
|
||||||
annees_str += "]"
|
annees_str += "]"
|
||||||
|
|
||||||
|
# Peuplement du template jinja
|
||||||
return HTMLBuilder(
|
return HTMLBuilder(
|
||||||
header,
|
header,
|
||||||
render_template(
|
render_template(
|
||||||
@ -585,11 +572,11 @@ def signal_assiduites_group():
|
|||||||
Returns:
|
Returns:
|
||||||
str: l'html généré
|
str: l'html généré
|
||||||
"""
|
"""
|
||||||
|
# Récupération des paramètres de l'url
|
||||||
formsemestre_id: int = request.args.get("formsemestre_id", -1)
|
formsemestre_id: int = request.args.get("formsemestre_id", -1)
|
||||||
moduleimpl_id: int = request.args.get("moduleimpl_id")
|
moduleimpl_id: int = request.args.get("moduleimpl_id")
|
||||||
date: str = request.args.get("jour", datetime.date.today().isoformat())
|
date: str = request.args.get("jour", datetime.date.today().isoformat())
|
||||||
group_ids: list[int] = request.args.get("group_ids", None)
|
group_ids: list[int] = request.args.get("group_ids", None)
|
||||||
|
|
||||||
if group_ids is None:
|
if group_ids is None:
|
||||||
group_ids = []
|
group_ids = []
|
||||||
else:
|
else:
|
||||||
@ -601,12 +588,14 @@ def signal_assiduites_group():
|
|||||||
moduleimpl_id = int(moduleimpl_id)
|
moduleimpl_id = int(moduleimpl_id)
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
moduleimpl_id = None
|
moduleimpl_id = None
|
||||||
|
|
||||||
# Vérification du formsemestre_id
|
# Vérification du formsemestre_id
|
||||||
try:
|
try:
|
||||||
formsemestre_id = int(formsemestre_id)
|
formsemestre_id = int(formsemestre_id)
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
formsemestre_id = None
|
formsemestre_id = None
|
||||||
|
|
||||||
|
# Gestion des groupes
|
||||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(
|
groups_infos = sco_groups_view.DisplayedGroupsInfos(
|
||||||
group_ids, moduleimpl_id=moduleimpl_id, formsemestre_id=formsemestre_id
|
group_ids, moduleimpl_id=moduleimpl_id, formsemestre_id=formsemestre_id
|
||||||
)
|
)
|
||||||
@ -617,10 +606,6 @@ def signal_assiduites_group():
|
|||||||
+ html_sco_header.sco_footer()
|
+ html_sco_header.sco_footer()
|
||||||
)
|
)
|
||||||
|
|
||||||
# --- URL DEFAULT ---
|
|
||||||
|
|
||||||
base_url: str = f"SignalAssiduiteGr?date={date}&{groups_infos.groups_query_args}"
|
|
||||||
|
|
||||||
# --- Filtrage par formsemestre ---
|
# --- Filtrage par formsemestre ---
|
||||||
formsemestre_id = groups_infos.formsemestre_id
|
formsemestre_id = groups_infos.formsemestre_id
|
||||||
|
|
||||||
@ -628,17 +613,22 @@ def signal_assiduites_group():
|
|||||||
if formsemestre.dept_id != g.scodoc_dept_id:
|
if formsemestre.dept_id != g.scodoc_dept_id:
|
||||||
abort(404, "groupes inexistants dans ce département")
|
abort(404, "groupes inexistants dans ce département")
|
||||||
|
|
||||||
|
# Vérification du forçage du module
|
||||||
require_module = sco_preferences.get_preference("forcer_module", formsemestre_id)
|
require_module = sco_preferences.get_preference("forcer_module", formsemestre_id)
|
||||||
|
|
||||||
|
# Récupération des étudiants des groupes
|
||||||
etuds = [
|
etuds = [
|
||||||
sco_etud.get_etud_info(etudid=m["etudid"], filled=True)[0]
|
sco_etud.get_etud_info(etudid=m["etudid"], filled=True)[0]
|
||||||
for m in groups_infos.members
|
for m in groups_infos.members
|
||||||
]
|
]
|
||||||
|
|
||||||
# --- Vérification de la date ---
|
# --- Vérification de la date ---
|
||||||
|
|
||||||
real_date = scu.is_iso_formated(date, True).date()
|
real_date = scu.is_iso_formated(date, True).date()
|
||||||
|
|
||||||
if real_date < formsemestre.date_debut or real_date > formsemestre.date_fin:
|
if real_date < formsemestre.date_debut or real_date > formsemestre.date_fin:
|
||||||
|
# Si le jour est hors semestre, indiquer une erreur
|
||||||
|
|
||||||
|
# Formatage des dates pour le message d'erreur
|
||||||
real_str = real_date.strftime("%d/%m/%Y")
|
real_str = real_date.strftime("%d/%m/%Y")
|
||||||
form_deb = formsemestre.date_debut.strftime("%d/%m/%Y")
|
form_deb = formsemestre.date_debut.strftime("%d/%m/%Y")
|
||||||
form_fin = formsemestre.date_fin.strftime("%d/%m/%Y")
|
form_fin = formsemestre.date_fin.strftime("%d/%m/%Y")
|
||||||
@ -662,8 +652,7 @@ def signal_assiduites_group():
|
|||||||
# Si aucun etudiant n'est inscrit au module choisi...
|
# Si aucun etudiant n'est inscrit au module choisi...
|
||||||
moduleimpl_id = None
|
moduleimpl_id = None
|
||||||
|
|
||||||
# --- Génération de l'HTML ---
|
# Récupération du nom des/du groupe(s)
|
||||||
sem = formsemestre.to_dict()
|
|
||||||
|
|
||||||
if groups_infos.tous_les_etuds_du_sem:
|
if groups_infos.tous_les_etuds_du_sem:
|
||||||
gr_tit = "en"
|
gr_tit = "en"
|
||||||
@ -676,12 +665,15 @@ def signal_assiduites_group():
|
|||||||
grp + ' <span class="fontred">' + groups_infos.groups_titles + "</span>"
|
grp + ' <span class="fontred">' + groups_infos.groups_titles + "</span>"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# --- Génération de l'HTML ---
|
||||||
|
|
||||||
header: str = html_sco_header.sco_header(
|
header: str = html_sco_header.sco_header(
|
||||||
page_title="Saisie journalière des assiduités",
|
page_title="Saisie journalière des assiduités",
|
||||||
init_qtip=True,
|
init_qtip=True,
|
||||||
javascripts=html_sco_header.BOOTSTRAP_MULTISELECT_JS
|
javascripts=html_sco_header.BOOTSTRAP_MULTISELECT_JS
|
||||||
+ [
|
+ [
|
||||||
# Voir fonctionnement JS
|
# Voir fonctionnement JS
|
||||||
|
# XXX Retirer moment
|
||||||
"js/etud_info.js",
|
"js/etud_info.js",
|
||||||
"js/groups_view.js",
|
"js/groups_view.js",
|
||||||
"js/assiduites.js",
|
"js/assiduites.js",
|
||||||
@ -694,6 +686,10 @@ def signal_assiduites_group():
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Récupération du semestre en dictionnaire
|
||||||
|
sem = formsemestre.to_dict()
|
||||||
|
|
||||||
|
# Peuplement du template jinja
|
||||||
return HTMLBuilder(
|
return HTMLBuilder(
|
||||||
header,
|
header,
|
||||||
_mini_timeline(),
|
_mini_timeline(),
|
||||||
@ -731,11 +727,12 @@ def visu_assiduites_group():
|
|||||||
Returns:
|
Returns:
|
||||||
str: l'html généré
|
str: l'html généré
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Récupération des paramètres de la requête
|
||||||
formsemestre_id: int = request.args.get("formsemestre_id", -1)
|
formsemestre_id: int = request.args.get("formsemestre_id", -1)
|
||||||
moduleimpl_id: int = request.args.get("moduleimpl_id")
|
moduleimpl_id: int = request.args.get("moduleimpl_id")
|
||||||
date: str = request.args.get("jour", datetime.date.today().isoformat())
|
date: str = request.args.get("jour", datetime.date.today().isoformat())
|
||||||
group_ids: list[int] = request.args.get("group_ids", None)
|
group_ids: list[int] = request.args.get("group_ids", None)
|
||||||
|
|
||||||
if group_ids is None:
|
if group_ids is None:
|
||||||
group_ids = []
|
group_ids = []
|
||||||
else:
|
else:
|
||||||
@ -755,6 +752,7 @@ def visu_assiduites_group():
|
|||||||
except (TypeError, ValueError) as exc:
|
except (TypeError, ValueError) as exc:
|
||||||
raise ScoValueError("identifiant de formsemestre invalide") from exc
|
raise ScoValueError("identifiant de formsemestre invalide") from exc
|
||||||
|
|
||||||
|
# Récupérations des/du groupe(s)
|
||||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(
|
groups_infos = sco_groups_view.DisplayedGroupsInfos(
|
||||||
group_ids, moduleimpl_id=moduleimpl_id, formsemestre_id=formsemestre_id
|
group_ids, moduleimpl_id=moduleimpl_id, formsemestre_id=formsemestre_id
|
||||||
)
|
)
|
||||||
@ -765,10 +763,6 @@ def visu_assiduites_group():
|
|||||||
+ html_sco_header.sco_footer()
|
+ html_sco_header.sco_footer()
|
||||||
)
|
)
|
||||||
|
|
||||||
# --- URL DEFAULT ---
|
|
||||||
|
|
||||||
base_url: str = f"SignalAssiduiteGr?date={date}&{groups_infos.groups_query_args}"
|
|
||||||
|
|
||||||
# --- Filtrage par formsemestre ---
|
# --- Filtrage par formsemestre ---
|
||||||
formsemestre_id = groups_infos.formsemestre_id
|
formsemestre_id = groups_infos.formsemestre_id
|
||||||
|
|
||||||
@ -776,10 +770,10 @@ def visu_assiduites_group():
|
|||||||
if formsemestre.dept_id != g.scodoc_dept_id:
|
if formsemestre.dept_id != g.scodoc_dept_id:
|
||||||
abort(404, "groupes inexistants dans ce département")
|
abort(404, "groupes inexistants dans ce département")
|
||||||
|
|
||||||
require_module = sco_preferences.get_preference(
|
# Vérfication du forçage du module
|
||||||
"abs_require_module", formsemestre_id
|
require_module = sco_preferences.get_preference("forcer_module", formsemestre_id)
|
||||||
)
|
|
||||||
|
|
||||||
|
# Récupération des étudiants du/des groupe(s)
|
||||||
etuds = [
|
etuds = [
|
||||||
sco_etud.get_etud_info(etudid=m["etudid"], filled=True)[0]
|
sco_etud.get_etud_info(etudid=m["etudid"], filled=True)[0]
|
||||||
for m in groups_infos.members
|
for m in groups_infos.members
|
||||||
@ -810,7 +804,6 @@ def visu_assiduites_group():
|
|||||||
moduleimpl_id = None
|
moduleimpl_id = None
|
||||||
|
|
||||||
# --- Génération de l'HTML ---
|
# --- Génération de l'HTML ---
|
||||||
sem = formsemestre.to_dict()
|
|
||||||
|
|
||||||
if groups_infos.tous_les_etuds_du_sem:
|
if groups_infos.tous_les_etuds_du_sem:
|
||||||
gr_tit = "en"
|
gr_tit = "en"
|
||||||
@ -841,6 +834,9 @@ def visu_assiduites_group():
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Récupération du semestre en dictionnaire
|
||||||
|
sem = formsemestre.to_dict()
|
||||||
|
|
||||||
return HTMLBuilder(
|
return HTMLBuilder(
|
||||||
header,
|
header,
|
||||||
_mini_timeline(),
|
_mini_timeline(),
|
||||||
@ -873,10 +869,14 @@ def visu_assiduites_group():
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
def etat_abs_date():
|
def etat_abs_date():
|
||||||
"""date_debut, date_fin en ISO"""
|
"""date_debut, date_fin en ISO"""
|
||||||
|
|
||||||
|
# Récupération des paramètre de la requête
|
||||||
date_debut_str = request.args.get("date_debut")
|
date_debut_str = request.args.get("date_debut")
|
||||||
date_fin_str = request.args.get("date_fin")
|
date_fin_str = request.args.get("date_fin")
|
||||||
title = request.args.get("desc")
|
title = request.args.get("desc")
|
||||||
group_ids: list[int] = request.args.get("group_ids", None)
|
group_ids: list[int] = request.args.get("group_ids", None)
|
||||||
|
|
||||||
|
# Vérification des dates
|
||||||
try:
|
try:
|
||||||
date_debut = datetime.datetime.fromisoformat(date_debut_str)
|
date_debut = datetime.datetime.fromisoformat(date_debut_str)
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
@ -885,6 +885,8 @@ def etat_abs_date():
|
|||||||
date_fin = datetime.datetime.fromisoformat(date_fin_str)
|
date_fin = datetime.datetime.fromisoformat(date_fin_str)
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
raise ScoValueError("date_fin invalide") from exc
|
raise ScoValueError("date_fin invalide") from exc
|
||||||
|
|
||||||
|
# Vérification des groupes
|
||||||
if group_ids is None:
|
if group_ids is None:
|
||||||
group_ids = []
|
group_ids = []
|
||||||
else:
|
else:
|
||||||
@ -893,25 +895,30 @@ def etat_abs_date():
|
|||||||
|
|
||||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids)
|
groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids)
|
||||||
|
|
||||||
|
# Récupération des étudiants des groupes
|
||||||
etuds = [
|
etuds = [
|
||||||
sco_etud.get_etud_info(etudid=m["etudid"], filled=True)[0]
|
sco_etud.get_etud_info(etudid=m["etudid"], filled=True)[0]
|
||||||
for m in groups_infos.members
|
for m in groups_infos.members
|
||||||
]
|
]
|
||||||
|
# Récupération des assiduites des étudiants
|
||||||
assiduites: Assiduite = Assiduite.query.filter(
|
assiduites: Assiduite = Assiduite.query.filter(
|
||||||
Assiduite.etudid.in_([e["etudid"] for e in etuds])
|
Assiduite.etudid.in_([e["etudid"] for e in etuds])
|
||||||
)
|
)
|
||||||
|
# Filtrage des assiduités en fonction des dates données
|
||||||
assiduites = scass.filter_by_date(
|
assiduites = scass.filter_by_date(
|
||||||
assiduites, Assiduite, date_debut, date_fin, False
|
assiduites, Assiduite, date_debut, date_fin, False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Génération d'objet étudiant simplifié (nom+lien cal, etat_assiduite)
|
||||||
etudiants: list[dict] = []
|
etudiants: list[dict] = []
|
||||||
for etud in etuds:
|
for etud in etuds:
|
||||||
|
# On récupère l'état de la première assiduité sur la période
|
||||||
assi = assiduites.filter_by(etudid=etud["etudid"]).first()
|
assi = assiduites.filter_by(etudid=etud["etudid"]).first()
|
||||||
|
|
||||||
etat = ""
|
etat = ""
|
||||||
if assi is not None and assi.etat != 0:
|
if assi is not None and assi.etat != 0:
|
||||||
etat = scu.EtatAssiduite.inverse().get(assi.etat).name
|
etat = scu.EtatAssiduite.inverse().get(assi.etat).name
|
||||||
|
|
||||||
|
# On génère l'objet simplifié
|
||||||
etudiant = {
|
etudiant = {
|
||||||
"nom": f"""<a href="{url_for(
|
"nom": f"""<a href="{url_for(
|
||||||
"assiduites.calendrier_etud",
|
"assiduites.calendrier_etud",
|
||||||
@ -923,8 +930,10 @@ def etat_abs_date():
|
|||||||
|
|
||||||
etudiants.append(etudiant)
|
etudiants.append(etudiant)
|
||||||
|
|
||||||
|
# On tri les étudiants
|
||||||
etudiants = list(sorted(etudiants, key=lambda x: x["nom"]))
|
etudiants = list(sorted(etudiants, key=lambda x: x["nom"]))
|
||||||
|
|
||||||
|
# Génération de l'HTML
|
||||||
header: str = html_sco_header.sco_header(
|
header: str = html_sco_header.sco_header(
|
||||||
page_title=safehtml.html_to_safe_html(title),
|
page_title=safehtml.html_to_safe_html(title),
|
||||||
init_qtip=True,
|
init_qtip=True,
|
||||||
@ -948,6 +957,8 @@ def etat_abs_date():
|
|||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
def visu_assi_group():
|
def visu_assi_group():
|
||||||
"""Visualisation de l'assiduité d'un groupe entre deux dates"""
|
"""Visualisation de l'assiduité d'un groupe entre deux dates"""
|
||||||
|
|
||||||
|
# Récupération des paramètres de la requête
|
||||||
dates = {
|
dates = {
|
||||||
"debut": request.args.get("date_debut"),
|
"debut": request.args.get("date_debut"),
|
||||||
"fin": request.args.get("date_fin"),
|
"fin": request.args.get("date_fin"),
|
||||||
@ -961,13 +972,17 @@ def visu_assi_group():
|
|||||||
group_ids = group_ids.split(",")
|
group_ids = group_ids.split(",")
|
||||||
map(str, group_ids)
|
map(str, group_ids)
|
||||||
|
|
||||||
|
# Récupération des groupes, du semestre et des étudiants
|
||||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids)
|
groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids)
|
||||||
formsemestre = db.session.get(FormSemestre, groups_infos.formsemestre_id)
|
formsemestre = db.session.get(FormSemestre, groups_infos.formsemestre_id)
|
||||||
etuds = etuds_sorted_from_ids([m["etudid"] for m in groups_infos.members])
|
etuds = etuds_sorted_from_ids([m["etudid"] for m in groups_infos.members])
|
||||||
|
|
||||||
|
# Génération du tableau des assiduités
|
||||||
table: TableAssi = TableAssi(
|
table: TableAssi = TableAssi(
|
||||||
etuds=etuds, dates=list(dates.values()), formsemestre=formsemestre
|
etuds=etuds, dates=list(dates.values()), formsemestre=formsemestre
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Export en XLS
|
||||||
if fmt.startswith("xls"):
|
if fmt.startswith("xls"):
|
||||||
return scu.send_file(
|
return scu.send_file(
|
||||||
table.excel(),
|
table.excel(),
|
||||||
@ -976,6 +991,7 @@ def visu_assi_group():
|
|||||||
suffix=scu.XLSX_SUFFIX,
|
suffix=scu.XLSX_SUFFIX,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# récupération du/des noms du/des groupes
|
||||||
if groups_infos.tous_les_etuds_du_sem:
|
if groups_infos.tous_les_etuds_du_sem:
|
||||||
gr_tit = ""
|
gr_tit = ""
|
||||||
grp = ""
|
grp = ""
|
||||||
@ -988,6 +1004,7 @@ def visu_assi_group():
|
|||||||
grp + ' <span class="fontred">' + groups_infos.groups_titles + "</span>"
|
grp + ' <span class="fontred">' + groups_infos.groups_titles + "</span>"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Génération de la page
|
||||||
return render_template(
|
return render_template(
|
||||||
"assiduites/pages/visu_assi.j2",
|
"assiduites/pages/visu_assi.j2",
|
||||||
assi_metric=scu.translate_assiduites_metric(
|
assi_metric=scu.translate_assiduites_metric(
|
||||||
@ -1040,26 +1057,27 @@ def test():
|
|||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.AbsChange)
|
@permission_required(Permission.AbsChange)
|
||||||
def signal_assiduites_diff():
|
def signal_assiduites_diff():
|
||||||
|
# Récupération des paramètres de la requête
|
||||||
group_ids: list[int] = request.args.get("group_ids", None)
|
group_ids: list[int] = request.args.get("group_ids", None)
|
||||||
formsemestre_id: int = request.args.get("formsemestre_id", -1)
|
formsemestre_id: int = request.args.get("formsemestre_id", -1)
|
||||||
date: str = request.args.get("jour", datetime.date.today().isoformat())
|
date: str = request.args.get("jour", datetime.date.today().isoformat())
|
||||||
|
|
||||||
date_deb: str = request.args.get("date_deb")
|
date_deb: str = request.args.get("date_deb")
|
||||||
date_fin: str = request.args.get("date_fin")
|
date_fin: str = request.args.get("date_fin")
|
||||||
|
|
||||||
semaine: str = request.args.get("semaine")
|
semaine: str = request.args.get("semaine")
|
||||||
|
|
||||||
|
# Dans le cas où on donne une semaine plutot qu'un jour
|
||||||
if semaine is not None:
|
if semaine is not None:
|
||||||
|
# On génère la semaine iso à partir de l'anne scolaire.
|
||||||
semaine = (
|
semaine = (
|
||||||
f"{scu.annee_scolaire()}-W{semaine}" if "W" not in semaine else semaine
|
f"{scu.annee_scolaire()}-W{semaine}" if "W" not in semaine else semaine
|
||||||
)
|
)
|
||||||
|
# On met à jour les dates avec le date de debut et fin de semaine
|
||||||
date_deb: datetime.date = datetime.datetime.strptime(
|
date_deb: datetime.date = datetime.datetime.strptime(
|
||||||
semaine + "-1", "%Y-W%W-%w"
|
semaine + "-1", "%Y-W%W-%w"
|
||||||
)
|
)
|
||||||
date_fin: datetime.date = date_deb + datetime.timedelta(days=6)
|
date_fin: datetime.date = date_deb + datetime.timedelta(days=6)
|
||||||
|
|
||||||
etudiants: list[dict] = []
|
etudiants: list[dict] = []
|
||||||
|
|
||||||
titre = None
|
titre = None
|
||||||
|
|
||||||
# Vérification du formsemestre_id
|
# Vérification du formsemestre_id
|
||||||
@ -1079,14 +1097,13 @@ def signal_assiduites_diff():
|
|||||||
elif real_date > formsemestre.date_fin:
|
elif real_date > formsemestre.date_fin:
|
||||||
date = formsemestre.date_fin.isoformat()
|
date = formsemestre.date_fin.isoformat()
|
||||||
|
|
||||||
|
# Vérification des groupes
|
||||||
if group_ids is None:
|
if group_ids is None:
|
||||||
group_ids = []
|
group_ids = []
|
||||||
else:
|
else:
|
||||||
group_ids = group_ids.split(",")
|
group_ids = group_ids.split(",")
|
||||||
map(str, group_ids)
|
map(str, group_ids)
|
||||||
|
|
||||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids)
|
groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids)
|
||||||
|
|
||||||
if not groups_infos.members:
|
if not groups_infos.members:
|
||||||
return (
|
return (
|
||||||
html_sco_header.sco_header(page_title="Assiduité: saisie différée")
|
html_sco_header.sco_header(page_title="Assiduité: saisie différée")
|
||||||
@ -1094,6 +1111,7 @@ def signal_assiduites_diff():
|
|||||||
+ html_sco_header.sco_footer()
|
+ html_sco_header.sco_footer()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Récupération des étudiants
|
||||||
etudiants.extend(
|
etudiants.extend(
|
||||||
[
|
[
|
||||||
sco_etud.get_etud_info(etudid=m["etudid"], filled=True)[0]
|
sco_etud.get_etud_info(etudid=m["etudid"], filled=True)[0]
|
||||||
@ -1103,6 +1121,8 @@ def signal_assiduites_diff():
|
|||||||
|
|
||||||
etudiants = list(sorted(etudiants, key=lambda x: x["nom"]))
|
etudiants = list(sorted(etudiants, key=lambda x: x["nom"]))
|
||||||
|
|
||||||
|
# Génération de l'HTML
|
||||||
|
|
||||||
header: str = html_sco_header.sco_header(
|
header: str = html_sco_header.sco_header(
|
||||||
page_title="Assiduité: saisie différée",
|
page_title="Assiduité: saisie différée",
|
||||||
init_qtip=True,
|
init_qtip=True,
|
||||||
@ -1169,13 +1189,17 @@ def signal_evaluation_abs(etudid: int = None, evaluation_id: int = None):
|
|||||||
Alors l'absence sera sur la période de l'évaluation
|
Alors l'absence sera sur la période de l'évaluation
|
||||||
Sinon L'utilisateur sera redirigé vers la page de saisie des absences de l'étudiant
|
Sinon L'utilisateur sera redirigé vers la page de saisie des absences de l'étudiant
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Récupération de l'étudiant concerné
|
||||||
etud: Identite = Identite.query.get_or_404(etudid)
|
etud: Identite = Identite.query.get_or_404(etudid)
|
||||||
if etud.dept_id != g.scodoc_dept_id:
|
if etud.dept_id != g.scodoc_dept_id:
|
||||||
abort(404, "étudiant inexistant dans ce département")
|
abort(404, "étudiant inexistant dans ce département")
|
||||||
|
|
||||||
|
# Récupération de l'évaluation concernée
|
||||||
evaluation: Evaluation = Evaluation.query.get_or_404(evaluation_id)
|
evaluation: Evaluation = Evaluation.query.get_or_404(evaluation_id)
|
||||||
|
|
||||||
delta: datetime.timedelta = evaluation.date_fin - evaluation.date_debut
|
delta: datetime.timedelta = evaluation.date_fin - evaluation.date_debut
|
||||||
|
# Si l'évaluation dure plus qu'un jour alors on redirige vers la page de saisie etudiant
|
||||||
if delta > datetime.timedelta(days=1):
|
if delta > datetime.timedelta(days=1):
|
||||||
# rediriger vers page saisie
|
# rediriger vers page saisie
|
||||||
return redirect(
|
return redirect(
|
||||||
@ -1191,7 +1215,7 @@ def signal_evaluation_abs(etudid: int = None, evaluation_id: int = None):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# créer l'assiduité
|
# Sinon on créé l'assiduité
|
||||||
|
|
||||||
try:
|
try:
|
||||||
assiduite_unique: Assiduite = Assiduite.create_assiduite(
|
assiduite_unique: Assiduite = Assiduite.create_assiduite(
|
||||||
@ -1202,8 +1226,8 @@ def signal_evaluation_abs(etudid: int = None, evaluation_id: int = None):
|
|||||||
moduleimpl=evaluation.moduleimpl,
|
moduleimpl=evaluation.moduleimpl,
|
||||||
)
|
)
|
||||||
except ScoValueError as see:
|
except ScoValueError as see:
|
||||||
|
# En cas d'erreur
|
||||||
msg: str = see.args[0]
|
msg: str = see.args[0]
|
||||||
|
|
||||||
if "Duplication" in msg:
|
if "Duplication" in msg:
|
||||||
msg = "Une autre assiduité concerne déjà cette période. En cliquant sur continuer vous serez redirigé vers la page de saisie des assiduités de l'étudiant."
|
msg = "Une autre assiduité concerne déjà cette période. En cliquant sur continuer vous serez redirigé vers la page de saisie des assiduités de l'étudiant."
|
||||||
dest: str = url_for(
|
dest: str = url_for(
|
||||||
@ -1222,6 +1246,7 @@ def signal_evaluation_abs(etudid: int = None, evaluation_id: int = None):
|
|||||||
db.session.add(assiduite_unique)
|
db.session.add(assiduite_unique)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
# on flash pour indiquer que l'absence a bien été créée puis on revient sur la page de l'évaluation
|
||||||
flash("L'absence a bien été créée")
|
flash("L'absence a bien été créée")
|
||||||
# rediriger vers la page d'évaluation
|
# rediriger vers la page d'évaluation
|
||||||
return redirect(
|
return redirect(
|
||||||
@ -1235,16 +1260,20 @@ def signal_evaluation_abs(etudid: int = None, evaluation_id: int = None):
|
|||||||
|
|
||||||
def generate_bul_list(etud: Identite, semestre: FormSemestre) -> str:
|
def generate_bul_list(etud: Identite, semestre: FormSemestre) -> str:
|
||||||
"""Génère la liste des assiduités d'un étudiant pour le bulletin mail"""
|
"""Génère la liste des assiduités d'un étudiant pour le bulletin mail"""
|
||||||
|
|
||||||
|
# On récupère la métrique d'assiduité
|
||||||
metrique: str = scu.translate_assiduites_metric(
|
metrique: str = scu.translate_assiduites_metric(
|
||||||
sco_preferences.get_preference("assi_metrique", formsemestre_id=semestre.id),
|
sco_preferences.get_preference("assi_metrique", formsemestre_id=semestre.id),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# On récupère le nombre maximum de ligne d'assiduité
|
||||||
max_nb: int = int(
|
max_nb: int = int(
|
||||||
sco_preferences.get_preference(
|
sco_preferences.get_preference(
|
||||||
"bul_mail_list_abs_nb", formsemestre_id=semestre.id
|
"bul_mail_list_abs_nb", formsemestre_id=semestre.id
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# On récupère les assiduités et les justificatifs de l'étudiant
|
||||||
assiduites = scass.filter_by_formsemestre(
|
assiduites = scass.filter_by_formsemestre(
|
||||||
etud.assiduites, Assiduite, semestre
|
etud.assiduites, Assiduite, semestre
|
||||||
).order_by(Assiduite.entry_date.desc())
|
).order_by(Assiduite.entry_date.desc())
|
||||||
@ -1252,10 +1281,17 @@ def generate_bul_list(etud: Identite, semestre: FormSemestre) -> str:
|
|||||||
etud.justificatifs, Justificatif, semestre
|
etud.justificatifs, Justificatif, semestre
|
||||||
).order_by(Justificatif.entry_date.desc())
|
).order_by(Justificatif.entry_date.desc())
|
||||||
|
|
||||||
|
# On calcule les statistiques
|
||||||
stats: dict = scass.get_assiduites_stats(
|
stats: dict = scass.get_assiduites_stats(
|
||||||
assiduites, metric=metrique, filtered={"split": True}
|
assiduites, metric=metrique, filtered={"split": True}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# On sépare :
|
||||||
|
# - abs_j = absences justifiées
|
||||||
|
# - abs_nj = absences non justifiées
|
||||||
|
# - retards = les retards
|
||||||
|
# - justifs = les justificatifs
|
||||||
|
|
||||||
abs_j: list[str] = [
|
abs_j: list[str] = [
|
||||||
{"date": _get_date_str(assi.date_debut, assi.date_fin)}
|
{"date": _get_date_str(assi.date_debut, assi.date_fin)}
|
||||||
for assi in assiduites
|
for assi in assiduites
|
||||||
@ -1302,13 +1338,36 @@ def generate_bul_list(etud: Identite, semestre: FormSemestre) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _get_date_str(deb: datetime.datetime, fin: datetime.datetime) -> str:
|
def _get_date_str(deb: datetime.datetime, fin: datetime.datetime) -> str:
|
||||||
|
"""
|
||||||
|
_get_date_str transforme une période en chaîne lisible
|
||||||
|
|
||||||
|
Args:
|
||||||
|
deb (datetime.datetime): date de début
|
||||||
|
fin (datetime.datetime): date de fin
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str:
|
||||||
|
"le dd/mm/yyyy de hh:MM à hh:MM" si les deux date sont sur le même jour
|
||||||
|
"du dd/mm/yyyy hh:MM audd/mm/yyyy hh:MM" sinon
|
||||||
|
"""
|
||||||
if deb.date() == fin.date():
|
if deb.date() == fin.date():
|
||||||
temps = deb.strftime("%d/%m/%Y %H:%M").split(" ") + [fin.strftime("%H:%M")]
|
temps = deb.strftime("%d/%m/%Y %H:%M").split(" ") + [fin.strftime("%H:%M")]
|
||||||
return f"le {temps[0]} de {temps[1]} à {temps[2]}"
|
return f"le {temps[0]} de {temps[1]} à {temps[2]}"
|
||||||
return f'du {deb.strftime("%d/%m/%Y %H:%M")} au {fin.strftime("%d/%m/%Y %H:%M")}'
|
return f'du {deb.strftime("%d/%m/%Y %H:%M")} au {fin.strftime("%d/%m/%Y %H:%M")}'
|
||||||
|
|
||||||
|
|
||||||
def _get_days_between_dates(deb: str, fin: str):
|
def _get_days_between_dates(deb: str, fin: str) -> str:
|
||||||
|
"""
|
||||||
|
_get_days_between_dates récupère tous les jours entre deux dates
|
||||||
|
|
||||||
|
Args:
|
||||||
|
deb (str): date de début
|
||||||
|
fin (str): date de fin
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: une chaine json représentant une liste des jours
|
||||||
|
['date_iso','date_iso2', ...]
|
||||||
|
"""
|
||||||
if deb is None or fin is None:
|
if deb is None or fin is None:
|
||||||
return "null"
|
return "null"
|
||||||
try:
|
try:
|
||||||
@ -1328,8 +1387,25 @@ def _get_days_between_dates(deb: str, fin: str):
|
|||||||
|
|
||||||
|
|
||||||
def _differee(
|
def _differee(
|
||||||
etudiants, moduleimpl_select, date=None, periode=None, formsemestre_id=None
|
etudiants: list[dict],
|
||||||
):
|
moduleimpl_select: str,
|
||||||
|
date: str = None,
|
||||||
|
periode: dict[str, str] = None,
|
||||||
|
formsemestre_id: int = None,
|
||||||
|
) -> str:
|
||||||
|
"""
|
||||||
|
_differee Génère un tableau de saisie différé
|
||||||
|
|
||||||
|
Args:
|
||||||
|
etudiants (list[dict]): la liste des étudiants (représentés par des dictionnaires)
|
||||||
|
moduleimpl_select (str): l'html représentant le selecteur de module
|
||||||
|
date (str, optional): la première date à afficher. Defaults to None.
|
||||||
|
periode (dict[str, str], optional):La période par défaut de la première colonne. Defaults to None.
|
||||||
|
formsemestre_id (int, optional): l'id du semestre pour le selecteur de module. Defaults to None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: le widget (html/css/js)
|
||||||
|
"""
|
||||||
if date is None:
|
if date is None:
|
||||||
date = datetime.date.today().isoformat()
|
date = datetime.date.today().isoformat()
|
||||||
|
|
||||||
@ -1356,9 +1432,7 @@ def _differee(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _module_selector(
|
def _module_selector(formsemestre: FormSemestre, moduleimpl_id: int = None) -> str:
|
||||||
formsemestre: FormSemestre, moduleimpl_id: int = None
|
|
||||||
) -> HTMLElement:
|
|
||||||
"""
|
"""
|
||||||
_module_selector Génère un HTMLSelectElement à partir des moduleimpl du formsemestre
|
_module_selector Génère un HTMLSelectElement à partir des moduleimpl du formsemestre
|
||||||
|
|
||||||
@ -1368,18 +1442,26 @@ def _module_selector(
|
|||||||
Returns:
|
Returns:
|
||||||
str: La représentation str d'un HTMLSelectElement
|
str: La représentation str d'un HTMLSelectElement
|
||||||
"""
|
"""
|
||||||
|
# récupération des ues du semestre
|
||||||
ntc: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
ntc: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
||||||
|
ues = ntc.get_ues_stat_dict()
|
||||||
|
|
||||||
modimpls_list: list[dict] = []
|
modimpls_list: list[dict] = []
|
||||||
ues = ntc.get_ues_stat_dict()
|
|
||||||
for ue in ues:
|
for ue in ues:
|
||||||
|
# Ajout des moduleimpl de chaque ue dans la liste des moduleimpls
|
||||||
modimpls_list += ntc.get_modimpls_dict(ue_id=ue["ue_id"])
|
modimpls_list += ntc.get_modimpls_dict(ue_id=ue["ue_id"])
|
||||||
|
|
||||||
|
# prévoie la sélection par défaut d'un moduleimpl s'il a été passé en paramètre
|
||||||
selected = "" if moduleimpl_id is not None else "selected"
|
selected = "" if moduleimpl_id is not None else "selected"
|
||||||
|
|
||||||
modules = []
|
# Vérification que le moduleimpl_id passé en paramètre est bien un entier
|
||||||
|
try:
|
||||||
|
moduleimpl_id = int(moduleimpl_id)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
moduleimpl_id = None
|
||||||
|
|
||||||
|
modules: list[dict[str, str | int]] = []
|
||||||
|
# Récupération de l'id et d'un nom lisible pour chaque moduleimpl
|
||||||
for modimpl in modimpls_list:
|
for modimpl in modimpls_list:
|
||||||
modname: str = (
|
modname: str = (
|
||||||
(modimpl["module"]["code"] or "")
|
(modimpl["module"]["code"] or "")
|
||||||
@ -1388,11 +1470,6 @@ def _module_selector(
|
|||||||
)
|
)
|
||||||
modules.append({"moduleimpl_id": modimpl["moduleimpl_id"], "name": modname})
|
modules.append({"moduleimpl_id": modimpl["moduleimpl_id"], "name": modname})
|
||||||
|
|
||||||
try:
|
|
||||||
moduleimpl_id = int(moduleimpl_id)
|
|
||||||
except (ValueError, TypeError):
|
|
||||||
moduleimpl_id = None
|
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"assiduites/widgets/moduleimpl_selector.j2",
|
"assiduites/widgets/moduleimpl_selector.j2",
|
||||||
selected=selected,
|
selected=selected,
|
||||||
@ -1401,13 +1478,30 @@ def _module_selector(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _dynamic_module_selector():
|
def _dynamic_module_selector() -> str:
|
||||||
|
"""
|
||||||
|
_dynamic_module_selector retourne l'html/css/javascript du selecteur de module dynamique
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: l'html/css/javascript du selecteur de module dynamique
|
||||||
|
"""
|
||||||
return render_template(
|
return render_template(
|
||||||
"assiduites/widgets/moduleimpl_dynamic_selector.j2",
|
"assiduites/widgets/moduleimpl_dynamic_selector.j2",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _timeline(formsemestre_id=None) -> HTMLElement:
|
def _timeline(formsemestre_id: int = None) -> str:
|
||||||
|
"""
|
||||||
|
_timeline retourne l'html de la timeline
|
||||||
|
|
||||||
|
Args:
|
||||||
|
formsemestre_id (int, optional): un formsemestre. Defaults to None.
|
||||||
|
Le formsemestre sert à obtenir la période par défaut de la timeline
|
||||||
|
sinon ce sera de 2 heure dès le début de la timeline
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: l'html en chaîne de caractères
|
||||||
|
"""
|
||||||
return render_template(
|
return render_template(
|
||||||
"assiduites/widgets/timeline.j2",
|
"assiduites/widgets/timeline.j2",
|
||||||
t_start=ScoDocSiteConfig.assi_get_rounded_time("assi_morning_time", "08:00:00"),
|
t_start=ScoDocSiteConfig.assi_get_rounded_time("assi_morning_time", "08:00:00"),
|
||||||
@ -1419,7 +1513,13 @@ def _timeline(formsemestre_id=None) -> HTMLElement:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _mini_timeline() -> HTMLElement:
|
def _mini_timeline() -> str:
|
||||||
|
"""
|
||||||
|
_mini_timeline Retourne l'html lié au mini timeline d'assiduités
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: l'html en chaîne de caractères
|
||||||
|
"""
|
||||||
return render_template(
|
return render_template(
|
||||||
"assiduites/widgets/minitimeline.j2",
|
"assiduites/widgets/minitimeline.j2",
|
||||||
t_start=ScoDocSiteConfig.assi_get_rounded_time("assi_morning_time", "08:00:00"),
|
t_start=ScoDocSiteConfig.assi_get_rounded_time("assi_morning_time", "08:00:00"),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- mode: python -*-
|
# -*- mode: python -*-
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
SCOVERSION = "9.6.49"
|
SCOVERSION = "9.6.50"
|
||||||
|
|
||||||
SCONAME = "ScoDoc"
|
SCONAME = "ScoDoc"
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ def downgrade_module(
|
|||||||
dept_etudid: list[int] = None
|
dept_etudid: list[int] = None
|
||||||
dept_id: int = None
|
dept_id: int = None
|
||||||
|
|
||||||
|
# Récupération du département si spécifié
|
||||||
if dept is not None:
|
if dept is not None:
|
||||||
departement: Departement = Departement.query.filter_by(acronym=dept).first()
|
departement: Departement = Departement.query.filter_by(acronym=dept).first()
|
||||||
|
|
||||||
@ -34,13 +35,16 @@ def downgrade_module(
|
|||||||
dept_etudid = [etud.id for etud in departement.etudiants]
|
dept_etudid = [etud.id for etud in departement.etudiants]
|
||||||
dept_id = departement.id
|
dept_id = departement.id
|
||||||
|
|
||||||
|
# Suppression des assiduités
|
||||||
if assiduites:
|
if assiduites:
|
||||||
_remove_assiduites(dept_etudid)
|
_remove_assiduites(dept_etudid)
|
||||||
|
# Suppression des justificatifs
|
||||||
if justificatifs:
|
if justificatifs:
|
||||||
_remove_justificatifs(dept_etudid)
|
_remove_justificatifs(dept_etudid)
|
||||||
_remove_justificatifs_archive(dept_id)
|
_remove_justificatifs_archive(dept_id)
|
||||||
|
|
||||||
|
# Si on supprime tout le module assiduité/justificatif alors on remet à zero
|
||||||
|
# les séquences postgres
|
||||||
if dept is None:
|
if dept is None:
|
||||||
if assiduites:
|
if assiduites:
|
||||||
db.session.execute(
|
db.session.execute(
|
||||||
@ -51,26 +55,52 @@ def downgrade_module(
|
|||||||
sa.text("ALTER SEQUENCE justificatifs_id_seq RESTART WITH 1")
|
sa.text("ALTER SEQUENCE justificatifs_id_seq RESTART WITH 1")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# On valide l'opération sur la bdd
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
# On affiche un message pour l'utilisateur
|
||||||
print(
|
print(
|
||||||
f"{TerminalColor.GREEN}Le module assiduité a bien été remis à zero.{TerminalColor.RESET}"
|
f"{TerminalColor.GREEN}Le module assiduité a bien été remis à zero.{TerminalColor.RESET}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _remove_assiduites(dept_etudid: str = None):
|
def _remove_assiduites(dept_etudid: str = None):
|
||||||
|
"""
|
||||||
|
_remove_assiduites Supprime les assiduités
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dept_etudid (str, optional): la liste des etudid d'un département. Defaults to None.
|
||||||
|
"""
|
||||||
if dept_etudid is None:
|
if dept_etudid is None:
|
||||||
|
# Si pas d'étudids alors on supprime toutes les assiduités
|
||||||
Assiduite.query.delete()
|
Assiduite.query.delete()
|
||||||
else:
|
else:
|
||||||
|
# Sinon on supprime que les assiduités des étudiants donnés
|
||||||
Assiduite.query.filter(Assiduite.etudid.in_(dept_etudid)).delete()
|
Assiduite.query.filter(Assiduite.etudid.in_(dept_etudid)).delete()
|
||||||
|
|
||||||
|
|
||||||
def _remove_justificatifs(dept_etudid: str = None):
|
def _remove_justificatifs(dept_etudid: str = None):
|
||||||
|
"""
|
||||||
|
_remove_justificatifs Supprime les justificatifs
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dept_etudid (str, optional): la liste des etudid d'un département. Defaults to None.
|
||||||
|
"""
|
||||||
if dept_etudid is None:
|
if dept_etudid is None:
|
||||||
|
# Si pas d'étudids alors on supprime tous les justificatifs
|
||||||
Justificatif.query.delete()
|
Justificatif.query.delete()
|
||||||
else:
|
else:
|
||||||
|
# Sinon on supprime que les justificatifs des étudiants donnés
|
||||||
Justificatif.query.filter(Justificatif.etudid.in_(dept_etudid)).delete()
|
Justificatif.query.filter(Justificatif.etudid.in_(dept_etudid)).delete()
|
||||||
|
|
||||||
|
|
||||||
def _remove_justificatifs_archive(dept_id: int = None):
|
def _remove_justificatifs_archive(dept_id: int = None):
|
||||||
|
"""
|
||||||
|
_remove_justificatifs_archive On supprime les archives des fichiers justificatifs
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dept_id (int, optional): l'id du département à supprimer . Defaults to None.
|
||||||
|
Si none : supprime tous les département
|
||||||
|
Sinon uniquement le département sélectionné
|
||||||
|
"""
|
||||||
JustificatifArchiver().remove_dept_archive(dept_id)
|
JustificatifArchiver().remove_dept_archive(dept_id)
|
||||||
|
Loading…
Reference in New Issue
Block a user