sweatstack 0.83.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.83.0 → sweatstack-0.85.0}/CHANGELOG.md +22 -0
  2. {sweatstack-0.83.0 → sweatstack-0.85.0}/PKG-INFO +2 -2
  3. {sweatstack-0.83.0 → sweatstack-0.85.0}/pyproject.toml +4 -2
  4. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/client.py +40 -9
  5. {sweatstack-0.83.0 → sweatstack-0.85.0}/tests/test_longitudinal_mean_max_after.py +38 -0
  6. {sweatstack-0.83.0 → sweatstack-0.85.0}/uv.lock +41 -26
  7. {sweatstack-0.83.0 → sweatstack-0.85.0}/.claude/settings.local.json +0 -0
  8. {sweatstack-0.83.0 → sweatstack-0.85.0}/.claude/skills/sweatstack-python/SKILL.md +0 -0
  9. {sweatstack-0.83.0 → sweatstack-0.85.0}/.claude/skills/sweatstack-python/client.md +0 -0
  10. {sweatstack-0.83.0 → sweatstack-0.85.0}/.claude/skills/sweatstack-python/data-models.md +0 -0
  11. {sweatstack-0.83.0 → sweatstack-0.85.0}/.claude/skills/sweatstack-python/fastapi.md +0 -0
  12. {sweatstack-0.83.0 → sweatstack-0.85.0}/.claude/skills/sweatstack-python/streamlit.md +0 -0
  13. {sweatstack-0.83.0 → sweatstack-0.85.0}/.gitignore +0 -0
  14. {sweatstack-0.83.0 → sweatstack-0.85.0}/.python-version +0 -0
  15. {sweatstack-0.83.0 → sweatstack-0.85.0}/AGENTS.md +0 -0
  16. {sweatstack-0.83.0 → sweatstack-0.85.0}/CONTRIBUTING.md +0 -0
  17. {sweatstack-0.83.0 → sweatstack-0.85.0}/DEVELOPMENT.md +0 -0
  18. {sweatstack-0.83.0 → sweatstack-0.85.0}/LICENSE +0 -0
  19. {sweatstack-0.83.0 → sweatstack-0.85.0}/Makefile +0 -0
  20. {sweatstack-0.83.0 → sweatstack-0.85.0}/README.md +0 -0
  21. {sweatstack-0.83.0 → sweatstack-0.85.0}/docs/conf.py +0 -0
  22. {sweatstack-0.83.0 → sweatstack-0.85.0}/docs/everything.rst +0 -0
  23. {sweatstack-0.83.0 → sweatstack-0.85.0}/docs/index.rst +0 -0
  24. {sweatstack-0.83.0 → sweatstack-0.85.0}/examples/fastapi_webhooks_example.py +0 -0
  25. {sweatstack-0.83.0 → sweatstack-0.85.0}/examples/send_webhook.py +0 -0
  26. {sweatstack-0.83.0 → sweatstack-0.85.0}/plans/001a_tests.md +0 -0
  27. {sweatstack-0.83.0 → sweatstack-0.85.0}/plans/001b_metadata.md +0 -0
  28. {sweatstack-0.83.0 → sweatstack-0.85.0}/plans/001c_dailies.md +0 -0
  29. {sweatstack-0.83.0 → sweatstack-0.85.0}/plans/002_TYPED_EXCEPTIONS.md +0 -0
  30. {sweatstack-0.83.0 → sweatstack-0.85.0}/plans/003_trace_test_linking.md +0 -0
  31. {sweatstack-0.83.0 → sweatstack-0.85.0}/plans/004_codebase_hygiene.md +0 -0
  32. {sweatstack-0.83.0 → sweatstack-0.85.0}/plans/005_ost_sport_bridge.md +0 -0
  33. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/Sweat Stack examples/Getting started.ipynb +0 -0
  34. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/__init__.py +0 -0
  35. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/cli.py +0 -0
  36. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/constants.py +0 -0
  37. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/exceptions.py +0 -0
  38. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/__init__.py +0 -0
  39. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/access_token_cache.py +0 -0
  40. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/config.py +0 -0
  41. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/dependencies.py +0 -0
  42. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/models.py +0 -0
  43. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/routes.py +0 -0
  44. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/session.py +0 -0
  45. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/token_stores.py +0 -0
  46. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/fastapi/webhooks.py +0 -0
  47. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/ipython_init.py +0 -0
  48. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/jupyterlab_oauth2_startup.py +0 -0
  49. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/openapi_schemas.py +0 -0
  50. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/py.typed +0 -0
  51. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/schemas.py +0 -0
  52. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/streamlit.py +0 -0
  53. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/sweatshell.py +0 -0
  54. {sweatstack-0.83.0 → sweatstack-0.85.0}/src/sweatstack/utils.py +0 -0
  55. {sweatstack-0.83.0 → sweatstack-0.85.0}/tests/__init__.py +0 -0
  56. {sweatstack-0.83.0 → sweatstack-0.85.0}/tests/test_access_token_cache.py +0 -0
  57. {sweatstack-0.83.0 → sweatstack-0.85.0}/tests/test_dailies.py +0 -0
  58. {sweatstack-0.83.0 → sweatstack-0.85.0}/tests/test_dtype_conversion.py +0 -0
  59. {sweatstack-0.83.0 → sweatstack-0.85.0}/tests/test_exceptions.py +0 -0
  60. {sweatstack-0.83.0 → sweatstack-0.85.0}/tests/test_metadata.py +0 -0
  61. {sweatstack-0.83.0 → sweatstack-0.85.0}/tests/test_public_surface.py +0 -0
  62. {sweatstack-0.83.0 → sweatstack-0.85.0}/tests/test_sport_ost.py +0 -0
  63. {sweatstack-0.83.0 → sweatstack-0.85.0}/tests/test_teams.py +0 -0
  64. {sweatstack-0.83.0 → sweatstack-0.85.0}/tests/test_tests.py +0 -0
  65. {sweatstack-0.83.0 → sweatstack-0.85.0}/tests/test_trace_test_linking.py +0 -0
  66. {sweatstack-0.83.0 → sweatstack-0.85.0}/tests/test_webhooks.py +0 -0
