def minutes_to_str(minutes: int) -> str: 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: return _REFERENCE_MINUTES.get(day_type, 465) def week_balance_minutes(actual_minutes: int, reference_minutes: int) -> int: return actual_minutes - reference_minutes import statistics as _stats def monthly_stats(entries: list) -> dict: """ Calcule médiane journalière et médiane hebdomadaire (semaines ISO) pour un groupe d'entrées. Les absences (total_minutes=0) sont incluses. """ 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]: """Retourne un dict {day_type: count} pour une liste d'entrées, sans les zéros.""" counts: dict[str, int] = {} for entry in entries: counts[entry.day_type] = counts.get(entry.day_type, 0) + 1 return counts