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