Assiduites : maj CountCalculator fixes #820

This commit is contained in:
Iziram 2024-01-05 10:06:16 +01:00
parent 85f0323a80
commit e0ca0100d0
8 changed files with 355 additions and 122 deletions

View File

@ -119,15 +119,15 @@ def check_ics_regexp(form, field):
class ConfigAssiduitesForm(FlaskForm): class ConfigAssiduitesForm(FlaskForm):
"Formulaire paramétrage Module Assiduité" "Formulaire paramétrage Module Assiduité"
morning_time = TimeField( assi_morning_time = TimeField(
"Début de la journée" "Début de la journée"
) # TODO utiliser TextField + timepicker voir AjoutAssiOrJustForm ) # TODO utiliser TextField + timepicker voir AjoutAssiOrJustForm
lunch_time = TimeField( assi_lunch_time = TimeField(
"Heure de midi (date pivot entre matin et après-midi)" "Heure de midi (date pivot entre matin et après-midi)"
) # TODO ) # 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)", "Granularité de la timeline (temps en minutes)",
places=0, places=0,
validators=[check_tick_time], validators=[check_tick_time],

View File

@ -25,6 +25,7 @@ from app.scodoc.sco_utils import (
EtatJustificatif, EtatJustificatif,
localize_datetime, localize_datetime,
is_assiduites_module_forced, is_assiduites_module_forced,
NonWorkDays,
) )
@ -154,6 +155,33 @@ class Assiduite(ScoDocModel):
) )
if date_fin.tzinfo is None: if date_fin.tzinfo is None:
log(f"Warning: create_assiduite: date_fin without timezone ({date_fin})") 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 # Vérification de non duplication des périodes
assiduites: Query = 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):

View File

@ -15,59 +15,222 @@ 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 app.models import ScoDocSiteConfig
from flask import g
class CountCalculator: 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__( def __init__(
self, self,
morning: time = time(8, 0), # TODO utiliser ScoDocSiteConfig morning: str = None,
noon: time = time(12, 0), noon: str = None,
after_noon: time = time(14, 00), evening: str = None,
evening: time = time(18, 0), nb_heures_par_jour: int = None,
skip_saturday: bool = True, # TODO préférence workdays
) -> None: ) -> None:
self.morning: time = morning # Transformation d'une heure "HH:MM" en time(h,m)
self.noon: time = noon STR_TIME = lambda x: time(*list(map(int, x.split(":"))))
self.after_noon: time = after_noon
self.evening: time = evening
self.skip_saturday: bool = skip_saturday
delta_total: timedelta = datetime.combine(date.min, evening) - datetime.combine( self.morning: time = STR_TIME(
date.min, morning 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] = [] delta_total: timedelta = datetime.combine(
self.half_days: list[tuple[date, bool]] = [] # tuple -> (date, morning:bool) date.min, self.evening
self.hours: float = 0.0 ) - 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): def reset(self):
"""Remet à zero les compteurs""" """Remet à zero les compteurs"""
self.days = [] self.data = {
self.half_days = [] "total": {
self.hours = 0.0 "journee": [],
self.count = 0 "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""" """Ajoute une demi-journée dans le comptage"""
key: tuple[date, bool] = (day, is_morning) 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""" """Ajoute un jour dans le comptage"""
if day not in self.days: count_key: str = self.get_count_key(assi.etat, assi.est_just)
self.days.append(day) 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: def is_in_morning(self, period: tuple[datetime, datetime]) -> bool:
"""Vérifiée si la période donnée fait partie du matin """Vérifiée si la période donnée fait partie du matin
@ -90,7 +253,9 @@ class CountCalculator:
""" """
interval_evening: tuple[datetime, datetime] = ( 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)), 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)""" """Calcule les métriques sur une assiduité longue (plus d'un jour)"""
pointer_date: date = assi.date_debut.date() + timedelta(days=1) 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_debut.date(), assi)
self.add_day(assi.date_fin.date()) self.add_day(assi.date_fin.date(), assi)
start_period: tuple[datetime, datetime] = ( start_period: tuple[datetime, datetime] = (
assi.date_debut, assi.date_debut,
@ -123,58 +282,67 @@ class CountCalculator:
scu.localize_datetime(datetime.combine(assi.date_fin.date(), self.morning)), scu.localize_datetime(datetime.combine(assi.date_fin.date(), self.morning)),
assi.date_fin, assi.date_fin,
) )
hours = 0.0
for period in (start_period, finish_period): for period in (start_period, finish_period):
if self.is_in_evening(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): 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(): while pointer_date < assi.date_fin.date():
# TODO : Utiliser la préférence de département : workdays if pointer_date.weekday() not in self.non_work_days:
if pointer_date.weekday() < (6 - self.skip_saturday): self.add_day(pointer_date, assi)
self.add_day(pointer_date) self.add_half_day(pointer_date, assi)
self.add_half_day(pointer_date) self.add_half_day(pointer_date, assi, False)
self.add_half_day(pointer_date, False) self.add_hours(self.nb_heures_par_jour, assi)
self.hours += self.hour_per_day
hours += self.hour_per_day
pointer_date += timedelta(days=1) pointer_date += timedelta(days=1)
self.hours += finish_hours.total_seconds() / 3600 # Gestion des heures des dates de début et des dates de fin
self.hours += self.hour_per_day - (start_hours.total_seconds() / 3600) 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): def compute_assiduites(self, assiduites: Query | list):
"""Calcule les métriques pour la collection d'assiduité donnée""" """Calcule les métriques pour la collection d'assiduité donnée"""
assi: Assiduite assi: Assiduite
for assi in assiduites: 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 delta: timedelta = assi.date_fin - assi.date_debut
if delta.days > 0: if delta.days > 0:
self.compute_long_assiduite(assi) self.compute_long_assiduite(assi)
continue continue
period: tuple[datetime, datetime] = (assi.date_debut, assi.date_fin) period: tuple[datetime, datetime] = (assi.date_debut, assi.date_fin)
deb_date: date = assi.date_debut.date() deb_date: date = assi.date_debut.date()
if self.is_in_morning(period): 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): 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""" """Retourne les métriques sous la forme d'un dictionnaire"""
return { return self.data["total"] if only_total else self.data
"compte": self.count,
"journee": len(self.days),
"demi": len(self.half_days),
"heure": round(self.hours, 2),
}
def get_assiduites_stats( def get_assiduites_stats(
@ -211,55 +379,34 @@ def get_assiduites_stats(
metrics: list[str] = metric.split(",") metrics: list[str] = metric.split(",")
output: dict = {} output: dict = {}
calculator: CountCalculator = CountCalculator() calculator: CountCalculator = CountCalculator()
calculator.compute_assiduites(assiduites)
if filtered is None or "split" not in filtered: if filtered is None or "split" not in filtered:
calculator.compute_assiduites(assiduites) count: dict = calculator.to_dict(only_total=True)
count: dict = calculator.to_dict()
for key, val in count.items(): for key, val in count.items():
if key in metrics: if key in metrics:
output[key] = val output[key] = val
return output if output else count return output if output else count
# Récupération des états
etats: list[str] = ( etats: list[str] = (
filtered["etat"].split(",") filtered["etat"].split(",")
if "etat" in filtered if "etat" in filtered
else ["absent", "present", "retard"] 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: for etat in etats:
output[etat] = _count_assiduites_etat(etat, assiduites, calculator, metrics) if etat != "present":
if "est_just" not in filtered: output[etat] = count[etat]
output[etat]["justifie"] = _count_assiduites_etat( output[etat]["justifie"] = count[etat + "_just"]
etat, assiduites, calculator, metrics, justifie=True output[etat]["non_justifie"] = count[etat + "_non_just"]
) else:
output[etat] = count[etat]
output["total"] = count["total"]
return output 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: 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

View File

@ -655,6 +655,17 @@ class BasePreferences:
"explanation": "Durée d'un créneau en heure. Utilisé dans les pages de saisie", "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", "assi_etat_defaut",
{ {

View File

@ -238,6 +238,47 @@ class EtatJustificatif(int, BiDirectionalEnum):
return etat in cls._value2member_map_ 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: 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

View File

@ -79,10 +79,10 @@ c'est à dire à la montre des étudiants.
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
{{ wtf.form_errors(form, hiddens="only") }} {{ wtf.form_errors(form, hiddens="only") }}
{{ wtf.form_field(form.morning_time) }} {{ wtf.form_field(form.assi_morning_time) }}
{{ wtf.form_field(form.lunch_time) }} {{ wtf.form_field(form.assi_lunch_time) }}
{{ wtf.form_field(form.afternoon_time) }} {{ wtf.form_field(form.assi_afternoon_time) }}
{{ wtf.form_field(form.tick_time) }} {{ wtf.form_field(form.assi_tick_time) }}
</div> </div>
</div> </div>
<div class="row"> <div class="row">

View File

@ -348,8 +348,8 @@ def _get_dates_from_assi_form(
Ramène ok=True si ok. Ramène ok=True si ok.
Met des messages d'erreur dans le form. Met des messages d'erreur dans le form.
""" """
debut_jour = "00:00" debut_jour = ScoDocSiteConfig.get("assi_morning_time", "08:00")
fin_jour = "23:59:59" fin_jour = ScoDocSiteConfig.get("assi_afternoon_time", "17:00")
date_fin = None date_fin = None
# On commence par convertir individuellement tous les champs # On commence par convertir individuellement tous les champs
try: try:

View File

@ -337,15 +337,19 @@ def config_assiduites():
("edt_ics_user_path", "Chemin vers les ics des enseignants"), ("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 form.validate_on_submit():
if ScoDocSiteConfig.set("assi_morning_time", form.data["morning_time"]): # --- Options assiduités
flash("Heure du début de la journée enregistrée") for opt_name, message in assi_options:
if ScoDocSiteConfig.set("assi_lunch_time", form.data["lunch_time"]): if ScoDocSiteConfig.set(opt_name, form.data[opt_name]):
flash("Heure de midi enregistrée") flash(f"{message} 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")
# --- Calendriers emploi du temps # --- Calendriers emploi du temps
for opt_name, message in edt_options: for opt_name, message in edt_options:
if ScoDocSiteConfig.set(opt_name, form.data[opt_name]): if ScoDocSiteConfig.set(opt_name, form.data[opt_name]):
@ -354,19 +358,21 @@ def config_assiduites():
return redirect(url_for("scodoc.configuration")) return redirect(url_for("scodoc.configuration"))
if request.method == "GET": 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) "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) "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) "assi_afternoon_time", datetime.time(18, 0, 0)
) )
try: 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: except ValueError:
form.tick_time.data = 15.0 form.assi_tick_time.data = 15.0
ScoDocSiteConfig.set("assi_tick_time", 15.0) ScoDocSiteConfig.set("assi_tick_time", 15.0)
# --- Emplois du temps # --- Emplois du temps
for opt_name, _ in edt_options: for opt_name, _ in edt_options: