Update opolka/ScoDoc from ScoDoc/ScoDoc #2

Merged
opolka merged 1272 commits from ScoDoc/ScoDoc:master into master 2024-05-27 09:11:04 +02:00
6 changed files with 44 additions and 47 deletions
Showing only changes of commit 22cfdc59f1 - Show all commits

View File

@ -14,6 +14,7 @@ from flask_login import current_user, login_required
from app import db, log from app import db, log
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.scodoc import sco_preferences
from app.api import api_bp as bp from app.api import api_bp as bp
from app.api import api_web_bp, get_model_api_object, tools from app.api import api_web_bp, get_model_api_object, tools
from app.decorators import permission_required, scodoc from app.decorators import permission_required, scodoc
@ -25,6 +26,7 @@ from app.models import (
Scolog, Scolog,
Justificatif, Justificatif,
) )
from flask_sqlalchemy.query import Query
from app.models.assiduites import get_assiduites_justif from app.models.assiduites import get_assiduites_justif
from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
@ -256,7 +258,7 @@ def assiduites(etudid: int = None, nip=None, ine=None, with_query: bool = False)
404, 404,
message="étudiant inconnu", message="étudiant inconnu",
) )
assiduites_query = etud.assiduites assiduites_query: Query = etud.assiduites
if with_query: if with_query:
assiduites_query = _filter_manager(request, assiduites_query) assiduites_query = _filter_manager(request, assiduites_query)
@ -961,7 +963,7 @@ def _count_manager(requested) -> tuple[str, dict]:
return (metric, filtered) return (metric, filtered)
def _filter_manager(requested, assiduites_query: Assiduite): def _filter_manager(requested, assiduites_query: Query) -> Query:
""" """
Retourne les assiduites entrées filtrées en fonction de la request Retourne les assiduites entrées filtrées en fonction de la request
""" """
@ -979,7 +981,7 @@ def _filter_manager(requested, assiduites_query: Assiduite):
fin = scu.is_iso_formated(fin, True) fin = scu.is_iso_formated(fin, True)
if (deb, fin) != (None, None): if (deb, fin) != (None, None):
assiduites_query: Assiduite = scass.filter_by_date( assiduites_query: Query = scass.filter_by_date(
assiduites_query, Assiduite, deb, fin assiduites_query, Assiduite, deb, fin
) )
@ -1017,11 +1019,11 @@ def _filter_manager(requested, assiduites_query: Assiduite):
falses: tuple[str] = ("f", "faux", "false") falses: tuple[str] = ("f", "faux", "false")
if est_just.lower() in trues: if est_just.lower() in trues:
assiduites_query: Assiduite = scass.filter_assiduites_by_est_just( assiduites_query: Query = scass.filter_assiduites_by_est_just(
assiduites_query, True assiduites_query, True
) )
elif est_just.lower() in falses: elif est_just.lower() in falses:
assiduites_query: Assiduite = scass.filter_assiduites_by_est_just( assiduites_query: Query = scass.filter_assiduites_by_est_just(
assiduites_query, False assiduites_query, False
) )
@ -1029,7 +1031,7 @@ def _filter_manager(requested, assiduites_query: Assiduite):
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:
assiduites_query: Assiduite = scass.filter_by_user_id(assiduites_query, user_id) assiduites_query: Query = scass.filter_by_user_id(assiduites_query, user_id)
return assiduites_query return assiduites_query

View File

