sweatstack 0.70.0__tar.gz → 0.72.0__tar.gz

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.
Files changed (65) hide show
  1. {sweatstack-0.70.0 → sweatstack-0.72.0}/.claude/settings.local.json +5 -1
  2. {sweatstack-0.70.0 → sweatstack-0.72.0}/.claude/skills/sweatstack-python/client.md +19 -9
  3. sweatstack-0.72.0/.gitignore +45 -0
  4. {sweatstack-0.70.0 → sweatstack-0.72.0}/CHANGELOG.md +30 -0
  5. sweatstack-0.72.0/CONTRIBUTING.md +9 -0
  6. sweatstack-0.72.0/LICENSE +21 -0
  7. sweatstack-0.72.0/PKG-INFO +46 -0
  8. {sweatstack-0.70.0 → sweatstack-0.72.0}/README.md +1 -2
  9. {sweatstack-0.70.0 → sweatstack-0.72.0}/examples/fastapi_webhooks_example.py +10 -7
  10. {sweatstack-0.70.0 → sweatstack-0.72.0}/pyproject.toml +28 -7
  11. {sweatstack-0.70.0 → sweatstack-0.72.0}/src/sweatstack/client.py +153 -80
  12. {sweatstack-0.70.0 → sweatstack-0.72.0}/tests/test_webhooks.py +3 -3
  13. {sweatstack-0.70.0 → sweatstack-0.72.0}/uv.lock +1 -1
  14. sweatstack-0.70.0/.gitignore +0 -17
  15. sweatstack-0.70.0/CLIENT_DTYPE_CONVERSION.md +0 -808
  16. sweatstack-0.70.0/CLIENT_LIBRARY_SKILL.md +0 -156
  17. sweatstack-0.70.0/FASTAPI_DOCS.md +0 -275
  18. sweatstack-0.70.0/FASTAPI_PLUGIN.md +0 -396
  19. sweatstack-0.70.0/FASTAPI_USER_SWITCHING.md +0 -858
  20. sweatstack-0.70.0/FASTAPI_WEBHOOKS.md +0 -1466
  21. sweatstack-0.70.0/LOCAL_AUTH.md +0 -299
  22. sweatstack-0.70.0/PKG-INFO +0 -29
  23. sweatstack-0.70.0/examples/tokens.db +0 -0
  24. sweatstack-0.70.0/fastapi_coaching_example.py +0 -97
  25. sweatstack-0.70.0/fastapi_example.py +0 -95
  26. sweatstack-0.70.0/fastapi_sweatstack.py +0 -66
  27. sweatstack-0.70.0/playground/.ipynb_checkpoints/Untitled-checkpoint.ipynb +0 -262
  28. sweatstack-0.70.0/playground/README.md +0 -0
  29. sweatstack-0.70.0/playground/Untitled.ipynb +0 -298
  30. sweatstack-0.70.0/playground/hello.py +0 -6
  31. sweatstack-0.70.0/playground/pyproject.toml +0 -12
  32. sweatstack-0.70.0/src/sweatstack/Sweat Stack examples/Getting started.ipynb +0 -351
  33. {sweatstack-0.70.0 → sweatstack-0.72.0}/.claude/skills/sweatstack-python/SKILL.md +0 -0
  34. {sweatstack-0.70.0 → sweatstack-0.72.0}/.claude/skills/sweatstack-python/data-models.md +0 -0
  35. {sweatstack-0.70.0 → sweatstack-0.72.0}/.claude/skills/sweatstack-python/fastapi.md +0 -0
  36. {sweatstack-0.70.0 → sweatstack-0.72.0}/.claude/skills/sweatstack-python/streamlit.md +0 -0
  37. {sweatstack-0.70.0 → sweatstack-0.72.0}/.python-version +0 -0
  38. {sweatstack-0.70.0 → sweatstack-0.72.0}/DEVELOPMENT.md +0 -0
  39. {sweatstack-0.70.0 → sweatstack-0.72.0}/Makefile +0 -0
  40. {sweatstack-0.70.0 → sweatstack-0.72.0}/docs/conf.py +0 -0
  41. {sweatstack-0.70.0 → sweatstack-0.72.0}/docs/everything.rst +0 -0
  42. {sweatstack-0.70.0 → sweatstack-0.72.0}/docs/index.rst +0 -0
  43. {sweatstack-0.70.0 → sweatstack-0.72.0}/examples/send_webhook.py +0 -0
  44. {sweatstack-0.70.0/playground → sweatstack-0.72.0/src/sweatstack}/Sweat Stack examples/Getting started.ipynb +0 -0
  45. {sweatstack-0.70.0 → sweatstack-0.72.0}/src/sweatstack/__init__.py +0 -0
  46. {sweatstack-0.70.0 → sweatstack-0.72.0}/src/sweatstack/cli.py +0 -0
  47. {sweatstack-0.70.0 → sweatstack-0.72.0}/src/sweatstack/constants.py +0 -0
  48. {sweatstack-0.70.0 → sweatstack-0.72.0}/src/sweatstack/fastapi/__init__.py +0 -0
  49. {sweatstack-0.70.0 → sweatstack-0.72.0}/src/sweatstack/fastapi/config.py +0 -0
  50. {sweatstack-0.70.0 → sweatstack-0.72.0}/src/sweatstack/fastapi/dependencies.py +0 -0
  51. {sweatstack-0.70.0 → sweatstack-0.72.0}/src/sweatstack/fastapi/models.py +0 -0
  52. {sweatstack-0.70.0 → sweatstack-0.72.0}/src/sweatstack/fastapi/routes.py +0 -0
  53. {sweatstack-0.70.0 → sweatstack-0.72.0}/src/sweatstack/fastapi/session.py +0 -0
  54. {sweatstack-0.70.0 → sweatstack-0.72.0}/src/sweatstack/fastapi/token_stores.py +0 -0
  55. {sweatstack-0.70.0 → sweatstack-0.72.0}/src/sweatstack/fastapi/webhooks.py +0 -0
  56. {sweatstack-0.70.0 → sweatstack-0.72.0}/src/sweatstack/ipython_init.py +0 -0
  57. {sweatstack-0.70.0 → sweatstack-0.72.0}/src/sweatstack/jupyterlab_oauth2_startup.py +0 -0
  58. {sweatstack-0.70.0 → sweatstack-0.72.0}/src/sweatstack/openapi_schemas.py +0 -0
  59. {sweatstack-0.70.0 → sweatstack-0.72.0}/src/sweatstack/py.typed +0 -0
  60. {sweatstack-0.70.0 → sweatstack-0.72.0}/src/sweatstack/schemas.py +0 -0
  61. {sweatstack-0.70.0 → sweatstack-0.72.0}/src/sweatstack/streamlit.py +0 -0
  62. {sweatstack-0.70.0 → sweatstack-0.72.0}/src/sweatstack/sweatshell.py +0 -0
  63. {sweatstack-0.70.0 → sweatstack-0.72.0}/src/sweatstack/utils.py +0 -0
  64. {sweatstack-0.70.0 → sweatstack-0.72.0}/tests/__init__.py +0 -0
  65. {sweatstack-0.70.0 → sweatstack-0.72.0}/tests/test_dtype_conversion.py +0 -0
