Merge branch 'modif' of https://scodoc.org/git/iziram/ScoDoc into iziram-rev
This commit is contained in:
commit
2fc978e515
@ -40,14 +40,7 @@ def assiduite(assiduite_id: int = None):
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
query = Assiduite.query.filter_by(id=assiduite_id)
|
return scu.get_model_api_object(Assiduite, assiduite_id)
|
||||||
# if g.scodoc_dept:
|
|
||||||
# query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id)
|
|
||||||
assiduite_query = query.first_or_404()
|
|
||||||
|
|
||||||
data = assiduite_query.to_dict()
|
|
||||||
|
|
||||||
return jsonify(_change_etat(data))
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/assiduites/<int:etudid>/count", defaults={"with_query": False})
|
@bp.route("/assiduites/<int:etudid>/count", defaults={"with_query": False})
|
||||||
@ -164,8 +157,8 @@ def assiduites(etudid: int = None, with_query: bool = False):
|
|||||||
|
|
||||||
data_set: list[dict] = []
|
data_set: list[dict] = []
|
||||||
for ass in assiduites_query.all():
|
for ass in assiduites_query.all():
|
||||||
data = ass.to_dict()
|
data = ass.to_dict(format_api=True)
|
||||||
data_set.append(_change_etat(data))
|
data_set.append(data)
|
||||||
|
|
||||||
return jsonify(data_set)
|
return jsonify(data_set)
|
||||||
|
|
||||||
@ -202,8 +195,8 @@ def assiduites_formsemestre(formsemestre_id: int, with_query: bool = False):
|
|||||||
|
|
||||||
data_set: list[dict] = []
|
data_set: list[dict] = []
|
||||||
for ass in assiduites_query.all():
|
for ass in assiduites_query.all():
|
||||||
data = ass.to_dict()
|
data = ass.to_dict(format_api=True)
|
||||||
data_set.append(_change_etat(data))
|
data_set.append(data)
|
||||||
|
|
||||||
return jsonify(data_set)
|
return jsonify(data_set)
|
||||||
|
|
||||||
@ -307,11 +300,10 @@ def _create_singular(
|
|||||||
etat = data.get("etat", None)
|
etat = data.get("etat", None)
|
||||||
if etat is None:
|
if etat is None:
|
||||||
errors.append("param 'etat': manquant")
|
errors.append("param 'etat': manquant")
|
||||||
elif etat not in scu.ETATS_ASSIDUITE:
|
elif not scu.EtatAssiduite.contains(etat):
|
||||||
errors.append("param 'etat': invalide")
|
errors.append("param 'etat': invalide")
|
||||||
|
|
||||||
data = _change_etat(data, False)
|
etat = scu.EtatAssiduite.get(etat)
|
||||||
etat = data.get("etat", None)
|
|
||||||
|
|
||||||
# cas 2 : date_debut
|
# cas 2 : date_debut
|
||||||
date_debut = data.get("date_debut", None)
|
date_debut = data.get("date_debut", None)
|
||||||
@ -418,7 +410,7 @@ def _delete_singular(assiduite_id: int, database):
|
|||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
# @permission_required(Permission.ScoAssiduiteChange)
|
# @permission_required(Permission.ScoAssiduiteChange)
|
||||||
def assiduite_cedit(assiduite_id: int):
|
def assiduite_edit(assiduite_id: int):
|
||||||
"""
|
"""
|
||||||
Edition d'une assiduité à partir de son id
|
Edition d'une assiduité à partir de son id
|
||||||
La requête doit avoir un content type "application/json":
|
La requête doit avoir un content type "application/json":
|
||||||
@ -438,11 +430,11 @@ def assiduite_cedit(assiduite_id: int):
|
|||||||
|
|
||||||
# Cas 1 : Etat
|
# Cas 1 : Etat
|
||||||
if data.get("etat") is not None:
|
if data.get("etat") is not None:
|
||||||
data = _change_etat(data, False)
|
etat = scu.EtatAssiduite.get(data.get("etat"))
|
||||||
if data.get("etat") is None:
|
if etat is None:
|
||||||
errors.append("param 'etat': invalide")
|
errors.append("param 'etat': invalide")
|
||||||
else:
|
else:
|
||||||
assiduite_unique.etat = data.get("etat")
|
assiduite_unique.etat = etat
|
||||||
|
|
||||||
# Cas 2 : Moduleimpl_id
|
# Cas 2 : Moduleimpl_id
|
||||||
moduleimpl_id = data.get("moduleimpl_id", False)
|
moduleimpl_id = data.get("moduleimpl_id", False)
|
||||||
@ -478,13 +470,6 @@ def assiduite_cedit(assiduite_id: int):
|
|||||||
|
|
||||||
|
|
||||||
# -- Utils --
|
# -- Utils --
|
||||||
def _change_etat(data: dict, from_int: bool = True):
|
|
||||||
"""change dans un json la valeur du champs état"""
|
|
||||||
if from_int:
|
|
||||||
data["etat"] = scu.ETAT_ASSIDUITE_NAME.get(data["etat"])
|
|
||||||
else:
|
|
||||||
data["etat"] = scu.ETATS_ASSIDUITE.get(data["etat"])
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def _count_manager(requested) -> tuple[str, dict]:
|
def _count_manager(requested) -> tuple[str, dict]:
|
||||||
|
@ -5,22 +5,22 @@
|
|||||||
##############################################################################
|
##############################################################################
|
||||||
"""ScoDoc 9 API : Assiduités
|
"""ScoDoc 9 API : Assiduités
|
||||||
"""
|
"""
|
||||||
import os
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from flask import g, jsonify, request
|
||||||
|
from flask_login import login_required
|
||||||
|
|
||||||
import app.scodoc.sco_assiduites as scass
|
import app.scodoc.sco_assiduites as scass
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
from app import db
|
from app import db
|
||||||
|
|
||||||
from app.api import api_bp as bp
|
from app.api import api_bp as bp
|
||||||
from app.api import api_web_bp
|
from app.api import api_web_bp
|
||||||
from app.scodoc.sco_exceptions import ScoValueError
|
|
||||||
from app.decorators import permission_required, scodoc
|
from app.decorators import permission_required, scodoc
|
||||||
from app.models import Identite, Justificatif
|
from app.models import Identite, Justificatif
|
||||||
|
from app.models.assiduites import is_period_conflicting
|
||||||
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_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
from flask import g, jsonify, request
|
|
||||||
from flask_login import login_required
|
|
||||||
from app.scodoc.sco_utils import json_error
|
from app.scodoc.sco_utils import json_error
|
||||||
|
|
||||||
|
|
||||||
@ -35,9 +35,8 @@ from app.scodoc.sco_utils import json_error
|
|||||||
# return jsonify("done")
|
# return jsonify("done")
|
||||||
|
|
||||||
# Partie Modèle
|
# Partie Modèle
|
||||||
# TODO: justificatif
|
|
||||||
@bp.route("/justificatif/<int:justif_id>")
|
@bp.route("/justificatif/<int:justif_id>")
|
||||||
@api_web_bp.route("/assiduite/<int:justif_id>")
|
@api_web_bp.route("/justificatif/<int:justif_id>")
|
||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
def justificatif(justif_id: int = None):
|
def justificatif(justif_id: int = None):
|
||||||
@ -54,19 +53,12 @@ def justificatif(justif_id: int = None):
|
|||||||
"raison": "une raison",
|
"raison": "une raison",
|
||||||
"entry_date": "2022-10-31T08:00+01:00",
|
"entry_date": "2022-10-31T08:00+01:00",
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
query = Justificatif.query.filter_by(id=justif_id)
|
return scu.get_model_api_object(Justificatif, justif_id)
|
||||||
if g.scodoc_dept:
|
|
||||||
query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id)
|
|
||||||
justificatif_unique = query.first_or_404()
|
|
||||||
|
|
||||||
data = justificatif_unique.to_dict()
|
|
||||||
|
|
||||||
return jsonify(_change_etat(data))
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: justificatifs[-query]
|
|
||||||
@bp.route("/justificatifs/<int:etudid>", defaults={"with_query": False})
|
@bp.route("/justificatifs/<int:etudid>", defaults={"with_query": False})
|
||||||
@bp.route("/justificatifs/<int:etudid>/query", defaults={"with_query": True})
|
@bp.route("/justificatifs/<int:etudid>/query", defaults={"with_query": True})
|
||||||
@api_web_bp.route("/justificatifs/<int:etudid>", defaults={"with_query": False})
|
@api_web_bp.route("/justificatifs/<int:etudid>", defaults={"with_query": False})
|
||||||
@ -110,13 +102,12 @@ def justificatifs(etudid: int = None, with_query: bool = False):
|
|||||||
|
|
||||||
data_set: list[dict] = []
|
data_set: list[dict] = []
|
||||||
for just in justificatifs_query.all():
|
for just in justificatifs_query.all():
|
||||||
data = just.to_dict()
|
data = just.to_dict(format_api=True)
|
||||||
data_set.append(_change_etat(data))
|
data_set.append(data)
|
||||||
|
|
||||||
return jsonify(data_set)
|
return jsonify(data_set)
|
||||||
|
|
||||||
|
|
||||||
# TODO: justificatif-create
|
|
||||||
@bp.route("/justificatif/<int:etudid>/create", methods=["POST"])
|
@bp.route("/justificatif/<int:etudid>/create", methods=["POST"])
|
||||||
@api_web_bp.route("/justificatif/<int:etudid>/create", methods=["POST"])
|
@api_web_bp.route("/justificatif/<int:etudid>/create", methods=["POST"])
|
||||||
@scodoc
|
@scodoc
|
||||||
@ -173,11 +164,10 @@ def _create_singular(
|
|||||||
etat = data.get("etat", None)
|
etat = data.get("etat", None)
|
||||||
if etat is None:
|
if etat is None:
|
||||||
errors.append("param 'etat': manquant")
|
errors.append("param 'etat': manquant")
|
||||||
elif etat not in scu.ETATS_JUSTIFICATIF:
|
elif not scu.EtatJustificatif.contains(etat):
|
||||||
errors.append("param 'etat': invalide")
|
errors.append("param 'etat': invalide")
|
||||||
|
|
||||||
data = _change_etat(data, False)
|
etat = scu.EtatJustificatif.get(etat)
|
||||||
etat = data.get("etat", None)
|
|
||||||
|
|
||||||
# cas 2 : date_debut
|
# cas 2 : date_debut
|
||||||
date_debut = data.get("date_debut", None)
|
date_debut = data.get("date_debut", None)
|
||||||
@ -224,7 +214,6 @@ def _create_singular(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# TODO: justificatif-edit
|
|
||||||
@bp.route("/justificatif/<int:justif_id>/edit", methods=["POST"])
|
@bp.route("/justificatif/<int:justif_id>/edit", methods=["POST"])
|
||||||
@api_web_bp.route("/justificatif/<int:justif_id>/edit", methods=["POST"])
|
@api_web_bp.route("/justificatif/<int:justif_id>/edit", methods=["POST"])
|
||||||
@login_required
|
@login_required
|
||||||
@ -235,9 +224,12 @@ def justif_edit(justif_id: int):
|
|||||||
"""
|
"""
|
||||||
Edition d'un justificatif à partir de son id
|
Edition d'un justificatif à partir de son id
|
||||||
La requête doit avoir un content type "application/json":
|
La requête doit avoir un content type "application/json":
|
||||||
|
|
||||||
{
|
{
|
||||||
"etat"?: str,
|
"etat"?: str,
|
||||||
"raison"?: str
|
"raison"?: str
|
||||||
|
"date_debut"?: str
|
||||||
|
"date_fin"?: str
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
justificatif_unique: Justificatif = Justificatif.query.filter_by(
|
justificatif_unique: Justificatif = Justificatif.query.filter_by(
|
||||||
@ -250,17 +242,58 @@ def justif_edit(justif_id: int):
|
|||||||
|
|
||||||
# Cas 1 : Etat
|
# Cas 1 : Etat
|
||||||
if data.get("etat") is not None:
|
if data.get("etat") is not None:
|
||||||
data = _change_etat(data, False)
|
etat = scu.EtatJustificatif.get(data.get("etat"))
|
||||||
if data.get("etat") is None:
|
if etat is None:
|
||||||
errors.append("param 'etat': invalide")
|
errors.append("param 'etat': invalide")
|
||||||
else:
|
else:
|
||||||
justificatif_unique.etat = data.get("etat")
|
justificatif_unique.etat = etat
|
||||||
|
|
||||||
# Cas 2 : raison
|
# Cas 2 : raison
|
||||||
raison = data.get("raison", False)
|
raison = 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
|
||||||
|
|
||||||
|
# cas 3 : date_debut
|
||||||
|
date_debut = data.get("date_debut", False)
|
||||||
|
if date_debut is not False:
|
||||||
|
if date_debut is None:
|
||||||
|
errors.append("param 'date_debut': manquant")
|
||||||
|
deb = scu.is_iso_formated(date_debut, convert=True)
|
||||||
|
if deb is None:
|
||||||
|
errors.append("param 'date_debut': format invalide")
|
||||||
|
|
||||||
|
if justificatif_unique.date_fin >= deb:
|
||||||
|
errors.append("param 'date_debut': date de début située après date de fin ")
|
||||||
|
|
||||||
|
# cas 4 : date_fin
|
||||||
|
date_fin = data.get("date_fin", False)
|
||||||
|
if date_fin is not False:
|
||||||
|
if date_fin is None:
|
||||||
|
errors.append("param 'date_fin': manquant")
|
||||||
|
fin = scu.is_iso_formated(date_fin, convert=True)
|
||||||
|
if fin is None:
|
||||||
|
errors.append("param 'date_fin': format invalide")
|
||||||
|
if justificatif_unique.date_debut <= fin:
|
||||||
|
errors.append("param 'date_fin': date de fin située avant date de début ")
|
||||||
|
|
||||||
|
# Vérification du conflit d'horaire
|
||||||
|
if (deb is not None) or (fin is not None):
|
||||||
|
deb = deb if deb is not None else justificatif_unique.date_debut
|
||||||
|
fin = fin if fin is not None else justificatif_unique.date_fin
|
||||||
|
|
||||||
|
justificatifs_list: list[Justificatif] = Justificatif.query.filter_by(
|
||||||
|
etuid=justificatif_unique.etudid
|
||||||
|
).all()
|
||||||
|
|
||||||
|
if is_period_conflicting(deb, fin, justificatifs_list):
|
||||||
|
errors.append(
|
||||||
|
"Modification de la plage horaire impossible: conflit avec les autres justificatifs"
|
||||||
|
)
|
||||||
|
justificatif_unique.date_debut = deb
|
||||||
|
justificatif_unique.date_fin = fin
|
||||||
|
|
||||||
if errors:
|
if errors:
|
||||||
err: str = ", ".join(errors)
|
err: str = ", ".join(errors)
|
||||||
return json_error(404, err)
|
return json_error(404, err)
|
||||||
@ -270,7 +303,6 @@ def justif_edit(justif_id: int):
|
|||||||
return jsonify({"OK": True})
|
return jsonify({"OK": True})
|
||||||
|
|
||||||
|
|
||||||
# TODO: justificatif-delete
|
|
||||||
@bp.route("/justificatif/delete", methods=["POST"])
|
@bp.route("/justificatif/delete", methods=["POST"])
|
||||||
@api_web_bp.route("/justificatif/delete", methods=["POST"])
|
@api_web_bp.route("/justificatif/delete", methods=["POST"])
|
||||||
@login_required
|
@login_required
|
||||||
@ -312,12 +344,18 @@ def _delete_singular(justif_id: int, database):
|
|||||||
).first()
|
).first()
|
||||||
if justificatif_unique is None:
|
if justificatif_unique is None:
|
||||||
return (404, "Justificatif non existant")
|
return (404, "Justificatif non existant")
|
||||||
|
|
||||||
|
archive_name: str = justificatif_unique.fichier
|
||||||
|
|
||||||
|
if archive_name is not None:
|
||||||
|
archiver: JustificatifArchiver = JustificatifArchiver()
|
||||||
|
archiver.delete_justificatif(justificatif_unique.etudid, archive_name)
|
||||||
|
|
||||||
database.session.delete(justificatif_unique)
|
database.session.delete(justificatif_unique)
|
||||||
return (200, "OK")
|
return (200, "OK")
|
||||||
|
|
||||||
|
|
||||||
# Partie archivage
|
# Partie archivage
|
||||||
# TODO: justificatif-import
|
|
||||||
@bp.route("/justificatif/import/<int:justif_id>", methods=["POST"])
|
@bp.route("/justificatif/import/<int:justif_id>", methods=["POST"])
|
||||||
@api_web_bp.route("/justificatif/import/<int:justif_id>", methods=["POST"])
|
@api_web_bp.route("/justificatif/import/<int:justif_id>", methods=["POST"])
|
||||||
@scodoc
|
@scodoc
|
||||||
@ -359,12 +397,11 @@ def justif_import(justif_id: int = None):
|
|||||||
|
|
||||||
return jsonify({"response": "imported"})
|
return jsonify({"response": "imported"})
|
||||||
except ScoValueError as err:
|
except ScoValueError as err:
|
||||||
return json_error(404, err.args[1])
|
return json_error(404, err.args[0])
|
||||||
|
|
||||||
|
|
||||||
# TODO: justificatif-export
|
@bp.route("/justificatif/export/<int:justif_id>/<filename>", methods=["POST"])
|
||||||
@bp.route("/justificatif/export/<int:justif_id>/<filename>", methods=["GET"])
|
@api_web_bp.route("/justificatif/export/<int:justif_id>/<filename>", methods=["POST"])
|
||||||
@api_web_bp.route("/justificatif/export/<int:justif_id>/<filename>", methods=["GET"])
|
|
||||||
@scodoc
|
@scodoc
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@ -391,10 +428,9 @@ def justif_export(justif_id: int = None, filename: str = None):
|
|||||||
archive_name, justificatif_unique.etudid, filename
|
archive_name, justificatif_unique.etudid, filename
|
||||||
)
|
)
|
||||||
except ScoValueError as err:
|
except ScoValueError as err:
|
||||||
return json_error(404, err.args[1])
|
return json_error(404, err.args[0])
|
||||||
|
|
||||||
|
|
||||||
# TODO: justificatif-remove
|
|
||||||
@bp.route("/justificatif/remove/<int:justif_id>", methods=["POST"])
|
@bp.route("/justificatif/remove/<int:justif_id>", methods=["POST"])
|
||||||
@api_web_bp.route("/justificatif/remove/<int:justif_id>", methods=["POST"])
|
@api_web_bp.route("/justificatif/remove/<int:justif_id>", methods=["POST"])
|
||||||
@scodoc
|
@scodoc
|
||||||
@ -404,7 +440,7 @@ 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">
|
||||||
|
|
||||||
@ -454,12 +490,11 @@ def justif_remove(justif_id: int = None):
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
except ScoValueError as err:
|
except ScoValueError as err:
|
||||||
return json_error(404, err.args[1])
|
return json_error(404, err.args[0])
|
||||||
|
|
||||||
return jsonify({"response": "removed"})
|
return jsonify({"response": "removed"})
|
||||||
|
|
||||||
|
|
||||||
# TODO: justificatif-list
|
|
||||||
@bp.route("/justificatif/list/<int:justif_id>", methods=["GET"])
|
@bp.route("/justificatif/list/<int:justif_id>", methods=["GET"])
|
||||||
@api_web_bp.route("/justificatif/list/<int:justif_id>", methods=["GET"])
|
@api_web_bp.route("/justificatif/list/<int:justif_id>", methods=["GET"])
|
||||||
@scodoc
|
@scodoc
|
||||||
@ -492,16 +527,29 @@ def justif_list(justif_id: int = None):
|
|||||||
|
|
||||||
# Partie justification
|
# Partie justification
|
||||||
# TODO: justificatif-justified
|
# TODO: justificatif-justified
|
||||||
|
@bp.route("/justificatif/justified/<int:justif_id>", methods=["GET"])
|
||||||
|
@api_web_bp.route("/justificatif/justified/<int:justif_id>", methods=["GET"])
|
||||||
|
@scodoc
|
||||||
|
@login_required
|
||||||
|
@permission_required(Permission.ScoView)
|
||||||
|
# @permission_required(Permission.ScoAssiduiteChange)
|
||||||
|
def justif_justified(justif_id: int = None):
|
||||||
|
"""
|
||||||
|
Liste assiduite_id justifiées par le justificatif
|
||||||
|
"""
|
||||||
|
|
||||||
|
query = Justificatif.query.filter_by(id=justif_id)
|
||||||
|
if g.scodoc_dept:
|
||||||
|
query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id)
|
||||||
|
|
||||||
|
justificatif_unique: Justificatif = query.first_or_404()
|
||||||
|
|
||||||
|
assiduites_list: list[int] = scass.justifies(justificatif_unique)
|
||||||
|
|
||||||
|
return jsonify(assiduites_list)
|
||||||
|
|
||||||
|
|
||||||
# -- Utils --
|
# -- Utils --
|
||||||
def _change_etat(data: dict, from_int: bool = True):
|
|
||||||
"""change dans un json la valeur du champs état"""
|
|
||||||
if from_int:
|
|
||||||
data["etat"] = scu.ETAT_JUSTIFICATIF_NAME.get(data["etat"])
|
|
||||||
else:
|
|
||||||
data["etat"] = scu.ETATS_JUSTIFICATIF.get(data["etat"])
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def _filter_manager(requested, justificatifs_query):
|
def _filter_manager(requested, justificatifs_query):
|
||||||
|
@ -33,10 +33,7 @@ import pandas as pd
|
|||||||
from app import db
|
from app import db
|
||||||
from app import models
|
from app import models
|
||||||
from app.models import (
|
from app.models import (
|
||||||
DispenseUE,
|
|
||||||
FormSemestre,
|
FormSemestre,
|
||||||
FormSemestreInscription,
|
|
||||||
Identite,
|
|
||||||
Module,
|
Module,
|
||||||
ModuleImpl,
|
ModuleImpl,
|
||||||
ModuleUECoef,
|
ModuleUECoef,
|
||||||
@ -218,31 +215,6 @@ def notes_sem_load_cube(formsemestre: FormSemestre) -> tuple:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def load_dispense_ues(
|
|
||||||
formsemestre: FormSemestre, etudids: pd.Index, ues: list[UniteEns]
|
|
||||||
) -> set[tuple[int, int]]:
|
|
||||||
"""Construit l'ensemble des
|
|
||||||
etudids = modimpl_inscr_df.index, # les etudids
|
|
||||||
ue_ids : modimpl_coefs_df.index, # les UE du formsemestre sans les UE bonus sport
|
|
||||||
|
|
||||||
Résultat: set de (etudid, ue_id).
|
|
||||||
"""
|
|
||||||
dispense_ues = set()
|
|
||||||
ue_sem_by_code = {ue.ue_code: ue for ue in ues}
|
|
||||||
# Prend toutes les dispenses obtenues par des étudiants de ce formsemestre,
|
|
||||||
# puis filtre sur inscrits et code d'UE UE
|
|
||||||
for dispense_ue in DispenseUE.query.join(
|
|
||||||
Identite, FormSemestreInscription
|
|
||||||
).filter_by(formsemestre_id=formsemestre.id):
|
|
||||||
if dispense_ue.etudid in etudids:
|
|
||||||
# UE dans le semestre avec même code ?
|
|
||||||
ue = ue_sem_by_code.get(dispense_ue.ue.ue_code)
|
|
||||||
if ue is not None:
|
|
||||||
dispense_ues.add((dispense_ue.etudid, ue.id))
|
|
||||||
|
|
||||||
return dispense_ues
|
|
||||||
|
|
||||||
|
|
||||||
def compute_ue_moys_apc(
|
def compute_ue_moys_apc(
|
||||||
sem_cube: np.array,
|
sem_cube: np.array,
|
||||||
etuds: list,
|
etuds: list,
|
||||||
|
@ -72,7 +72,7 @@ class ResultatsSemestreBUT(NotesTableCompat):
|
|||||||
modimpl.module.ue.type != UE_SPORT
|
modimpl.module.ue.type != UE_SPORT
|
||||||
for modimpl in self.formsemestre.modimpls_sorted
|
for modimpl in self.formsemestre.modimpls_sorted
|
||||||
]
|
]
|
||||||
self.dispense_ues = moy_ue.load_dispense_ues(
|
self.dispense_ues = DispenseUE.load_formsemestre_dispense_ues_set(
|
||||||
self.formsemestre, self.modimpl_inscr_df.index, self.ues
|
self.formsemestre, self.modimpl_inscr_df.index, self.ues
|
||||||
)
|
)
|
||||||
self.etud_moy_ue = moy_ue.compute_ue_moys_apc(
|
self.etud_moy_ue = moy_ue.compute_ue_moys_apc(
|
||||||
|
@ -51,14 +51,18 @@ class Assiduite(db.Model):
|
|||||||
|
|
||||||
entry_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
|
entry_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
|
||||||
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self, format_api=True) -> dict:
|
||||||
|
etat = self.etat
|
||||||
|
|
||||||
|
if format_api:
|
||||||
|
etat = EtatJustificatif.inverse().get(self.etat).name
|
||||||
data = {
|
data = {
|
||||||
"assiduite_id": self.assiduite_id,
|
"assiduite_id": self.assiduite_id,
|
||||||
"etudid": self.etudid,
|
"etudid": self.etudid,
|
||||||
"moduleimpl_id": self.moduleimpl_id,
|
"moduleimpl_id": self.moduleimpl_id,
|
||||||
"date_debut": self.date_debut,
|
"date_debut": self.date_debut,
|
||||||
"date_fin": self.date_fin,
|
"date_fin": self.date_fin,
|
||||||
"etat": self.etat,
|
"etat": etat,
|
||||||
"desc": self.desc,
|
"desc": self.desc,
|
||||||
"entry_date": self.entry_date,
|
"entry_date": self.entry_date,
|
||||||
}
|
}
|
||||||
@ -78,17 +82,8 @@ class Assiduite(db.Model):
|
|||||||
# Vérification de non duplication des périodes
|
# Vérification de non duplication des périodes
|
||||||
assiduites: list[Assiduite] = etud.assiduites.all()
|
assiduites: list[Assiduite] = etud.assiduites.all()
|
||||||
|
|
||||||
date_debut = localize_datetime(date_debut)
|
assiduites: list[Justificatif] = etud.assiduites.all()
|
||||||
date_fin = localize_datetime(date_fin)
|
if is_period_conflicting(date_debut, date_fin, assiduites):
|
||||||
assiduites = [
|
|
||||||
ass
|
|
||||||
for ass in assiduites
|
|
||||||
if is_period_overlapping(
|
|
||||||
(date_debut, date_fin),
|
|
||||||
(ass.date_debut, ass.date_fin),
|
|
||||||
)
|
|
||||||
]
|
|
||||||
if len(assiduites) != 0:
|
|
||||||
raise ScoValueError(
|
raise ScoValueError(
|
||||||
"Duplication des assiduités (la période rentrée rentre en conflit avec une assiduité enregistrée)"
|
"Duplication des assiduités (la période rentrée rentre en conflit avec une assiduité enregistrée)"
|
||||||
)
|
)
|
||||||
@ -156,13 +151,20 @@ class Justificatif(db.Model):
|
|||||||
# Archive_id -> sco_archives_justificatifs.py
|
# Archive_id -> sco_archives_justificatifs.py
|
||||||
fichier = db.Column(db.Text())
|
fichier = db.Column(db.Text())
|
||||||
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self, format_api: bool = False) -> dict:
|
||||||
|
"""transformation de l'objet en dictionnaire sérialisable"""
|
||||||
|
|
||||||
|
etat = self.etat
|
||||||
|
|
||||||
|
if format_api:
|
||||||
|
etat = EtatJustificatif.inverse().get(self.etat).name
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"justif_id": self.justif_id,
|
"justif_id": self.justif_id,
|
||||||
"etudid": self.etudid,
|
"etudid": self.etudid,
|
||||||
"date_debut": self.date_debut,
|
"date_debut": self.date_debut,
|
||||||
"date_fin": self.date_fin,
|
"date_fin": self.date_fin,
|
||||||
"etat": self.etat,
|
"etat": etat,
|
||||||
"raison": self.raison,
|
"raison": self.raison,
|
||||||
"fichier": self.fichier,
|
"fichier": self.fichier,
|
||||||
"entry_date": self.entry_date,
|
"entry_date": self.entry_date,
|
||||||
@ -181,23 +183,12 @@ class Justificatif(db.Model):
|
|||||||
"""Créer un nouveau justificatif pour l'étudiant"""
|
"""Créer un nouveau justificatif pour l'étudiant"""
|
||||||
# Vérification de non duplication des périodes
|
# Vérification de non duplication des périodes
|
||||||
justificatifs: list[Justificatif] = etud.justificatifs.all()
|
justificatifs: list[Justificatif] = etud.justificatifs.all()
|
||||||
|
if is_period_conflicting(date_debut, date_fin, justificatifs):
|
||||||
date_debut = localize_datetime(date_debut)
|
|
||||||
date_fin = localize_datetime(date_fin)
|
|
||||||
justificatifs = [
|
|
||||||
just
|
|
||||||
for just in justificatifs
|
|
||||||
if is_period_overlapping(
|
|
||||||
(date_debut, date_fin),
|
|
||||||
(just.date_debut, just.date_fin),
|
|
||||||
)
|
|
||||||
]
|
|
||||||
if len(justificatifs) != 0:
|
|
||||||
raise ScoValueError(
|
raise ScoValueError(
|
||||||
"Duplication des justificatifs (la période rentrée rentre en conflit avec un justificatif enregistré)"
|
"Duplication des justificatifs (la période rentrée rentre en conflit avec un justificatif enregistré)"
|
||||||
)
|
)
|
||||||
|
|
||||||
nouv_assiduite = Justificatif(
|
nouv_justificatif = Justificatif(
|
||||||
date_debut=date_debut,
|
date_debut=date_debut,
|
||||||
date_fin=date_fin,
|
date_fin=date_fin,
|
||||||
etat=etat,
|
etat=etat,
|
||||||
@ -205,4 +196,28 @@ class Justificatif(db.Model):
|
|||||||
raison=raison,
|
raison=raison,
|
||||||
)
|
)
|
||||||
|
|
||||||
return nouv_assiduite
|
return nouv_justificatif
|
||||||
|
|
||||||
|
|
||||||
|
def is_period_conflicting(
|
||||||
|
date_debut: datetime,
|
||||||
|
date_fin: datetime,
|
||||||
|
collection: list[Assiduite or Justificatif],
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Vérifie si une date n'entre pas en collision
|
||||||
|
avec les justificatifs ou assiduites déjà présentes
|
||||||
|
"""
|
||||||
|
|
||||||
|
date_debut = localize_datetime(date_debut)
|
||||||
|
date_fin = localize_datetime(date_fin)
|
||||||
|
unified = [
|
||||||
|
uni
|
||||||
|
for uni in collection
|
||||||
|
if is_period_overlapping(
|
||||||
|
(date_debut, date_fin),
|
||||||
|
(uni.date_debut, uni.date_fin),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
return len(unified) != 0
|
||||||
|
@ -256,12 +256,23 @@ class UniteEns(db.Model):
|
|||||||
|
|
||||||
class DispenseUE(db.Model):
|
class DispenseUE(db.Model):
|
||||||
"""Dispense d'UE
|
"""Dispense d'UE
|
||||||
Utilisé en PCC (BUT) pour indiquer les étudiants redoublants avec une UE capitalisée
|
Utilisé en APC (BUT) pour indiquer les étudiants redoublants avec une UE capitalisée
|
||||||
qu'ils ne refont pas.
|
qu'ils ne refont pas.
|
||||||
|
La dispense d'UE n'est PAS une validation:
|
||||||
|
- elle n'est pas affectée par les décisions de jury (pas effacée)
|
||||||
|
- elle est associée à un formsemestre
|
||||||
|
- elle ne permet pas la délivrance d'ECTS ou du diplôme.
|
||||||
|
|
||||||
|
On utilise cette dispense et non une "inscription" par souci d'efficacité:
|
||||||
|
en général, la grande majorité des étudiants suivront toutes les UEs de leur parcours,
|
||||||
|
la dispense étant une exception.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__table_args__ = (db.UniqueConstraint("ue_id", "etudid"),)
|
__table_args__ = (db.UniqueConstraint("formsemestre_id", "ue_id", "etudid"),)
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
formsemestre_id = formsemestre_id = db.Column(
|
||||||
|
db.Integer, db.ForeignKey("notes_formsemestre.id"), index=True, nullable=True
|
||||||
|
)
|
||||||
ue_id = db.Column(
|
ue_id = db.Column(
|
||||||
db.Integer,
|
db.Integer,
|
||||||
db.ForeignKey(UniteEns.id, ondelete="CASCADE"),
|
db.ForeignKey(UniteEns.id, ondelete="CASCADE"),
|
||||||
@ -280,3 +291,25 @@ class DispenseUE(db.Model):
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"""<{self.__class__.__name__} {self.id} etud={
|
return f"""<{self.__class__.__name__} {self.id} etud={
|
||||||
repr(self.etud)} ue={repr(self.ue)}>"""
|
repr(self.etud)} ue={repr(self.ue)}>"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load_formsemestre_dispense_ues_set(
|
||||||
|
cls, formsemestre: "FormSemestre", etudids: pd.Index, ues: list[UniteEns]
|
||||||
|
) -> set[tuple[int, int]]:
|
||||||
|
"""Construit l'ensemble des
|
||||||
|
etudids = modimpl_inscr_df.index, # les etudids
|
||||||
|
ue_ids : modimpl_coefs_df.index, # les UE du formsemestre sans les UE bonus sport
|
||||||
|
|
||||||
|
Résultat: set de (etudid, ue_id).
|
||||||
|
"""
|
||||||
|
# Prend toutes les dispenses obtenues par des étudiants de ce formsemestre,
|
||||||
|
# puis filtre sur inscrits et ues
|
||||||
|
ue_ids = {ue.id for ue in ues}
|
||||||
|
dispense_ues = {
|
||||||
|
(dispense_ue.etudid, dispense_ue.ue_id)
|
||||||
|
for dispense_ue in DispenseUE.query.filter_by(
|
||||||
|
formsemestre_id=formsemestre.id
|
||||||
|
)
|
||||||
|
if dispense_ue.etudid in etudids and dispense_ue.ue_id in ue_ids
|
||||||
|
}
|
||||||
|
return dispense_ues
|
||||||
|
@ -89,7 +89,7 @@ class BaseArchiver(object):
|
|||||||
self.archive_type = archive_type
|
self.archive_type = archive_type
|
||||||
self.initialized = False
|
self.initialized = False
|
||||||
self.root = None
|
self.root = None
|
||||||
self.dept_id = getattr(g, "scodoc_dept_id")
|
self.dept_id = None
|
||||||
|
|
||||||
def set_dept_id(self, dept_id: int):
|
def set_dept_id(self, dept_id: int):
|
||||||
"set dept"
|
"set dept"
|
||||||
@ -115,6 +115,8 @@ class BaseArchiver(object):
|
|||||||
finally:
|
finally:
|
||||||
scu.GSL.release()
|
scu.GSL.release()
|
||||||
self.initialized = True
|
self.initialized = True
|
||||||
|
if self.dept_id is None:
|
||||||
|
self.dept_id = getattr(g, "scodoc_dept_id")
|
||||||
|
|
||||||
def get_obj_dir(self, oid: int):
|
def get_obj_dir(self, oid: int):
|
||||||
"""
|
"""
|
||||||
|
@ -19,9 +19,6 @@ class JustificatifArchiver(BaseArchiver):
|
|||||||
├── [_description.txt]
|
├── [_description.txt]
|
||||||
└── [<filename.ext>]
|
└── [<filename.ext>]
|
||||||
|
|
||||||
|
|
||||||
TODO:
|
|
||||||
- Faire fonction suppression fichier unique dans archive
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -38,6 +35,7 @@ class JustificatifArchiver(BaseArchiver):
|
|||||||
"""
|
"""
|
||||||
Ajoute un fichier dans une archive "justificatif" pour l'etudid donné
|
Ajoute un fichier dans une archive "justificatif" pour l'etudid donné
|
||||||
Retourne l'archive_name utilisé
|
Retourne l'archive_name utilisé
|
||||||
|
TODO: renvoie archive_name + filename
|
||||||
"""
|
"""
|
||||||
self._set_dept(etudid)
|
self._set_dept(etudid)
|
||||||
if archive_name is None:
|
if archive_name is None:
|
||||||
@ -104,9 +102,8 @@ class JustificatifArchiver(BaseArchiver):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _set_dept(self, etudid: int):
|
def _set_dept(self, etudid: int):
|
||||||
if g.scodoc_dept is None or g.scodoc_dept_id is None:
|
"""
|
||||||
etud: Identite = Identite.query.filter_by(id=etudid).first()
|
Mets à jour le dept_id de l'archiver en fonction du département de l'étudiant
|
||||||
dept: Departement = Departement.query.filter_by(id=etud.dept_id).first()
|
"""
|
||||||
|
etud: Identite = Identite.query.filter_by(id=etudid).first()
|
||||||
g.scodoc_dept = dept.acronym
|
self.set_dept_id(etud.dept_id)
|
||||||
g.scodoc_dept_id = dept.id
|
|
||||||
|
@ -85,7 +85,7 @@ def filter_assiduites_by_etat(assiduites: Assiduite, etat: str) -> Assiduite:
|
|||||||
Filtrage d'une collection d'assiduites en fonction de leur état
|
Filtrage d'une collection d'assiduites en fonction de leur état
|
||||||
"""
|
"""
|
||||||
etats: list[str] = list(etat.split(","))
|
etats: list[str] = list(etat.split(","))
|
||||||
etats = [scu.ETATS_ASSIDUITE.get(e, -1) for e in etats]
|
etats = [scu.EtatAssiduite.get(e, -1) for e in etats]
|
||||||
return assiduites.filter(Assiduite.etat.in_(etats))
|
return assiduites.filter(Assiduite.etat.in_(etats))
|
||||||
|
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ def filter_justificatifs_by_etat(
|
|||||||
Filtrage d'une collection de justificatifs en fonction de leur état
|
Filtrage d'une collection de justificatifs en fonction de leur état
|
||||||
"""
|
"""
|
||||||
etats: list[str] = list(etat.split(","))
|
etats: list[str] = list(etat.split(","))
|
||||||
etats = [scu.ETATS_JUSTIFICATIF.get(e, -1) for e in etats]
|
etats = [scu.EtatJustificatif.get(e, -1) for e in etats]
|
||||||
return justificatifs.filter(Justificatif.etat.in_(etats))
|
return justificatifs.filter(Justificatif.etat.in_(etats))
|
||||||
|
|
||||||
|
|
||||||
@ -172,3 +172,31 @@ def filter_by_formsemestre(assiduites_query: Assiduite, formsemestre: FormSemest
|
|||||||
Assiduite.date_debut >= formsemestre.date_debut
|
Assiduite.date_debut >= formsemestre.date_debut
|
||||||
)
|
)
|
||||||
return assiduites_query.filter(Assiduite.date_fin <= formsemestre.date_fin)
|
return assiduites_query.filter(Assiduite.date_fin <= formsemestre.date_fin)
|
||||||
|
|
||||||
|
|
||||||
|
def justifies(justi: Justificatif) -> list[int]:
|
||||||
|
"""
|
||||||
|
Retourne la liste des assiduite_id qui sont justifié par la justification
|
||||||
|
Une assiduité est justifiée si elle est STRICTEMENT comprise dans la plage du justificatif
|
||||||
|
et que l'état du justificatif est "validé"
|
||||||
|
"""
|
||||||
|
|
||||||
|
justified: list[int] = []
|
||||||
|
|
||||||
|
if justi.etat != scu.EtatJustificatif.VALIDE:
|
||||||
|
return justified
|
||||||
|
|
||||||
|
assiduites_query: Assiduite = Assiduite.query.join(
|
||||||
|
Justificatif, Assiduite.etudid == Justificatif.etudid
|
||||||
|
).filter(Assiduite.etat != scu.EtatAssiduite.PRESENT)
|
||||||
|
|
||||||
|
assiduites_query = filter_assiduites_by_date(
|
||||||
|
assiduites_query, justi.date_debut, True
|
||||||
|
)
|
||||||
|
assiduites_query = filter_assiduites_by_date(
|
||||||
|
assiduites_query, justi.date_fin, False
|
||||||
|
)
|
||||||
|
|
||||||
|
justified = [assi.id for assi in assiduites_query.all()]
|
||||||
|
|
||||||
|
return justified
|
||||||
|
@ -36,7 +36,7 @@ from flask_login import current_user
|
|||||||
|
|
||||||
from app import db, log
|
from app import db, log
|
||||||
|
|
||||||
from app.models import ModuleImpl, ScolarNews
|
from app.models import Evaluation, ModuleImpl, ScolarNews
|
||||||
from app.models.evaluations import evaluation_enrich_dict, check_evaluation_args
|
from app.models.evaluations import evaluation_enrich_dict, check_evaluation_args
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
import app.scodoc.notesdb as ndb
|
import app.scodoc.notesdb as ndb
|
||||||
|
@ -93,7 +93,7 @@ _formsemestreEditor = ndb.EditableTable(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_formsemestre(formsemestre_id, raise_soft_exc=False):
|
def get_formsemestre(formsemestre_id: int):
|
||||||
"list ONE formsemestre"
|
"list ONE formsemestre"
|
||||||
if formsemestre_id is None:
|
if formsemestre_id is None:
|
||||||
raise ValueError("get_formsemestre: id manquant")
|
raise ValueError("get_formsemestre: id manquant")
|
||||||
@ -105,10 +105,8 @@ def get_formsemestre(formsemestre_id, raise_soft_exc=False):
|
|||||||
sems = do_formsemestre_list(args={"formsemestre_id": formsemestre_id})
|
sems = do_formsemestre_list(args={"formsemestre_id": formsemestre_id})
|
||||||
if not sems:
|
if not sems:
|
||||||
log(f"get_formsemestre: invalid formsemestre_id ({formsemestre_id})")
|
log(f"get_formsemestre: invalid formsemestre_id ({formsemestre_id})")
|
||||||
if raise_soft_exc:
|
raise ScoValueError(f"semestre {formsemestre_id} inconnu !")
|
||||||
raise ScoValueError(f"semestre {formsemestre_id} inconnu !")
|
|
||||||
else:
|
|
||||||
raise ValueError(f"semestre {formsemestre_id} inconnu !")
|
|
||||||
g.stored_get_formsemestre[formsemestre_id] = sems[0]
|
g.stored_get_formsemestre[formsemestre_id] = sems[0]
|
||||||
return sems[0]
|
return sems[0]
|
||||||
|
|
||||||
|
@ -1629,7 +1629,9 @@ def do_formsemestre_delete(formsemestre_id):
|
|||||||
req = """DELETE FROM notes_formsemestre_etapes
|
req = """DELETE FROM notes_formsemestre_etapes
|
||||||
WHERE formsemestre_id=%(formsemestre_id)s"""
|
WHERE formsemestre_id=%(formsemestre_id)s"""
|
||||||
cursor.execute(req, {"formsemestre_id": formsemestre_id})
|
cursor.execute(req, {"formsemestre_id": formsemestre_id})
|
||||||
|
# --- Dispenses d'UE
|
||||||
|
req = """DELETE FROM "dispenseUE" WHERE formsemestre_id=%(formsemestre_id)s"""
|
||||||
|
cursor.execute(req, {"formsemestre_id": formsemestre_id})
|
||||||
# --- Destruction du semestre
|
# --- Destruction du semestre
|
||||||
sco_formsemestre._formsemestreEditor.delete(cnx, formsemestre_id)
|
sco_formsemestre._formsemestreEditor.delete(cnx, formsemestre_id)
|
||||||
|
|
||||||
|
@ -523,7 +523,8 @@ def retreive_formsemestre_from_request() -> int:
|
|||||||
# Element HTML decrivant un semestre (barre de menu et infos)
|
# Element HTML decrivant un semestre (barre de menu et infos)
|
||||||
def formsemestre_page_title(formsemestre_id=None):
|
def formsemestre_page_title(formsemestre_id=None):
|
||||||
"""Element HTML decrivant un semestre (barre de menu et infos)
|
"""Element HTML decrivant un semestre (barre de menu et infos)
|
||||||
Cherche dans la requete si un semestre est défini (formsemestre_id ou moduleimpl ou evaluation ou group)
|
Cherche dans la requete si un semestre est défini
|
||||||
|
via (formsemestre_id ou moduleimpl ou evaluation ou group)
|
||||||
"""
|
"""
|
||||||
formsemestre_id = (
|
formsemestre_id = (
|
||||||
formsemestre_id
|
formsemestre_id
|
||||||
@ -540,15 +541,13 @@ def formsemestre_page_title(formsemestre_id=None):
|
|||||||
return ""
|
return ""
|
||||||
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||||
|
|
||||||
h = render_template(
|
return render_template(
|
||||||
"formsemestre_page_title.j2",
|
"formsemestre_page_title.j2",
|
||||||
formsemestre=formsemestre,
|
formsemestre=formsemestre,
|
||||||
scu=scu,
|
scu=scu,
|
||||||
sem_menu_bar=formsemestre_status_menubar(formsemestre),
|
sem_menu_bar=formsemestre_status_menubar(formsemestre),
|
||||||
)
|
)
|
||||||
|
|
||||||
return h
|
|
||||||
|
|
||||||
|
|
||||||
def fill_formsemestre(sem):
|
def fill_formsemestre(sem):
|
||||||
"""Add some useful fields to help display formsemestres"""
|
"""Add some useful fields to help display formsemestres"""
|
||||||
@ -768,8 +767,7 @@ def formsemestre_description_table(
|
|||||||
caption=title,
|
caption=title,
|
||||||
html_caption=title,
|
html_caption=title,
|
||||||
html_class="table_leftalign formsemestre_description",
|
html_class="table_leftalign formsemestre_description",
|
||||||
base_url="%s?formsemestre_id=%s&with_evals=%s"
|
base_url=f"{request.base_url}?formsemestre_id={formsemestre_id}&with_evals={with_evals}",
|
||||||
% (request.base_url, formsemestre_id, with_evals),
|
|
||||||
page_title=title,
|
page_title=title,
|
||||||
html_title=html_sco_header.html_sem_header(
|
html_title=html_sco_header.html_sem_header(
|
||||||
"Description du semestre", with_page_header=False
|
"Description du semestre", with_page_header=False
|
||||||
@ -923,7 +921,7 @@ def _make_listes_sem(formsemestre: FormSemestre, with_absences=True):
|
|||||||
f"""<h4><a
|
f"""<h4><a
|
||||||
href="{
|
href="{
|
||||||
url_for("scolar.edit_partition_form",
|
url_for("scolar.edit_partition_form",
|
||||||
formsemestre_id=formsemestre.id,
|
formsemestre_id=formsemestre.id,
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
)
|
)
|
||||||
}">Ajouter une partition</a></h4>"""
|
}">Ajouter une partition</a></h4>"""
|
||||||
@ -980,14 +978,14 @@ def formsemestre_status_head(formsemestre_id: int = None, page_title: str = None
|
|||||||
),
|
),
|
||||||
f"""<table>
|
f"""<table>
|
||||||
<tr><td class="fichetitre2">Formation: </td><td>
|
<tr><td class="fichetitre2">Formation: </td><td>
|
||||||
<a href="{url_for('notes.ue_table',
|
<a href="{url_for('notes.ue_table',
|
||||||
scodoc_dept=g.scodoc_dept, formation_id=sem.formation.id)}"
|
scodoc_dept=g.scodoc_dept, formation_id=sem.formation.id)}"
|
||||||
class="discretelink" title="Formation {
|
class="discretelink" title="Formation {
|
||||||
formation.acronyme}, v{formation.version}">{formation.titre}</a>
|
formation.acronyme}, v{formation.version}">{formation.titre}</a>
|
||||||
""",
|
""",
|
||||||
]
|
]
|
||||||
if sem.semestre_id >= 0:
|
if sem.semestre_id >= 0:
|
||||||
H.append(", %s %s" % (parcours.SESSION_NAME, sem.semestre_id))
|
H.append(f", {parcours.SESSION_NAME} {sem.semestre_id}")
|
||||||
if sem.modalite:
|
if sem.modalite:
|
||||||
H.append(f" en {sem.modalite}")
|
H.append(f" en {sem.modalite}")
|
||||||
if sem.etapes:
|
if sem.etapes:
|
||||||
@ -1091,7 +1089,8 @@ def formsemestre_status(formsemestre_id=None, check_parcours=True):
|
|||||||
elif datetime.date.today() > formsemestre.date_fin:
|
elif datetime.date.today() > formsemestre.date_fin:
|
||||||
if formsemestre.etat:
|
if formsemestre.etat:
|
||||||
H.append(
|
H.append(
|
||||||
"""<span class="formsemestre_status_warning">semestre du passé non verrouillé</span>"""
|
"""<span
|
||||||
|
class="formsemestre_status_warning">semestre terminé mais non verrouillé</span>"""
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
H.append(
|
H.append(
|
||||||
@ -1101,7 +1100,8 @@ def formsemestre_status(formsemestre_id=None, check_parcours=True):
|
|||||||
|
|
||||||
if sco_preferences.get_preference("bul_show_all_evals", formsemestre_id):
|
if sco_preferences.get_preference("bul_show_all_evals", formsemestre_id):
|
||||||
H.append(
|
H.append(
|
||||||
"""<div class="formsemestre_status_warning">Toutes évaluations (même incomplètes) visibles</div>"""
|
"""<div class="formsemestre_status_warning"
|
||||||
|
>Toutes évaluations (même incomplètes) visibles</div>"""
|
||||||
)
|
)
|
||||||
|
|
||||||
if nt.expr_diagnostics:
|
if nt.expr_diagnostics:
|
||||||
@ -1215,6 +1215,11 @@ def formsemestre_tableau_modules(
|
|||||||
prev_ue_id = None
|
prev_ue_id = None
|
||||||
for modimpl in modimpls:
|
for modimpl in modimpls:
|
||||||
mod: Module = Module.query.get(modimpl["module_id"])
|
mod: Module = Module.query.get(modimpl["module_id"])
|
||||||
|
moduleimpl_status_url = url_for(
|
||||||
|
"notes.moduleimpl_status",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
moduleimpl_id=modimpl["moduleimpl_id"],
|
||||||
|
)
|
||||||
mod_descr = "Module " + (mod.titre or "")
|
mod_descr = "Module " + (mod.titre or "")
|
||||||
if mod.is_apc():
|
if mod.is_apc():
|
||||||
coef_descr = ", ".join(
|
coef_descr = ", ".join(
|
||||||
@ -1240,7 +1245,7 @@ def formsemestre_tableau_modules(
|
|||||||
prev_ue_id = ue["ue_id"]
|
prev_ue_id = ue["ue_id"]
|
||||||
titre = ue["titre"]
|
titre = ue["titre"]
|
||||||
if use_ue_coefs:
|
if use_ue_coefs:
|
||||||
titre += " <b>(coef. %s)</b>" % (ue["coefficient"] or 0.0)
|
titre += f""" <b>(coef. {ue["coefficient"] or 0.0})</b>"""
|
||||||
H.append(
|
H.append(
|
||||||
f"""<tr class="formsemestre_status_ue"><td colspan="4">
|
f"""<tr class="formsemestre_status_ue"><td colspan="4">
|
||||||
<span class="status_ue_acro">{ue["acronyme"]}</span>
|
<span class="status_ue_acro">{ue["acronyme"]}</span>
|
||||||
@ -1280,23 +1285,18 @@ def formsemestre_tableau_modules(
|
|||||||
H.append(f'<tr class="formsemestre_status{fontorange}">')
|
H.append(f'<tr class="formsemestre_status{fontorange}">')
|
||||||
|
|
||||||
H.append(
|
H.append(
|
||||||
f"""<td class="formsemestre_status_code""><a
|
f"""
|
||||||
href="{url_for('notes.moduleimpl_status',
|
<td class="formsemestre_status_code""><a
|
||||||
scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl['moduleimpl_id'])}"
|
href="{moduleimpl_status_url}"
|
||||||
title="{mod_descr}" class="stdlink">{mod.code}</a></td>"""
|
title="{mod_descr}" class="stdlink">{mod.code}</a></td>
|
||||||
)
|
<td class="scotext"><a href="{moduleimpl_status_url}" title="{mod_descr}"
|
||||||
H.append(
|
class="formsemestre_status_link">{mod.abbrev or mod.titre or ""}</a>
|
||||||
f"""<td class="scotext"><a href="{
|
|
||||||
url_for( "notes.moduleimpl_status",
|
|
||||||
scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl["moduleimpl_id"]
|
|
||||||
) }" title="{mod_descr}" class="formsemestre_status_link">{mod.abbrev or mod.titre or ""}</a>
|
|
||||||
</td>
|
</td>
|
||||||
<td class="formsemestre_status_inscrits">{len(mod_inscrits)}</td>
|
<td class="formsemestre_status_inscrits">{len(mod_inscrits)}</td>
|
||||||
<td class="resp scotext">
|
<td class="resp scotext">
|
||||||
<a class="discretelink" href="{
|
<a class="discretelink" href="{moduleimpl_status_url}" title="{mod_ens}">{
|
||||||
url_for("notes.moduleimpl_status",
|
sco_users.user_info(modimpl["responsable_id"])["prenomnom"]
|
||||||
scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl["moduleimpl_id"]
|
}</a>
|
||||||
) }" title="{mod_ens}">{ sco_users.user_info(modimpl["responsable_id"])["prenomnom"] }</a>
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
"""
|
"""
|
||||||
@ -1331,18 +1331,21 @@ def formsemestre_tableau_modules(
|
|||||||
)
|
)
|
||||||
if nb_evals != 0:
|
if nb_evals != 0:
|
||||||
H.append(
|
H.append(
|
||||||
'<a href="moduleimpl_status?moduleimpl_id=%s" class="formsemestre_status_link">%s prévues, %s ok</a>'
|
f"""<a href="{moduleimpl_status_url}"
|
||||||
% (modimpl["moduleimpl_id"], nb_evals, etat["nb_evals_completes"])
|
class="formsemestre_status_link">{nb_evals} prévues,
|
||||||
|
{etat["nb_evals_completes"]} ok</a>"""
|
||||||
)
|
)
|
||||||
if etat["nb_evals_en_cours"] > 0:
|
if etat["nb_evals_en_cours"] > 0:
|
||||||
H.append(
|
H.append(
|
||||||
', <span><a class="redlink" href="moduleimpl_status?moduleimpl_id=%s" title="Il manque des notes">%s en cours</a></span>'
|
f""", <span><a class="redlink" href="{moduleimpl_status_url}"
|
||||||
% (modimpl["moduleimpl_id"], etat["nb_evals_en_cours"])
|
title="Il manque des notes">{
|
||||||
|
etat["nb_evals_en_cours"]
|
||||||
|
} en cours</a></span>"""
|
||||||
)
|
)
|
||||||
if etat["attente"]:
|
if etat["attente"]:
|
||||||
H.append(
|
H.append(
|
||||||
' <span><a class="redlink" href="moduleimpl_status?moduleimpl_id=%s" title="Il y a des notes en attente">[en attente]</a></span>'
|
f""" <span><a class="redlink" href="{moduleimpl_status_url}"
|
||||||
% modimpl["moduleimpl_id"]
|
title="Il y a des notes en attente">[en attente]</a></span>"""
|
||||||
)
|
)
|
||||||
elif mod.module_type == ModuleType.MALUS:
|
elif mod.module_type == ModuleType.MALUS:
|
||||||
nb_malus_notes = sum(
|
nb_malus_notes = sum(
|
||||||
@ -1352,10 +1355,10 @@ def formsemestre_tableau_modules(
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
H.append(
|
H.append(
|
||||||
"""<td class="malus">
|
f"""<td class="malus">
|
||||||
<a href="moduleimpl_status?moduleimpl_id=%s" class="formsemestre_status_link">malus (%d notes)</a>
|
<a href="{moduleimpl_status_url}" class="formsemestre_status_link">malus
|
||||||
|
({nb_malus_notes} notes)</a>
|
||||||
"""
|
"""
|
||||||
% (modimpl["moduleimpl_id"], nb_malus_notes)
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Invalid module_type {mod.module_type}") # a bug
|
raise ValueError(f"Invalid module_type {mod.module_type}") # a bug
|
||||||
|
@ -300,9 +300,7 @@ def get_group_infos(group_id, etat=None): # was _getlisteetud
|
|||||||
|
|
||||||
cnx = ndb.GetDBConnexion()
|
cnx = ndb.GetDBConnexion()
|
||||||
group = get_group(group_id)
|
group = get_group(group_id)
|
||||||
sem = sco_formsemestre.get_formsemestre(
|
sem = sco_formsemestre.get_formsemestre(group["formsemestre_id"])
|
||||||
group["formsemestre_id"], raise_soft_exc=True
|
|
||||||
)
|
|
||||||
|
|
||||||
members = get_group_members(group_id, etat=etat)
|
members = get_group_members(group_id, etat=etat)
|
||||||
# add human readable description of state:
|
# add human readable description of state:
|
||||||
|
@ -7,7 +7,7 @@ from flask import g
|
|||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
|
|
||||||
from app.auth.models import User
|
from app.auth.models import User
|
||||||
|
from app.models import FormSemestre
|
||||||
import app.scodoc.notesdb as ndb
|
import app.scodoc.notesdb as ndb
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
from app.scodoc import html_sco_header
|
from app.scodoc import html_sco_header
|
||||||
@ -164,18 +164,14 @@ def check_access_diretud(formsemestre_id, required_permission=Permission.ScoImpl
|
|||||||
return True, ""
|
return True, ""
|
||||||
|
|
||||||
|
|
||||||
def can_change_groups(formsemestre_id):
|
def can_change_groups(formsemestre_id: int) -> bool:
|
||||||
"Vrai si l'utilisateur peut changer les groupes dans ce semestre"
|
"Vrai si l'utilisateur peut changer les groupes dans ce semestre"
|
||||||
from app.scodoc import sco_formsemestre
|
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||||
|
if not formsemestre.etat:
|
||||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id, raise_soft_exc=True)
|
|
||||||
if not sem["etat"]:
|
|
||||||
return False # semestre verrouillé
|
return False # semestre verrouillé
|
||||||
if current_user.has_permission(Permission.ScoEtudChangeGroups):
|
if current_user.has_permission(Permission.ScoEtudChangeGroups):
|
||||||
return True # admin, chef dept
|
return True # typiquement admin, chef dept
|
||||||
if current_user.id in sem["responsables"]:
|
return formsemestre.est_responsable(current_user)
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def can_handle_passwd(user: User, allow_admindepts=False) -> bool:
|
def can_handle_passwd(user: User, allow_admindepts=False) -> bool:
|
||||||
|
@ -32,7 +32,7 @@ import base64
|
|||||||
import bisect
|
import bisect
|
||||||
import copy
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
from enum import IntEnum
|
from enum import IntEnum, Enum
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
@ -50,17 +50,17 @@ from PIL import Image as PILImage
|
|||||||
import pydot
|
import pydot
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
import dateutil.parser as dtparser
|
||||||
import flask
|
import flask
|
||||||
from flask import g, request
|
from flask import g, request
|
||||||
from flask import flash, url_for, make_response, jsonify
|
from flask import flash, url_for, make_response, jsonify
|
||||||
from werkzeug.http import HTTP_STATUS_CODES
|
from werkzeug.http import HTTP_STATUS_CODES
|
||||||
|
|
||||||
from config import Config
|
from config import Config
|
||||||
from app import log
|
from app import log, db
|
||||||
from app.scodoc.sco_vdi import ApoEtapeVDI
|
from app.scodoc.sco_vdi import ApoEtapeVDI
|
||||||
from app.scodoc.sco_codes_parcours import NOTES_TOLERANCE, CODES_EXPL
|
from app.scodoc.sco_codes_parcours import NOTES_TOLERANCE, CODES_EXPL
|
||||||
from app.scodoc import sco_xml
|
from app.scodoc import sco_xml
|
||||||
from app.scodoc.intervals import intervalmap
|
|
||||||
|
|
||||||
import sco_version
|
import sco_version
|
||||||
|
|
||||||
@ -88,7 +88,43 @@ ETATS_INSCRIPTION = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class EtatAssiduite(IntEnum):
|
def get_model_api_object(model_cls: db.Model, model_id: int):
|
||||||
|
from app.models import Identite
|
||||||
|
|
||||||
|
query = model_cls.query.filter_by(id=model_id)
|
||||||
|
if g.scodoc_dept:
|
||||||
|
query = query.join(Identite).filter_by(dept_id=g.scodoc_dept_id)
|
||||||
|
unique: model_cls = query.first_or_404()
|
||||||
|
|
||||||
|
return jsonify(unique.to_dict(format_api=True))
|
||||||
|
|
||||||
|
|
||||||
|
class BiDirectionalEnum(Enum):
|
||||||
|
"""Permet la recherche inverse d'un enum
|
||||||
|
Condition : les clés et les valeurs doivent être uniques
|
||||||
|
les clés doivent être en MAJUSCULES
|
||||||
|
"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def contains(cls, attr: str):
|
||||||
|
return attr.upper() in cls._member_names_
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, attr: str, default: any = None):
|
||||||
|
val = None
|
||||||
|
try:
|
||||||
|
val = cls[attr.upper()]
|
||||||
|
except (KeyError, AttributeError):
|
||||||
|
val = default
|
||||||
|
return val
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def inverse(cls):
|
||||||
|
"""Retourne un dictionnaire représentant la map inverse de l'Enum"""
|
||||||
|
return cls._value2member_map_
|
||||||
|
|
||||||
|
|
||||||
|
class EtatAssiduite(int, BiDirectionalEnum):
|
||||||
"""Code des états d'assiduité"""
|
"""Code des états d'assiduité"""
|
||||||
|
|
||||||
# Stockés en BD ne pas modifier
|
# Stockés en BD ne pas modifier
|
||||||
@ -110,7 +146,7 @@ ETATS_ASSIDUITE = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class EtatJustificatif(IntEnum):
|
class EtatJustificatif(int, BiDirectionalEnum):
|
||||||
"""Code des états des justificatifs"""
|
"""Code des états des justificatifs"""
|
||||||
|
|
||||||
# Stockés en BD ne pas modifier
|
# Stockés en BD ne pas modifier
|
||||||
@ -121,21 +157,6 @@ class EtatJustificatif(IntEnum):
|
|||||||
MODIFIE = 3
|
MODIFIE = 3
|
||||||
|
|
||||||
|
|
||||||
ETAT_JUSTIFICATIF_NAME = {
|
|
||||||
EtatJustificatif.VALIDE: "validé",
|
|
||||||
EtatJustificatif.NON_VALIDE: "non validé",
|
|
||||||
EtatJustificatif.ATTENTE: "en attente",
|
|
||||||
EtatJustificatif.MODIFIE: "modifié",
|
|
||||||
}
|
|
||||||
|
|
||||||
ETATS_JUSTIFICATIF = {
|
|
||||||
"validé": EtatJustificatif.VALIDE,
|
|
||||||
"non vaidé": EtatJustificatif.NON_VALIDE,
|
|
||||||
"en attente": EtatJustificatif.ATTENTE,
|
|
||||||
"modifié": EtatJustificatif.MODIFIE,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def is_iso_formated(date: str, convert=False) -> bool or datetime.datetime or None:
|
def is_iso_formated(date: str, convert=False) -> bool or datetime.datetime or None:
|
||||||
"""
|
"""
|
||||||
Vérifie si une date est au format iso
|
Vérifie si une date est au format iso
|
||||||
@ -147,7 +168,6 @@ def is_iso_formated(date: str, convert=False) -> bool or datetime.datetime or No
|
|||||||
|
|
||||||
Retourne None sinon
|
Retourne None sinon
|
||||||
"""
|
"""
|
||||||
import dateutil.parser as dtparser
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
date: datetime.datetime = dtparser.isoparse(date)
|
date: datetime.datetime = dtparser.isoparse(date)
|
||||||
|
@ -1621,10 +1621,24 @@ def etud_desinscrit_ue(etudid, formsemestre_id, ue_id):
|
|||||||
ue = UniteEns.query.get_or_404(ue_id)
|
ue = UniteEns.query.get_or_404(ue_id)
|
||||||
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||||
if ue.formation.is_apc():
|
if ue.formation.is_apc():
|
||||||
if DispenseUE.query.filter_by(etudid=etudid, ue_id=ue_id).count() == 0:
|
if (
|
||||||
disp = DispenseUE(ue_id=ue_id, etudid=etudid)
|
DispenseUE.query.filter_by(
|
||||||
|
formsemestre_id=formsemestre_id, etudid=etudid, ue_id=ue_id
|
||||||
|
).count()
|
||||||
|
== 0
|
||||||
|
):
|
||||||
|
disp = DispenseUE(
|
||||||
|
formsemestre_id=formsemestre_id, ue_id=ue_id, etudid=etudid
|
||||||
|
)
|
||||||
db.session.add(disp)
|
db.session.add(disp)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
log(f"etud_desinscrit_ue {etud} {ue}")
|
||||||
|
Scolog.logdb(
|
||||||
|
method="etud_desinscrit_ue",
|
||||||
|
etudid=etud.id,
|
||||||
|
msg=f"Désinscription de l'UE {ue.acronyme} de {formsemestre.titre_annee()}",
|
||||||
|
commit=True,
|
||||||
|
)
|
||||||
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre.id)
|
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre.id)
|
||||||
else:
|
else:
|
||||||
sco_moduleimpl_inscriptions.do_etud_desinscrit_ue_classic(
|
sco_moduleimpl_inscriptions.do_etud_desinscrit_ue_classic(
|
||||||
|
@ -10,58 +10,86 @@ import sqlalchemy as sa
|
|||||||
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = 'dbcf2175e87f'
|
revision = "dbcf2175e87f"
|
||||||
down_revision = '5c7b208355df'
|
down_revision = "5c7b208355df"
|
||||||
branch_labels = None
|
branch_labels = None
|
||||||
depends_on = None
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade():
|
def upgrade():
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
op.create_table('justificatifs',
|
op.create_table(
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
"justificatifs",
|
||||||
sa.Column('date_debut', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('date_fin', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
sa.Column(
|
||||||
sa.Column('etudid', sa.Integer(), nullable=False),
|
"date_debut",
|
||||||
sa.Column('etat', sa.Integer(), nullable=False),
|
sa.DateTime(timezone=True),
|
||||||
sa.Column('entry_date', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
server_default=sa.text("now()"),
|
||||||
sa.Column('raison', sa.Text(), nullable=True),
|
nullable=False,
|
||||||
sa.Column('fichier', sa.Text(), nullable=True),
|
),
|
||||||
sa.ForeignKeyConstraint(['etudid'], ['identite.id'], ondelete='CASCADE'),
|
sa.Column(
|
||||||
sa.PrimaryKeyConstraint('id')
|
"date_fin",
|
||||||
|
sa.DateTime(timezone=True),
|
||||||
|
server_default=sa.text("now()"),
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
sa.Column("etudid", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("etat", sa.Integer(), nullable=False),
|
||||||
|
sa.Column(
|
||||||
|
"entry_date",
|
||||||
|
sa.DateTime(timezone=True),
|
||||||
|
server_default=sa.text("now()"),
|
||||||
|
nullable=True,
|
||||||
|
),
|
||||||
|
sa.Column("raison", sa.Text(), nullable=True),
|
||||||
|
sa.Column("fichier", sa.Text(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(["etudid"], ["identite.id"], ondelete="CASCADE"),
|
||||||
|
sa.PrimaryKeyConstraint("id"),
|
||||||
)
|
)
|
||||||
op.create_index(op.f('ix_justificatifs_etudid'), 'justificatifs', ['etudid'], unique=False)
|
op.create_index(
|
||||||
op.create_table('assiduites',
|
op.f("ix_justificatifs_etudid"), "justificatifs", ["etudid"], unique=False
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
)
|
||||||
sa.Column('date_debut', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
op.create_table(
|
||||||
sa.Column('date_fin', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
"assiduites",
|
||||||
sa.Column('moduleimpl_id', sa.Integer(), nullable=True),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('etudid', sa.Integer(), nullable=False),
|
sa.Column(
|
||||||
sa.Column('etat', sa.Integer(), nullable=False),
|
"date_debut",
|
||||||
sa.Column('desc', sa.Text(), nullable=True),
|
sa.DateTime(timezone=True),
|
||||||
sa.Column('entry_date', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
server_default=sa.text("now()"),
|
||||||
sa.ForeignKeyConstraint(['etudid'], ['identite.id'], ondelete='CASCADE'),
|
nullable=False,
|
||||||
sa.ForeignKeyConstraint(['moduleimpl_id'], ['notes_moduleimpl.id'], ondelete='SET NULL'),
|
),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.Column(
|
||||||
|
"date_fin",
|
||||||
|
sa.DateTime(timezone=True),
|
||||||
|
server_default=sa.text("now()"),
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
sa.Column("moduleimpl_id", sa.Integer(), nullable=True),
|
||||||
|
sa.Column("etudid", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("etat", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("desc", sa.Text(), nullable=True),
|
||||||
|
sa.Column(
|
||||||
|
"entry_date",
|
||||||
|
sa.DateTime(timezone=True),
|
||||||
|
server_default=sa.text("now()"),
|
||||||
|
nullable=True,
|
||||||
|
),
|
||||||
|
sa.ForeignKeyConstraint(["etudid"], ["identite.id"], ondelete="CASCADE"),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["moduleimpl_id"], ["notes_moduleimpl.id"], ondelete="SET NULL"
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint("id"),
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
op.f("ix_assiduites_etudid"), "assiduites", ["etudid"], unique=False
|
||||||
)
|
)
|
||||||
op.create_index(op.f('ix_assiduites_etudid'), 'assiduites', ['etudid'], unique=False)
|
|
||||||
op.drop_constraint('dispenseUE_formsemestre_id_ue_id_etudid_key', 'dispenseUE', type_='unique')
|
|
||||||
op.drop_index('ix_dispenseUE_formsemestre_id', table_name='dispenseUE')
|
|
||||||
op.create_unique_constraint(None, 'dispenseUE', ['ue_id', 'etudid'])
|
|
||||||
op.drop_constraint('dispenseUE_formsemestre_id_fkey', 'dispenseUE', type_='foreignkey')
|
|
||||||
op.drop_column('dispenseUE', 'formsemestre_id')
|
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
def downgrade():
|
def downgrade():
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
op.add_column('dispenseUE', sa.Column('formsemestre_id', sa.INTEGER(), autoincrement=False, nullable=True))
|
op.drop_index(op.f("ix_assiduites_etudid"), table_name="assiduites")
|
||||||
op.create_foreign_key('dispenseUE_formsemestre_id_fkey', 'dispenseUE', 'notes_formsemestre', ['formsemestre_id'], ['id'])
|
op.drop_table("assiduites")
|
||||||
op.drop_constraint(None, 'dispenseUE', type_='unique')
|
op.drop_index(op.f("ix_justificatifs_etudid"), table_name="justificatifs")
|
||||||
op.create_index('ix_dispenseUE_formsemestre_id', 'dispenseUE', ['formsemestre_id'], unique=False)
|
op.drop_table("justificatifs")
|
||||||
op.create_unique_constraint('dispenseUE_formsemestre_id_ue_id_etudid_key', 'dispenseUE', ['formsemestre_id', 'ue_id', 'etudid'])
|
|
||||||
op.drop_index(op.f('ix_assiduites_etudid'), table_name='assiduites')
|
|
||||||
op.drop_table('assiduites')
|
|
||||||
op.drop_index(op.f('ix_justificatifs_etudid'), table_name='justificatifs')
|
|
||||||
op.drop_table('justificatifs')
|
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Test de l'api Assiduité
|
Test de l'api justificatif
|
||||||
|
|
||||||
Ecrit par HARTMANN Matthias
|
Ecrit par HARTMANN Matthias
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ def test_route_create(api_headers):
|
|||||||
# -== Unique ==-
|
# -== Unique ==-
|
||||||
|
|
||||||
# Bon fonctionnement
|
# Bon fonctionnement
|
||||||
data = create_data("validé", "01")
|
data = create_data("valide", "01")
|
||||||
|
|
||||||
res = POST_JSON(f"/justificatif/{ETUDID}/create", [data], api_headers)
|
res = POST_JSON(f"/justificatif/{ETUDID}/create", [data], api_headers)
|
||||||
check_fields(res, BATCH_FIELD)
|
check_fields(res, BATCH_FIELD)
|
||||||
@ -129,7 +129,7 @@ def test_route_create(api_headers):
|
|||||||
|
|
||||||
TO_REMOVE.append(res["success"]["0"]["justif_id"])
|
TO_REMOVE.append(res["success"]["0"]["justif_id"])
|
||||||
|
|
||||||
data2 = create_data("modifié", "02", "raison")
|
data2 = create_data("modifie", "02", "raison")
|
||||||
res = POST_JSON(f"/justificatif/{ETUDID}/create", [data2], api_headers)
|
res = POST_JSON(f"/justificatif/{ETUDID}/create", [data2], api_headers)
|
||||||
check_fields(res, BATCH_FIELD)
|
check_fields(res, BATCH_FIELD)
|
||||||
assert len(res["success"]) == 1
|
assert len(res["success"]) == 1
|
||||||
@ -160,7 +160,7 @@ def test_route_create(api_headers):
|
|||||||
|
|
||||||
# Bon Fonctionnement
|
# Bon Fonctionnement
|
||||||
|
|
||||||
etats = ["validé", "modifé", "non validé", "en attente"]
|
etats = ["valide", "modifie", "non_valide", "attente"]
|
||||||
data = [
|
data = [
|
||||||
create_data(etats[d % 4], 10 + d, "raison" if d % 2 else None)
|
create_data(etats[d % 4], 10 + d, "raison" if d % 2 else None)
|
||||||
for d in range(randint(3, 5))
|
for d in range(randint(3, 5))
|
||||||
@ -175,10 +175,10 @@ def test_route_create(api_headers):
|
|||||||
# Mauvais Fonctionnement
|
# Mauvais Fonctionnement
|
||||||
|
|
||||||
data2 = [
|
data2 = [
|
||||||
create_data("modifié", "01"),
|
create_data("modifie", "01"),
|
||||||
create_data(None, "25"),
|
create_data(None, "25"),
|
||||||
create_data("blabla", 26),
|
create_data("blabla", 26),
|
||||||
create_data("validé", 32),
|
create_data("valide", 32),
|
||||||
]
|
]
|
||||||
|
|
||||||
res = POST_JSON(f"/justificatif/{ETUDID}/create", data2, api_headers)
|
res = POST_JSON(f"/justificatif/{ETUDID}/create", data2, api_headers)
|
||||||
@ -201,7 +201,7 @@ def test_route_edit(api_headers):
|
|||||||
|
|
||||||
# Bon fonctionnement
|
# Bon fonctionnement
|
||||||
|
|
||||||
data = {"etat": "modifié", "raison": "test"}
|
data = {"etat": "modifie", "raison": "test"}
|
||||||
res = POST_JSON(f"/justificatif/{TO_REMOVE[0]}/edit", data, api_headers)
|
res = POST_JSON(f"/justificatif/{TO_REMOVE[0]}/edit", data, api_headers)
|
||||||
assert res == {"OK": True}
|
assert res == {"OK": True}
|
||||||
|
|
||||||
@ -209,6 +209,8 @@ def test_route_edit(api_headers):
|
|||||||
res = POST_JSON(f"/justificatif/{TO_REMOVE[1]}/edit", data, api_headers)
|
res = POST_JSON(f"/justificatif/{TO_REMOVE[1]}/edit", data, api_headers)
|
||||||
assert res == {"OK": True}
|
assert res == {"OK": True}
|
||||||
|
|
||||||
|
# TODO: Modification date deb / fin
|
||||||
|
|
||||||
# Mauvais fonctionnement
|
# Mauvais fonctionnement
|
||||||
|
|
||||||
check_failure_post(f"/justificatif/{FAUX}/edit", api_headers, data)
|
check_failure_post(f"/justificatif/{FAUX}/edit", api_headers, data)
|
||||||
@ -288,7 +290,7 @@ def send_file(justif_id: int, filename: str, headers):
|
|||||||
def check_failure_send(
|
def check_failure_send(
|
||||||
justif_id: int,
|
justif_id: int,
|
||||||
headers,
|
headers,
|
||||||
filename: str = "/opt/scodoc/tests/api/test_api_justificatif.txt",
|
filename: str = "tests/api/test_api_justificatif.txt",
|
||||||
err: str = None,
|
err: str = None,
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
@ -305,12 +307,13 @@ def test_import_justificatif(api_headers):
|
|||||||
|
|
||||||
# Bon fonctionnement
|
# Bon fonctionnement
|
||||||
|
|
||||||
filename: str = "/opt/scodoc/tests/api/test_api_justificatif.txt"
|
filename: str = "tests/api/test_api_justificatif.txt"
|
||||||
|
|
||||||
resp: dict = send_file(1, filename, api_headers)
|
resp: dict = send_file(1, filename, api_headers)
|
||||||
assert "response" in resp
|
assert "response" in resp
|
||||||
assert resp["response"] == "imported"
|
assert resp["response"] == "imported"
|
||||||
|
|
||||||
filename: str = "/opt/scodoc/tests/api/test_api_justificatif2.txt"
|
filename: str = "tests/api/test_api_justificatif2.txt"
|
||||||
resp: dict = send_file(1, filename, api_headers)
|
resp: dict = send_file(1, filename, api_headers)
|
||||||
assert "response" in resp
|
assert "response" in resp
|
||||||
assert resp["response"] == "imported"
|
assert resp["response"] == "imported"
|
||||||
@ -339,13 +342,32 @@ def test_list_justificatifs(api_headers):
|
|||||||
check_failure_get(f"/justificatif/list/{FAUX}", api_headers)
|
check_failure_get(f"/justificatif/list/{FAUX}", api_headers)
|
||||||
|
|
||||||
|
|
||||||
|
def post_export(id: int, fname: str, api_headers):
|
||||||
|
url: str = API_URL + f"/justificatif/export/{id}/{fname}"
|
||||||
|
res = requests.post(url, headers=api_headers)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def test_export(api_headers):
|
||||||
|
# Bon fonctionnement
|
||||||
|
|
||||||
|
assert post_export(1, "test_api_justificatif.txt", api_headers).status_code == 200
|
||||||
|
|
||||||
|
# Mauvais fonctionnement
|
||||||
|
assert (
|
||||||
|
post_export(FAUX, "test_api_justificatif.txt", api_headers).status_code == 404
|
||||||
|
)
|
||||||
|
assert post_export(1, "blabla.txt", api_headers).status_code == 404
|
||||||
|
assert post_export(2, "blabla.txt", api_headers).status_code == 404
|
||||||
|
|
||||||
|
|
||||||
def test_remove_justificatif(api_headers):
|
def test_remove_justificatif(api_headers):
|
||||||
|
|
||||||
# Bon fonctionnement
|
# Bon fonctionnement
|
||||||
|
|
||||||
filename: str = "/opt/scodoc/tests/api/test_api_justificatif.txt"
|
filename: str = "tests/api/test_api_justificatif.txt"
|
||||||
send_file(2, filename, api_headers)
|
send_file(2, filename, api_headers)
|
||||||
filename: str = "/opt/scodoc/tests/api/test_api_justificatif2.txt"
|
filename: str = "tests/api/test_api_justificatif2.txt"
|
||||||
send_file(2, filename, api_headers)
|
send_file(2, filename, api_headers)
|
||||||
|
|
||||||
res: dict = POST_JSON("/justificatif/remove/1", {"remove": "all"}, api_headers)
|
res: dict = POST_JSON("/justificatif/remove/1", {"remove": "all"}, api_headers)
|
||||||
@ -372,3 +394,15 @@ def test_remove_justificatif(api_headers):
|
|||||||
|
|
||||||
check_failure_post("/justificatif/remove/2", api_headers, {})
|
check_failure_post("/justificatif/remove/2", api_headers, {})
|
||||||
check_failure_post(f"/justificatif/remove/{FAUX}", api_headers, {"remove": "all"})
|
check_failure_post(f"/justificatif/remove/{FAUX}", api_headers, {"remove": "all"})
|
||||||
|
check_failure_post("/justificatif/remove/1", api_headers, {"remove": "all"})
|
||||||
|
|
||||||
|
|
||||||
|
def test_justified(api_headers):
|
||||||
|
# Bon fonctionnement
|
||||||
|
|
||||||
|
res: list = GET("/justificatif/justified/1", api_headers)
|
||||||
|
assert isinstance(res, list)
|
||||||
|
|
||||||
|
# Mauvais fonctionnement
|
||||||
|
|
||||||
|
check_failure_get(f"/justificatif/justified/{FAUX}", api_headers)
|
||||||
|
@ -14,7 +14,7 @@ from app import db
|
|||||||
|
|
||||||
from app.scodoc import sco_formsemestre
|
from app.scodoc import sco_formsemestre
|
||||||
import app.scodoc.sco_assiduites as scass
|
import app.scodoc.sco_assiduites as scass
|
||||||
from app.models import Assiduite, Identite, FormSemestre, ModuleImpl
|
from app.models import Assiduite, Justificatif, Identite, FormSemestre, ModuleImpl
|
||||||
from app.scodoc.sco_exceptions import ScoValueError
|
from app.scodoc.sco_exceptions import ScoValueError
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
|
|
||||||
@ -115,13 +115,226 @@ def test_general(test_client):
|
|||||||
etud_faux = Identite.query.filter_by(id=etud_faux_dict["id"]).first()
|
etud_faux = Identite.query.filter_by(id=etud_faux_dict["id"]).first()
|
||||||
|
|
||||||
ajouter_assiduites(etuds, moduleimpls, etud_faux)
|
ajouter_assiduites(etuds, moduleimpls, etud_faux)
|
||||||
verifier_comptage_et_filtrage(
|
justificatifs: list[Justificatif] = ajouter_justificatifs(etuds[0])
|
||||||
|
verifier_comptage_et_filtrage_assiduites(
|
||||||
etuds, moduleimpls, (formsemestre_1, formsemestre_2, formsemestre_3)
|
etuds, moduleimpls, (formsemestre_1, formsemestre_2, formsemestre_3)
|
||||||
)
|
)
|
||||||
editer_supprimer_assiduiter(etuds, moduleimpls)
|
verifier_filtrage_justificatifs(etuds[0], justificatifs)
|
||||||
|
|
||||||
|
editer_supprimer_assiduites(etuds, moduleimpls)
|
||||||
|
editer_supprimer_justificatif(etuds[0])
|
||||||
|
|
||||||
|
|
||||||
def editer_supprimer_assiduiter(etuds: list[Identite], moduleimpls: list[int]):
|
def ajouter_justificatifs(etud):
|
||||||
|
|
||||||
|
obj_justificatifs = [
|
||||||
|
{
|
||||||
|
"etat": scu.EtatJustificatif.ATTENTE,
|
||||||
|
"deb": "2022-09-03T08:00+01:00",
|
||||||
|
"fin": "2022-09-03T09:59:59+01:00",
|
||||||
|
"raison": None,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"etat": scu.EtatJustificatif.VALIDE,
|
||||||
|
"deb": "2023-01-03T07:00+01:00",
|
||||||
|
"fin": "2023-01-03T11:00+01:00",
|
||||||
|
"raison": None,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"etat": scu.EtatJustificatif.VALIDE,
|
||||||
|
"deb": "2022-09-03T10:00:00+01:00",
|
||||||
|
"fin": "2022-09-03T12:00+01:00",
|
||||||
|
"raison": None,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"etat": scu.EtatJustificatif.NON_VALIDE,
|
||||||
|
"deb": "2022-09-03T14:00:00+01:00",
|
||||||
|
"fin": "2022-09-03T15:00+01:00",
|
||||||
|
"raison": "Description",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"etat": scu.EtatJustificatif.MODIFIE,
|
||||||
|
"deb": "2023-01-03T11:30+01:00",
|
||||||
|
"fin": "2023-01-03T12:00+01:00",
|
||||||
|
"raison": None,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
justificatifs = [
|
||||||
|
Justificatif.create_justificatif(
|
||||||
|
etud,
|
||||||
|
scu.is_iso_formated(just["deb"], True),
|
||||||
|
scu.is_iso_formated(just["fin"], True),
|
||||||
|
just["etat"],
|
||||||
|
just["raison"],
|
||||||
|
)
|
||||||
|
for just in obj_justificatifs
|
||||||
|
]
|
||||||
|
# Vérification de la création des justificatifs
|
||||||
|
assert [
|
||||||
|
justi for justi in justificatifs if not isinstance(justi, Justificatif)
|
||||||
|
] == [], "La création des justificatifs de base n'est pas OK"
|
||||||
|
|
||||||
|
# Vérification de la gestion des erreurs
|
||||||
|
|
||||||
|
test_assiduite = {
|
||||||
|
"etat": scu.EtatJustificatif.ATTENTE,
|
||||||
|
"deb": "2023-01-03T11:00:01+01:00",
|
||||||
|
"fin": "2023-01-03T12:00+01:00",
|
||||||
|
"raison": "Description",
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
Justificatif.create_justificatif(
|
||||||
|
etud,
|
||||||
|
scu.is_iso_formated(test_assiduite["deb"], True),
|
||||||
|
scu.is_iso_formated(test_assiduite["fin"], True),
|
||||||
|
test_assiduite["etat"],
|
||||||
|
test_assiduite["raison"],
|
||||||
|
)
|
||||||
|
except ScoValueError as excp:
|
||||||
|
assert (
|
||||||
|
excp.args[0]
|
||||||
|
== "Duplication des justificatifs (la période rentrée rentre en conflit avec un justificatif enregistré)"
|
||||||
|
)
|
||||||
|
return justificatifs
|
||||||
|
|
||||||
|
|
||||||
|
def verifier_filtrage_justificatifs(etud: Identite, justificatifs: list[Justificatif]):
|
||||||
|
"""
|
||||||
|
- vérifier le filtrage des justificatifs (etat, debut, fin)
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Vérification du filtrage classique
|
||||||
|
|
||||||
|
# Etat
|
||||||
|
assert (
|
||||||
|
scass.filter_justificatifs_by_etat(etud.justificatifs, "valide").count() == 2
|
||||||
|
), "Filtrage de l'état 'valide' mauvais"
|
||||||
|
assert (
|
||||||
|
scass.filter_justificatifs_by_etat(etud.justificatifs, "attente").count() == 1
|
||||||
|
), f"Filtrage de l'état 'attente' mauvais"
|
||||||
|
assert (
|
||||||
|
scass.filter_justificatifs_by_etat(etud.justificatifs, "modifie").count() == 1
|
||||||
|
), "Filtrage de l'état 'modifie' mauvais"
|
||||||
|
assert (
|
||||||
|
scass.filter_justificatifs_by_etat(etud.justificatifs, "non_valide").count()
|
||||||
|
== 1
|
||||||
|
), "Filtrage de l'état 'non_valide' mauvais"
|
||||||
|
assert (
|
||||||
|
scass.filter_justificatifs_by_etat(etud.justificatifs, "valide,modifie").count()
|
||||||
|
== 3
|
||||||
|
), "Filtrage de l'état 'valide,modifie' mauvais"
|
||||||
|
assert (
|
||||||
|
scass.filter_justificatifs_by_etat(
|
||||||
|
etud.justificatifs, "valide,modifie,attente"
|
||||||
|
).count()
|
||||||
|
== 4
|
||||||
|
), "Filtrage de l'état 'valide,modifie,attente' mauvais"
|
||||||
|
assert (
|
||||||
|
scass.filter_justificatifs_by_etat(
|
||||||
|
etud.justificatifs, "valide,modifie,attente,non_valide"
|
||||||
|
).count()
|
||||||
|
== 5
|
||||||
|
), "Filtrage de l'état 'valide,modifie,attente,_non_valide' mauvais"
|
||||||
|
|
||||||
|
assert (
|
||||||
|
scass.filter_justificatifs_by_etat(etud.justificatifs, "autre").count() == 0
|
||||||
|
), "Filtrage de l'état 'autre' mauvais"
|
||||||
|
|
||||||
|
# Date début
|
||||||
|
date = scu.localize_datetime("2022-09-01T10:00+01:00")
|
||||||
|
assert (
|
||||||
|
scass.filter_justificatifs_by_date(etud.justificatifs, date, sup=True).count()
|
||||||
|
== 5
|
||||||
|
), "Filtrage 'Date début' mauvais 1"
|
||||||
|
date = scu.localize_datetime("2022-09-03T08:00:00+01:00")
|
||||||
|
assert (
|
||||||
|
scass.filter_justificatifs_by_date(etud.justificatifs, date, sup=True).count()
|
||||||
|
== 5
|
||||||
|
), "Filtrage 'Date début' mauvais 2"
|
||||||
|
date = scu.localize_datetime("2022-09-03T09:00:00+01:00")
|
||||||
|
assert (
|
||||||
|
scass.filter_justificatifs_by_date(etud.justificatifs, date, sup=True).count()
|
||||||
|
== 4
|
||||||
|
), "Filtrage 'Date début' mauvais 3"
|
||||||
|
date = scu.localize_datetime("2022-09-03T09:00:02+01:00")
|
||||||
|
assert (
|
||||||
|
scass.filter_justificatifs_by_date(etud.justificatifs, date, sup=True).count()
|
||||||
|
== 4
|
||||||
|
), "Filtrage 'Date début' mauvais 4"
|
||||||
|
|
||||||
|
# Date fin
|
||||||
|
date = scu.localize_datetime("2022-09-01T10:00+01:00")
|
||||||
|
assert (
|
||||||
|
scass.filter_justificatifs_by_date(etud.justificatifs, date, sup=False).count()
|
||||||
|
== 0
|
||||||
|
), "Filtrage 'Date fin' mauvais 1"
|
||||||
|
date = scu.localize_datetime("2022-09-03T10:00:00+01:00")
|
||||||
|
assert (
|
||||||
|
scass.filter_justificatifs_by_date(etud.justificatifs, date, sup=False).count()
|
||||||
|
== 1
|
||||||
|
), "Filtrage 'Date fin' mauvais 2"
|
||||||
|
date = scu.localize_datetime("2022-09-03T10:00:01+01:00")
|
||||||
|
assert (
|
||||||
|
scass.filter_justificatifs_by_date(etud.justificatifs, date, sup=False).count()
|
||||||
|
== 1
|
||||||
|
), "Filtrage 'Date fin' mauvais 3"
|
||||||
|
date = scu.localize_datetime("2023-01-04T13:00:01+01:00")
|
||||||
|
assert (
|
||||||
|
scass.filter_justificatifs_by_date(etud.justificatifs, date, sup=False).count()
|
||||||
|
== 5
|
||||||
|
), "Filtrage 'Date fin' mauvais 4"
|
||||||
|
date = scu.localize_datetime("2023-01-03T11:00:01+01:00")
|
||||||
|
assert (
|
||||||
|
scass.filter_justificatifs_by_date(etud.justificatifs, date, sup=False).count()
|
||||||
|
== 4
|
||||||
|
), "Filtrage 'Date fin' mauvais 5"
|
||||||
|
|
||||||
|
# Justifications des assiduites
|
||||||
|
|
||||||
|
assert len(scass.justifies(justificatifs[2])) == 1, "Justifications mauvais"
|
||||||
|
assert len(scass.justifies(justificatifs[0])) == 0, f"Justifications mauvais"
|
||||||
|
|
||||||
|
|
||||||
|
def editer_supprimer_justificatif(etud: Identite):
|
||||||
|
"""
|
||||||
|
Troisième Partie:
|
||||||
|
- Vérification de l'édition des justificatifs
|
||||||
|
- Vérification de la suppression des justificatifs
|
||||||
|
"""
|
||||||
|
|
||||||
|
justi: Justificatif = etud.justificatifs.first()
|
||||||
|
|
||||||
|
# Modification de l'état
|
||||||
|
justi.etat = scu.EtatJustificatif.MODIFIE
|
||||||
|
db.session.add(justi)
|
||||||
|
# Modification du moduleimpl
|
||||||
|
justi.date_debut = scu.localize_datetime("2023-02-03T11:00:01+01:00")
|
||||||
|
justi.fin = scu.localize_datetime("2023-02-03T12:00:01+01:00")
|
||||||
|
db.session.add(justi)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# Vérification du changement
|
||||||
|
assert (
|
||||||
|
scass.filter_justificatifs_by_etat(etud.justificatifs, "modifie").count() == 2
|
||||||
|
), "Edition de justificatif mauvais"
|
||||||
|
assert (
|
||||||
|
scass.filter_justificatifs_by_date(
|
||||||
|
etud.justificatifs, scu.localize_datetime("2023-02-03T11:00:00+01:00")
|
||||||
|
).count()
|
||||||
|
== 1
|
||||||
|
), "Edition de justificatif mauvais"
|
||||||
|
|
||||||
|
# Supression d'une assiduité
|
||||||
|
|
||||||
|
db.session.delete(justi)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
assert etud.justificatifs.count() == 4, "Supression de justificatif mauvais"
|
||||||
|
|
||||||
|
|
||||||
|
def editer_supprimer_assiduites(etuds: list[Identite], moduleimpls: list[int]):
|
||||||
"""
|
"""
|
||||||
Troisième Partie:
|
Troisième Partie:
|
||||||
- Vérification de l'édition des assiduitées
|
- Vérification de l'édition des assiduitées
|
||||||
@ -217,8 +430,8 @@ def ajouter_assiduites(
|
|||||||
assiduites = [
|
assiduites = [
|
||||||
Assiduite.create_assiduite(
|
Assiduite.create_assiduite(
|
||||||
etud,
|
etud,
|
||||||
ass["deb"],
|
scu.is_iso_formated(ass["deb"], True),
|
||||||
ass["fin"],
|
scu.is_iso_formated(ass["fin"], True),
|
||||||
ass["etat"],
|
ass["etat"],
|
||||||
ass["moduleimpl"],
|
ass["moduleimpl"],
|
||||||
ass["desc"],
|
ass["desc"],
|
||||||
@ -244,8 +457,8 @@ def ajouter_assiduites(
|
|||||||
try:
|
try:
|
||||||
Assiduite.create_assiduite(
|
Assiduite.create_assiduite(
|
||||||
etuds[0],
|
etuds[0],
|
||||||
test_assiduite["deb"],
|
scu.is_iso_formated(test_assiduite["deb"], True),
|
||||||
test_assiduite["fin"],
|
scu.is_iso_formated(test_assiduite["fin"], True),
|
||||||
test_assiduite["etat"],
|
test_assiduite["etat"],
|
||||||
test_assiduite["moduleimpl"],
|
test_assiduite["moduleimpl"],
|
||||||
test_assiduite["desc"],
|
test_assiduite["desc"],
|
||||||
@ -258,8 +471,8 @@ def ajouter_assiduites(
|
|||||||
try:
|
try:
|
||||||
Assiduite.create_assiduite(
|
Assiduite.create_assiduite(
|
||||||
etud_faux,
|
etud_faux,
|
||||||
test_assiduite["deb"],
|
scu.is_iso_formated(test_assiduite["deb"], True),
|
||||||
test_assiduite["fin"],
|
scu.is_iso_formated(test_assiduite["fin"], True),
|
||||||
test_assiduite["etat"],
|
test_assiduite["etat"],
|
||||||
test_assiduite["moduleimpl"],
|
test_assiduite["moduleimpl"],
|
||||||
test_assiduite["desc"],
|
test_assiduite["desc"],
|
||||||
@ -268,7 +481,7 @@ def ajouter_assiduites(
|
|||||||
assert excp.args[0] == "L'étudiant n'est pas inscrit au moduleimpl"
|
assert excp.args[0] == "L'étudiant n'est pas inscrit au moduleimpl"
|
||||||
|
|
||||||
|
|
||||||
def verifier_comptage_et_filtrage(
|
def verifier_comptage_et_filtrage_assiduites(
|
||||||
etuds: list[Identite], moduleimpls: list[int], formsemestres: tuple[int]
|
etuds: list[Identite], moduleimpls: list[int], formsemestres: tuple[int]
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
|
@ -123,7 +123,13 @@ def test_ue_moy(test_client):
|
|||||||
modimpl.module.ue.type != UE_SPORT for modimpl in formsemestre.modimpls_sorted
|
modimpl.module.ue.type != UE_SPORT for modimpl in formsemestre.modimpls_sorted
|
||||||
]
|
]
|
||||||
etud_moy_ue = moy_ue.compute_ue_moys_apc(
|
etud_moy_ue = moy_ue.compute_ue_moys_apc(
|
||||||
sem_cube, etuds, modimpls, modimpl_inscr_df, modimpl_coefs_df, modimpl_mask
|
sem_cube,
|
||||||
|
etuds,
|
||||||
|
modimpls,
|
||||||
|
modimpl_inscr_df,
|
||||||
|
modimpl_coefs_df,
|
||||||
|
modimpl_mask,
|
||||||
|
set(),
|
||||||
)
|
)
|
||||||
assert etud_moy_ue[ue1.id][etudid] == n1
|
assert etud_moy_ue[ue1.id][etudid] == n1
|
||||||
assert etud_moy_ue[ue2.id][etudid] == n1
|
assert etud_moy_ue[ue2.id][etudid] == n1
|
||||||
|
Loading…
Reference in New Issue
Block a user