Skip to content

Commit

Permalink
updated docs and enchanced docstring test (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
jpstroop authored Mar 4, 2025
1 parent 7ffb909 commit 21b30d6
Show file tree
Hide file tree
Showing 26 changed files with 3,104 additions and 1,268 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ Additional documentation:
- To understand the logging implemementation, see [LOGGING](docs/LOGGING.md)
- To understand validations and the exception hierarchy, see
[VALIDATIONS_AND_EXCEPTIONS](docs/VALIDATIONS_AND_EXCEPTIONS.md)
- It's work checking out
[Fitbit's Best Practices](https://dev.fitbit.com/build/reference/web-api/developer-guide/best-practices/)
- For some general development guidelines, see
[DEVELOPMENT](docs/DEVELOPMENT.md).
- For style guidelines (mostly enforced through varius linters and formatters)
Expand Down
39 changes: 8 additions & 31 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@

- Creat and Test that all methods have an alias in `Client` and that the
signatures match
- Make sure that `ClientValidationException` is getting used for arbitrary
validations like:

```python
if not food_id and not (food_name and calories):
Expand All @@ -45,41 +43,20 @@ if not food_id and not (food_name and calories):
params[str(key)] = float(value)
```

It needs to change to:

```python
for key, value in nutritional_values.items():
if isinstance(key, NutritionalValue):
if key == NutritionalValue.CALORIES_FROM_FAT:
params[key.value] = int(value)
else:
params[key.value] = float(value)
else:
params[str(key)] = float(value)
```

see: test_create_food_calories_from_fat_must_be_integer(nutrition_resource)

- exceptions.py

- Should ClientValidationException really subclass FitbitAPIException? It
doesn't need the API lookup mapping (`exception_type`) or a `status_code`,
so we may just be able to simplify it. The most important thing is that the
user understands that the message came from the client prior to the API
call.

- Resource docstrings/documentation:
- Should ClientValidationException really subclass FitbitAPIException? IT
SHOULD SUBCLASS ValueError doesn't need the API lookup mapping
(`exception_type`) or a `status_code`, so we may just be able to simplify
it. The most important thing is that the user understands that the message
came from the client prior to the API call.

- Update the list of exceptions that are raised in the doctring for
`base._make_request`
- Review all documentation and between **API docs, return types, and
exceptions**, update the doctrings
- Update "Returns" in all resource docstrings
- Review and add link to
https://dev.fitbit.com/build/reference/web-api/developer-guide/best-practices/
in README--they still apply!
- Make sure we aren't using

- Set up CI!
- Make sure that `ClientValidationException` is getting used for arbitrary
validations like

## Longer term TODOs

Expand Down
84 changes: 52 additions & 32 deletions fitbit_client/resources/active_zone_minutes.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,44 +14,62 @@


class ActiveZoneMinutesResource(BaseResource):
"""
Handles Fitbit Active Zone Minutes (AZM) API endpoints for retrieving user's
heart-pumping activity data throughout the day.
"""Provides access to Fitbit Active Zone Minutes (AZM) API for heart rate-based activity metrics.
This resource handles endpoints for retrieving Active Zone Minutes (AZM) data, which measures
the time users spend in target heart rate zones during exercise or daily activities. AZM
is a scientifically-validated way to track activity intensity based on personalized heart
rate zones rather than just steps.
Active Zone Minutes (AZM) measure the time spent in target heart rate zones.
Different zones contribute differently to the total AZM count:
- Fat Burn zone: 1 minute = 1 AZM
- Cardio zone: 1 minute = 2 AZM
- Peak zone: 1 minute = 2 AZM
- Fat Burn zone: 1 minute = 1 AZM (moderate intensity)
- Cardio zone: 1 minute = 2 AZM (high intensity)
- Peak zone: 1 minute = 2 AZM (maximum effort)
API Reference: https://dev.fitbit.com/build/reference/web-api/active-zone-minutes-timeseries/
Required Scopes:
- activity (for all AZM endpoints)
Note:
- Heart rate zones are personalized based on the user's resting heart rate and age
- The American Heart Association recommends 150 minutes of moderate (Fat Burn) or
75 minutes of vigorous (Cardio/Peak) activity per week
- AZM data is available from the date the user first set up their Fitbit device
- Historical data older than 3 years may not be available through the API
- Not all Fitbit devices support AZM tracking (requires heart rate monitoring)
- The date range endpoints are useful for analyzing weekly and monthly AZM totals
"""

@validate_date_param()
def get_azm_timeseries_by_date(
self, date: str, period: Period = Period.ONE_DAY, user_id: str = "-", debug: bool = False
) -> JSONDict:
"""
Get Active Zone Minutes time series data for a period starting from the specified date.
"""Returns Active Zone Minutes time series data for a period ending on the specified date.
API Reference: https://dev.fitbit.com/build/reference/web-api/active-zone-minutes-timeseries/get-azm-timeseries-by-date/
Args:
date: The end date of the period in yyyy-MM-dd format or 'today'
date: The end date of the period in YYYY-MM-DD format or 'today'
period: The range for which data will be returned. Only Period.ONE_DAY (1d) is supported.
user_id: The encoded ID of the user. Use "-" (dash) for current logged-in user.
debug: If True, a prints a curl command to stdout to help with debugging (default: False)
user_id: Optional user ID, defaults to current user ("-")
debug: If True, prints a curl command to stdout to help with debugging (default: False)
Returns:
Daily AZM data including:
- activeZoneMinutes: Total count of active zone minutes
- fatBurnActiveZoneMinutes: Minutes in fat burn zone (1 minute = 1 AZM)
- cardioActiveZoneMinutes: Minutes in cardio zone (1 minute = 2 AZM)
- peakActiveZoneMinutes: Minutes in peak zone (1 minute = 2 AZM)
JSONDict: Daily Active Zone Minutes data
Raises:
ValueError: If period is not Period.ONE_DAY
InvalidDateException: If date format is invalid
fitbit_client.exceptions.InvalidDateException: If date format is invalid
Note:
- Only Period.ONE_DAY (1d) is currently supported by the Fitbit API
- activeZoneMinutes is the sum total of all zone minutes with cardio and peak
minutes counting double (fatBurn + (cardio × 2) + (peak × 2))
- Fat burn zone is typically 50-69% of max heart rate (moderate intensity)
- Cardio zone is typically 70-84% of max heart rate (high intensity)
- Peak zone is typically 85%+ of max heart rate (maximum effort)
- Days with no AZM data will show all metrics as zero
"""
if period != Period.ONE_DAY:
raise ValueError("Only 1d period is supported for AZM time series")
Expand All @@ -67,30 +85,32 @@ def get_azm_timeseries_by_date(
def get_azm_timeseries_by_interval(
self, start_date: str, end_date: str, user_id: str = "-", debug: bool = False
) -> JSONDict:
"""
Get Active Zone Minutes time series data for a specified date range.
"""Returns Active Zone Minutes time series data for a specified date range.
API Reference: https://dev.fitbit.com/build/reference/web-api/active-zone-minutes-timeseries/get-azm-timeseries-by-interval/
Args:
start_date: The start date in yyyy-MM-dd format or 'today'
end_date: The end date in yyyy-MM-dd format or 'today'
user_id: The encoded ID of the user. Use "-" (dash) for current logged-in user.
debug: If True, a prints a curl command to stdout to help with debugging (default: False)
start_date: The start date in YYYY-MM-DD format or 'today'
end_date: The end date in YYYY-MM-DD format or 'today'
user_id: Optional user ID, defaults to current user ("-")
debug: If True, prints a curl command to stdout to help with debugging (default: False)
Returns:
Daily AZM data for each date in the range including:
- activeZoneMinutes: Total count of active zone minutes
- fatBurnActiveZoneMinutes: Minutes in fat burn zone (1 minute = 1 AZM)
- cardioActiveZoneMinutes: Minutes in cardio zone (1 minute = 2 AZM)
- peakActiveZoneMinutes: Minutes in peak zone (1 minute = 2 AZM)
JSONDict: Daily Active Zone Minutes data for each date in the range
Raises:
InvalidDateException: If date format is invalid
InvalidDateRangeException: If date range is invalid or exceeds 1095 days
fitbit_client.exceptions.InvalidDateException: If date format is invalid
fitbit_client.exceptions.InvalidDateRangeException: If date range is invalid or exceeds 1095 days
Note:
Maximum date range is 1095 days (approximately 3 years)
- Maximum date range is 1095 days (approximately 3 years)
- Each day's entry includes separate counts for each heart rate zone
- activeZoneMinutes is the total AZM with cardio and peak minutes counting double
- This endpoint is useful for calculating weekly or monthly AZM totals
- Days with no AZM data will have all metrics as zero
- Active Zone Minutes does not support subscription notifications (webhooks),
but can be queried after activity notifications arrive
- Weekly AZM goals can be tracked by summing 7 consecutive days of data
"""
result = self._make_request(
f"activities/active-zone-minutes/date/{start_date}/{end_date}.json",
Expand Down
Loading

0 comments on commit 21b30d6

Please sign in to comment.