@@ -14,7 +14,11 @@
14
14
  "Bash(uv run ruff:*)",
15
15
  "Bash(chmod:*)",
16
16
  "mcp__acp__Bash",
17
- "Bash(wc:*)"
17
+ "Bash(wc:*)",
18
+ "Bash(ls:*)",
19
+ "Bash(git:*)",
20
+ "Bash(python3:*)",
21
+ "Bash(test:*)"
18
22
  ],
19
23
  "deny": []
20
24
  }
@@ -102,33 +102,43 @@ Aggregated time-series across multiple activities. One request instead of loopin
102
102
 
103
103
  ```python
104
104
  df = client.get_longitudinal_data(
105
- sport=Sport.cycling, # single sport
105
+ sports=[Sport.cycling_road], # list of Sport enum or strings
106
106
  start=date(2025, 1, 1), # required
107
107
  end=date(2025, 12, 31), # optional (defaults to today)
108
108
  metrics=[Metric.power, Metric.heart_rate], # optional
109
109
  )
110
110
 
111
- # Or filter by multiple sports
111
+ # Multiple sports
112
112
  df = client.get_longitudinal_data(
113
113
  sports=[Sport.cycling_road, Sport.cycling_gravel],
114
114
  start=date(2025, 1, 1),
115
115
  )
116
116
 
117
117
  # Longitudinal mean-max (best efforts across time range)
118
- df = client.get_longitudinal_mean_max(sport=Sport.cycling, start=date(2025, 1, 1))
118
+ df = client.get_longitudinal_mean_max(
119
+ sports=[Sport.cycling_road],
120
+ metric="power",
121
+ start=date(2025, 1, 1),
122
+ )
119
123
 
120
124
  # Longitudinal AWD
121
- df = client.get_longitudinal_awd(sport=Sport.cycling, start=date(2025, 1, 1))
125
+ df = client.get_longitudinal_awd(
126
+ sports=[Sport.cycling_road],
127
+ metric="power",
128
+ start=date(2025, 1, 1),
129
+ )
122
130
  ```
