Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,5 @@ _scripts
_cov_html
secrets.json
tokens.json
TODO.mdø

69 changes: 67 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ try:
client.authenticate()

# Make a request (e.g., get user profile)
# You can access resources directly:
profile = client.user.get_profile()
# Or use method aliases for shorter syntax:
profile = client.get_profile()
print(dumps(profile, indent=2))

except Exception as e:
Expand All @@ -59,6 +62,29 @@ The response will always be the body of the API response, and is almost always a
`Dict`, `List` or `None`. `nutrition.get_activity_tcx` is the exception. It
returns XML (as a `str`).

## Method Aliases

All resource methods are available directly from the client instance. This means
you can use:

```python
# Short form with method aliases
client.get_profile()
client.get_daily_activity_summary(date="2025-03-06")
client.get_sleep_log_by_date(date="2025-03-06")
```

Instead of the longer form:

```python
# Standard resource access
client.user.get_profile()
client.activity.get_daily_activity_summary(date="2025-03-06")
client.sleep.get_sleep_log_by_date(date="2025-03-06")
```

Both approaches are equivalent, but aliases provide a more concise syntax.

## Authentication

Uses a local callback server to automatically handle the OAuth2 flow:
Expand All @@ -69,6 +95,8 @@ client = FitbitClient(
client_secret="YOUR_CLIENT_SECRET",
redirect_uri="YOUR_REGISTERED_REDIRECT_URI",
token_cache_path="/tmp/fb_tokens.json" # Optional: saves tokens between sessions
redirect_uri="YOUR_REGISTERED_REDIRECT_URI",
token_cache_path="/tmp/fb_tokens.json" # Optional: saves tokens between sessions
)

# Will open browser and handle callback automatically
Expand All @@ -79,15 +107,45 @@ The `token_cache_path` parameter allows you to persist authentication tokens
between sessions. If provided, the client will:

1. Load existing tokens from this file if available (avoiding re-authentication)

2. Save new or refreshed tokens to this file automatically
3. Handle token refresh when expired tokens are detected

3. Handle token refresh when expired tokens are detected The `token_cache_path`
parameter allows you to persist authentication tokens between sessions. If
provided, the client will:

4. Load existing tokens from this file if available (avoiding re-authentication)

5. Save new or refreshed tokens to this file automatically

6. Handle token refresh when expired tokens are detected

## Setting Up Your Fitbit App

1. Go to dev.fitbit.com and create a new application
2. Set OAuth 2.0 Application Type to "Personal"
3. Set Callback URL to "https://localhost:8080" (or your preferred local URL)
4. Copy your Client ID and Client Secret
4. Set Callback URL to "https://localhost:8080" (or your preferred local URL)
5. Copy your Client ID and Client Secret

## Additional Documentation

### For API Library Users

- [LOGGING.md](docs/LOGGING.md): Understanding the dual-logger system
- [TYPES.md](docs/TYPES.md): JSON type system and method return types
- [NAMING.md](docs/NAMING.md): API method naming conventions
- [VALIDATIONS.md](docs/VALIDATIONS.md): Input parameter validation
- [ERROR_HANDLING.md](docs/ERROR_HANDLING.md): Exception hierarchy and handling

