47 lines
1.6 KiB
Python
47 lines
1.6 KiB
Python
"""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,
|
||
}
|