Files
tableau-de-bord/app/business/time_calc.py
Antoine Van Elstraete 2d85fffd8d doc: document models and business rules
Co-authored-by: OpenAI/GPT-5.6-Terra <vibecoder@antoineve.me>
Co-authored-by: Google/Gemini-3.5-Flash <vibecoder@antoineve.me>
Co-authored-by: DeepSeek/DeepSeek-v4-Pro <vibecoder@antoineve.me>
Co-authored-by: DeepSeek/DeepSeek-v4-Flash <vibecoder@antoineve.me>
2026-08-13 10:58:49 +02:00

110 lines
3.5 KiB
Python

def minutes_to_str(minutes: int) -> str:
"""
Convertit une durée en minutes en une chaîne formatée lisible (ex: "7h45" ou "-1h15").
Paramètres :
minutes (int) : Le nombre de minutes à convertir (peut être négatif).
Retour :
str : La chaîne formatée au format "[signe]HhMM".
"""
sign = "-" if minutes < 0 else ""
minutes = abs(minutes)
return f"{sign}{minutes // 60}h{minutes % 60:02d}"
_REFERENCE_MINUTES = {
"WORK": 465,
"TT": 465,
"FORMATION": 465,
"GARDE": 600,
"ASTREINTE": 0,
"RTT": 0,
"CONGE": 0,
"MALADE": 0,
"FERIE": 0,
}
def work_minutes_reference(day_type: str) -> int:
"""
Retourne la durée de travail de référence en minutes pour un type de journée donné.
Les durées de référence sont :
- WORK, TT, FORMATION : 7h45 (465 minutes)
- GARDE : 10h00 (600 minutes)
- ASTREINTE, RTT, CONGE, MALADE, FERIE : 0 minute
Paramètres :
day_type (str) : Le type de journée (ex: "WORK", "TT", "CONGE").
Retour :
int : La durée de référence en minutes. Par défaut 465 minutes si le type est inconnu.
"""
return _REFERENCE_MINUTES.get(day_type, 465)
def week_balance_minutes(actual_minutes: int, reference_minutes: int) -> int:
"""
Calcule l'écart (solde) entre les minutes réellement travaillées et les minutes de référence.
Paramètres :
actual_minutes (int) : Le total des minutes travaillées.
reference_minutes (int) : Le total des minutes de référence.
Retour :
int : L'écart en minutes (positif si heures supplémentaires, négatif si déficit).
"""
return actual_minutes - reference_minutes
import statistics as _stats
def monthly_stats(entries: list) -> dict:
"""
Calcule la médiane journalière et la médiane hebdomadaire (semaines ISO) pour un groupe d'entrées.
Les absences (durée de travail de 0 minute) sont incluses dans les calculs.
Les semaines sont regroupées selon le calendrier ISO (année, numéro de semaine).
Paramètres :
entries (list) : Une liste d'objets `WorkEntry`.
Retour :
dict : Un dictionnaire contenant :
- "median_daily_min" (int) : La médiane des minutes travaillées par jour.
- "median_weekly_min" (int) : La médiane des minutes travaillées par semaine ISO.
"""
if not entries:
return {"median_daily_min": 0, "median_weekly_min": 0}
daily = [e.total_minutes() for e in entries]
median_daily = int(_stats.median(daily))
weekly: dict[tuple, int] = {}
for e in entries:
key = e.date.isocalendar()[:2] # (year, isoweek)
weekly[key] = weekly.get(key, 0) + e.total_minutes()
median_weekly = int(_stats.median(weekly.values()))
return {"median_daily_min": median_daily, "median_weekly_min": median_weekly}
def count_day_types(entries: list) -> dict[str, int]:
"""
Comptabilise le nombre d'occurrences de chaque type de journée dans une liste d'entrées.
Paramètres :
entries (list) : Une liste d'objets `WorkEntry`.
Retour :
dict[str, int] : Un dictionnaire associant chaque type de journée présent à son nombre d'occurrences.
Les types de journées non représentés ne figurent pas dans le dictionnaire.
"""
counts: dict[str, int] = {}
for entry in entries:
counts[entry.day_type] = counts.get(entry.day_type, 0) + 1
return counts