123
131
 
124
132
  The DataFrame has a timezone-aware datetime index and includes an `activity_id` column — group by it for per-activity aggregation.
125
133
 
126
- **Local caching** for reproducible analysis (avoids re-fetching on reruns):
134
+ **Local caching** for reproducible analysis (avoids re-fetching on reruns). Caches `get_longitudinal_data()` and `get_longitudinal_mean_max()`:
127
135
  ```python
128
- import os
129
- os.environ["SWEATSTACK_LOCAL_CACHE"] = "true"
136
+ import sweatstack
137
+ sweatstack.enable_cache() # platform cache dir
138
+ sweatstack.enable_cache(path="./my_cache") # custom dir
130
139
  # Use fixed end dates (not "today") to get stable cache hits
131
- df = client.get_longitudinal_data(sport=Sport.cycling, start=date(2025, 1, 1), end=date(2025, 3, 31))
140
+ df = client.get_longitudinal_data(sports=[Sport.cycling], start=date(2025, 1, 1), end=date(2025, 3, 31))
141
+ df = client.get_longitudinal_mean_max(sports=[Sport.cycling], metric="power", start=date(2025, 1, 1))
132
142
  ```
133
143
 
134
144
  ## Traces
@@ -252,7 +262,7 @@ df = sweatstack.get_latest_activity_data()
252
262
 
253
263
  - **Sport enum uses underscores:** `Sport.cycling_road`, not `Sport("road")` or `Sport.cycling.road`. String values use dots: `"cycling.road"`.
254
264
  - **`start` is required for longitudinal endpoints.** Unlike `get_activities()` where all filters are optional.
255
- - **`sport` (singular) vs `sports` (list):** `get_latest_activity(sport=...)` takes one. `get_activities(sports=[...])` and `get_longitudinal_data(sports=[...])` take a list.
265
+ - **`sport` (singular) vs `sports` (list):** `get_latest_activity(sport=...)` and `create_trace(sport=...)` take a single sport. All other methods that filter by sport use `sports=[...]` (list). The singular `sport` parameter on longitudinal methods is deprecated.
256
266
  - **DataFrames have standard dtypes.** The library converts API-optimized types (Int16, float16) to float64/datetime64[ns] automatically.
257
267
  - **`as_dataframe=True`** is available on `get_activities()` and `get_traces()`. Time-series methods (`get_activity_data`, `get_longitudinal_data`, etc.) always return DataFrames.
258
268
  - **`summary` fields are optional.** Always null-check: `activity.summary.power.mean if activity.summary and activity.summary.power else None`.
