Danny Cunningham π π€ |
Jerome Lelong π |
Jonatan Samoocha π» π π π§ |
Leah Wasser π» π π |
Yotam π |
Γmile Nadeau π» π π π§ |
{py:func}\`package.module.function\`,
* {py:func}\`package.module.Class.method\`,
* {py:class}\`package.module.class\`, or
* {py:mod}\`package.module\`.
Sphinx will create a link to the automatically generated page for that
function/class/module.
### About the documentation CI build
Once you create a pull request, GitHub actions will build the docs and
check for any syntax or url errors. Once the PR is approved and merged into the main branch of the `stravalib/stravalib`
repository, the docs will build and be [available at the readthedocs website](https://stravalib.readthedocs.io/en/latest/).
### Cleanup of documentation and package build files
To clean up all documentation build folders and files, run the following
command from the root of the `stravalib` directory:
```bash
$ nox -s clean-docs
```
To clean up build files such as the package **.whl**, and other temporary files
created when building `stravalib` distributions and running tests, run:
```bash
$ nox -s clean_build
```
stravalib-2.2/docs/contributing/how-to-contribute.md 0000664 0000000 0000000 00000000044 14751741554 0022731 0 ustar 00root root 0000000 0000000
```{include} ../../CONTRIBUTING.md
stravalib-2.2/docs/contributing/inheritance.md 0000664 0000000 0000000 00000007433 14751741554 0021642 0 ustar 00root root 0000000 0000000 # Stravalib's Inheritance Patterns
Stravalib's API is built directly from Strava's `swagger.json` example response
data. We use the Pydantic `BaseModel` combined with `datacodegen` to build our
base model objects (found in `strava_model.py`), which we then enhance and
update to align with the data actually returned by the Strava API.
This page will help you understand Stravalib's inheritance patterns.
In our experience maintaining Stravalib, the Strava API online specification
and the `swagger.json` file don't always align perfectly with the data actually
returned by Strava. As a result, you will notice that we frequently overwrite
data types to match the actual data we see returned from the Strava API for
each endpoint. [More information on this process can be found here.](ci_api_updates)
Below, we present the inheritance schema for Stravalib.
## Inheritance overview
At a high level, there are two modules:
* {py:mod}`stravalib.model` and
* {py:mod}`stravalib.strava_model`.
`strava_model` is generated automatically using a CI build from the Strava API.
Stravalib uses the model.py module to do a few things:
1. It supports inheritance of the {py:class}`stravalib.model.BoundClientEntity`, which supports API calls for lazily loaded properties,
2. it allows us to override {py:mod}`stravalib.strava_model` attributes that have typed attributes that don't align with the actual API responses, and
3. allows us to add attributes that are found in the returned data but not documented or found in the swagger.json response.
The full inheritance pattern, which includes inheritance from both `strava_model` and `pydantic.BaseModel`, is below.
### Inheritance diagram showing the relationship between strava_model.py and model.py
:::{mermaid}
classDiagram
direction BT
%% Activities in model namespace
`model.MetaActivity` --|> `model.BoundClientEntity`
`model.SummaryActivity` --|> `model.MetaActivity`
`model.DetailedActivity` --|> `model.SummaryActivity`
%% Activities in strava_model namespace
`strava_model.SummaryActivity` --|> `strava_model.MetaActivity`
`strava_model.DetailedActivity` --|> `strava_model.SummaryActivity`
%% Define inheritance relationships between model and strava_model
`model.MetaActivity` --|> `strava_model.MetaActivity`
`model.SummaryActivity` --|> `strava_model.SummaryActivity`
`model.DetailedActivity` --|> `strava_model.DetailedActivity`
:::
## Model object inheritance patterns
The {py:mod}`stravalib.model` module contains core objects that inherit
from and modify objects in {py:mod}`strava_model.py`. The `strava_model.py`
file is generated directly from the Strava `swagger.json` API response.
Our main client class provides methods for making API `GET` and `PUT`
requests. To support these requests and enable lazily loaded operations, all
Strava model meta-level objects inherit from `BoundClientEntity`, which stores the token
credentials needed for authenticated API calls. This ensures that summary and detailed-level objects can also support lazily loaded operations.
### Inheritance diagram showing the relationship between Detailed, Summary and Meta classes and BoundClientEntity
:::{mermaid}
classDiagram
direction BT
%% Activities
MetaActivity --|> BoundClientEntity
SummaryActivity --|> MetaActivity
DetailedActivity --|> SummaryActivity
%% Athletes
MetaAthlete --|> BoundClientEntity
SummaryAthlete --|> MetaAthlete
DetailedAthlete --|> SummaryAthlete
%% Clubs
MetaClub --|> BoundClientEntity
SummaryClub --|> MetaClub
DetailedClub --|> SummaryClub
:::
stravalib-2.2/docs/contributing/intro.md 0000664 0000000 0000000 00000002343 14751741554 0020477 0 ustar 00root root 0000000 0000000 # Contribute to stravalib
We welcome contributions of all kinds to stravalib! Below are
some resources to help you get started.
::::{grid} 1 1 1 2
:class-container: text-center
:gutter: 3
:::{grid-item-card}
:link: how-to-contribute
:link-type: doc
β¨ **Contributing Guide** β¨
^^^
Contributing guidelines for stravalib.
:::
:::{grid-item-card}
:link: development-guide
:link-type: doc
β¨ **Development Guide** β¨
^^^
Learn about our development infrastructure and workflows.
:::
:::{grid-item-card}
:link: build-release-guide
:link-type: doc
β¨ **Build & Release Guide** β¨
^^^
Learn about our build and release workflow. We use version control based versioning.
:::
:::{grid-item-card}
:link: resources-for-new-contributors
:link-type: doc
β¨ **Contributor Resources** β¨
^^^
If you are new to contributing to open source software, these resources will help you get started.
:::
:::{toctree}
:hidden:
:caption: Contributing
:maxdepth: 2
Contributing Guide <../contributing/how-to-contribute>
Development Guide <../contributing/development-guide>
Inheritance <../contributing/inheritance>
Build & Release Guide <../contributing/build-release-guide>
New Contributor Resources <../contributing/resources-for-new-contributors>
:::
stravalib-2.2/docs/contributing/resources-for-new-contributors.md 0000664 0000000 0000000 00000002510 14751741554 0025460 0 ustar 00root root 0000000 0000000 # Contributor resources
## Contributing Code
**Is this your first contribution?**
Please take a look at these resources to learn about git and pull requests (don't
hesitate to ask questions:
* [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/).
* Aaron Meurer's [tutorial on the git workflow](https://www.asmeurer.com/git-workflow/)
* [How to Contribute to an Open Source Project on GitHub](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github)
If you're new to working with git, GitHub, and the Unix Shell, we recommend
starting with the [Software Carpentry](https://software-carpentry.org/) lessons,
which are available in English and Spanish:
* [Version Control with Git](https://swcarpentry.github.io/git-novice/) / spanish: [Control de
versiones con Git](https://swcarpentry.github.io/git-novice-es/)
* [The Unix Shell](https://swcarpentry.github.io/shell-novice/) / spanish:
[La Terminal de Unix](https://swcarpentry.github.io/shell-novice-es/)
## Additional contribution resources
For more information on contributing to open source projects, checkout:
* [GitHub's contribution guide](https://docs.github.com/en)
is a great starting point if you are new to version control.
* The [Zen of Scientific Software Maintenance](https://jrleeman.github.io/ScientificSoftwareMaintenance/)
stravalib-2.2/docs/get-started/ 0000775 0000000 0000000 00000000000 14751741554 0016534 5 ustar 00root root 0000000 0000000 stravalib-2.2/docs/get-started/activities.md 0000664 0000000 0000000 00000021031 14751741554 0021217 0 ustar 00root root 0000000 0000000 (activities)=
# Get Strava activity data
This page overviews working with your Strava activity data using the stravalib Python library.
## Retrieve an activity
To access data for a given activity, use the `client.get_activity` method and provide the `activity_id`.
The `client.get_activity` method returns a {py:class}`stravalib.model.DetailedActivity` object.
:::{note}
All of the commands below require you first to authenticate using a client object containing a token.
`from stravalib.client import Client`
:::
```{code-block} pycon
# This command assumes that you have already authenticated the client object.
>>> activity = client.get_activity(1234)
>>> type(activity)
Figure from the Strava documentation showing the application settings. You can
name your application whatever you wish. You can add any website URL if you are grabbing your data locally. For this tutorial, use `localhost` for the Authorization Callback
Domain if you are only using this locally. Otherwise, you likely have a website
URL that you will enter here.
:::
:::{figure-md} fig-target
:class: myclass
Figure from the Strava documentation showing the application settings. This screenshot shows the final application you should have once you follow the
steps above. Notice that you can see the access token scope and expiration time and date on the app page. These are values that you will work with below. Copy the **Client ID** and **Client secret** to a file
(see below for more).
:::
:::{important}
Remember to store your client ID and client secret values somewhere safe.
Do not EVER commit secrets or token values to `.git` or push it to GitHub (unless you have
encrypted it)!
:::
### Save your secret and access token values in a text file
Notice that there is a client secret (hidden
in the screenshot) and a token in the first screenshot above. Next, you will copy both the secret and the token to use (and reuse) in your code.
Do the following:
* Create a `client_secrets.txt` file.
* Add the client secret and access token values on a single line separated
by a comma in the format that you see below:
`secret_value_here, access_token_value_here`
:::{note}
The format described above is not required for a Strava workflow. This is just an example of how to store relevant information that you will need to reuse if you intent to update the data in your workflow regularly. If you are creating a web application, you should store this information
securely somewhere in your application's database or web infrastructure.
:::
## Step 2: Setup Strava authentication
Once you have created your `client_secrets.txt` file, you are ready to setup
authentication using **Python**.
At the top of your code, import the {py:class}`stravalib.client.Client` class.
In this tutorial, you will also import `webbrowser` to launch a web browser from your code to
login to Strava. `webbrowser` behaves similarly to a web application.
```python
# Use webbrowser to launch a browser from Python
import webbrowser
import json
from stravalib.client import Client
```
Next, read the `client_id` and `access_token` from the `client_secrets.txt` file that you created earlier. Then, create a **stravalib** `Client()` object.
Below, the `client_id` and `access_token` are read from the client_secrets.txt file. The `.strip()` method removes any extra spaces, and `.split(",")` separates the two values. You then create a `Client()` object from stravalib, which will be used to interact with the Strava API and manage your data.
```python
# Read the client_id and access_token from the secrets file
client_id, access_token = open("client_secrets.txt").read().strip().split(",")
# Create a stravalib Client() object
client = Client()
```
The `Client()` object is what you'll use to interact with Strava. It stores your authentication details and provides methods to:
* Retrieve different types of data from Strava, such as activities and club data.
* Modify activity and club data on Strava (if you have a token with write permissions).
You will learn more about read vs. write token scopes below.
## Step 3: Login and authorize Strava to interact with your code
Now that your developer app is created, it's time to authenticate with Strava.
1. You have the client secret and token from your Strava account's developer app, and
2. You have the `Client()` object from **stravalib**.
### Create a URL to Authenticate and Define the Token Scope
Next, you'll define the scope of your authentication (what permissions you need) and set up a redirect URL.
1. **Define the Scope**:
The scope determines what permissions your app will have when interacting with Strava. If you only need to **read** your data, a read-only scope is enough. However, if you want to **write** data (e.g., upload or create activities), you'll need to request write permissions.
2. **Set a Redirect URL**:
After you log in to Strava and approve the scope permissions, Strava will redirect you to a specific URL. This is the `redirect_url` you need to define. If you're just authenticating on your local machine to access your own data, you can use something simple like `localhost` for the `redirect_url`.
```{tip}
If you are building an app, then your redirect URL might be the
URL of your app.
```
### Define the Scope for Strava API access
When working with Strava's API, you must define your application's permissions, or **scope**. The scope determines what your app can access or modify. If you only want to **download** your data, you can use **read-only** permissions.
#### Example scope values for read-only access
The example below shows a Python list that defines read-only access. This scope allows your application to access a user's profile and activity data without modifying anything:
```python
# Read-only scope values
request_scope = ["read_all", "profile:read_all", "activity:read_all"]
```
#### Example scope values for read and write access
You must include a "write" scope if you need to modify and upload data to Strava (e.g., creating or modifying activities). Hereβs an example of a scope that includes both read and write permissions:
```python
# Read and write scope values
request_scope = ["read_all", "profile:read_all", "activity:write", "activity:read_all"]
```
The `activity:write` scope allows your app to upload or modify activity data to Strava.
:::{tip}
[Learn more about request scope options from the Strava documentation here.](https://developers.strava.com/docs/authentication/#details-about-requesting-access)
:::
In this tutorial, you will limit your scope to **read-only** as you are only
looking at data in this tutorial rather than modifying it.
You also set the `redirect_url` to **localhost** (to be opened on your computer
locally) URL:
```python
# Create a localhost URL
redirect_url = "http://127.0.0.1:5000/authorization"
# Define a read-only scope
request_scope = ["read_all", "profile:read_all", "activity:read_all"]
# Create an authorization URL using stravalib
url = client.authorization_url(
client_id=client_id,
redirect_uri=redirect_url,
scope=request_scope,
)
```
Your URL will look something like this:
```python
print(url)
# 'https://www.strava.com/oauth/authorize?client_id=123456&redirect_uri=http%3A%2F%2F127.0.0.1%3A5000%2Fauthorization&approval_prompt=auto&response_type=code&scope=read_all%2Cprofile%3Aread_all%2Cactivity%3Aread_all'
```
### Authenticate with Strava using Python
Now that you've created your Strava authentication URL, you're ready to use it to get permission to access data from the Strava API.
In this section, you'll use Python's `webbrowser.open` method to:
1. Open the URL directly from your Python code in a web browser.
2. Authenticate with Strava (login and allow your app to access the developer app you created earlier).
3. Strava will redirect you to a "page not found" screen with a long URL after authentication. This might look like an error, but it's correct! The URL contains the code that you need to complete authentication.
The URL will look something like this:
```text
http://127.0.0.1:5000/authorization?state=&code=xxxxxxxxxxx&scope=read
```
4. Copy the value between **code** and the **&** symbol. The `xxxxxxxxxxx` in the example represents the code you'll need to authenticate with the API.
Now you're ready to use this code to authenticate!
```python
# Open the url that you created above in a web browser
webbrowser.open(url)
print(
"""You will see a URL that looks like this:
http://127.0.0.1:5000/authorization?state=&code=12323423423423423423423550&scope=read,activity:read_all,profile:read_all,read_all
Copy the values between code= and & in the URL that you see in the browser."""
)
# Using input allows you to copy the code into your Python console (or Jupyter Notebook)
code = input("Please enter the code that you received: ")
print(
f"Great! Your code is {code}\n"
"Next, I will exchange that code for a token.\n"
"I only have to do this once."
)
```
You only need to get a code from Strava once.
Next, you exchange the code for an access token. After this, you can refresh the token as often as needed to continue accessing your data.
In the example below, you'll exchange the code for a token using {py:func}`stravalib.client.Client.exchange_code_for_token()`. The token response contains both an access token (valid for 6 hours) and a refresh token, which you will use to get a new access token once the current one expires.
You can save the token response as a `.json` file so that you can reuse the refresh token later without going through the authentication process again.
```python
token_response = client.exchange_code_for_token(
client_id=client_id, client_secret=client_secret, code=code
)
# Save the token response as a JSON file
with open(json_path, "w") as f:
json.dump(token_response, f)
print("Token saved - hooray!")
# Access and refresh tokens
access_token = token_response["access_token"]
refresh_token = token_response["refresh_token"] # Use this after 6 hours
```
## Step 4: Use and refresh your Strava API token
Strava provides you with an access token that is good for 6 hours. You can refresh the token as needed if you wish to grab
more data after that 6-hour window has ended.
You can refresh the token using the
{py:func}`stravalib.client.Client.refresh_access_token()` method.
Earlier, you saved the `refresh_token` and `expires_at` values to a JSON file. You can use the refresh token stored in the JSON file to refresh your token for another 6 hours of use as many times as needed.
This might be most useful if you try to process your data again--for example, the next day and you don't have the refresh token value stored and accessible to Python.
```python
# Open the token JSON file that you saved earlier
with open(json_path, "r") as f:
token_response_refresh = json.load(f)
print(token_response_refresh)
# Output:
# {'access_token': 'ab0667a99d17b7c278d9f730f733ad09016306cf',
# 'refresh_token': '9f8d5689c93e83c7b0c69a8585010d4762e8b2ac',
# 'expires_at': 1726560054}
```
```python
refresh_response = client.refresh_access_token(
client_id=client_id, # Stored in the secrets.txt file above
client_secret=client_secret,
refresh_token=refresh_token, # Stored in your JSON file
)
# Check that the refresh worked
client.get_athlete()
# View the newly refreshed token
print(client.access_token)
```
## Wrap up
You now know how to:
1. Setup an app within your Strava account
2. Connect that app to your Python code using stravalib
3. Request and refresh a token that allows you to request and update data in your Strava account
stravalib-2.2/docs/get-started/index.md 0000664 0000000 0000000 00000003347 14751741554 0020174 0 ustar 00root root 0000000 0000000 # Get Started Using Stravalib
```{toctree}
:hidden:
:caption: Get Started
Install Stravalib