forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -119,15 +119,15 @@ def check_ics_regexp(form, field):
|
||||
class ConfigAssiduitesForm(FlaskForm):
|
||||
"Formulaire paramétrage Module Assiduité"
|
||||
|
||||
morning_time = TimeField(
|
||||
assi_morning_time = TimeField(
|
||||
"Début de la journée"
|
||||
) # TODO utiliser TextField + timepicker voir AjoutAssiOrJustForm
|
||||
lunch_time = TimeField(
|
||||
assi_lunch_time = TimeField(
|
||||
"Heure de midi (date pivot entre matin et après-midi)"
|
||||
) # TODO
|
||||
afternoon_time = TimeField("Fin de la journée") # TODO
|
||||
assi_afternoon_time = TimeField("Fin de la journée") # TODO
|
||||
|
||||
tick_time = DecimalField(
|
||||
assi_tick_time = DecimalField(
|
||||
"Granularité de la timeline (temps en minutes)",
|
||||
places=0,
|
||||
validators=[check_tick_time],
|
||||
|
@ -25,6 +25,7 @@ from app.scodoc.sco_utils import (
|
||||
EtatJustificatif,
|
||||
localize_datetime,
|
||||
is_assiduites_module_forced,
|
||||
NonWorkDays,
|
||||
)
|
||||
|
||||
|
||||
@ -154,6 +155,33 @@ class Assiduite(ScoDocModel):
|
||||
)
|
||||
if date_fin.tzinfo is None:
|
||||
log(f"Warning: create_assiduite: date_fin without timezone ({date_fin})")
|
||||
|
||||
# Vérification jours non travaillés
|
||||
# -> vérifie si la date de début ou la date de fin est sur un jour non travaillé
|
||||
# On récupère les formsemestres des dates de début et de fin
|
||||
formsemetre_date_debut: FormSemestre = get_formsemestre_from_data(
|
||||
{
|
||||
"etudid": etud.id,
|
||||
"date_debut": date_debut,
|
||||
"date_fin": date_debut,
|
||||
}
|
||||
)
|
||||
formsemetre_date_fin: FormSemestre = get_formsemestre_from_data(
|
||||
{
|
||||
"etudid": etud.id,
|
||||
"date_debut": date_fin,
|
||||
"date_fin": date_fin,
|
||||
}
|
||||
)
|
||||
if date_debut.weekday() in NonWorkDays.get_all_non_work_days(
|
||||
formsemestre_id=formsemetre_date_debut
|
||||
):
|
||||
raise ScoValueError("La date de début n'est pas un jour travaillé")
|
||||
if date_fin.weekday() in NonWorkDays.get_all_non_work_days(
|
||||
formsemestre_id=formsemetre_date_fin
|
||||
):
|
||||
raise ScoValueError("La date de fin n'est pas un jour travaillé")
|
||||
|
||||
# Vérification de non duplication des périodes
|
||||
assiduites: Query = etud.assiduites
|
||||
if is_period_conflicting(date_debut, date_fin, assiduites, Assiduite):
|
||||
|
@ -15,59 +15,222 @@ from app.scodoc import sco_formsemestre_inscriptions
|
||||
from app.scodoc import sco_preferences
|
||||
from app.scodoc import sco_cache
|
||||
from app.scodoc import sco_etud
|
||||
from app.models import ScoDocSiteConfig
|
||||
from flask import g
|
||||
|
||||
|
||||
class CountCalculator:
|
||||
"""Classe qui gére le comptage des assiduités"""
|
||||
"""
|
||||
La classe CountCalculator est conçue pour gérer le comptage des assiduités,
|
||||
en calculant le nombre total de jours complets,
|
||||
de demi-journées, et d'heures passées sur une période donnée.
|
||||
Elle prend en compte les jours non travaillés,
|
||||
les horaires de travail standard et les assiduités s'étendant sur plusieurs jours.
|
||||
|
||||
# TODO documenter
|
||||
Utilisation :
|
||||
------------
|
||||
1. Initialisation : La classe peut être initialisée avec des horaires personnalisés
|
||||
pour le matin, le midi et le soir, ainsi qu'une durée de pause déjeuner.
|
||||
Si non spécifiés, les valeurs par défaut seront chargées depuis la configuration `ScoDocSiteConfig`.
|
||||
Exemple d'initialisation :
|
||||
calculator = CountCalculator(morning="08:00", noon="13:00", evening="18:00", nb_heures_par_jour=8)
|
||||
|
||||
2. Ajout d'assiduités :
|
||||
Exemple d'ajout d'assiduité :
|
||||
- calculator.compute_assiduites(etudiant.assiduites)
|
||||
- calculator.compute_assiduites([<Assiduite>, <Assiduite>, <Assiduite>, <Assiduite>])
|
||||
|
||||
3. Accès aux métriques : Après l'ajout des assiduités, on peut accéder aux métriques telles que :
|
||||
le nombre total de jours, de demi-journées et d'heures calculées.
|
||||
Exemple d'accès aux métriques :
|
||||
metrics = calculator.to_dict()
|
||||
|
||||
4.Réinitialisation du comptage: Si besoin on peut réinitialisé le compteur sans perdre la configuration
|
||||
(horaires personnalisés)
|
||||
Exemple de réinitialisation :
|
||||
calculator.reset()
|
||||
|
||||
Méthodes Principales :
|
||||
---------------------
|
||||
- reset() : Réinitialise les compteurs de la classe.
|
||||
- add_half_day(day: date, is_morning: bool) : Ajoute une demi-journée au comptage.
|
||||
- add_day(day: date) : Ajoute un jour complet au comptage.
|
||||
- compute_long_assiduite(assi: Assiduite) : Traite les assiduités s'étendant sur plus d'un jour.
|
||||
- compute_assiduites(assiduites: Query | list) : Calcule les métriques pour une collection d'assiduités.
|
||||
- to_dict() : Retourne les métriques sous forme de dictionnaire.
|
||||
|
||||
Notes :
|
||||
------
|
||||
|
||||
Détails des calculs des heures:
|
||||
Pour les assiduités courtes (<= 1 jour):
|
||||
heures = assi.deb - assi.fin
|
||||
Pour les assiduités longues (> 1 jour):
|
||||
heures =
|
||||
heures(assi.deb => fin_journee)
|
||||
nb_heure_par_jour * (nb_jours-2) +
|
||||
heures(assi.fin => fin_journee)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
morning: time = time(8, 0), # TODO utiliser ScoDocSiteConfig
|
||||
noon: time = time(12, 0),
|
||||
after_noon: time = time(14, 00),
|
||||
evening: time = time(18, 0),
|
||||
skip_saturday: bool = True, # TODO préférence workdays
|
||||
morning: str = None,
|
||||
noon: str = None,
|
||||
evening: str = None,
|
||||
nb_heures_par_jour: int = None,
|
||||
) -> None:
|
||||
self.morning: time = morning
|
||||
self.noon: time = noon
|
||||
self.after_noon: time = after_noon
|
||||
self.evening: time = evening
|
||||
self.skip_saturday: bool = skip_saturday
|
||||
# Transformation d'une heure "HH:MM" en time(h,m)
|
||||
STR_TIME = lambda x: time(*list(map(int, x.split(":"))))
|
||||
|
||||
delta_total: timedelta = datetime.combine(date.min, evening) - datetime.combine(
|
||||
date.min, morning
|
||||
self.morning: time = STR_TIME(
|
||||
morning if morning else ScoDocSiteConfig.get("assi_morning_time", "08:00")
|
||||
)
|
||||
# Date pivot pour déterminer les demi-journées
|
||||
self.noon: time = STR_TIME(
|
||||
noon if noon else ScoDocSiteConfig.get("assi_lunch_time", "13:00")
|
||||
)
|
||||
self.evening: time = STR_TIME(
|
||||
evening if evening else ScoDocSiteConfig.get("assi_afternoon_time", "18:00")
|
||||
)
|
||||
delta_lunch: timedelta = datetime.combine(
|
||||
date.min, after_noon
|
||||
) - datetime.combine(date.min, noon)
|
||||
|
||||
self.hour_per_day: float = (delta_total - delta_lunch).total_seconds() / 3600
|
||||
self.non_work_days: list[
|
||||
scu.NonWorkDays
|
||||
] = scu.NonWorkDays.get_all_non_work_days(dept_id=g.scodoc_dept_id)
|
||||
|
||||
self.days: list[date] = []
|
||||
self.half_days: list[tuple[date, bool]] = [] # tuple -> (date, morning:bool)
|
||||
self.hours: float = 0.0
|
||||
delta_total: timedelta = datetime.combine(
|
||||
date.min, self.evening
|
||||
) - datetime.combine(date.min, self.morning)
|
||||
|
||||
self.count: int = 0
|
||||
# Sera utilisé pour les assiduités longues (> 1 journée)
|
||||
self.nb_heures_par_jour = (
|
||||
nb_heures_par_jour
|
||||
if nb_heures_par_jour
|
||||
else sco_preferences.get_preference(
|
||||
"nb_heures_par_jour", dept_id=g.scodoc_dept_id
|
||||
)
|
||||
)
|
||||
|
||||
self.data = {}
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
"""Remet à zero les compteurs"""
|
||||
self.days = []
|
||||
self.half_days = []
|
||||
self.hours = 0.0
|
||||
self.count = 0
|
||||
self.data = {
|
||||
"total": {
|
||||
"journee": [],
|
||||
"demi": [],
|
||||
"heure": 0,
|
||||
"compte": 0,
|
||||
},
|
||||
"absent": {
|
||||
"journee": [],
|
||||
"demi": [],
|
||||
"heure": 0,
|
||||
"compte": 0,
|
||||
},
|
||||
"absent_just": {
|
||||
"journee": [],
|
||||
"demi": [],
|
||||
"heure": 0,
|
||||
"compte": 0,
|
||||
},
|
||||
"absent_non_just": {
|
||||
"journee": [],
|
||||
"demi": [],
|
||||
"heure": 0,
|
||||
"compte": 0,
|
||||
},
|
||||
"retard": {
|
||||
"journee": [],
|
||||
"demi": [],
|
||||
"heure": 0,
|
||||
"compte": 0,
|
||||
},
|
||||
"retard_just": {
|
||||
"journee": [],
|
||||
"demi": [],
|
||||
"heure": 0,
|
||||
"compte": 0,
|
||||
},
|
||||
"retard_non_just": {
|
||||
"journee": [],
|
||||
"demi": [],
|
||||
"heure": 0,
|
||||
"compte": 0,
|
||||
},
|
||||
"present": {
|
||||
"journee": [],
|
||||
"demi": [],
|
||||
"heure": 0,
|
||||
"compte": 0,
|
||||
},
|
||||
}
|
||||
|
||||
def add_half_day(self, day: date, is_morning: bool = True):
|
||||
def get_count_key(self, etat: scu.EtatAssiduite, justi: bool = False) -> str:
|
||||
"""Récupère une clé de dictionnaire en fonction de l'état de l'assiduité
|
||||
et si elle est justifié
|
||||
"""
|
||||
keys: dict[EtatAssiduite, str] = {
|
||||
scu.EtatAssiduite.ABSENT: "absent",
|
||||
scu.EtatAssiduite.RETARD: "retard",
|
||||
scu.EtatAssiduite.PRESENT: "present",
|
||||
}
|
||||
count_key: str = keys.get(etat)
|
||||
if etat != scu.EtatAssiduite.PRESENT:
|
||||
count_key += "_just" if justi else "_non_just"
|
||||
return count_key
|
||||
|
||||
def add_half_day(self, day: date, assi: Assiduite, is_morning: bool = True):
|
||||
"""Ajoute une demi-journée dans le comptage"""
|
||||
key: tuple[date, bool] = (day, is_morning)
|
||||
if key not in self.half_days:
|
||||
self.half_days.append(key)
|
||||
|
||||
def add_day(self, day: date):
|
||||
count_key: str = self.get_count_key(assi.etat, assi.est_just)
|
||||
if assi.etat != scu.EtatAssiduite.PRESENT:
|
||||
_key: str = scu.EtatAssiduite.inverse().get(assi.etat).name.lower()
|
||||
if key not in self.data[_key]["demi"]:
|
||||
self.data[_key]["demi"].append(day)
|
||||
|
||||
if key not in self.data["total"]["demi"]:
|
||||
self.data["total"]["demi"].append(key)
|
||||
if key not in self.data[count_key]["demi"]:
|
||||
self.data[count_key]["demi"].append(key)
|
||||
|
||||
def add_day(self, day: date, assi: Assiduite):
|
||||
"""Ajoute un jour dans le comptage"""
|
||||
if day not in self.days:
|
||||
self.days.append(day)
|
||||
count_key: str = self.get_count_key(assi.etat, assi.est_just)
|
||||
if assi.etat != scu.EtatAssiduite.PRESENT:
|
||||
key: str = scu.EtatAssiduite.inverse().get(assi.etat).name.lower()
|
||||
if day not in self.data[key]["journee"]:
|
||||
self.data[key]["journee"].append(day)
|
||||
|
||||
if day not in self.data["total"]["journee"]:
|
||||
self.data["total"]["journee"].append(day)
|
||||
if day not in self.data[count_key]["journee"]:
|
||||
self.data[count_key]["journee"].append(day)
|
||||
|
||||
def add_hours(self, hours: float, assi: Assiduite):
|
||||
"""Ajoute des heures dans le comptage"""
|
||||
|
||||
count_key: str = self.get_count_key(assi.etat, assi.est_just)
|
||||
if assi.etat != scu.EtatAssiduite.PRESENT:
|
||||
self.data[scu.EtatAssiduite.inverse().get(assi.etat).name.lower()][
|
||||
"heure"
|
||||
] += hours
|
||||
|
||||
self.data[count_key]["heure"] += hours
|
||||
self.data["total"]["heure"] += hours
|
||||
|
||||
def add_count(self, assi: Assiduite):
|
||||
"""Ajoute 1 count dans le comptage"""
|
||||
|
||||
count_key: str = self.get_count_key(assi.etat, assi.est_just)
|
||||
if assi.etat != scu.EtatAssiduite.PRESENT:
|
||||
self.data[scu.EtatAssiduite.inverse().get(assi.etat).name.lower()][
|
||||
"compte"
|
||||
] += 1
|
||||
|
||||
self.data[count_key]["compte"] += 1
|
||||
self.data["total"]["compte"] += 1
|
||||
|
||||
def is_in_morning(self, period: tuple[datetime, datetime]) -> bool:
|
||||
"""Vérifiée si la période donnée fait partie du matin
|
||||
@ -90,7 +253,9 @@ class CountCalculator:
|
||||
"""
|
||||
|
||||
interval_evening: tuple[datetime, datetime] = (
|
||||
scu.localize_datetime(datetime.combine(period[0].date(), self.after_noon)),
|
||||
scu.localize_datetime(
|
||||
datetime.combine(period[0].date(), self.noon) + timedelta(seconds=1)
|
||||
),
|
||||
scu.localize_datetime(datetime.combine(period[0].date(), self.evening)),
|
||||
)
|
||||
|
||||
@ -102,15 +267,9 @@ class CountCalculator:
|
||||
"""Calcule les métriques sur une assiduité longue (plus d'un jour)"""
|
||||
|
||||
pointer_date: date = assi.date_debut.date() + timedelta(days=1)
|
||||
start_hours: timedelta = assi.date_debut - scu.localize_datetime(
|
||||
datetime.combine(assi.date_debut, self.morning)
|
||||
)
|
||||
finish_hours: timedelta = assi.date_fin - scu.localize_datetime(
|
||||
datetime.combine(assi.date_fin, self.morning)
|
||||
)
|
||||
|
||||
self.add_day(assi.date_debut.date())
|
||||
self.add_day(assi.date_fin.date())
|
||||
self.add_day(assi.date_debut.date(), assi)
|
||||
self.add_day(assi.date_fin.date(), assi)
|
||||
|
||||
start_period: tuple[datetime, datetime] = (
|
||||
assi.date_debut,
|
||||
@ -123,58 +282,67 @@ class CountCalculator:
|
||||
scu.localize_datetime(datetime.combine(assi.date_fin.date(), self.morning)),
|
||||
assi.date_fin,
|
||||
)
|
||||
hours = 0.0
|
||||
for period in (start_period, finish_period):
|
||||
if self.is_in_evening(period):
|
||||
self.add_half_day(period[0].date(), False)
|
||||
self.add_half_day(period[0].date(), assi, False)
|
||||
if self.is_in_morning(period):
|
||||
self.add_half_day(period[0].date())
|
||||
self.add_half_day(period[0].date(), assi)
|
||||
|
||||
while pointer_date < assi.date_fin.date():
|
||||
# TODO : Utiliser la préférence de département : workdays
|
||||
if pointer_date.weekday() < (6 - self.skip_saturday):
|
||||
self.add_day(pointer_date)
|
||||
self.add_half_day(pointer_date)
|
||||
self.add_half_day(pointer_date, False)
|
||||
self.hours += self.hour_per_day
|
||||
hours += self.hour_per_day
|
||||
if pointer_date.weekday() not in self.non_work_days:
|
||||
self.add_day(pointer_date, assi)
|
||||
self.add_half_day(pointer_date, assi)
|
||||
self.add_half_day(pointer_date, assi, False)
|
||||
self.add_hours(self.nb_heures_par_jour, assi)
|
||||
|
||||
pointer_date += timedelta(days=1)
|
||||
|
||||
self.hours += finish_hours.total_seconds() / 3600
|
||||
self.hours += self.hour_per_day - (start_hours.total_seconds() / 3600)
|
||||
# Gestion des heures des dates de début et des dates de fin
|
||||
deb_hours = (start_period[1] - start_period[0]).total_seconds() / 3600
|
||||
fin_hours = (finish_period[1] - finish_period[0]).total_seconds() / 3600
|
||||
|
||||
self.add_hours(deb_hours + fin_hours, assi)
|
||||
|
||||
def compute_assiduites(self, assiduites: Query | list):
|
||||
"""Calcule les métriques pour la collection d'assiduité donnée"""
|
||||
assi: Assiduite
|
||||
for assi in assiduites:
|
||||
self.count += 1
|
||||
# Ajout vérification workday
|
||||
# (Si préférence mise après avoir déjà noté des assiduités)
|
||||
if assi.date_debut.weekday() in self.non_work_days:
|
||||
continue
|
||||
|
||||
self.add_count(assi)
|
||||
|
||||
delta: timedelta = assi.date_fin - assi.date_debut
|
||||
|
||||
if delta.days > 0:
|
||||
self.compute_long_assiduite(assi)
|
||||
|
||||
continue
|
||||
|
||||
period: tuple[datetime, datetime] = (assi.date_debut, assi.date_fin)
|
||||
deb_date: date = assi.date_debut.date()
|
||||
if self.is_in_morning(period):
|
||||
self.add_half_day(deb_date)
|
||||
self.add_half_day(deb_date, assi)
|
||||
if self.is_in_evening(period):
|
||||
self.add_half_day(deb_date, False)
|
||||
self.add_half_day(deb_date, assi, False)
|
||||
|
||||
self.add_day(deb_date)
|
||||
self.add_day(deb_date, assi)
|
||||
|
||||
self.hours += delta.total_seconds() / 3600
|
||||
self.add_hours(delta.total_seconds() / 3600, assi)
|
||||
self.setup_data()
|
||||
|
||||
def to_dict(self) -> dict[str, int | float]:
|
||||
def setup_data(self):
|
||||
"""Met en forme les données
|
||||
pour les journées et les demi-journées : au lieu d'avoir list[str] on a le nombre (len(list[str]))
|
||||
"""
|
||||
for key in self.data:
|
||||
self.data[key]["journee"] = len(self.data[key]["journee"])
|
||||
self.data[key]["demi"] = len(self.data[key]["demi"])
|
||||
|
||||
def to_dict(self, only_total: bool = True) -> dict[str, int | float]:
|
||||
"""Retourne les métriques sous la forme d'un dictionnaire"""
|
||||
return {
|
||||
"compte": self.count,
|
||||
"journee": len(self.days),
|
||||
"demi": len(self.half_days),
|
||||
"heure": round(self.hours, 2),
|
||||
}
|
||||
return self.data["total"] if only_total else self.data
|
||||
|
||||
|
||||
def get_assiduites_stats(
|
||||
@ -211,55 +379,34 @@ def get_assiduites_stats(
|
||||
metrics: list[str] = metric.split(",")
|
||||
output: dict = {}
|
||||
calculator: CountCalculator = CountCalculator()
|
||||
calculator.compute_assiduites(assiduites)
|
||||
|
||||
if filtered is None or "split" not in filtered:
|
||||
calculator.compute_assiduites(assiduites)
|
||||
count: dict = calculator.to_dict()
|
||||
|
||||
count: dict = calculator.to_dict(only_total=True)
|
||||
for key, val in count.items():
|
||||
if key in metrics:
|
||||
output[key] = val
|
||||
return output if output else count
|
||||
|
||||
# Récupération des états
|
||||
etats: list[str] = (
|
||||
filtered["etat"].split(",")
|
||||
if "etat" in filtered
|
||||
else ["absent", "present", "retard"]
|
||||
)
|
||||
|
||||
# Préparation du dictionnaire de retour avec les valeurs du calcul
|
||||
count: dict = calculator.to_dict(only_total=False)
|
||||
for etat in etats:
|
||||
output[etat] = _count_assiduites_etat(etat, assiduites, calculator, metrics)
|
||||
if "est_just" not in filtered:
|
||||
output[etat]["justifie"] = _count_assiduites_etat(
|
||||
etat, assiduites, calculator, metrics, justifie=True
|
||||
)
|
||||
|
||||
if etat != "present":
|
||||
output[etat] = count[etat]
|
||||
output[etat]["justifie"] = count[etat + "_just"]
|
||||
output[etat]["non_justifie"] = count[etat + "_non_just"]
|
||||
else:
|
||||
output[etat] = count[etat]
|
||||
output["total"] = count["total"]
|
||||
return output
|
||||
|
||||
|
||||
def _count_assiduites_etat(
|
||||
etat: str,
|
||||
assiduites: Query,
|
||||
calculator: CountCalculator,
|
||||
metrics: list[str],
|
||||
justifie: bool = False,
|
||||
): # TODO type retour ?
|
||||
# TODO documenter
|
||||
calculator.reset()
|
||||
etat_num: int = scu.EtatAssiduite.get(etat, -1)
|
||||
assiduites_etat: Query = assiduites.filter(Assiduite.etat == etat_num)
|
||||
if justifie:
|
||||
assiduites_etat = assiduites_etat.filter(Assiduite.est_just == True)
|
||||
|
||||
calculator.compute_assiduites(assiduites_etat)
|
||||
count_etat: dict = calculator.to_dict()
|
||||
output_etat: dict = {}
|
||||
for key, val in count_etat.items():
|
||||
if key in metrics:
|
||||
output_etat[key] = val
|
||||
return output_etat if output_etat else count_etat
|
||||
|
||||
|
||||
def filter_assiduites_by_etat(assiduites: Assiduite, etat: str) -> Query:
|
||||
"""
|
||||
Filtrage d'une collection d'assiduites en fonction de leur état
|
||||
|
@ -655,6 +655,17 @@ class BasePreferences:
|
||||
"explanation": "Durée d'un créneau en heure. Utilisé dans les pages de saisie",
|
||||
},
|
||||
),
|
||||
(
|
||||
"nb_heures_par_jour",
|
||||
{
|
||||
"initvalue": 8,
|
||||
"size": 10,
|
||||
"title": "Nombre d'heures de travail dans une journée",
|
||||
"type": "int",
|
||||
"explanation": "Est utilisé dans le calcul de la métrique 'heure'. ",
|
||||
"category": "assi",
|
||||
},
|
||||
),
|
||||
(
|
||||
"assi_etat_defaut",
|
||||
{
|
||||
|
@ -238,6 +238,47 @@ class EtatJustificatif(int, BiDirectionalEnum):
|
||||
return etat in cls._value2member_map_
|
||||
|
||||
|
||||
class NonWorkDays(int, BiDirectionalEnum):
|
||||
"""Correspondance entre les jours et les numéros de jours"""
|
||||
|
||||
LUN = 0
|
||||
MAR = 1
|
||||
MER = 2
|
||||
JEU = 3
|
||||
VEN = 4
|
||||
SAM = 5
|
||||
DIM = 6
|
||||
|
||||
@classmethod
|
||||
def get_all_non_work_days(
|
||||
cls, formsemestre_id: int = None, dept_id: int = None
|
||||
) -> list["NonWorkDays"]:
|
||||
"""
|
||||
get_all_non_work_days Récupère la liste des non workdays (str) depuis les préférences
|
||||
puis renvoie une liste BiDirectionnalEnum<int> NonWorkDays
|
||||
|
||||
Example:
|
||||
non_work_days : list[NonWorkDays] = NonWorkDays.get_all_non_work_days(dept_id=g.scodoc_dept_id)
|
||||
if datetime.datetime.now().weekday() in non_work_days:
|
||||
print("Aujourd'hui est un jour non travaillé")
|
||||
|
||||
Args:
|
||||
formsemestre_id (int, optional): id d'un formsemestre . Defaults to None.
|
||||
dept_id (int, optional): id d'un départment. Defaults to None.
|
||||
|
||||
Returns:
|
||||
list[NonWorkDays]: La liste des NonWorkDays en version BiDirectionnalEnum<int>
|
||||
"""
|
||||
from app.scodoc import sco_preferences
|
||||
|
||||
return [
|
||||
cls.get(day.strip())
|
||||
for day in sco_preferences.get_preference(
|
||||
"non_travail", formsemestre_id=formsemestre_id, dept_id=dept_id
|
||||
).split(",")
|
||||
]
|
||||
|
||||
|
||||
def is_iso_formated(date: str, convert=False) -> bool or datetime.datetime or None:
|
||||
"""
|
||||
Vérifie si une date est au format iso
|
||||
|
@ -79,10 +79,10 @@ c'est à dire à la montre des étudiants.
|
||||
{{ form.hidden_tag() }}
|
||||
{{ wtf.form_errors(form, hiddens="only") }}
|
||||
|
||||
{{ wtf.form_field(form.morning_time) }}
|
||||
{{ wtf.form_field(form.lunch_time) }}
|
||||
{{ wtf.form_field(form.afternoon_time) }}
|
||||
{{ wtf.form_field(form.tick_time) }}
|
||||
{{ wtf.form_field(form.assi_morning_time) }}
|
||||
{{ wtf.form_field(form.assi_lunch_time) }}
|
||||
{{ wtf.form_field(form.assi_afternoon_time) }}
|
||||
{{ wtf.form_field(form.assi_tick_time) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
@ -348,8 +348,8 @@ def _get_dates_from_assi_form(
|
||||
Ramène ok=True si ok.
|
||||
Met des messages d'erreur dans le form.
|
||||
"""
|
||||
debut_jour = "00:00"
|
||||
fin_jour = "23:59:59"
|
||||
debut_jour = ScoDocSiteConfig.get("assi_morning_time", "08:00")
|
||||
fin_jour = ScoDocSiteConfig.get("assi_afternoon_time", "17:00")
|
||||
date_fin = None
|
||||
# On commence par convertir individuellement tous les champs
|
||||
try:
|
||||
|
@ -337,15 +337,19 @@ def config_assiduites():
|
||||
("edt_ics_user_path", "Chemin vers les ics des enseignants"),
|
||||
)
|
||||
|
||||
assi_options = (
|
||||
("assi_morning_time", "Heure du début de la journée"),
|
||||
("assi_lunch_time", "Heure du midi"),
|
||||
("assi_afternoon_time", "Heure du fin de la journée"),
|
||||
("assi_tick_time", "Granularité de la timeline"),
|
||||
)
|
||||
|
||||
if form.validate_on_submit():
|
||||
if ScoDocSiteConfig.set("assi_morning_time", form.data["morning_time"]):
|
||||
flash("Heure du début de la journée enregistrée")
|
||||
if ScoDocSiteConfig.set("assi_lunch_time", form.data["lunch_time"]):
|
||||
flash("Heure de midi enregistrée")
|
||||
if ScoDocSiteConfig.set("assi_afternoon_time", form.data["afternoon_time"]):
|
||||
flash("Heure de fin de la journée enregistrée")
|
||||
if ScoDocSiteConfig.set("assi_tick_time", float(form.data["tick_time"])):
|
||||
flash("Granularité de la timeline enregistrée")
|
||||
# --- Options assiduités
|
||||
for opt_name, message in assi_options:
|
||||
if ScoDocSiteConfig.set(opt_name, form.data[opt_name]):
|
||||
flash(f"{message} enregistrée")
|
||||
|
||||
# --- Calendriers emploi du temps
|
||||
for opt_name, message in edt_options:
|
||||
if ScoDocSiteConfig.set(opt_name, form.data[opt_name]):
|
||||
@ -354,19 +358,21 @@ def config_assiduites():
|
||||
return redirect(url_for("scodoc.configuration"))
|
||||
|
||||
if request.method == "GET":
|
||||
form.morning_time.data = ScoDocSiteConfig.get(
|
||||
form.assi_morning_time.data = ScoDocSiteConfig.get(
|
||||
"assi_morning_time", datetime.time(8, 0, 0)
|
||||
)
|
||||
form.lunch_time.data = ScoDocSiteConfig.get(
|
||||
form.assi_lunch_time.data = ScoDocSiteConfig.get(
|
||||
"assi_lunch_time", datetime.time(13, 0, 0)
|
||||
)
|
||||
form.afternoon_time.data = ScoDocSiteConfig.get(
|
||||
form.assi_afternoon_time.data = ScoDocSiteConfig.get(
|
||||
"assi_afternoon_time", datetime.time(18, 0, 0)
|
||||
)
|
||||
try:
|
||||
form.tick_time.data = float(ScoDocSiteConfig.get("assi_tick_time", 15.0))
|
||||
form.assi_tick_time.data = float(
|
||||
ScoDocSiteConfig.get("assi_tick_time", 15.0)
|
||||
)
|
||||
except ValueError:
|
||||
form.tick_time.data = 15.0
|
||||
form.assi_tick_time.data = 15.0
|
||||
ScoDocSiteConfig.set("assi_tick_time", 15.0)
|
||||
# --- Emplois du temps
|
||||
for opt_name, _ in edt_options:
|
||||
|
Loading…
Reference in New Issue
Block a user