@@ -6,6 +6,28 @@ 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
+
20
+ ## [0.84.0] - 2026-06-17
21
+
22
+ ### Added
23
+
24
+ - `get_longitudinal_mean_max(by=...)`: pass `by="duration"` (with `after`, for `power`) to get the fatigue curve indexed by duration instead of by intensity. Default `by="intensity"` is unchanged.
25
+
26
+ ### Changed
27
+
28
+ - Require `pyarrow>=20`: pyarrow 18/19 fail to read the server's parquet ("Repetition level histogram size mismatch") for longitudinal and adaptive-sampling responses; pyarrow 20+ reads them correctly.
29
+
30
+
9
31
  ## [0.83.0] - 2026-06-16
10
32
 
11
33
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sweatstack
3
- Version: 0.83.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/
@@ -26,7 +26,7 @@ Requires-Dist: httpx>=0.28.1
26
26
  Requires-Dist: open-sport-taxonomy[pydantic]>=0.10.0
27
27
  Requires-Dist: pandas>=2.2.3
28
28
  Requires-Dist: platformdirs>=4.0.0
29
- Requires-Dist: pyarrow>=18.0.0
29
+ Requires-Dist: pyarrow>=20.0.0
30
30
  Requires-Dist: pydantic>=2.10.5
31
31
  Provides-Extra: fastapi
32
32
  Requires-Dist: cryptography>=41.0.0; extra == 'fastapi'
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sweatstack"
3
- version = "0.83.0"
3
+ version = "0.85.0"
4
4
  description = "The official Python client for SweatStack"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -26,7 +26,9 @@ dependencies = [
26
26
  "open-sport-taxonomy[pydantic]>=0.10.0",
27
27
  "pandas>=2.2.3",
28
28
  "platformdirs>=4.0.0",
29
- "pyarrow>=18.0.0",
29
+ # >=20: 18/19 raise "Repetition level histogram size mismatch" reading the server's
30
+ # polars-written parquet (size-statistics histograms); fixed in pyarrow 20.
31
+ "pyarrow>=20.0.0",
30
32
  "pydantic>=2.10.5",
31
33
  ]
32
34
 
@@ -1465,6 +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"] | None = None,
1468
1469
  ) -> pd.DataFrame:
1469
1470
  """Gets the mean-max curve for one or more sports and a metric.
1470
1471
 
@@ -1482,11 +1483,19 @@ class Client(_OAuth2Mixin, _DelegationMixin, _TokenStorageMixin, _LocalCacheMixi
1482
1483
  enveloped across rides. The returned DataFrame then has an ``after``
1483
1484
  column (one curve per value). Max 5 values; the date range is capped at
1484
1485
  1 year when ``after`` is used.
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"``.
1485
1494
 
1486
1495
  Returns:
1487
- pd.DataFrame: A pandas DataFrame containing the mean-max curve data, indexed
1488
- by the metric value. With ``after``, an ``after`` column distinguishes
1489
- the fatigue states.
1496
+ pd.DataFrame: A pandas DataFrame containing the mean-max curve data, indexed by
1497
+ the metric value (``by="intensity"``) or by duration (``by="duration"``).
1498
+ With ``after``, an ``after`` column distinguishes the fatigue states.
1490
1499
 
1491
1500
  Raises:
1492
1501
  ValueError: If both ``sport`` and ``sports`` are provided, or neither is provided.
@@ -1505,10 +1514,31 @@ class Client(_OAuth2Mixin, _DelegationMixin, _TokenStorageMixin, _LocalCacheMixi
1505
1514
  raise ValueError("'sports' is required.")
1506
1515
  metric = self._enums_to_strings([metric])[0]
1507
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
+
1508
1536
  params = {
1509
1537
  "sport": self._enums_to_strings(sports),
1510
1538
  "metric": metric,
1511
1539
  }
1540
+ if by is not None:
1541
+ params["by"] = by
1512
1542
  if start is not None:
1513
1543
  params["start"] = start
1514
1544
  if end is not None:
@@ -1531,7 +1561,7 @@ class Client(_OAuth2Mixin, _DelegationMixin, _TokenStorageMixin, _LocalCacheMixi
1531
1561
  cache_key = self._generate_cache_key("mean_max", **params)
1532
1562
  cached = self._read_cache("mean_max", cache_key)
1533
1563
  if cached is not None:
1534
- 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)
1535
1565
 
1536
1566
  with self._http_client() as client:
1537
1567
  response = client.get(
@@ -1543,15 +1573,16 @@ class Client(_OAuth2Mixin, _DelegationMixin, _TokenStorageMixin, _LocalCacheMixi
1543
1573
  if self._cache_enabled():
1544
1574
  self._write_cache("mean_max", cache_key, response.content)
1545
1575
 
1546
- 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)
1547
1577
 
1548
- def _shape_mean_max(self, df: pd.DataFrame, metric: str, after) -> pd.DataFrame:
1578
+ def _shape_mean_max(self, df: pd.DataFrame, metric: str, after, by: str = "intensity") -> pd.DataFrame:
1549
1579
  """Standard post-processing for mean-max responses. The ``after`` response is
1550
- index-free on the wire; restore the metric-value index so its shape matches the
1551
- no-``after`` curve (with an extra ``after`` column)."""
1580
+ index-free on the wire; restore the natural index so its shape matches the
1581
+ no-``after`` curve (with an extra ``after`` column): the metric value for
1582
+ ``by="intensity"``, or ``duration`` for ``by="duration"``."""
1552
1583
  df = self._postprocess_dataframe(df)
1553
1584
  if after is not None:
1554
- df = df.set_index(metric)
1585
+ df = df.set_index("duration" if by == "duration" else metric)
1555
1586
  return df
1556
1587
 