@ -26,6 +26,7 @@ from app.scodoc.sco_archives_justificatifs import JustificatifArchiver
from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
from app.scodoc.sco_utils import json_error from app.scodoc.sco_utils import json_error
from flask_sqlalchemy.query import Query
# Partie Modèle # Partie Modèle
@ -261,7 +262,7 @@ def _create_singular(
# TOUT EST OK # TOUT EST OK
try: try:
nouv_justificatif: Justificatif = Justificatif.create_justificatif( nouv_justificatif: Query = Justificatif.create_justificatif(
date_debut=deb, date_debut=deb,
date_fin=fin, date_fin=fin,
etat=etat, etat=etat,
@ -307,7 +308,7 @@ def justif_edit(justif_id: int):
"date_fin"?: str "date_fin"?: str
} }
""" """
justificatif_unique: Justificatif = Justificatif.query.filter_by( justificatif_unique: Query = Justificatif.query.filter_by(
id=justif_id id=justif_id
).first_or_404() ).first_or_404()
@ -426,9 +427,7 @@ def justif_delete():
def _delete_singular(justif_id: int, database): def _delete_singular(justif_id: int, database):
justificatif_unique: Justificatif = Justificatif.query.filter_by( justificatif_unique: Query = Justificatif.query.filter_by(id=justif_id).first()
id=justif_id
).first()
if justificatif_unique is None: if justificatif_unique is None:
return (404, "Justificatif non existant") return (404, "Justificatif non existant")
@ -470,7 +469,7 @@ def justif_import(justif_id: int = None):
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")
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)
@ -509,11 +508,11 @@ 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
""" """
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: Justificaitf = query.first_or_404()
archive_name: str = justificatif_unique.fichier archive_name: str = justificatif_unique.fichier
if archive_name is None: if archive_name is None:
@ -551,7 +550,7 @@ def justif_remove(justif_id: int = None):
data: dict = request.get_json(force=True) data: dict = request.get_json(force=True)
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)
@ -604,7 +603,7 @@ def justif_list(justif_id: int = None):
Liste les fichiers du justificatif Liste les fichiers du justificatif
""" """
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)
@ -642,7 +641,7 @@ def justif_justifies(justif_id: int = None):
Liste assiduite_id justifiées par le justificatif Liste assiduite_id justifiées par le justificatif
""" """
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)
@ -676,13 +675,13 @@ def _filter_manager(requested, justificatifs_query):
fin = scu.is_iso_formated(fin, True) fin = scu.is_iso_formated(fin, True)
if (deb, fin) != (None, None): if (deb, fin) != (None, None):
justificatifs_query: Justificatif = scass.filter_by_date( justificatifs_query: Query = scass.filter_by_date(
justificatifs_query, Justificatif, deb, fin justificatifs_query, Justificatif, deb, fin
) )
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: Justificatif = scass.filter_by_user_id( justificatifs_query: Query = scass.filter_by_user_id(
justificatifs_query, user_id justificatifs_query, user_id
) )

View File

@ -14,6 +14,8 @@ from app.scodoc.sco_utils import (
localize_datetime, localize_datetime,
) )
from flask_sqlalchemy.query import Query
class Assiduite(db.Model): class Assiduite(db.Model):
""" """
@ -124,7 +126,7 @@ class Assiduite(db.Model):
) -> object or int: ) -> object or int:
"""Créer une nouvelle assiduité pour l'étudiant""" """Créer une nouvelle assiduité pour l'étudiant"""
# Vérification de non duplication des périodes # Vérification de non duplication des périodes
assiduites: list[Assiduite] = etud.assiduites assiduites: Query = etud.assiduites
if is_period_conflicting(date_debut, date_fin, assiduites, Assiduite): if is_period_conflicting(date_debut, date_fin, assiduites, Assiduite):
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)"
@ -307,7 +309,7 @@ class Justificatif(db.Model):
def is_period_conflicting( def is_period_conflicting(
date_debut: datetime, date_debut: datetime,
date_fin: datetime, date_fin: datetime,
collection: list[Assiduite or Justificatif], collection: Query,
collection_cls: Assiduite or Justificatif, collection_cls: Assiduite or Justificatif,
) -> bool: ) -> bool:
""" """

View File

