sweatstack 0.79.0__tar.gz → 0.81.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 (68) hide show
  1. {sweatstack-0.79.0 → sweatstack-0.81.0}/.claude/settings.local.json +9 -1
  2. {sweatstack-0.79.0 → sweatstack-0.81.0}/AGENTS.md +6 -2
  3. {sweatstack-0.79.0 → sweatstack-0.81.0}/CHANGELOG.md +65 -0
  4. {sweatstack-0.79.0 → sweatstack-0.81.0}/PKG-INFO +6 -1
  5. sweatstack-0.81.0/README.md +9 -0
  6. {sweatstack-0.79.0 → sweatstack-0.81.0}/docs/everything.rst +7 -2
  7. sweatstack-0.81.0/plans/005_ost_sport_bridge.md +515 -0
  8. {sweatstack-0.79.0 → sweatstack-0.81.0}/pyproject.toml +2 -1
  9. sweatstack-0.81.0/src/sweatstack/cli.py +48 -0
  10. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/client.py +15 -4
  11. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/openapi_schemas.py +1 -48
  12. sweatstack-0.81.0/src/sweatstack/schemas.py +94 -0
  13. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/streamlit.py +7 -7
  14. {sweatstack-0.79.0 → sweatstack-0.81.0}/tests/test_public_surface.py +1 -0
  15. sweatstack-0.81.0/tests/test_sport_ost.py +91 -0
  16. {sweatstack-0.79.0 → sweatstack-0.81.0}/tests/test_tests.py +5 -5
  17. {sweatstack-0.79.0 → sweatstack-0.81.0}/uv.lock +17 -1
  18. sweatstack-0.79.0/README.md +0 -5
  19. sweatstack-0.79.0/src/sweatstack/cli.py +0 -24
  20. sweatstack-0.79.0/src/sweatstack/schemas.py +0 -214
  21. {sweatstack-0.79.0 → sweatstack-0.81.0}/.claude/skills/sweatstack-python/SKILL.md +0 -0
  22. {sweatstack-0.79.0 → sweatstack-0.81.0}/.claude/skills/sweatstack-python/client.md +0 -0
  23. {sweatstack-0.79.0 → sweatstack-0.81.0}/.claude/skills/sweatstack-python/data-models.md +0 -0
  24. {sweatstack-0.79.0 → sweatstack-0.81.0}/.claude/skills/sweatstack-python/fastapi.md +0 -0
  25. {sweatstack-0.79.0 → sweatstack-0.81.0}/.claude/skills/sweatstack-python/streamlit.md +0 -0
  26. {sweatstack-0.79.0 → sweatstack-0.81.0}/.gitignore +0 -0
  27. {sweatstack-0.79.0 → sweatstack-0.81.0}/.python-version +0 -0
  28. {sweatstack-0.79.0 → sweatstack-0.81.0}/CONTRIBUTING.md +0 -0
  29. {sweatstack-0.79.0 → sweatstack-0.81.0}/DEVELOPMENT.md +0 -0
  30. {sweatstack-0.79.0 → sweatstack-0.81.0}/LICENSE +0 -0
  31. {sweatstack-0.79.0 → sweatstack-0.81.0}/Makefile +0 -0
  32. {sweatstack-0.79.0 → sweatstack-0.81.0}/docs/conf.py +0 -0
  33. {sweatstack-0.79.0 → sweatstack-0.81.0}/docs/index.rst +0 -0
  34. {sweatstack-0.79.0 → sweatstack-0.81.0}/examples/fastapi_webhooks_example.py +0 -0
  35. {sweatstack-0.79.0 → sweatstack-0.81.0}/examples/send_webhook.py +0 -0
  36. {sweatstack-0.79.0 → sweatstack-0.81.0}/plans/001a_tests.md +0 -0
  37. {sweatstack-0.79.0 → sweatstack-0.81.0}/plans/001b_metadata.md +0 -0
  38. {sweatstack-0.79.0 → sweatstack-0.81.0}/plans/001c_dailies.md +0 -0
  39. {sweatstack-0.79.0 → sweatstack-0.81.0}/plans/002_TYPED_EXCEPTIONS.md +0 -0
  40. {sweatstack-0.79.0 → sweatstack-0.81.0}/plans/003_trace_test_linking.md +0 -0
  41. {sweatstack-0.79.0 → sweatstack-0.81.0}/plans/004_codebase_hygiene.md +0 -0
  42. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/Sweat Stack examples/Getting started.ipynb +0 -0
  43. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/__init__.py +0 -0
  44. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/constants.py +0 -0
  45. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/exceptions.py +0 -0
  46. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/fastapi/__init__.py +0 -0
  47. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/fastapi/access_token_cache.py +0 -0
  48. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/fastapi/config.py +0 -0
  49. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/fastapi/dependencies.py +0 -0
  50. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/fastapi/models.py +0 -0
  51. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/fastapi/routes.py +0 -0
  52. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/fastapi/session.py +0 -0
  53. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/fastapi/token_stores.py +0 -0
  54. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/fastapi/webhooks.py +0 -0
  55. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/ipython_init.py +0 -0
  56. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/jupyterlab_oauth2_startup.py +0 -0
  57. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/py.typed +0 -0
  58. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/sweatshell.py +0 -0
  59. {sweatstack-0.79.0 → sweatstack-0.81.0}/src/sweatstack/utils.py +0 -0
  60. {sweatstack-0.79.0 → sweatstack-0.81.0}/tests/__init__.py +0 -0
  61. {sweatstack-0.79.0 → sweatstack-0.81.0}/tests/test_access_token_cache.py +0 -0
  62. {sweatstack-0.79.0 → sweatstack-0.81.0}/tests/test_dailies.py +0 -0
  63. {sweatstack-0.79.0 → sweatstack-0.81.0}/tests/test_dtype_conversion.py +0 -0
  64. {sweatstack-0.79.0 → sweatstack-0.81.0}/tests/test_exceptions.py +0 -0
  65. {sweatstack-0.79.0 → sweatstack-0.81.0}/tests/test_metadata.py +0 -0
  66. {sweatstack-0.79.0 → sweatstack-0.81.0}/tests/test_teams.py +0 -0
  67. {sweatstack-0.79.0 → sweatstack-0.81.0}/tests/test_trace_test_linking.py +0 -0
  68. {sweatstack-0.79.0 → sweatstack-0.81.0}/tests/test_webhooks.py +0 -0