1557
1588
  def get_longitudinal_awd(
@@ -78,3 +78,41 @@ def test_no_after_is_unchanged(client):
78
78
  result = client.get_longitudinal_mean_max(sports=["cycling"], metric="power")
79
79
  assert "after" not in http.get.call_args.kwargs["params"]
80
80
  assert result.index.name == "power"
81
+
82
+
83
+ def _index_free_by_duration_response() -> bytes:
84
+ """A by=duration long-format response (duration-indexed on the wire as columns)."""
85
+ df = pd.DataFrame({
86
+ "duration": pd.to_timedelta([5, 60, 300, 5, 60], unit="s"),
87
+ "power": [400.0, 300.0, 250.0, 380.0, 290.0],
88
+ "activity_id": ["a", "a", "a", "b", "b"],
89
+ "sport": ["cycling"] * 5,
90
+ "after": [0.0, 0.0, 0.0, 50.0, 50.0],
91
+ })
92
+ buf = BytesIO()
93
+ df.to_parquet(buf, index=False)
94
+ return buf.getvalue()
95
+
96
+
97
+ def test_by_defaults_to_intensity_and_is_sent(client):
98
+ _result, params = _call(client, after=[0, 500])
99
+ assert params["by"] == "intensity"
100
+
101
+
102
+ def test_by_duration_is_sent_and_duration_indexed(client):
103
+ response = MagicMock(status_code=200, content=_index_free_by_duration_response())
104
+ http = MagicMock()
105
+ http.__enter__ = MagicMock(return_value=http)
106
+ http.__exit__ = MagicMock(return_value=False)
107
+ http.get.return_value = response
108
+ with patch.object(client, "_http_client", return_value=http), \
109
+ patch.object(client, "_raise_for_status"), \
110
+ patch.object(client, "_cache_enabled", return_value=False):
111
+ result = client.get_longitudinal_mean_max(
112
+ sports=["cycling"], metric="power", after=[0, 50], by="duration")
113
+ params = http.get.call_args.kwargs["params"]
114
+ assert params["by"] == "duration"
115
+ # duration-indexed (not metric-indexed), with power + after as columns
116
+ assert result.index.name == "duration"
117
+ assert "power" in result.columns and "after" in result.columns
118
+ assert set(result["after"].unique()) == {0.0, 50.0}
@@ -1782,30 +1782,45 @@ wheels = [
1782
1782
 
1783
1783
  [[package]]
1784
1784
  name = "pyarrow"
1785
- version = "19.0.0"
1786
- source = { registry = "https://pypi.org/simple" }
1787
- sdist = { url = "https://files.pythonhosted.org/packages/7b/01/fe1fd04744c2aa038e5a11c7a4adb3d62bce09798695e54f7274b5977134/pyarrow-19.0.0.tar.gz", hash = "sha256:8d47c691765cf497aaeed4954d226568563f1b3b74ff61139f2d77876717084b", size = 1129096, upload-time = "2025-01-16T04:24:25.844Z" }
1788
- wheels = [
1789
- { url = "https://files.pythonhosted.org/packages/bc/2e/152885f5ef421e80dae68b9c133ab261934f93a6d5e16b61d79c0ed597fb/pyarrow-19.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:a7bbe7109ab6198688b7079cbad5a8c22de4d47c4880d8e4847520a83b0d1b68", size = 30667964, upload-time = "2025-01-16T04:21:15.594Z" },
1790
- { url = "https://files.pythonhosted.org/packages/80/c2/08bbee9a8610a47c9a1466845f405baf53a639ddd947c5133d8ba13544b6/pyarrow-19.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:4624c89d6f777c580e8732c27bb8e77fd1433b89707f17c04af7635dd9638351", size = 32125039, upload-time = "2025-01-16T04:21:22.681Z" },
1791
- { url = "https://files.pythonhosted.org/packages/d2/56/06994df823212f5688d3c8bf4294928b12c9be36681872853655724d28c6/pyarrow-19.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b6d3ce4288793350dc2d08d1e184fd70631ea22a4ff9ea5c4ff182130249d9b", size = 41140729, upload-time = "2025-01-16T04:21:31.655Z" },
1792
- { url = "https://files.pythonhosted.org/packages/94/65/38ad577c98140a9db71e9e1e594b6adb58a7478a5afec6456a8ca2df7f70/pyarrow-19.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:450a7d27e840e4d9a384b5c77199d489b401529e75a3b7a3799d4cd7957f2f9c", size = 42202267, upload-time = "2025-01-16T04:21:37.523Z" },
1793
- { url = "https://files.pythonhosted.org/packages/b6/1f/966b722251a7354114ccbb71cf1a83922023e69efd8945ebf628a851ec4c/pyarrow-19.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:a08e2a8a039a3f72afb67a6668180f09fddaa38fe0d21f13212b4aba4b5d2451", size = 40505858, upload-time = "2025-01-16T04:21:43.639Z" },
1794
- { url = "https://files.pythonhosted.org/packages/3b/5e/6bc81aa7fc9affc7d1c03b912fbcc984ca56c2a18513684da267715dab7b/pyarrow-19.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f43f5aef2a13d4d56adadae5720d1fed4c1356c993eda8b59dace4b5983843c1", size = 42084973, upload-time = "2025-01-16T04:21:52.705Z" },
1795
- { url = "https://files.pythonhosted.org/packages/53/c3/2f56da818b6a4758cbd514957c67bd0f078ebffa5390ee2e2bf0f9e8defc/pyarrow-19.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:2f672f5364b2d7829ef7c94be199bb88bf5661dd485e21d2d37de12ccb78a136", size = 25241976, upload-time = "2025-01-16T04:21:59.088Z" },
1796
- { url = "https://files.pythonhosted.org/packages/f5/b9/ba07ed3dd6b6e4f379b78e9c47c50c8886e07862ab7fa6339ac38622d755/pyarrow-19.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:cf3bf0ce511b833f7bc5f5bb3127ba731e97222023a444b7359f3a22e2a3b463", size = 30651291, upload-time = "2025-01-16T04:22:05.239Z" },
1797
- { url = "https://files.pythonhosted.org/packages/ad/10/0d304243c8277035298a68a70807efb76199c6c929bb3363c92ac9be6a0d/pyarrow-19.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:4d8b0c0de0a73df1f1bf439af1b60f273d719d70648e898bc077547649bb8352", size = 32100461, upload-time = "2025-01-16T04:22:11.927Z" },
1798
- { url = "https://files.pythonhosted.org/packages/8a/61/bcfc5182e11831bca3f849945b9b106e09fd10ded773dff466658e972a45/pyarrow-19.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92aff08e23d281c69835e4a47b80569242a504095ef6a6223c1f6bb8883431d", size = 41132491, upload-time = "2025-01-16T04:22:18.958Z" },
1799
- { url = "https://files.pythonhosted.org/packages/8e/87/2915a29049ec352dc69a967fbcbd76b0180319233de0daf8bd368df37099/pyarrow-19.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3b78eff5968a1889a0f3bc81ca57e1e19b75f664d9c61a42a604bf9d8402aae", size = 42192529, upload-time = "2025-01-16T04:22:26.089Z" },
1800
- { url = "https://files.pythonhosted.org/packages/48/18/44e5542b2707a8afaf78b5b88c608f261871ae77787eac07b7c679ca6f0f/pyarrow-19.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:b34d3bde38eba66190b215bae441646330f8e9da05c29e4b5dd3e41bde701098", size = 40495363, upload-time = "2025-01-16T04:22:32.544Z" },
1801
- { url = "https://files.pythonhosted.org/packages/ba/d6/5096deb7599bbd20bc2768058fe23bc725b88eb41bee58303293583a2935/pyarrow-19.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5418d4d0fab3a0ed497bad21d17a7973aad336d66ad4932a3f5f7480d4ca0c04", size = 42074075, upload-time = "2025-01-16T04:22:39.01Z" },
1802
- { url = "https://files.pythonhosted.org/packages/2c/df/e3c839c04c284c9ec3d62b02a8c452b795d9b07b04079ab91ce33484d4c5/pyarrow-19.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:e82c3d5e44e969c217827b780ed8faf7ac4c53f934ae9238872e749fa531f7c9", size = 25239803, upload-time = "2025-01-16T04:22:44.424Z" },
1803
- { url = "https://files.pythonhosted.org/packages/6a/d3/a6d4088e906c7b5d47792256212606d2ae679046dc750eee0ae167338e5c/pyarrow-19.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:f208c3b58a6df3b239e0bb130e13bc7487ed14f39a9ff357b6415e3f6339b560", size = 30695401, upload-time = "2025-01-16T04:22:50.337Z" },
1804
- { url = "https://files.pythonhosted.org/packages/94/25/70040fd0e397dd1b937f459eaeeec942a76027357491dca0ada09d1322af/pyarrow-19.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:c751c1c93955b7a84c06794df46f1cec93e18610dcd5ab7d08e89a81df70a849", size = 32104680, upload-time = "2025-01-16T04:22:56.657Z" },
1805
- { url = "https://files.pythonhosted.org/packages/4e/f9/92783290cc0d80ca16d34b0c126305bfacca4b87dd889c8f16c6ef2a8fd7/pyarrow-19.0.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b903afaa5df66d50fc38672ad095806443b05f202c792694f3a604ead7c6ea6e", size = 41076754, upload-time = "2025-01-16T04:23:02.689Z" },
1806
- { url = "https://files.pythonhosted.org/packages/05/46/2c9870f50a495c72e2b8982ae29a9b1680707ea936edc0de444cec48f875/pyarrow-19.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22a4bc0937856263df8b94f2f2781b33dd7f876f787ed746608e06902d691a5", size = 42163133, upload-time = "2025-01-16T04:23:11.029Z" },
1807
- { url = "https://files.pythonhosted.org/packages/7b/2f/437922b902549228fb15814e8a26105bff2787ece466a8d886eb6699efad/pyarrow-19.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:5e8a28b918e2e878c918f6d89137386c06fe577cd08d73a6be8dafb317dc2d73", size = 40452210, upload-time = "2025-01-16T04:23:18.288Z" },
1808
- { url = "https://files.pythonhosted.org/packages/36/ef/1d7975053af9d106da973bac142d0d4da71b7550a3576cc3e0b3f444d21a/pyarrow-19.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:29cd86c8001a94f768f79440bf83fee23963af5e7bc68ce3a7e5f120e17edf89", size = 42077618, upload-time = "2025-01-16T04:23:25.555Z" },
1785
+ version = "24.0.0"
1786
+ source = { registry = "https://pypi.org/simple" }
1787
+ sdist = { url = "https://files.pythonhosted.org/packages/91/13/13e1069b351bdc3881266e11147ffccf687505dbb0ea74036237f5d454a5/pyarrow-24.0.0.tar.gz", hash = "sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83", size = 1180261, upload-time = "2026-04-21T10:51:25.837Z" }
1788
+ wheels = [
1789
+ { url = "https://files.pythonhosted.org/packages/b4/a9/9686d9f07837f91f775e8932659192e02c74f9d8920524b480b85212cc68/pyarrow-24.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810", size = 34981559, upload-time = "2026-04-21T10:47:22.17Z" },
1790
+ { url = "https://files.pythonhosted.org/packages/80/b6/0ddf0e9b6ead3474ab087ae598c76b031fc45532bf6a63f3a553440fb258/pyarrow-24.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a", size = 36663654, upload-time = "2026-04-21T10:47:28.315Z" },
1791
+ { url = "https://files.pythonhosted.org/packages/7c/3b/926382efe8ce27ba729071d3566ade6dfb86bdf112f366000196b2f5780a/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66", size = 45679394, upload-time = "2026-04-21T10:47:34.821Z" },
1792
+ { url = "https://files.pythonhosted.org/packages/b3/7a/829f7d9dfd37c207206081d6dad474d81dde29952401f07f2ba507814818/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb", size = 48863122, upload-time = "2026-04-21T10:47:42.056Z" },
1793
+ { url = "https://files.pythonhosted.org/packages/5f/e8/f88ce625fe8babaae64e8db2d417c7653adb3019b08aae85c5ed787dc816/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e", size = 49376032, upload-time = "2026-04-21T10:47:48.967Z" },
1794
+ { url = "https://files.pythonhosted.org/packages/36/7a/82c363caa145fff88fb475da50d3bf52bb024f61917be5424c3392eaf878/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6", size = 51929490, upload-time = "2026-04-21T10:47:55.981Z" },
1795
+ { url = "https://files.pythonhosted.org/packages/66/1c/e3e72c8014ad2743ca64a701652c733cc5cbcee15c0463a32a8c55518d9e/pyarrow-24.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826", size = 27355660, upload-time = "2026-04-21T10:48:01.718Z" },
1796
+ { url = "https://files.pythonhosted.org/packages/6f/d3/a1abf004482026ddc17f4503db227787fa3cfe41ec5091ff20e4fea55e57/pyarrow-24.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba", size = 34976759, upload-time = "2026-04-21T10:48:07.258Z" },
1797
+ { url = "https://files.pythonhosted.org/packages/4f/4a/34f0a36d28a2dd32225301b79daad44e243dc1a2bb77d43b60749be255c4/pyarrow-24.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68", size = 36658471, upload-time = "2026-04-21T10:48:13.347Z" },
1798
+ { url = "https://files.pythonhosted.org/packages/1f/78/543b94712ae8bb1a6023bcc1acf1a740fbff8286747c289cd9468fced2a5/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2", size = 45675981, upload-time = "2026-04-21T10:48:20.201Z" },
1799
+ { url = "https://files.pythonhosted.org/packages/84/9f/8fb7c222b100d314137fa40ec050de56cd8c6d957d1cfff685ce72f15b17/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0", size = 48859172, upload-time = "2026-04-21T10:48:27.541Z" },
1800
+ { url = "https://files.pythonhosted.org/packages/a7/d3/1ea72538e6c8b3b475ed78d1049a2c518e655761ea50fe1171fc855fcab7/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495", size = 49385733, upload-time = "2026-04-21T10:48:34.7Z" },
1801
+ { url = "https://files.pythonhosted.org/packages/c3/be/c3d8b06a1ba35f2260f8e1f771abbee7d5e345c0937aab90675706b1690a/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f", size = 51934335, upload-time = "2026-04-21T10:48:42.099Z" },
1802
+ { url = "https://files.pythonhosted.org/packages/9c/62/89e07a1e7329d2cde3e3c6994ba0839a24977a2beda8be6005ea3d860b99/pyarrow-24.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91", size = 27271748, upload-time = "2026-04-21T10:49:42.532Z" },
1803
+ { url = "https://files.pythonhosted.org/packages/17/1a/cff3a59f80b5b1658549d46611b67163f65e0664431c076ad728bf9d5af4/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275", size = 35238554, upload-time = "2026-04-21T10:48:48.526Z" },
1804
+ { url = "https://files.pythonhosted.org/packages/a8/99/cce0f42a327bfef2c420fb6078a3eb834826e5d6697bf3009fe11d2ad051/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b", size = 36782301, upload-time = "2026-04-21T10:48:55.181Z" },
1805
+ { url = "https://files.pythonhosted.org/packages/2a/66/8e560d5ff6793ca29aca213c53eec0dd482dd46cb93b2819e5aab52e4252/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42", size = 45721929, upload-time = "2026-04-21T10:49:03.676Z" },
1806
+ { url = "https://files.pythonhosted.org/packages/27/0c/a26e25505d030716e078d9f16eb74973cbf0b33b672884e9f9da1c83b871/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b", size = 48825365, upload-time = "2026-04-21T10:49:11.714Z" },
1807
+ { url = "https://files.pythonhosted.org/packages/5f/eb/771f9ecb0c65e73fe9dccdd1717901b9594f08c4515d000c7c62df573811/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37", size = 49451819, upload-time = "2026-04-21T10:49:21.474Z" },
1808
+ { url = "https://files.pythonhosted.org/packages/48/da/61ae89a88732f5a785646f3ec6125dbb640fa98a540eb2b9889caa561403/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca", size = 51909252, upload-time = "2026-04-21T10:49:31.164Z" },
1809
+ { url = "https://files.pythonhosted.org/packages/cb/1a/8dd5cafab7b66573fa91c03d06d213356ad4edd71813aa75e08ce2b3a844/pyarrow-24.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d", size = 27388127, upload-time = "2026-04-21T10:49:37.334Z" },
1810
+ { url = "https://files.pythonhosted.org/packages/ad/80/d022a34ff05d2cbedd8ccf841fc1f532ecfa9eb5ed1711b56d0e0ea71fc9/pyarrow-24.0.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838", size = 35007997, upload-time = "2026-04-21T10:49:48.796Z" },
1811
+ { url = "https://files.pythonhosted.org/packages/1a/ff/f01485fda6f4e5d441afb8dd5e7681e4db18826c1e271852f5d3957d6a80/pyarrow-24.0.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b", size = 36678720, upload-time = "2026-04-21T10:49:55.858Z" },
1812
+ { url = "https://files.pythonhosted.org/packages/9e/c2/2d2d5fea814237923f71b36495211f20b43a1576f9a4d6da7e751a64ec6f/pyarrow-24.0.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795", size = 45741852, upload-time = "2026-04-21T10:50:04.624Z" },
1813
+ { url = "https://files.pythonhosted.org/packages/8e/3a/28ba9c1c1ebdbb5f1b94dfebb46f207e52e6a554b7fe4132540fde29a3a0/pyarrow-24.0.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26", size = 48889852, upload-time = "2026-04-21T10:50:12.293Z" },
1814
+ { url = "https://files.pythonhosted.org/packages/df/51/4a389acfd31dca009f8fb82d7f510bb4130f2b3a8e18cf00194d0687d8ac/pyarrow-24.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde", size = 49445207, upload-time = "2026-04-21T10:50:20.677Z" },
1815
+ { url = "https://files.pythonhosted.org/packages/19/4b/0bab2b23d2ae901b1b9a03c0efd4b2d070256f8ce3fc43f6e58c167b2081/pyarrow-24.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76", size = 51954117, upload-time = "2026-04-21T10:50:29.14Z" },
1816
+ { url = "https://files.pythonhosted.org/packages/29/88/f4e9145da0417b3d2c12035a8492b35ff4a3dbc653e614fcfb51d9dedb38/pyarrow-24.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e", size = 28001155, upload-time = "2026-04-21T10:51:22.337Z" },
1817
+ { url = "https://files.pythonhosted.org/packages/79/4f/46a49a63f43526da895b1a45bbb51d5baf8e4d77159f8528fc3e5490007f/pyarrow-24.0.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05", size = 35250387, upload-time = "2026-04-21T10:50:35.552Z" },
1818
+ { url = "https://files.pythonhosted.org/packages/a0/da/d5e0cd5ef00796922404806d5f00325cdadc3441ce2c13fe7115f2df9a64/pyarrow-24.0.0-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a", size = 36797102, upload-time = "2026-04-21T10:50:42.417Z" },
1819
+ { url = "https://files.pythonhosted.org/packages/34/c7/5904145b0a593a05236c882933d439b5720f0a145381179063722fbfc123/pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072", size = 45745118, upload-time = "2026-04-21T10:50:49.324Z" },
1820
+ { url = "https://files.pythonhosted.org/packages/13/d3/cca42fe166d1c6e4d5b80e530b7949104d10e17508a90ae202dac205ce2a/pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931", size = 48844765, upload-time = "2026-04-21T10:50:55.579Z" },
1821
+ { url = "https://files.pythonhosted.org/packages/b0/49/942c3b79878ba928324d1e17c274ed84581db8c0a749b24bcf4cbdf15bd3/pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699", size = 49471890, upload-time = "2026-04-21T10:51:02.439Z" },
1822
+ { url = "https://files.pythonhosted.org/packages/76/97/ff71431000a75d84135a1ace5ca4ba11726a231a8007bbb320a4c54075d5/pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136", size = 51932250, upload-time = "2026-04-21T10:51:10.576Z" },
1823
+ { url = "https://files.pythonhosted.org/packages/51/be/6f79d55816d5c22557cf27533543d5d70dfe692adfbee4b99f2760674f38/pyarrow-24.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19", size = 28131282, upload-time = "2026-04-21T10:51:16.815Z" },
1809
1824
  ]
1810
1825
 
1811
1826
  [[package]]
@@ -2585,7 +2600,7 @@ wheels = [
2585
2600
 
2586
2601
  [[package]]
2587
2602
  name = "sweatstack"
2588
- version = "0.82.0"
2603
+ version = "0.84.0"
2589
2604
  source = { editable = "." }
2590
2605
  dependencies = [
2591
2606
  { name = "email-validator" },
@@ -2633,7 +2648,7 @@ requires-dist = [
2633
2648
  { name = "open-sport-taxonomy", extras = ["pydantic"], specifier = ">=0.10.0" },
2634
2649
  { name = "pandas", specifier = ">=2.2.3" },
2635
2650
  { name = "platformdirs", specifier = ">=4.0.0" },
2636
- { name = "pyarrow", specifier = ">=18.0.0" },
2651
+ { name = "pyarrow", specifier = ">=20.0.0" },
2637
2652
  { name = "pydantic", specifier = ">=2.10.5" },
2638
2653
  { name = "streamlit", marker = "extra == 'streamlit'", specifier = ">=1.42.0" },
2639
2654
  ]
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