sweatstack 0.11.1__py3-none-any.whl → 0.12.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sweatstack/cli.py +2 -2
- sweatstack/client.py +461 -441
- sweatstack/jupyterlab_oauth2_startup.py +5 -0
- sweatstack/openapi_schemas.py +368 -0
- sweatstack/py.typed +0 -0
- sweatstack/schemas.py +3 -229
- sweatstack/utils.py +13 -0
- sweatstack-0.12.0.dist-info/METADATA +85 -0
- sweatstack-0.12.0.dist-info/RECORD +14 -0
- {sweatstack-0.11.1.dist-info → sweatstack-0.12.0.dist-info}/WHEEL +1 -1
- sweatstack/Sweat Stack examples/Getting started.ipynb +0 -28784
- sweatstack/plotting.py +0 -251
- sweatstack-0.11.1.dist-info/METADATA +0 -359
- sweatstack-0.11.1.dist-info/RECORD +0 -13
- {sweatstack-0.11.1.dist-info → sweatstack-0.12.0.dist-info}/entry_points.txt +0 -0
sweatstack/plotting.py
DELETED
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
from typing import List, Tuple, Union
|
|
2
|
-
from datetime import date
|
|
3
|
-
|
|
4
|
-
import plotly.graph_objects as go
|
|
5
|
-
from plotly.subplots import make_subplots
|
|
6
|
-
|
|
7
|
-
from .schemas import Metric, Sport
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
METRIC_LABELS = {
|
|
11
|
-
"power": "power [Watt]",
|
|
12
|
-
"heart_rate": "heart rate [bpm]",
|
|
13
|
-
"speed": "speed [m/s]",
|
|
14
|
-
"cadence": "cadence [/min])",
|
|
15
|
-
"distance": "distance [m]",
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
def get_metric_label(metric):
|
|
19
|
-
match metric:
|
|
20
|
-
case "power":
|
|
21
|
-
return "power [Watt]"
|
|
22
|
-
case "heart_rate":
|
|
23
|
-
return "heart rate [bpm]"
|
|
24
|
-
case "speed":
|
|
25
|
-
return "speed [m/s]"
|
|
26
|
-
case "cadence":
|
|
27
|
-
return "cadence [/min])"
|
|
28
|
-
case "distance":
|
|
29
|
-
return "distance [m]"
|
|
30
|
-
case "duration":
|
|
31
|
-
return "duration [s]"
|
|
32
|
-
case _:
|
|
33
|
-
return metric
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
METRIC_COLORS = {
|
|
37
|
-
"power": "blue",
|
|
38
|
-
"heart_rate": "red",
|
|
39
|
-
"speed": "green",
|
|
40
|
-
"cadence": "orange",
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
plotting_backend = "plotly"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
class PlottingMixin:
|
|
48
|
-
def _get_plotting_backend(self):
|
|
49
|
-
global plotting_backend
|
|
50
|
-
match plotting_backend:
|
|
51
|
-
case "plotly":
|
|
52
|
-
return PlotlyPlottingBackend()
|
|
53
|
-
case _:
|
|
54
|
-
raise ValueError(f"Unsupported plotting backend: {plotting_backend}")
|
|
55
|
-
|
|
56
|
-
def plot_activity_data(self, activity_id, metrics=None, subplots=True):
|
|
57
|
-
plotting_backend = self._get_plotting_backend()
|
|
58
|
-
|
|
59
|
-
activity_data = self.get_activity_data(activity_id)
|
|
60
|
-
|
|
61
|
-
return plotting_backend.plot_activity_data(activity_data, metrics, subplots)
|
|
62
|
-
|
|
63
|
-
def plot_latest_activity_data(self, metrics=None, subplots=True):
|
|
64
|
-
latest_activity = self.get_latest_activity()
|
|
65
|
-
return self.plot_activity_data(latest_activity.id, metrics, subplots)
|
|
66
|
-
|
|
67
|
-
def plot_scatter(self, x, y, x_label=None, y_label=None):
|
|
68
|
-
plotting_backend = self._get_plotting_backend()
|
|
69
|
-
return plotting_backend.plot_scatter(
|
|
70
|
-
x=x,
|
|
71
|
-
y=y,
|
|
72
|
-
x_label=x_label,
|
|
73
|
-
y_label=y_label
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
def plot_mean_max(
|
|
77
|
-
self,
|
|
78
|
-
*,
|
|
79
|
-
sport: Union[Sport, str],
|
|
80
|
-
metric: Union[Metric, str],
|
|
81
|
-
start: Union[date, str] = None,
|
|
82
|
-
end: Union[date, str] = None,
|
|
83
|
-
windows: List[Tuple[Union[date, str], Union[date, str]]] = None,
|
|
84
|
-
):
|
|
85
|
-
if (start is not None or end is not None) and windows is not None:
|
|
86
|
-
raise ValueError("Cannot specify both start/end and windows")
|
|
87
|
-
|
|
88
|
-
if windows is None:
|
|
89
|
-
windows = [(start, end)]
|
|
90
|
-
|
|
91
|
-
data = []
|
|
92
|
-
for start, end in windows:
|
|
93
|
-
data.append(
|
|
94
|
-
(
|
|
95
|
-
self.get_mean_max(
|
|
96
|
-
sport=sport,
|
|
97
|
-
metric=metric,
|
|
98
|
-
start=start,
|
|
99
|
-
end=end,
|
|
100
|
-
),
|
|
101
|
-
start,
|
|
102
|
-
end,
|
|
103
|
-
)
|
|
104
|
-
)
|
|
105
|
-
plotting_backend = self._get_plotting_backend()
|
|
106
|
-
return plotting_backend.plot_mean_max(data, metric)
|
|
107
|
-
|
|
108
|
-
class BasePlottingBackend:
|
|
109
|
-
def plot_activity_data(self, data, metrics=None, subplots=True):
|
|
110
|
-
raise NotImplementedError("Subclass must implement abstract method")
|
|
111
|
-
|
|
112
|
-
def plot_scatter(self, x, y, x_label=None, y_label=None):
|
|
113
|
-
raise NotImplementedError("Subclass must implement abstract method")
|
|
114
|
-
|
|
115
|
-
def plot_mean_max(self, data, metric):
|
|
116
|
-
raise NotImplementedError("Subclass must implement abstract method")
|
|
117
|
-
|
|
118
|
-
def preprocess_metrics(self, metrics: List[str], columns: List[str]) -> List[Tuple[str, str, str]]:
|
|
119
|
-
"""
|
|
120
|
-
Preprocess the given metrics.
|
|
121
|
-
|
|
122
|
-
Args:
|
|
123
|
-
metrics (List[str]): A list of metric names.
|
|
124
|
-
|
|
125
|
-
Returns:
|
|
126
|
-
List[Tuple[str, str, str]]: An ordered list of tuples, each containing
|
|
127
|
-
a metric name, its corresponding label, and its color.
|
|
128
|
-
"""
|
|
129
|
-
|
|
130
|
-
if not metrics:
|
|
131
|
-
metrics = [col for col in columns if col not in ["sport", "sub_sport", "lap", "lap_trigger", "duration"]]
|
|
132
|
-
elif missing_metrics := set(metrics) - set(columns):
|
|
133
|
-
raise ValueError(f"The following metrics are not present in the data: {', '.join(missing_metrics)}")
|
|
134
|
-
|
|
135
|
-
priority_metrics = ["power", "heart_rate", "speed", "cadence"]
|
|
136
|
-
existing_priority_metrics = [metric for metric in priority_metrics if metric in metrics]
|
|
137
|
-
remaining_metrics = sorted(col for col in metrics if col not in existing_priority_metrics)
|
|
138
|
-
|
|
139
|
-
sorted_metrics = existing_priority_metrics + remaining_metrics
|
|
140
|
-
|
|
141
|
-
return [(metric, METRIC_LABELS.get(metric, metric), METRIC_COLORS.get(metric, None)) for metric in sorted_metrics]
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
class PlotlyPlottingBackend(BasePlottingBackend):
|
|
145
|
-
def plot_activity_data(self, data, metrics=None, subplots=True):
|
|
146
|
-
metrics = self.preprocess_metrics(metrics, data.columns)
|
|
147
|
-
if subplots:
|
|
148
|
-
fig = make_subplots(
|
|
149
|
-
rows=len(metrics),
|
|
150
|
-
cols=1,
|
|
151
|
-
shared_xaxes=True,
|
|
152
|
-
vertical_spacing=0.02
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
for i, (metric, label, color) in enumerate(metrics, 1):
|
|
156
|
-
params = {}
|
|
157
|
-
if color:
|
|
158
|
-
params["line"] = dict(color=color)
|
|
159
|
-
fig.add_trace(
|
|
160
|
-
go.Scatter(
|
|
161
|
-
x=data.index,
|
|
162
|
-
y=data[metric],
|
|
163
|
-
name=metric,
|
|
164
|
-
**params,
|
|
165
|
-
),
|
|
166
|
-
row=i,
|
|
167
|
-
col=1,
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
fig.update_layout(
|
|
171
|
-
height=600,
|
|
172
|
-
yaxis_title=metrics[0][1],
|
|
173
|
-
hovermode="x unified",
|
|
174
|
-
spikedistance=-1,
|
|
175
|
-
hoverdistance=-1,
|
|
176
|
-
)
|
|
177
|
-
fig.update_yaxes(
|
|
178
|
-
title_text=label,
|
|
179
|
-
row=i,
|
|
180
|
-
col=1,
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
else:
|
|
184
|
-
fig = go.Figure()
|
|
185
|
-
for i, (metric, label, color) in enumerate(metrics):
|
|
186
|
-
params = {}
|
|
187
|
-
if color:
|
|
188
|
-
params["line"] = dict(color=color)
|
|
189
|
-
fig.add_trace(
|
|
190
|
-
go.Scatter(
|
|
191
|
-
x=data.index,
|
|
192
|
-
y=data[metric],
|
|
193
|
-
name=metric,
|
|
194
|
-
yaxis=f"y{i+1}",
|
|
195
|
-
**params,
|
|
196
|
-
)
|
|
197
|
-
)
|
|
198
|
-
if i > 0:
|
|
199
|
-
fig.update_layout({
|
|
200
|
-
f'yaxis{i+1}': {'overlaying': 'y', 'side': 'right', 'title': label, "position": 1 - (i * 0.07), "color": color}
|
|
201
|
-
|
|
202
|
-
})
|
|
203
|
-
else:
|
|
204
|
-
fig.update_layout({
|
|
205
|
-
"yaxis1": {"color": color}
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
fig.update_layout(
|
|
210
|
-
height=600,
|
|
211
|
-
yaxis_title=metrics[0][1],
|
|
212
|
-
xaxis=dict(domain=[0, 1 - (len(metrics) - 1) * 0.07]),
|
|
213
|
-
hovermode="x unified",
|
|
214
|
-
spikedistance=-1,
|
|
215
|
-
hoverdistance=-1,
|
|
216
|
-
)
|
|
217
|
-
|
|
218
|
-
return fig
|
|
219
|
-
|
|
220
|
-
def plot_scatter(self, x, y, x_label=None, y_label=None):
|
|
221
|
-
fig = go.Figure(
|
|
222
|
-
data=go.Scatter(x=x, y=y, mode='markers'),
|
|
223
|
-
layout=go.Layout(
|
|
224
|
-
xaxis_title=x_label if x_label is not None else x.name,
|
|
225
|
-
yaxis_title=y_label if y_label is not None else y.name,
|
|
226
|
-
width=800,
|
|
227
|
-
height=800
|
|
228
|
-
)
|
|
229
|
-
)
|
|
230
|
-
|
|
231
|
-
fig.show()
|
|
232
|
-
|
|
233
|
-
def plot_mean_max(self, data, metric):
|
|
234
|
-
fig = go.Figure()
|
|
235
|
-
for mean_max, start, end in data:
|
|
236
|
-
fig.add_trace(
|
|
237
|
-
go.Scatter(
|
|
238
|
-
x=mean_max.dt.total_seconds(),
|
|
239
|
-
y=mean_max.index,
|
|
240
|
-
name=f"{start if start is not None else ''} - {end if end is not None else ''}",
|
|
241
|
-
mode="lines",
|
|
242
|
-
)
|
|
243
|
-
)
|
|
244
|
-
fig.update_layout(
|
|
245
|
-
xaxis_title=get_metric_label("duration"),
|
|
246
|
-
yaxis_title=get_metric_label(metric),
|
|
247
|
-
xaxis_type="log",
|
|
248
|
-
width=800,
|
|
249
|
-
height=600
|
|
250
|
-
)
|
|
251
|
-
fig.show()
|
|
@@ -1,359 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: sweatstack
|
|
3
|
-
Version: 0.11.1
|
|
4
|
-
Summary: The official Python library for SweatStack
|
|
5
|
-
Requires-Python: >=3.10
|
|
6
|
-
Requires-Dist: pandas
|
|
7
|
-
Requires-Dist: pydantic
|
|
8
|
-
Requires-Dist: requests
|
|
9
|
-
Provides-Extra: dev
|
|
10
|
-
Requires-Dist: datamodel-code-generator; extra == 'dev'
|
|
11
|
-
Provides-Extra: ipython
|
|
12
|
-
Requires-Dist: ipython; extra == 'ipython'
|
|
13
|
-
Requires-Dist: pyarrow; extra == 'ipython'
|
|
14
|
-
Provides-Extra: jupyterlab
|
|
15
|
-
Requires-Dist: jupyterlab; extra == 'jupyterlab'
|
|
16
|
-
Requires-Dist: matplotlib; extra == 'jupyterlab'
|
|
17
|
-
Requires-Dist: plotly; extra == 'jupyterlab'
|
|
18
|
-
Requires-Dist: pyarrow; extra == 'jupyterlab'
|
|
19
|
-
Provides-Extra: parquet
|
|
20
|
-
Requires-Dist: pyarrow; extra == 'parquet'
|
|
21
|
-
Provides-Extra: plotting
|
|
22
|
-
Requires-Dist: matplotlib; extra == 'plotting'
|
|
23
|
-
Requires-Dist: nbformat; extra == 'plotting'
|
|
24
|
-
Requires-Dist: plotly; extra == 'plotting'
|
|
25
|
-
Description-Content-Type: text/markdown
|
|
26
|
-
|
|
27
|
-
# SweatStack Python Library
|
|
28
|
-
|
|
29
|
-
## Overview
|
|
30
|
-
|
|
31
|
-
This is the Python library for [Sweat Stack](https://sweatstack.no), a powerfull application designed for athletes, coaches, and sports scientists to analyze athletic performance data. This library provides a seamless interface to interact with the SweatStack API, allowing users to retrieve, analyze, and visualize activity data and performance metrics.
|
|
32
|
-
|
|
33
|
-
## Installation
|
|
34
|
-
|
|
35
|
-
We recommend using `uv` to manage Python and install the library.
|
|
36
|
-
Read more about `uv` [here](https://docs.astral.sh/uv/getting-started/).
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
uv pip install sweatstack
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
You can also install it with `pip` (or `pipx`) directly.
|
|
43
|
-
```bash
|
|
44
|
-
python -m pip install sweatstack
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Quickstart
|
|
48
|
-
|
|
49
|
-
If you have `uv` installed, the fastest way to get started is to run the following command in your terminal:
|
|
50
|
-
```
|
|
51
|
-
uvx --from "sweatstack[jupyterlab]" sweatlab
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
This will open a JupyterLab instance with the SweatStack library pre-imported and authenticated via the browser authentication flow.
|
|
55
|
-
|
|
56
|
-
```
|
|
57
|
-
uvx --from "sweatstack[ipython]" sweatshell
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
This will open an interactive Python shell with the SweatStack library pre-imported and it will automatically trigger the browser authentication flow.
|
|
61
|
-
|
|
62
|
-
Alternatively, you can open a Python shell of your own choice, install the library and get started:
|
|
63
|
-
|
|
64
|
-
```python
|
|
65
|
-
import sweatstack as ss
|
|
66
|
-
|
|
67
|
-
ss.login()
|
|
68
|
-
|
|
69
|
-
latest_activity = ss.get_latest_activity()
|
|
70
|
-
|
|
71
|
-
print(latest_activity) # `latest_activity` is a pandas DataFrame
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
## Authentication
|
|
76
|
-
|
|
77
|
-
To be able to access your data in Sweat Stack, you need to authenticate the library with your Sweat Stack account.
|
|
78
|
-
The easiest way to do this is to use your browser to login:
|
|
79
|
-
|
|
80
|
-
```python
|
|
81
|
-
import sweatstack as ss
|
|
82
|
-
|
|
83
|
-
ss.login()
|
|
84
|
-
```
|
|
85
|
-
This will automaticallyset the appropriate authentication tokens in your Python code.
|
|
86
|
-
|
|
87
|
-
Alternatively, you can set the `SWEAT_STACK_API_KEY` environment variable to your API key.
|
|
88
|
-
You can create an API key [here](https://app.sweatstack.com/account/api-keys).
|
|
89
|
-
|
|
90
|
-
```python
|
|
91
|
-
import os
|
|
92
|
-
|
|
93
|
-
import sweatstack as ss
|
|
94
|
-
|
|
95
|
-
os.environ["SWEAT_STACK_API_KEY"] = "your_api_key_here"
|
|
96
|
-
|
|
97
|
-
# Now you can use the library
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
## Listing activities
|
|
102
|
-
|
|
103
|
-
To list activities, you can use the `list_activities()` function:
|
|
104
|
-
|
|
105
|
-
```python
|
|
106
|
-
import sweatstack as ss
|
|
107
|
-
|
|
108
|
-
for activity in ss.list_activities():
|
|
109
|
-
print(activity)
|
|
110
|
-
```
|
|
111
|
-
> **Info:** This method returns a summary of the activities, not the actual timeseries data.
|
|
112
|
-
> To get the actual data, you need to use the `get_activity_data()` or `get_latest_activity_data()`) methods documented below.
|
|
113
|
-
|
|
114
|
-
## Getting activity summaries
|
|
115
|
-
|
|
116
|
-
To get the summary of an activity, you can use the `get_activity()` function:
|
|
117
|
-
|
|
118
|
-
```python
|
|
119
|
-
import sweatstack as ss
|
|
120
|
-
|
|
121
|
-
activity = ss.get_activity(activity_id)
|
|
122
|
-
print(activity)
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
To quickly the latest activity, you can use the `get_latest_activity()` function:
|
|
126
|
-
|
|
127
|
-
```python
|
|
128
|
-
import sweatstack as ss
|
|
129
|
-
|
|
130
|
-
activity = ss.get_latest_activity()
|
|
131
|
-
print(activity)
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
## Getting activity data
|
|
135
|
-
|
|
136
|
-
To get the timeseries data of one activity, you can use the `get_activity_data()` method:
|
|
137
|
-
|
|
138
|
-
```python
|
|
139
|
-
import sweatstack as ss
|
|
140
|
-
|
|
141
|
-
data = ss.get_activity_data(activity_id)
|
|
142
|
-
print(data)
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
This method returns a pandas DataFrame.
|
|
146
|
-
If your are not familiar with pandas and/or DataFrames, start by reading this [introduction](https://pandas.pydata.org/docs/user_guide/10min.html).
|
|
147
|
-
|
|
148
|
-
Similar as for the summaries, you can use the `get_latest_activity_data()` method to get the timeseries data of the latest activity:
|
|
149
|
-
|
|
150
|
-
```python
|
|
151
|
-
import sweatstack as ss
|
|
152
|
-
|
|
153
|
-
data = ss.get_latest_activity_data()
|
|
154
|
-
print(data)
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
To get the timeseries data of multiple activities, you can use the `get_longitudinal_data()` method:
|
|
158
|
-
|
|
159
|
-
```python
|
|
160
|
-
import sweatstack as ss
|
|
161
|
-
|
|
162
|
-
longitudinal_data = ss.get_longitudinal_data(
|
|
163
|
-
start=date.today() - timedelta(days=180),
|
|
164
|
-
sport="running",
|
|
165
|
-
metrics=["power", "heart_rate"],
|
|
166
|
-
)
|
|
167
|
-
print(longitudinal_data)
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
Because the result of `get_longitudinal_data()` can be very large, the data is retrieved in a compressed format (parquet) that requires the `pyarrow` library to be installed. If you intend to use this method, make sure to install the `sweatstack` library with this extra dependency:
|
|
171
|
-
```
|
|
172
|
-
uv pip install sweatstack[parquet]
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
Also note that depending on the amount of data that you requested, this might take a while.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
## Plotting
|
|
179
|
-
|
|
180
|
-
To plot data, there are a few plotting methods available.
|
|
181
|
-
|
|
182
|
-
```python
|
|
183
|
-
import sweatstack as ss
|
|
184
|
-
|
|
185
|
-
ss.plot_activity_data(activity_id)
|
|
186
|
-
```
|
|
187
|
-
...wil plot the all the available columns from the specified activity.
|
|
188
|
-
There is also a `ss.plot_latest_activity_data()` method that will plot the latest activity data.
|
|
189
|
-
|
|
190
|
-
Additionally, there is a `ss.plot_data()` method that you can use to for example plot longitudinal data or for more generic use. This method requires you to pass the actual data as a pandas DataFrame and will not work with the activity id's used above.
|
|
191
|
-
|
|
192
|
-
```python
|
|
193
|
-
import sweatstack as ss
|
|
194
|
-
|
|
195
|
-
ss.plot_data(data)
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
All of these methods accept a `metrics` argument, which is a list of metrics that you want to plot, as well as a `subplots` argument, which is a boolean that specifies whether you want to plot each metrics in subplots or not.
|
|
199
|
-
Example:
|
|
200
|
-
|
|
201
|
-
```python
|
|
202
|
-
import sweatstack as ss
|
|
203
|
-
|
|
204
|
-
ss.plot_latest_activity_data(metrics=["heart_rate", "power"], subplots=True)
|
|
205
|
-
ss.plot_data(data, metrics=["heart_rate", "power"], subplots=False)
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
There is also a `ss.plot_scatter()` method that you can use to plot a scatter plot of any two metrics:
|
|
209
|
-
|
|
210
|
-
```python
|
|
211
|
-
import sweatstack as ss
|
|
212
|
-
|
|
213
|
-
ss.plot_scatter(x=data["power"], y=data["heart_rate"])
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
For plotting mean-max data, you can use the `ss.plot_mean_max()` method:
|
|
217
|
-
|
|
218
|
-
```python
|
|
219
|
-
import sweatstack as ss
|
|
220
|
-
|
|
221
|
-
ss.plot_mean_max(
|
|
222
|
-
sport="running",
|
|
223
|
-
metric="power",
|
|
224
|
-
)
|
|
225
|
-
```
|
|
226
|
-
By default, this method will plot the mean-max data for the last 30 days.
|
|
227
|
-
But you can provide explicit start and end dates (both optional) to plot the mean-max data for a different time period and these can provided as both `date` objects and `str` objects (e.g. "1970-01-01").
|
|
228
|
-
|
|
229
|
-
```python
|
|
230
|
-
from datetime import date, timedelta
|
|
231
|
-
|
|
232
|
-
import sweatstack as ss
|
|
233
|
-
|
|
234
|
-
ss.plot_mean_max(
|
|
235
|
-
sport="running",
|
|
236
|
-
metric="power",
|
|
237
|
-
start=date.today() - timedelta(days=180),
|
|
238
|
-
end=date.today(),
|
|
239
|
-
)
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
To plot the mean-max data for multiple time windows, you can provide a list of start and end dates as the `windows` argument:
|
|
243
|
-
|
|
244
|
-
```python
|
|
245
|
-
from datetime import date, timedelta
|
|
246
|
-
|
|
247
|
-
import sweatstack as ss
|
|
248
|
-
|
|
249
|
-
ss.plot_mean_max(
|
|
250
|
-
sport="running",
|
|
251
|
-
metric="power",
|
|
252
|
-
windows=[
|
|
253
|
-
(date.today() - timedelta(days=180), date.today()),
|
|
254
|
-
(date.today() - timedelta(days=30), date.today() - timedelta(days=180)),
|
|
255
|
-
],
|
|
256
|
-
)
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
This opens up a lot of possibilities, for example to plot the mean-max data for each 30 day window in the last 300 days:
|
|
260
|
-
```python
|
|
261
|
-
from datetime import date, timedelta
|
|
262
|
-
|
|
263
|
-
import sweatstack as ss
|
|
264
|
-
|
|
265
|
-
ss.plot_mean_max(
|
|
266
|
-
sport="running",
|
|
267
|
-
metric="power",
|
|
268
|
-
windows=[(date.today() - timedelta(days=i + 30), date.today() - timedelta(days=i + 1)) for i in range(0, 120, 30)],
|
|
269
|
-
)
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
At the moment, only the Plotly plotting backend is available, but more plotting backends (like Matplotlib) will be added in the future.
|
|
273
|
-
|
|
274
|
-
Please note that these plotting methods are just there for your convenience.
|
|
275
|
-
If you want to customize your plots, we recommend using a plotting library like Plotly or Matplotlib directly.
|
|
276
|
-
[This](https://pandas.pydata.org/docs/user_guide/visualization.html) page from the pandas documentation gives a good overview of the available plotting options for the `pandas.DataFrames` and `pandas.Series` that this library returns.
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
## Accessing other user's data
|
|
280
|
-
|
|
281
|
-
By default, the library will give you access to your own data.
|
|
282
|
-
|
|
283
|
-
You can list all users you have access to with the `list_accessible_users()` method:
|
|
284
|
-
|
|
285
|
-
```python
|
|
286
|
-
import sweatstack as ss
|
|
287
|
-
|
|
288
|
-
for user in ss.list_accessible_users():
|
|
289
|
-
print(user)
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
You can switch to another user by using the `switch_user()` method:
|
|
293
|
-
|
|
294
|
-
```python
|
|
295
|
-
import sweatstack as ss
|
|
296
|
-
|
|
297
|
-
ss.switch_user(user)
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
Calling any of the methods above will return the data for the user you switched to.
|
|
301
|
-
|
|
302
|
-
You can easily switch back to your original user by calling the `switch_to_root_user()` method:
|
|
303
|
-
|
|
304
|
-
```python
|
|
305
|
-
import sweatstack as ss
|
|
306
|
-
|
|
307
|
-
ss.switch_to_root_user()
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
## Uploading activities
|
|
312
|
-
|
|
313
|
-
To upload activities (only .fit files at this moment), you can use the `upload_activity()` method:
|
|
314
|
-
|
|
315
|
-
```python
|
|
316
|
-
ss.upload_activity("path/to/activity.fit")
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
To upload multiple activities, you can use the `batch_upload_activities()` method:
|
|
320
|
-
```python
|
|
321
|
-
import sweatstack as ss
|
|
322
|
-
|
|
323
|
-
ss.batch_upload_activities(files=["path/to/activity1.fit", "path/to/activity2.fit", "path/to/activity3.fit"])
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
For both the `upload_activity()` and `batch_upload_activities()` methods, you can pass files as a file path (string or pathlib.Path) or file-like object.
|
|
327
|
-
|
|
328
|
-
In addition to this, `batch_upload_activities()` also accepts a `directory` argument (string or pathlib.Path), allowing you to upload all activities in a directory:
|
|
329
|
-
|
|
330
|
-
```python
|
|
331
|
-
import sweatstack as ss
|
|
332
|
-
|
|
333
|
-
ss.batch_upload_activities(directory="path/to/directory")
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
## Metrics
|
|
338
|
-
|
|
339
|
-
The API supports the following metrics:
|
|
340
|
-
- `power`: Power in Watt
|
|
341
|
-
- `speed`: Speed in m/s
|
|
342
|
-
- `heart_rate`: Heart rate in BPM
|
|
343
|
-
- `smo2`: Muscle oxygen saturation in %
|
|
344
|
-
- `core_temperature`: Core body temperature in °C
|
|
345
|
-
- `altitude`: Altitude in meters
|
|
346
|
-
- `cadence`: Cadence in RPM
|
|
347
|
-
- `temperature`: Ambient temperature in °C
|
|
348
|
-
- `distance`: Distance in m
|
|
349
|
-
- `longitude`: Longitude in degrees
|
|
350
|
-
- `latitude`: Latitude in degrees
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
## Sports
|
|
354
|
-
|
|
355
|
-
The API supports the following sports:
|
|
356
|
-
- `running`: Running
|
|
357
|
-
- `cycling`: Cycling
|
|
358
|
-
|
|
359
|
-
More sports will be added in the future.
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
sweatstack/__init__.py,sha256=tiVfgKlswRPaDMEy0gA7u8rveqEYZTA_kyB9lJ3J6Sc,21
|
|
2
|
-
sweatstack/cli.py,sha256=dYgvMOSGFC5ol_Z_2DYSOdNPQlHGsRIfh4y4qCmBLME,712
|
|
3
|
-
sweatstack/client.py,sha256=0ll_CglekhruGZlgvRETElr3xDxXiw5Q-bIdyR0Tt_E,20174
|
|
4
|
-
sweatstack/ipython_init.py,sha256=zBGUlMFkdpLvsNpOpwrNaKRUpUZhzaICvH8ODJgMPcI,229
|
|
5
|
-
sweatstack/jupyterlab_oauth2_startup.py,sha256=rMAXH4KINI-FitNSumnUSy7oS9CoKajhNxFyERSimHw,1005
|
|
6
|
-
sweatstack/plotting.py,sha256=t21he_YYoOQAGR6UiXBtv01fRd2TydC88pYouzR676w,8229
|
|
7
|
-
sweatstack/schemas.py,sha256=I-30xwI6HjZ60np3wBdP9IBL9SIFyF5Tj1q7MMOtvuI,8802
|
|
8
|
-
sweatstack/sweatshell.py,sha256=MYLNcWbOdceqKJ3S0Pe8dwHXEeYsGJNjQoYUXpMTftA,333
|
|
9
|
-
sweatstack/Sweat Stack examples/Getting started.ipynb,sha256=k2hiSffWecoQ0VxjdpDcgFzBXDQiYEebhnAYlu8cgX8,6335204
|
|
10
|
-
sweatstack-0.11.1.dist-info/METADATA,sha256=0gaAm2NfzLLVSY889VsSby75kfe_qYPC8prJS0isLQo,10477
|
|
11
|
-
sweatstack-0.11.1.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
12
|
-
sweatstack-0.11.1.dist-info/entry_points.txt,sha256=kCzOUQI3dqbTpEYqtgYDeiKFaqaA7BMlV6D24BMzCFU,208
|
|
13
|
-
sweatstack-0.11.1.dist-info/RECORD,,
|
|
File without changes
|