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.
- {sweatstack-0.84.0 → sweatstack-0.85.0}/CHANGELOG.md +11 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/PKG-INFO +1 -1
- {sweatstack-0.84.0 → sweatstack-0.85.0}/pyproject.toml +1 -1
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/client.py +32 -7
- {sweatstack-0.84.0 → sweatstack-0.85.0}/uv.lock +1 -1
- {sweatstack-0.84.0 → sweatstack-0.85.0}/.claude/settings.local.json +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/.claude/skills/sweatstack-python/SKILL.md +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/.claude/skills/sweatstack-python/client.md +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/.claude/skills/sweatstack-python/data-models.md +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/.claude/skills/sweatstack-python/fastapi.md +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/.claude/skills/sweatstack-python/streamlit.md +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/.gitignore +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/.python-version +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/AGENTS.md +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/CONTRIBUTING.md +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/DEVELOPMENT.md +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/LICENSE +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/Makefile +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/README.md +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/docs/conf.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/docs/everything.rst +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/docs/index.rst +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/examples/fastapi_webhooks_example.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/examples/send_webhook.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/plans/001a_tests.md +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/plans/001b_metadata.md +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/plans/001c_dailies.md +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/plans/002_TYPED_EXCEPTIONS.md +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/plans/003_trace_test_linking.md +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/plans/004_codebase_hygiene.md +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/plans/005_ost_sport_bridge.md +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/Sweat Stack examples/Getting started.ipynb +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/__init__.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/cli.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/constants.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/exceptions.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/__init__.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/access_token_cache.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/config.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/dependencies.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/models.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/routes.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/session.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/token_stores.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/webhooks.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/ipython_init.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/jupyterlab_oauth2_startup.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/openapi_schemas.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/py.typed +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/schemas.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/streamlit.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/sweatshell.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/utils.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/__init__.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_access_token_cache.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_dailies.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_dtype_conversion.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_exceptions.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_longitudinal_mean_max_after.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_metadata.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_public_surface.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_sport_ost.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_teams.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_tests.py +0 -0
- {sweatstack-0.84.0 → sweatstack-0.85.0}/tests/test_trace_test_linking.py +0 -0
- {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
|
|
@@ -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"] =
|
|
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
|
|
1487
|
-
|
|
1488
|
-
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sweatstack-0.84.0 → sweatstack-0.85.0}/src/sweatstack/Sweat Stack examples/Getting started.ipynb
RENAMED
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|