It's also worth reviewing
[Fitbit's Best Practices](https://dev.fitbit.com/build/reference/web-api/developer-guide/best-practices/)
for API usage.

### Project Best Practices

- [DEVELOPMENT.md](docs/DEVELOPMENT.md): Development environment and guidelines
- [STYLE.md](docs/STYLE.md): Code style and formatting standards

## Additional Documentation

Expand Down Expand Up @@ -120,6 +178,13 @@ The methods are implemented in comments and should work, but I have not had a
chance to verify them since this requires a publicly accessible server to
receive webhook notifications.

If you're using this library with subscriptions and would like to help test and
implement this functionality, please open an issue or pull request!
[webhook subscriptions](https://dev.fitbit.com/build/reference/web-api/developer-guide/using-subscriptions/).
The methods are implemented in comments and should work, but I have not had a
chance to verify them since this requires a publicly accessible server to
receive webhook notifications.

If you're using this library with subscriptions and would like to help test and
implement this functionality, please open an issue or pull request!

Expand Down
26 changes: 5 additions & 21 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,6 @@

## TODOs:

- Create and Test that all methods have an alias in `Client` and that the
signatures match

- Improve README for end users:

- Add more common use cases examples beyond basic profile retrieval
- Explain token persistence between sessions (DONE)
- Provide overview of available resources/endpoints
- Verify correct callback URI guidance (check if "https://localhost:8080" is
actually the correct/optimal value to recommend)

- ✅ Review and improve all documentation files in docs/ from an end-user
perspective

- ✅ Split NAMING_AND_TYPING.md into TYPES.md and NAMING.md
- ✅ Split VALIDATIONS_AND_EXCEPTIONS.md into VALIDATIONS.md and
ERROR_HANDLING.md
- ✅ Update cross-references between documentation files
- ✅ Fix intraday data support information in DEVELOPMENT.md
- ✅ Add information about disabling data logging to LOGGING.md

- PyPi deployment

- For all `create_...`methods, add the ID from the response to logs and maybe
Expand All @@ -38,6 +17,11 @@
- Rename to `_base`? Files it first, makes it clearer that everything in it is
private

- client.py:

- Creat and Test that all methods have an alias in `Client` and that the
signatures match

- CI:

* Read and implement:
Expand Down
60 changes: 60 additions & 0 deletions docs/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
- [Logging System](#logging-system)
- [Application Logger](#application-logger)
- [Data Logger](#data-logger)
- [API Design](#api-design)
- [Resource-Based API](#resource-based-api)
- [Method Aliases](#method-aliases)
- [Testing](#testing)
- [Test Organization](#test-organization)
- [Standard Test Fixtures](#standard-test-fixtures)
Expand Down Expand Up @@ -235,6 +238,63 @@ Data log entries contain:
This logging system provides both operational visibility through the application
logger and structured data capture through the data logger.

## API Design

The client implements a dual-level API design pattern that balances both
organization and ease-of-use.

### Resource-Based API

The primary API structure is resource-based, organizing related endpoints into
dedicated resource classes:

- `client.user` - User profile and badges endpoints
- `client.activity` - Activity tracking, goals, and summaries
- `client.sleep` - Sleep logs and goals
- etc.

This organization provides a clean separation of concerns and makes the code
more maintainable by grouping related functionality.

### Method Aliases

To improve developer experience, all resource methods are also available
directly from the client instance through aliases. This means developers can
choose between two equivalent approaches:

```python
# Standard resource-based access
client.user.get_profile()
client.activity.get_daily_activity_summary(date="2025-03-06")

# Direct access via method aliases
client.get_profile()
client.get_daily_activity_summary(date="2025-03-06")
```

#### Rationale for Method Aliases

Method aliases were implemented for several important reasons:

1. **Reduced Verbosity**: Typing `client.resource_name.method_name(...)` with
many parameters can be tedious, especially when used frequently.

2. **Flatter API Surface**: Many modern APIs prefer a flatter design that avoids
deep nesting, making the API more straightforward to use.

3. **Method Name Uniqueness**: All resource methods in the Fitbit API have
unique names (e.g., there's only one `get_profile()` method), making it safe
to expose these methods directly on the client.

4. **Preserve Both Options**: By maintaining both the resource-based access and
direct aliases, developers can choose the approach that best fits their needs
\- organization or conciseness.

All method aliases are set up in the `_setup_method_aliases()` method in the
`FitbitClient` class, which is called during initialization. Each alias is a
direct reference to the corresponding resource method, ensuring consistent
behavior regardless of how the method is accessed.

## Testing

The project uses pytest for testing and follows a consistent testing approach
Expand Down
Loading