@@ -23,7 +23,15 @@
23
23
  "mcp__sentry__get_sentry_resource",
24
24
  "Bash(awk *)",
25
25
  "Bash(git show *)",
26
- "Read(//tmp/**)"
26
+ "Read(//tmp/**)",
27
+ "WebFetch(domain:pypi.org)",
28
+ "WebFetch(domain:github.com)",
29
+ "WebFetch(domain:raw.githubusercontent.com)",
30
+ "Bash(uv add *)",
31
+ "Bash(sed -n '42,50p' src/sweatstack/client.py)",
32
+ "Bash(sed -n '1017,1022p' src/sweatstack/client.py)",
33
+ "Bash(sed -i '' 's/sport=cycling\\\\.road&sport=cycling\\\\.tt/sport=cycling.road\\\\&sport=cycling.time_trial/' docs-dev/changelog.md)",
34
+ "Bash(sed -n '25p' docs-dev/changelog.md)"
27
35
  ],
28
36
  "deny": []
29
37
  }
@@ -16,7 +16,7 @@ you add or change a surface, the default is to mirror the server:
16
16
  | `POST /api/v1/traces/` | `client.create_trace(...)` |
17
17
  | `PUT /api/v1/traces/{id}` (full-replace)| `client.update_trace(trace_id, ...)` |
18
18
  | `DELETE /api/v1/dailies/{id}` | `client.delete_daily(daily_id)` |
19
- | query param `?sport=cycling&sport=running` | `sports=[Sport.cycling, Sport.running]` |
19
+ | query param `?sport=cycling&sport=running` | `sports=[Sport("cycling"), Sport("running")]` |
20
20
  | JSON body field `test_id` | kwarg `test_id` |
21
21
  | OpenAPI enum value `"auto"` | `TraceResolution.auto` |
22
22
 
@@ -44,7 +44,7 @@ and pagination shape. Don't redesign the API in Python.
44
44
  ```
45
45
  src/sweatstack/
46
46
  ├── openapi_schemas.py # AUTO-GENERATED. Never hand-edit.
47
- ├── schemas.py # Re-exports + Enum._missing_ / display_name helpers.
47
+ ├── schemas.py # Re-exports (incl. OST Sport/Modifier) + Metric/Scope/DailyMeasure helpers.
48
48
  ├── exceptions.py # Public error contract. No httpx types leak.
49
49
  ├── client.py # Single Client class + module-level singletons.
50
50
  ├── utils.py # Dataframe / JWT helpers.
@@ -72,6 +72,10 @@ Skipping step 3 silently breaks the public surface. Same for new enums.
72
72
  bottom of `client.py`. Forgetting is silent.
73
73
  - **Enum-typed params accept `Enum | str`** and route through
74
74
  `_enums_to_strings`. Don't introduce strict-enum-only parameters.
75
+ - **`Sport` is the OpenSportTaxonomy type** (`open_sport_taxonomy.Sport`), not a generated enum.
76
+ Construct with `Sport("cycling.road")` or `Sport.parse(value)`; serialise with `str(sport)`. Codegen
77
+ binds the `sport` field to OST's permissive `SportField` (cli.py `_bind_sport_to_ost`), so regen is
78
+ safe and `sport`-typed fields keep decoding to `Sport`.
75
79
  - **Tests are offline.** No network calls. Use `Client.__new__(Client)` to
76
80
  bypass init when you need an instance for a helper method.
77
81
  - **`update_*` methods are full-replace.** Document the silent-clear
@@ -5,6 +5,71 @@ All notable changes to this project will be documented in this file.
5
5
  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
+ ## [0.81.0] - 2026-06-16
9
+
10
+ The SweatStack API has fully adopted [OpenSportTaxonomy](https://github.com/SweatStack/open-sport-taxonomy)
11
+ (OST), and so has this client. `sweatstack.Sport` is now the OST `Sport` type instead of a bespoke enum.
12
+ This is a **breaking change** for code that uses `Sport`.
13
+
14
+ ### Changed (breaking)
15
+ - `sweatstack.Sport` is now `open_sport_taxonomy.Sport` — a rich type (`.code`, `.label`, `.modifiers`,
16
+ `.parent`, `.is_subsport_of()`, `.resolve()`, `Sport.parse()`, `Sport.all()`) rather than a string
17
+ enum. There are no `Sport.cycling_road`-style members; construct a known sport with
18
+ `Sport("cycling.road")` or parse external input with `Sport.parse(value)`. The bespoke helpers
19
+ (`root_sport()`, `parent_sport()`, `is_sub_sport_of()`, `is_root_sport()`, `display_name()`) are
20
+ removed in favour of OST's native API.
21
+ - Sport values are now OST values, e.g. `cycling.trainer` → `cycling+stationary`, `cycling.tt` →
22
+ `cycling.time_trial`, `cross_country_skiing` → `xc_skiing`, `unknown` → `generic`. Response data,
23
+ longitudinal DataFrames and `get_sports()` all return OST values; requests send OST values.
24
+
25
+ ### Added
26
+ - `sweatstack.Modifier` (re-exported from OpenSportTaxonomy) for inspecting sport modifiers. (For typed
27
+ sport annotations, `StandardSport` is available from `open_sport_taxonomy` directly.)
28
+ - `open-sport-taxonomy[pydantic]` is now a runtime dependency. Response models consume `sport` via OST's
29
+ permissive `SportField`, so sports newer than the bundled taxonomy are preserved rather than rejected.
30
+
31
+ ### Migration
32
+ Hand the following prompt to a coding agent, or apply it by hand:
33
+
34
+ ```text
35
+ Migrate this codebase to sweatstack 0.81.0, which replaces its custom `Sport` enum with the
36
+ OpenSportTaxonomy type (`open_sport_taxonomy.Sport`). `from sweatstack import Sport` is now that type.
37
+
38
+ 1. Construction (there are no enum members like `Sport.cycling_road`):
39
+ - Known sport in app code -> `Sport("cycling.road")` (raises on an unknown code/modifier).
40
+ - From an API/string value -> `Sport.parse(value)` (permissive; never raises; preserves unknown).
41
+ - For typed annotations/autocomplete of the standard catalogue ->
42
+ `from open_sport_taxonomy import StandardSport` (a Literal).
43
+
44
+ 2. These sport VALUES changed; update hardcoded strings or members:
45
+ cycling.trainer->cycling+stationary, running.treadmill->running+stationary,
46
+ rowing.ergometer->rowing+stationary, cycling.tt->cycling.time_trial,
47
+ cycling.mountainbike->cycling.mountain, cross_country_skiing[.classic|.skate]->xc_skiing[...],
48
+ unknown->generic. ("stationary" etc. are now modifiers, appended with `+`; see sport.modifiers.)
49
+
50
+ 3. Methods / attributes:
51
+ Sport.cycling_road -> Sport("cycling.road")
52
+ sport.value -> str(sport) (canonical, incl. modifiers) or sport.code
53
+ sport.display_name() -> sport.label
54
+ sport.parent_sport() -> sport.parent
55
+ sport.is_sub_sport_of(x) -> sport.is_subsport_of(x) (x is a single Sport; for a list use
56
+ any(sport.is_subsport_of(s) for s in xs))
57
+ sport.root_sport() -> Sport(sport.code.split(".")[0])
58
+ sport.is_root_sport() -> ("." not in sport.code and not sport.modifiers)
59
+ for s in Sport: ... -> for s in Sport.all(): ...
60
+
61
+ 4. Equality works: `activity.sport == Sport("cycling+stationary")`. Compare against the NEW value.
62
+
63
+ After editing, run the test suite and fix any remaining references. Do not add a compatibility shim;
64
+ migrate call sites to the OST API directly.
65
+ ```
66
+
67
+
68
+ ## [0.80.0] - 2026-06-12
69
+
70
+ ### Changed
71
+ - Makes the Sport enum forward compatible with OpenSportTaxonomy sports.
72
+
8
73
 
9
74
  ## [0.79.0] - 2026-05-28
10
75
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sweatstack
3
- Version: 0.79.0
3
+ Version: 0.81.0
4
4
  Summary: The official Python client for SweatStack
5
5
  Project-URL: Homepage, https://sweatstack.no
6
6
  Project-URL: Documentation, https://docs.sweatstack.no/getting-started/
@@ -23,6 +23,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
23
  Requires-Python: >=3.10
24
24
  Requires-Dist: email-validator>=2.2.0
25
25
  Requires-Dist: httpx>=0.28.1
26
+ Requires-Dist: open-sport-taxonomy[pydantic]>=0.10.0
26
27
  Requires-Dist: pandas>=2.2.3
27
28
  Requires-Dist: platformdirs>=4.0.0
28
29
  Requires-Dist: pyarrow>=18.0.0
@@ -43,3 +44,7 @@ Description-Content-Type: text/markdown
43
44
  This is the official Python client library for SweatStack.
44
45
 
45
46
  Documentation can be found [here](https://docs.sweatstack.no/getting-started/).
47
+
48
+ Sports follow [OpenSportTaxonomy](https://open-sport-taxonomy.sweatstack.no): `sweatstack.Sport` is the
49
+ OST `Sport` type. See the [OpenSportTaxonomy Python guide](https://github.com/SweatStack/open-sport-taxonomy/blob/main/python/README.md)
50
+ for the full `Sport` API.
@@ -0,0 +1,9 @@
1
+ # SweatStack Python client library
2
+
3
+ This is the official Python client library for SweatStack.
4
+
5
+ Documentation can be found [here](https://docs.sweatstack.no/getting-started/).
6
+
7
+ Sports follow [OpenSportTaxonomy](https://open-sport-taxonomy.sweatstack.no): `sweatstack.Sport` is the
8
+ OST `Sport` type. See the [OpenSportTaxonomy Python guide](https://github.com/SweatStack/open-sport-taxonomy/blob/main/python/README.md)
9
+ for the full `Sport` API.
@@ -151,8 +151,13 @@ sweatstack.schemas
151
151
  Sport
152
152
  ~~~~~
153
153
 
154
- .. autoclass:: sweatstack.schemas.Sport
155
- :members: root_sport, parent_sport, is_sub_sport_of, is_root_sport, display_name
154
+ ``sweatstack.Sport`` is the `OpenSportTaxonomy <https://github.com/SweatStack/open-sport-taxonomy>`_
155
+ ``Sport`` type. Construct a known sport with ``Sport("cycling.road")`` and parse external/API input
156
+ with ``Sport.parse(value)``. See the OpenSportTaxonomy documentation for the full API (``code``,
157
+ ``label``, ``modifiers``, ``parent``, ``disciplines``, ``is_subsport_of``, ``resolve``, ``all``).
158
+
159
+ .. autoclass:: sweatstack.Sport
160
+ :members: parse, resolve, is_subsport_of, all
156
161
  :undoc-members:
157
162
 
158
163
  Metric