@ -13,6 +13,7 @@ from app.scodoc import sco_formsemestre_inscriptions
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
from app.scodoc import sco_cache from app.scodoc import sco_cache
from app.scodoc import sco_etud from app.scodoc import sco_etud
from flask_sqlalchemy.query import Query
class CountCalculator: class CountCalculator:
@ -167,7 +168,7 @@ class CountCalculator:
self.hours += delta.total_seconds() / 3600 self.hours += delta.total_seconds() / 3600
def to_dict(self) -> dict[str, object]: def to_dict(self) -> dict[str, int or float]:
"""Retourne les métriques sous la forme d'un dictionnaire""" """Retourne les métriques sous la forme d'un dictionnaire"""
return { return {
"compte": self.count, "compte": self.count,
@ -178,8 +179,8 @@ class CountCalculator:
def get_assiduites_stats( def get_assiduites_stats(
assiduites: Assiduite, metric: str = "all", filtered: dict[str, object] = None assiduites: Query, metric: str = "all", filtered: dict[str, object] = None
) -> Assiduite: ) -> dict[str, int or float]:
"""Compte les assiduités en fonction des filtres""" """Compte les assiduités en fonction des filtres"""
if filtered is not None: if filtered is not None:
@ -218,7 +219,7 @@ def get_assiduites_stats(
return output if output else count return output if output else count
def filter_assiduites_by_etat(assiduites: Assiduite, etat: str) -> Assiduite: def filter_assiduites_by_etat(assiduites: Assiduite, etat: str) -> Query:
""" """
Filtrage d'une collection d'assiduites en fonction de leur état Filtrage d'une collection d'assiduites en fonction de leur état
""" """
@ -227,9 +228,7 @@ def filter_assiduites_by_etat(assiduites: Assiduite, etat: str) -> Assiduite:
return assiduites.filter(Assiduite.etat.in_(etats)) return assiduites.filter(Assiduite.etat.in_(etats))
def filter_assiduites_by_est_just( def filter_assiduites_by_est_just(assiduites: Assiduite, est_just: bool) -> Query:
assiduites: Assiduite, est_just: bool
) -> Justificatif:
""" """
Filtrage d'une collection d'assiduites en fonction de s'ils sont justifiés Filtrage d'une collection d'assiduites en fonction de s'ils sont justifiés
""" """
@ -239,7 +238,7 @@ def filter_assiduites_by_est_just(
def filter_by_user_id( def filter_by_user_id(
collection: Assiduite or Justificatif, collection: Assiduite or Justificatif,
user_id: int, user_id: int,
) -> Justificatif: ) -> Query:
""" """
Filtrage d'une collection en fonction de l'user_id Filtrage d'une collection en fonction de l'user_id
""" """
@ -252,7 +251,7 @@ def filter_by_date(
date_deb: datetime = None, date_deb: datetime = None,
date_fin: datetime = None, date_fin: datetime = None,
strict: bool = False, strict: bool = False,
): ) -> Query:
""" """
Filtrage d'une collection d'assiduites en fonction d'une date Filtrage d'une collection d'assiduites en fonction d'une date
""" """
@ -272,9 +271,7 @@ def filter_by_date(
) )
def filter_justificatifs_by_etat( def filter_justificatifs_by_etat(justificatifs: Justificatif, etat: str) -> Query:
justificatifs: Justificatif, etat: str
) -> Justificatif:
""" """
Filtrage d'une collection de justificatifs en fonction de leur état Filtrage d'une collection de justificatifs en fonction de leur état
""" """
@ -283,9 +280,7 @@ def filter_justificatifs_by_etat(
return justificatifs.filter(Justificatif.etat.in_(etats)) return justificatifs.filter(Justificatif.etat.in_(etats))
def filter_by_module_impl( def filter_by_module_impl(assiduites: Assiduite, module_impl_id: int or None) -> Query:
assiduites: Assiduite, module_impl_id: int or None
) -> Assiduite:
""" """
Filtrage d'une collection d'assiduites en fonction de l'ID du module_impl Filtrage d'une collection d'assiduites en fonction de l'ID du module_impl
""" """
@ -296,7 +291,7 @@ def filter_by_formsemestre(
collection_query: Assiduite or Justificatif, collection_query: Assiduite or Justificatif,
collection_class: Assiduite or Justificatif, collection_class: Assiduite or Justificatif,
formsemestre: FormSemestre, formsemestre: FormSemestre,
): ) -> Query:
""" """
Filtrage d'une collection en fonction d'un formsemestre Filtrage d'une collection en fonction d'un formsemestre
""" """
@ -323,7 +318,7 @@ def filter_by_formsemestre(
return collection_result.filter(collection_class.date_fin <= form_date_fin) return collection_result.filter(collection_class.date_fin <= form_date_fin)
def justifies(justi: Justificatif, obj: bool = False) -> list[int]: def justifies(justi: Justificatif, obj: bool = False) -> list[int] or Query:
""" """
Retourne la liste des assiduite_id qui sont justifié par la justification Retourne la liste des assiduite_id qui sont justifié par la justification
Une assiduité est justifiée si elle est COMPLETEMENT ou PARTIELLEMENT comprise dans la plage du justificatif Une assiduité est justifiée si elle est COMPLETEMENT ou PARTIELLEMENT comprise dans la plage du justificatif
@ -347,7 +342,7 @@ def justifies(justi: Justificatif, obj: bool = False) -> list[int]:
def get_all_justified( def get_all_justified(
etudid: int, date_deb: datetime = None, date_fin: datetime = None etudid: int, date_deb: datetime = None, date_fin: datetime = None
) -> list[Assiduite]: ) -> Query:
"""Retourne toutes les assiduités justifiées sur une période""" """Retourne toutes les assiduités justifiées sur une période"""
if date_deb is None: if date_deb is None:
@ -432,7 +427,7 @@ def invalidate_assiduites_count(etudid, sem):
"""Invalidate (clear) cached counts""" """Invalidate (clear) cached counts"""
date_debut = sem["date_debut_iso"] date_debut = sem["date_debut_iso"]
date_fin = sem["date_fin_iso"] date_fin = sem["date_fin_iso"]
for met in ["demi", "journee", "compte", "heure"]: for met in sco_preferences.ASSIDUITES_METRIC_LABEL.values():
key = str(etudid) + "_" + date_debut + "_" + date_fin + f"{met}_assiduites" key = str(etudid) + "_" + date_debut + "_" + date_fin + f"{met}_assiduites"
sco_cache.AbsSemEtudCache.delete(key) sco_cache.AbsSemEtudCache.delete(key)

View File

@ -200,7 +200,7 @@ _INSTALLED_FONTS = ", ".join(sco_pdf.get_available_font_names())
ASSIDUITES_METRIC_LABEL = { ASSIDUITES_METRIC_LABEL = {
# l'ordre est important, c'est celui-du menu. Le defaut en 1er donc. # l'ordre est important, c'est celui-du menu. Le defaut en 1er donc.
"1/2 J.": "demi-journée", "1/2 J.": "demi",
"J.": "journee", "J.": "journee",
"H.": "heure", "H.": "heure",
} }
@ -636,6 +636,7 @@ class BasePreferences(object):
"type": "float", "type": "float",
"category": "assi", "category": "assi",
"only_global": True, "only_global": True,
"explanation": "Durée d'un créneau en heure. Utilisé dans les pages de saisie",
}, },
), ),
( (
@ -665,7 +666,7 @@ class BasePreferences(object):
{ {
"initvalue": "1/2 J.", "initvalue": "1/2 J.",
"input_type": "menu", "input_type": "menu",
"labels": list(ASSIDUITES_METRIC_LABEL.values()), "labels": list(ASSIDUITES_METRIC_LABEL.keys()),
"allowed_values": list(ASSIDUITES_METRIC_LABEL.keys()), "allowed_values": list(ASSIDUITES_METRIC_LABEL.keys()),
"title": "Métrique de l'assiduité", "title": "Métrique de l'assiduité",
"explanation": "Unité utilisée dans la fiche étudiante, les bilans et les calculs", "explanation": "Unité utilisée dans la fiche étudiante, les bilans et les calculs",

View File

@ -327,11 +327,9 @@ def bilan_etud():
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"
assi_metric = { assi_metric = sco_preferences.ASSIDUITES_METRIC_LABEL.get(
"H.": "heure", sco_preferences.get_preference("assi_metrique", dept_id=g.scodoc_dept_id)
"J.": "journee", )
"1/2 J.": "demi",
}.get(sco_preferences.get_preference("assi_metrique", dept_id=g.scodoc_dept_id))
return HTMLBuilder( return HTMLBuilder(
header, header,