@@ -0,0 +1,45 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Environment variables and secrets
13
+ .env
14
+ .env.*
15
+
16
+ # Databases and local storage
17
+ *.db
18
+ *.sqlite
19
+ *.sqlite3
20
+
21
+ # IDE and editor configuration
22
+ .idea/
23
+ .vscode/
24
+ *.swp
25
+ *.swo
26
+ *~
27
+
28
+ # OS files
29
+ .DS_Store
30
+
31
+ # Test artifacts
32
+ .pytest_cache/
33
+
34
+ # Documentation
35
+ docs/_build/
36
+
37
+ # Internal docs dump
38
+ dump/
39
+
40
+ # Playground
41
+ playground/
42
+
43
+ # Claude skills (except our own)
44
+ .claude/skills/*
45
+ !.claude/skills/sweatstack-python/
@@ -6,6 +6,36 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
 
9
+ ## [0.72.0] - 2026-03-13
10
+
11
+ ### Added
12
+ - `sweatstack.enable_cache()` function to enable local caching via a simple API call instead of environment variables.
13
+ - Local caching for `get_longitudinal_mean_max()` responses (previously only `get_longitudinal_data()` was cached).
14
+
15
+ ### Changed
16
+ - Cache directory now defaults to the platform cache dir (`platformdirs.user_cache_dir`) instead of the system temp directory.
17
+
18
+ ### Fixed
19
+ - `AttributeError` on Python <3.11 when the API returns an error response (`add_note` is a Python 3.11+ feature).
20
+
21
+ ### Removed
22
+ - `SWEATSTACK_LOCAL_CACHE` and `SWEATSTACK_CACHE_DIR` environment variables. Use `sweatstack.enable_cache()` instead.
23
+
24
+
25
+ ## [0.71.0] - 2026-03-13
26
+
27
+ ### Added
28
+ - `sports` (list) parameter on `get_longitudinal_mean_max()` and `get_longitudinal_awd()` for multi-sport support. The existing `sport` (single) parameter remains for backwards compatibility.
29
+ - `start` and `end` date-range parameters on `get_longitudinal_mean_max()` and `get_longitudinal_awd()`.
30
+
31
+ ### Changed
32
+ - `get_activities()`, `get_traces()`, and `get_longitudinal_data()` now send the `sport` query key to the API instead of `sports`.
33
+
34
+ ### Deprecated
35
+ - `sport` (singular) parameter on `get_longitudinal_mean_max()`, `get_longitudinal_awd()`, and `get_longitudinal_data()`. Use `sports` (list) instead.
36
+ - `date` and `window_days` parameters on `get_longitudinal_mean_max()` and `get_longitudinal_awd()`. Use `start`/`end` instead.
37
+
38
+
9
39
  ## [0.70.0] - 2026-03-13
10
40
 
11
41
  ### Added
@@ -0,0 +1,9 @@
1
+ # Contributing
2
+
3
+ Thank you for your interest in the SweatStack Python client library!
4
+
5
+ This repository is open-sourced primarily to make it easier for developers and AI coding agents to understand and work with the library. At this time, we are **not accepting external contributions** such as pull requests or feature requests.
6
+
7
+ If you've found a bug or have a question about using the library, feel free to [open an issue](https://github.com/SweatStack/sweatstack-python/issues).
8
+
9
+ For general questions about SweatStack, please refer to the [developer documentation](https://developer.sweatstack.no/getting-started/).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-present Aart Goossens
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,46 @@
1
+ Metadata-Version: 2.4
2
+ Name: sweatstack
3
+ Version: 0.72.0
4
+ Summary: The official Python client for SweatStack
5
+ Project-URL: Homepage, https://sweatstack.no
6
+ Project-URL: Documentation, https://developer.sweatstack.no/getting-started/
7
+ Project-URL: Repository, https://github.com/SweatStack/sweatstack-python
8
+ Project-URL: Issues, https://github.com/SweatStack/sweatstack-python/issues
9
+ Project-URL: Changelog, https://github.com/SweatStack/sweatstack-python/blob/main/CHANGELOG.md
10
+ Author-email: Aart Goossens <aart@sweatstack.no>
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: api,client,fitness,sports,sweatstack
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Requires-Python: >=3.9
25
+ Requires-Dist: email-validator>=2.2.0
26
+ Requires-Dist: httpx>=0.28.1
27
+ Requires-Dist: pandas>=2.2.3
28
+ Requires-Dist: platformdirs>=4.0.0
29
+ Requires-Dist: pyarrow>=18.0.0
30
+ Requires-Dist: pydantic>=2.10.5
31
+ Provides-Extra: fastapi
32
+ Requires-Dist: cryptography>=41.0.0; extra == 'fastapi'
33
+ Requires-Dist: fastapi[standard]>=0.100.0; extra == 'fastapi'
34
+ Provides-Extra: jupyter
35
+ Requires-Dist: ipython>=8.31.0; extra == 'jupyter'
36
+ Requires-Dist: jupyterlab>=4.3.4; extra == 'jupyter'
37
+ Requires-Dist: matplotlib>=3.10.0; extra == 'jupyter'
38
+ Provides-Extra: streamlit
39
+ Requires-Dist: streamlit>=1.42.0; extra == 'streamlit'
40
+ Description-Content-Type: text/markdown
41
+
42
+ # SweatStack Python client library
43
+
44
+ This is the official Python client library for SweatStack.
45
+
46
+ Documentation can be found [here](https://developer.sweatstack.no/getting-started/).
@@ -1,6 +1,5 @@
1
1
  # SweatStack Python client library
2
2
 
3
-
4
3
  This is the official Python client library for SweatStack.
5
4
 
6
- Documentation can be found [here](https://developer.sweatstack.no/getting-started/).
5
+ Documentation can be found [here](https://developer.sweatstack.no/getting-started/).
@@ -9,6 +9,14 @@ import logging
9
9
  import os
10
10
  from pathlib import Path
11
11
 
12
+ # Required environment variables:
13
+ # SWEATSTACK_CLIENT_ID
14
+ # SWEATSTACK_CLIENT_SECRET
15
+ # SWEATSTACK_SESSION_SECRET
16
+ # SWEATSTACK_WEBHOOK_SECRET
17
+ # SWEATSTACK_ENCRYPTION_KEY (for EncryptedSQLiteTokenStore)
18
+ # APP_URL (e.g. http://localhost:8001)
19
+
12
20
  from fastapi import FastAPI, Request
13
21
  from fastapi.responses import HTMLResponse, JSONResponse
14
22
 
@@ -28,18 +36,13 @@ logger = logging.getLogger(__name__)
28
36
 
29
37
  db_path = Path(__file__).parent / "tokens.db"
30
38
  token_store = EncryptedSQLiteTokenStore(
31
- encryption_key="8BIvhwKdURGpXCAGcfWm3Aw1gXNmqc3zWJGfnDCXVs4=",
39
+ encryption_key=os.environ["SWEATSTACK_ENCRYPTION_KEY"],
32
40
  db_path=db_path,
33
41
  )
34
42
 
35
43
  configure(
36
- client_id="1da141eeffa54c4d",
37
- client_secret="vgwMFP6uTQa3K5bBc1mDqU33xnHVwhyqVq-NLacopD0",
38
- session_secret="5YviwYhzDdmzsqbcEIP0QXzoiehIy8lqBWLtzaeBU8Q=",
39
- app_url="http://localhost:8001",
40
- webhook_secret="whsec_development_secret_key",
41
44
  token_store=token_store,
42
- )
45
+ ) # Uses SWEATSTACK_* environment variables (CLIENT_ID, CLIENT_SECRET, SESSION_SECRET, WEBHOOK_SECRET, APP_URL)
43
46
 
44
47
  app = FastAPI(title="SweatStack Webhooks Example")
45
48
  instrument(app)
@@ -1,12 +1,26 @@
1
1
  [project]
2
2
  name = "sweatstack"
3
- version = "0.70.0"
3
+ version = "0.72.0"
4
4
  description = "The official Python client for SweatStack"
5
5
  readme = "README.md"
6
+ license = "MIT"
6
7
  authors = [
7
- { name = "Aart Goossens", email = "aart@gssns.io" }
8
+ { name = "Aart Goossens", email = "aart@sweatstack.no" }
8
9
  ]
9
10
  requires-python = ">=3.9"
11
+ keywords = ["sweatstack", "sports", "fitness", "api", "client"]
12
+ classifiers = [
13
+ "Development Status :: 4 - Beta",
14
+ "Intended Audience :: Developers",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.9",
18
+ "Programming Language :: Python :: 3.10",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Topic :: Software Development :: Libraries :: Python Modules",
23
+ ]
10
24
  dependencies = [
11
25
  "email-validator>=2.2.0",
12
26
  "httpx>=0.28.1",
@@ -16,6 +30,13 @@ dependencies = [
16
30
  "pydantic>=2.10.5",
17
31
  ]
18
32
 
33
+ [project.urls]
34
+ Homepage = "https://sweatstack.no"
35
+ Documentation = "https://developer.sweatstack.no/getting-started/"
36
+ Repository = "https://github.com/SweatStack/sweatstack-python"
37
+ Issues = "https://github.com/SweatStack/sweatstack-python/issues"
38
+ Changelog = "https://github.com/SweatStack/sweatstack-python/blob/main/CHANGELOG.md"
39
+
19
40
  [project.optional-dependencies]
20
41
  streamlit = [
21
42
  "streamlit>=1.42.0",
@@ -30,6 +51,11 @@ fastapi = [
30
51
  "cryptography>=41.0.0",
31
52
  ]
32
53
 
54
+ [project.scripts]
55
+ generate-response-models = "sweatstack.cli:generate_response_models"
56
+ sweatshell = "sweatstack.sweatshell:run_ipython"
57
+ sweatlab = "sweatstack.jupyterlab_oauth2_startup:start_jupyterlab_with_oauth"
58
+
33
59
  [build-system]
34
60
  requires = ["hatchling"]
35
61
  build-backend = "hatchling.build"
@@ -45,8 +71,3 @@ dev = [
45
71
 
46
72
  [tool.uv.workspace]
47
73
  members = ["playground"]
48
-
49
- [project.scripts]
50
- generate-response-models = "sweatstack.cli:generate_response_models"
51
- sweatshell = "sweatstack.sweatshell:run_ipython"
52
- sweatlab = "sweatstack.jupyterlab_oauth2_startup:start_jupyterlab_with_oauth"