sweatstack 0.84.0__tar.gz → 0.85.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 (66) hide show
  1. {sweatstack-0.84.0 → sweatstack-0.85.0}/CHANGELOG.md +11 -0
  2. {sweatstack-0.84.0 → sweatstack-0.85.0}/PKG-INFO +1 -1
  3. {sweatstack-0.84.0 → sweatstack-0.85.0}/pyproject.toml +1 -1
  4. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/client.py +32 -7
  5. {sweatstack-0.84.0 → sweatstack-0.85.0}/uv.lock +1 -1
  6. {sweatstack-0.84.0 → sweatstack-0.85.0}/.claude/settings.local.json +0 -0
  7. {sweatstack-0.84.0 → sweatstack-0.85.0}/.claude/skills/sweatstack-python/SKILL.md +0 -0
  8. {sweatstack-0.84.0 → sweatstack-0.85.0}/.claude/skills/sweatstack-python/client.md +0 -0
  9. {sweatstack-0.84.0 → sweatstack-0.85.0}/.claude/skills/sweatstack-python/data-models.md +0 -0
  10. {sweatstack-0.84.0 → sweatstack-0.85.0}/.claude/skills/sweatstack-python/fastapi.md +0 -0
  11. {sweatstack-0.84.0 → sweatstack-0.85.0}/.claude/skills/sweatstack-python/streamlit.md +0 -0
  12. {sweatstack-0.84.0 → sweatstack-0.85.0}/.gitignore +0 -0
  13. {sweatstack-0.84.0 → sweatstack-0.85.0}/.python-version +0 -0
  14. {sweatstack-0.84.0 → sweatstack-0.85.0}/AGENTS.md +0 -0
  15. {sweatstack-0.84.0 → sweatstack-0.85.0}/CONTRIBUTING.md +0 -0
  16. {sweatstack-0.84.0 → sweatstack-0.85.0}/DEVELOPMENT.md +0 -0
  17. {sweatstack-0.84.0 → sweatstack-0.85.0}/LICENSE +0 -0
  18. {sweatstack-0.84.0 → sweatstack-0.85.0}/Makefile +0 -0
  19. {sweatstack-0.84.0 → sweatstack-0.85.0}/README.md +0 -0
  20. {sweatstack-0.84.0 → sweatstack-0.85.0}/docs/conf.py +0 -0
  21. {sweatstack-0.84.0 → sweatstack-0.85.0}/docs/everything.rst +0 -0
  22. {sweatstack-0.84.0 → sweatstack-0.85.0}/docs/index.rst +0 -0
  23. {sweatstack-0.84.0 → sweatstack-0.85.0}/examples/fastapi_webhooks_example.py +0 -0
  24. {sweatstack-0.84.0 → sweatstack-0.85.0}/examples/send_webhook.py +0 -0
  25. {sweatstack-0.84.0 → sweatstack-0.85.0}/plans/001a_tests.md +0 -0
  26. {sweatstack-0.84.0 → sweatstack-0.85.0}/plans/001b_metadata.md +0 -0
  27. {sweatstack-0.84.0 → sweatstack-0.85.0}/plans/001c_dailies.md +0 -0
  28. {sweatstack-0.84.0 → sweatstack-0.85.0}/plans/002_TYPED_EXCEPTIONS.md +0 -0
  29. {sweatstack-0.84.0 → sweatstack-0.85.0}/plans/003_trace_test_linking.md +0 -0
  30. {sweatstack-0.84.0 → sweatstack-0.85.0}/plans/004_codebase_hygiene.md +0 -0
  31. {sweatstack-0.84.0 → sweatstack-0.85.0}/plans/005_ost_sport_bridge.md +0 -0
  32. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/Sweat Stack examples/Getting started.ipynb +0 -0
  33. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/__init__.py +0 -0
  34. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/cli.py +0 -0
  35. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/constants.py +0 -0
  36. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/exceptions.py +0 -0
  37. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/__init__.py +0 -0
  38. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/access_token_cache.py +0 -0
  39. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/config.py +0 -0
  40. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/dependencies.py +0 -0
  41. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/models.py +0 -0
  42. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/routes.py +0 -0
  43. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/session.py +0 -0
  44. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/token_stores.py +0 -0
  45. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/webhooks.py +0 -0
  46. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/ipython_init.py +0 -0
  47. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/jupyterlab_oauth2_startup.py +0 -0
  48. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/openapi_schemas.py +0 -0
  49. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/py.typed +0 -0
  50. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/schemas.py +0 -0
  51. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/streamlit.py +0 -0
  52. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/sweatshell.py +0 -0
  53. {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/utils.py +0 -0
  54. {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/__init__.py +0 -0
  55. {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_access_token_cache.py +0 -0
  56. {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_dailies.py +0 -0
  57. {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_dtype_conversion.py +0 -0
  58. {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_exceptions.py +0 -0
  59. {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_longitudinal_mean_max_after.py +0 -0
  60. {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_metadata.py +0 -0
  61. {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_public_surface.py +0 -0
  62. {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_sport_ost.py +0 -0
  63. {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_teams.py +0 -0
  64. {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_tests.py +0 -0
  65. {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_trace_test_linking.py +0 -0
  66. {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_webhooks.py +0 -0
@@ -6,6 +6,17 @@ 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.85.0] - 2026-06-17
10
+
11
+ ### Changed
12
+
13
+ - `get_longitudinal_mean_max(by=...)` now defaults to `None`: the `by` parameter is omitted from the request so the server picks the orientation. For `after` (fatigue) with `metric="power"` the server now defaults to `by="duration"`; every other case stays `by="intensity"`. The returned frame is indexed on whichever orientation the server used. Passing `by` explicitly still works.
14
+
15
+ ### Deprecated
16
+
17
+ - `by="intensity"` for the `after` (fatigue) case is deprecated and now raises a `DeprecationWarning`; `by="duration"` is the default and only supported orientation going forward. Pass `by="duration"` or leave `by` unset. (`by="intensity"` remains the only orientation for `metric="speed"`.)
18
+
19
+
9
20
  ## [0.84.0] - 2026-06-17
10
21
 
11
22
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sweatstack
3
- Version: 0.84.0
3
+ Version: 0.85.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/
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sweatstack"
3
- version = "0.84.0"
3
+ version = "0.85.0"
4
4
  description = "The official Python client for SweatStack"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -1465,7 +1465,7 @@ class Client(_OAuth2Mixin, _DelegationMixin, _TokenStorageMixin, _LocalCacheMixi
1465
1465
  date: date | str | None = None,
1466
1466
  window_days: int | None = None,
1467
1467
  after: list[float] | float | None = None,
1468
- by: Literal["intensity", "duration"] = "intensity",
1468
+ by: Literal["intensity", "duration"] | None = None,
1469
1469
  ) -> pd.DataFrame:
1470
1470
  """Gets the mean-max curve for one or more sports and a metric.
1471
1471
 
@@ -1483,9 +1483,14 @@ class Client(_OAuth2Mixin, _DelegationMixin, _TokenStorageMixin, _LocalCacheMixi
1483
1483
  enveloped across rides. The returned DataFrame then has an ``after``
1484
1484
  column (one curve per value). Max 5 values; the date range is capped at
1485
1485
  1 year when ``after`` is used.
1486
- by: Axis to index the curve on. ``"intensity"`` (default) indexes by the metric
1487
- value (unchanged). ``"duration"`` indexes by duration via the fast segment
1488
- kernel; currently requires ``after`` and ``metric="power"``.
1486
+ by: Axis to index the curve on, only meaningful with ``after``. ``"duration"``
1487
+ indexes by duration via the fast segment kernel (requires ``metric="power"``);
1488
+ ``"intensity"`` indexes by the metric value and is **deprecated**. For the
1489
+ ``after`` case ``"duration"`` will become the only supported orientation, and
1490
+ more generally ``"duration"`` is set to become the default and only option, so
1491
+ we recommend passing ``by="duration"`` explicitly. When left as ``None``
1492
+ (default), the server picks: ``after`` with ``metric="power"`` resolves to
1493
+ ``"duration"``, every other case to ``"intensity"``.
1489
1494
 
1490
1495
  Returns:
1491
1496
  pd.DataFrame: A pandas DataFrame containing the mean-max curve data, indexed by
@@ -1509,11 +1514,31 @@ class Client(_OAuth2Mixin, _DelegationMixin, _TokenStorageMixin, _LocalCacheMixi
1509
1514
  raise ValueError("'sports' is required.")
1510
1515
  metric = self._enums_to_strings([metric])[0]
1511
1516
 
1517
+ # For the fatigue ('after') case, by='duration' is the default and the only
1518
+ # orientation going forward; by='intensity' is deprecated there. (For metric='speed'
1519
+ # it is still the only option, so no warning.)
1520
+ if by == "intensity" and after is not None and metric == "power":
1521
+ warnings.warn(
1522
+ "by='intensity' is deprecated for 'after' (fatigue) mean-max; by='duration' "
1523
+ "is the default and only supported orientation going forward. Pass "
1524
+ "by='duration' or leave 'by' unset.",
1525
+ DeprecationWarning,
1526
+ stacklevel=2,
1527
+ )
1528
+
1529
+ # Mirror the server's default resolution so the returned frame is indexed on the
1530
+ # orientation the server actually used. ``by=None`` omits the param (the server
1531
+ # decides and the deprecated value-indexed path is not forced).
1532
+ effective_by = by if by is not None else (
1533
+ "duration" if (after is not None and metric == "power") else "intensity"
1534
+ )
1535
+
1512
1536
  params = {
1513
1537
  "sport": self._enums_to_strings(sports),
1514
1538
  "metric": metric,
1515
- "by": by,
1516
1539
  }
1540
+ if by is not None:
1541
+ params["by"] = by
1517
1542
  if start is not None:
1518
1543
  params["start"] = start
1519
1544
  if end is not None:
@@ -1536,7 +1561,7 @@ class Client(_OAuth2Mixin, _DelegationMixin, _TokenStorageMixin, _LocalCacheMixi
1536
1561
  cache_key = self._generate_cache_key("mean_max", **params)
1537
1562
  cached = self._read_cache("mean_max", cache_key)
1538
1563
  if cached is not None:
1539
- return self._shape_mean_max(pd.read_parquet(BytesIO(cached)), metric, after, by)
1564
+ return self._shape_mean_max(pd.read_parquet(BytesIO(cached)), metric, after, effective_by)
1540
1565
 
1541
1566
  with self._http_client() as client:
1542
1567
  response = client.get(
@@ -1548,7 +1573,7 @@ class Client(_OAuth2Mixin, _DelegationMixin, _TokenStorageMixin, _LocalCacheMixi
1548
1573
  if self._cache_enabled():
1549
1574
  self._write_cache("mean_max", cache_key, response.content)
1550
1575
 
1551
- return self._shape_mean_max(pd.read_parquet(BytesIO(response.content)), metric, after, by)
1576
+ return self._shape_mean_max(pd.read_parquet(BytesIO(response.content)), metric, after, effective_by)
1552
1577
 
1553
1578
  def _shape_mean_max(self, df: pd.DataFrame, metric: str, after, by: str = "intensity") -> pd.DataFrame:
1554
1579
  """Standard post-processing for mean-max responses. The ``after`` response is
@@ -2600,7 +2600,7 @@ wheels = [
2600
2600
 
2601
2601
  [[package]]
2602
2602
  name = "sweatstack"
2603
- version = "0.83.0"
2603
+ version = "0.84.0"
2604
2604
  source = { editable = "." }
2605
2605
  dependencies = [
2606
2606
  { name = "email-validator" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes