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
3 changed files with 31 additions and 16 deletions
Showing only changes of commit 397e3acea2 - Show all commits

View File

@ -119,9 +119,13 @@ 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("Début de la journée") morning_time = TimeField(
lunch_time = TimeField("Heure de midi (date pivot entre Matin et Après Midi)") "Début de la journée"
afternoon_time = TimeField("Fin de la journée") ) # TODO utiliser TextField + timepicker voir AjoutAssiOrJustForm
lunch_time = TimeField(
"Heure de midi (date pivot entre matin et après-midi)"
) # TODO
afternoon_time = TimeField("Fin de la journée") # TODO
tick_time = DecimalField( tick_time = DecimalField(
"Granularité de la timeline (temps en minutes)", "Granularité de la timeline (temps en minutes)",

View File

@ -20,13 +20,15 @@ from app.scodoc import sco_etud
class CountCalculator: class CountCalculator:
"""Classe qui gére le comptage des assiduités""" """Classe qui gére le comptage des assiduités"""
# TODO documenter
def __init__( def __init__(
self, self,
morning: time = time(8, 0), morning: time = time(8, 0), # TODO utiliser ScoDocSiteConfig
noon: time = time(12, 0), noon: time = time(12, 0),
after_noon: time = time(14, 00), after_noon: time = time(14, 00),
evening: time = time(18, 0), evening: time = time(18, 0),
skip_saturday: bool = True, skip_saturday: bool = True, # TODO préférence workdays
) -> None: ) -> None:
self.morning: time = morning self.morning: time = morning
self.noon: time = noon self.noon: time = noon
@ -50,14 +52,14 @@ class CountCalculator:
self.count: int = 0 self.count: int = 0
def reset(self): def reset(self):
"""Remet à zero le compteur""" """Remet à zero les compteurs"""
self.days = [] self.days = []
self.half_days = [] self.half_days = []
self.hours = 0.0 self.hours = 0.0
self.count = 0 self.count = 0
def add_half_day(self, day: date, is_morning: bool = True): def add_half_day(self, day: date, 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: if key not in self.half_days:
self.half_days.append(key) self.half_days.append(key)
@ -67,7 +69,7 @@ class CountCalculator:
if day not in self.days: if day not in self.days:
self.days.append(day) self.days.append(day)
def check_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
(Test sur la date de début) (Test sur la date de début)
""" """
@ -82,7 +84,7 @@ class CountCalculator:
) )
return in_morning return in_morning
def check_in_evening(self, period: tuple[datetime, datetime]) -> bool: def is_in_evening(self, period: tuple[datetime, datetime]) -> bool:
"""Vérifie si la période fait partie de l'aprèm """Vérifie si la période fait partie de l'aprèm
(test sur la date de début) (test sur la date de début)
""" """
@ -123,9 +125,9 @@ class CountCalculator:
) )
hours = 0.0 hours = 0.0
for period in (start_period, finish_period): for period in (start_period, finish_period):
if self.check_in_evening(period): if self.is_in_evening(period):
self.add_half_day(period[0].date(), False) self.add_half_day(period[0].date(), False)
if self.check_in_morning(period): if self.is_in_morning(period):
self.add_half_day(period[0].date()) self.add_half_day(period[0].date())
while pointer_date < assi.date_fin.date(): while pointer_date < assi.date_fin.date():
@ -156,9 +158,9 @@ class CountCalculator:
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.check_in_morning(period): if self.is_in_morning(period):
self.add_half_day(deb_date) self.add_half_day(deb_date)
if self.check_in_evening(period): if self.is_in_evening(period):
self.add_half_day(deb_date, False) self.add_half_day(deb_date, False)
self.add_day(deb_date) self.add_day(deb_date)
@ -241,7 +243,8 @@ def _count_assiduites_etat(
calculator: CountCalculator, calculator: CountCalculator,
metrics: list[str], metrics: list[str],
justifie: bool = False, justifie: bool = False,
): ): # TODO type retour ?
# TODO documenter
calculator.reset() calculator.reset()
etat_num: int = scu.EtatAssiduite.get(etat, -1) etat_num: int = scu.EtatAssiduite.get(etat, -1)
assiduites_etat: Query = assiduites.filter(Assiduite.etat == etat_num) assiduites_etat: Query = assiduites.filter(Assiduite.etat == etat_num)
@ -298,7 +301,7 @@ def filter_by_date(
if date_fin is None: if date_fin is None:
date_fin = datetime.max date_fin = datetime.max
date_deb = scu.localize_datetime(date_deb) date_deb = scu.localize_datetime(date_deb) # TODO A modifier (timezone ?)
date_fin = scu.localize_datetime(date_fin) date_fin = scu.localize_datetime(date_fin)
if not strict: if not strict:
return collection.filter( return collection.filter(
@ -414,6 +417,7 @@ def create_absence(
est_just: bool = False, est_just: bool = False,
) -> int: ) -> int:
"""TODO: doc, dire quand l'utiliser""" """TODO: doc, dire quand l'utiliser"""
# TODO
etud: Identite = Identite.query.filter_by(etudid=etudid).first_or_404() etud: Identite = Identite.query.filter_by(etudid=etudid).first_or_404()
assiduite_unique: Assiduite = Assiduite.create_assiduite( assiduite_unique: Assiduite = Assiduite.create_assiduite(
etud=etud, etud=etud,
@ -495,6 +499,7 @@ def get_assiduites_count_in_interval(
""" """
date_debut_iso = date_debut_iso or date_debut.isoformat() date_debut_iso = date_debut_iso or date_debut.isoformat()
date_fin_iso = date_fin_iso or date_fin.isoformat() date_fin_iso = date_fin_iso or date_fin.isoformat()
# TODO Question: pourquoi ne pas cacher toutes les métriques, si l'API les veut toutes ?
key = f"{etudid}_{date_debut_iso}_{date_fin_iso}{metrique}_assiduites" key = f"{etudid}_{date_debut_iso}_{date_fin_iso}{metrique}_assiduites"
r = sco_cache.AbsSemEtudCache.get(key) r = sco_cache.AbsSemEtudCache.get(key)

View File

@ -60,9 +60,15 @@ async function load_ics_sample() {
<h1>Configuration du suivi de l'assiduité</h1> <h1>Configuration du suivi de l'assiduité</h1>
<div class="help"> Ces paramètres seront utilisés par tous les départements et <div class="help">
<p>Ces paramètres seront utilisés par tous les départements et
affectent notamment les comptages d'absences de tous les bulletins des affectent notamment les comptages d'absences de tous les bulletins des
étudiants&nbsp;: ne changer que lorsque c'est vraiment nécessaire. étudiants&nbsp;: ne changer que lorsque c'est vraiment nécessaire.
</p>
<p>
Les heures de ScoDoc sont exprimées en <em>heures locale du serveur</em>,
c'est à dire à la montre des étudiants.
</p>
</div> </div>
</div> </div>