"""Explicit automatic and manual five-zone configuration for running load.""" from datetime import date, datetime from itertools import pairwise from zoneinfo import ZoneInfo # Published as Stride's formula, not an exact reproduction of Strava's zones. ZONE_FRACTIONS = (0.65, 0.80, 0.87, 0.95) def zones(user, today=None): today = today or datetime.now(ZoneInfo(user.timezone)).date() config = user.hr_zones or {"mode": "auto"} mode = config.get("mode", "auto") maximum = None if mode == "custom": boundaries = config["boundaries"] source = "Manual zone boundaries" else: maximum = config.get("max_hr") if mode == "max" else None if maximum is not None: source = "Manual maximum HR" elif config.get("birthday"): birthday = date.fromisoformat(config["birthday"]) age = ( today.year - birthday.year - ((today.month, today.day) < (birthday.month, birthday.day)) ) maximum = 220 - age source = "Automatic · 220 minus age · updates on your birthday" else: maximum = 190 source = "Automatic · default maximum 190 bpm (birthday not set)" boundaries = [round(maximum * fraction) for fraction in ZONE_FRACTIONS] labels = [f"0–{boundaries[0] - 1}"] labels += [f"{a}–{b - 1}" for a, b in pairwise(boundaries)] labels += [f"{boundaries[-1]}+"] return { "boundaries": boundaries, "source": source, "max_hr": maximum, "mode": mode, "labels": labels, "config": config, }