sweatstack 0.46.0__py3-none-any.whl → 0.47.0__py3-none-any.whl

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/client.py CHANGED
@@ -16,7 +16,7 @@ from http.server import BaseHTTPRequestHandler, HTTPServer
16
16
  from importlib.metadata import version
17
17
  from io import BytesIO
18
18
  from pathlib import Path
19
- from typing import Any, Generator, get_type_hints, List, Literal
19
+ from typing import Any, Dict, Generator, get_type_hints, List, Literal
20
20
  from urllib.parse import parse_qs, urlparse
21
21
 
22
22
  import httpx
@@ -25,7 +25,8 @@ from platformdirs import user_data_dir
25
25
 
26
26
  from .constants import DEFAULT_URL
27
27
  from .schemas import (
28
- ActivityDetails, ActivitySummary, Metric, Sport, TraceDetails, UserInfoResponse, UserSummary
28
+ ActivityDetails, ActivitySummary, BackfillStatus, Metric, Sport,
29
+ TraceDetails, UserInfoResponse, UserSummary
29
30
  )
30
31
  from .utils import decode_jwt_body, make_dataframe_streamlit_compatible
31
32
 
@@ -1230,6 +1231,67 @@ class Client(OAuth2Mixin, DelegationMixin, TokenStorageMixin):
1230
1231
 
1231
1232
  return self._get_user_by_id(user_id)
1232
1233
 
1234
+ def _parse_backfill_line(self, line: str) -> BackfillStatus | None:
1235
+ """Parse a single NDJSON line from backfill status stream."""
1236
+ try:
1237
+ return BackfillStatus.model_validate_json(line)
1238
+ except Exception:
1239
+ pass
1240
+ return None
1241
+
1242
+ def watch_backfill_status(self, *, auto_reconnect: bool = False) -> Generator[BackfillStatus, None, None]:
1243
+ """Watches backfill status from the activities backfill-status endpoint.
1244
+
1245
+ This method connects to the backfill status event stream and yields
1246
+ backfill_loaded_until timestamps as they are received. The connection
1247
+ automatically closes after 60 seconds, but can be configured to auto-reconnect.
1248
+
1249
+ Args:
1250
+ auto_reconnect: Whether to automatically reconnect when the connection
1251
+ closes and continue receiving updates. Defaults to False.
1252
+
1253
+ Yields:
1254
+ BackfillStatus: A BackfillStatus object for each received message.
1255
+
1256
+ Raises:
1257
+ HTTPStatusError: If the API request fails.
1258
+ """
1259
+ while True:
1260
+ try:
1261
+ with self._http_client() as client:
1262
+ with client.stream("GET", "/api/v1/activities/backfill-status") as response:
1263
+ self._raise_for_status(response)
1264
+
1265
+ for line in response.iter_lines():
1266
+ if line.strip():
1267
+ parsed = self._parse_backfill_line(line)
1268
+ if parsed:
1269
+ yield parsed
1270
+
1271
+ except httpx.RequestError:
1272
+ if not auto_reconnect:
1273
+ raise
1274
+ time.sleep(1)
1275
+ if not auto_reconnect:
1276
+ break
1277
+
1278
+ def get_backfill_status(self) -> BackfillStatus:
1279
+ """Gets the current backfill status from the activities backfill-status endpoint.
1280
+
1281
+ This method connects to the backfill status event stream and returns
1282
+ the first backfill_loaded_until timestamp received.
1283
+
1284
+ Returns:
1285
+ BackfillStatus: A BackfillStatus object containing the current backfill status.
1286
+
1287
+ Raises:
1288
+ HTTPStatusError: If the API request fails.
1289
+ ValueError: If no status message is received.
1290
+ """
1291
+ for status in self.watch_backfill_status(auto_reconnect=False):
1292
+ return status
1293
+ raise ValueError("No backfill status received")
1294
+
1233
1295
 
1234
1296
  _default_client = Client()
1235
1297
 
@@ -1276,6 +1338,9 @@ _generate_singleton_methods(
1276
1338
  "get_userinfo",
1277
1339
  "whoami",
1278
1340
 
1341
+ "get_backfill_status",
1342
+ "watch_backfill_status",
1343
+
1279
1344
  "get_activities",
1280
1345
 
1281
1346
  "get_activity",
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: openapi.json
3
- # timestamp: 2025-08-01T16:59:37+00:00
3
+ # timestamp: 2025-08-07T11:42:52+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -36,6 +36,12 @@ class Prompt(Enum):
36
36
  select_account = 'select_account'
37
37
 
38
38
 
39
+ class BackfillStatus(BaseModel):
40
+ backfill_loaded_until: Optional[datetime] = Field(
41
+ ..., title='Backfill Loaded Until'
42
+ )
43
+
44
+
39
45
  class BodyAddEmailPartialsAddEmailPost(BaseModel):
40
46
  email: EmailStr = Field(..., title='Email')
41
47
 
@@ -664,6 +670,7 @@ class BodyLoginPostLoginPost(BaseModel):
664
670
  email: EmailStr = Field(..., title='Email')
665
671
  password: SecretStr = Field(..., title='Password')
666
672
  tz: Tz = Field(..., title='Tz')
673
+ state: Optional[str] = Field(None, title='State')
667
674
 
668
675
 
669
676
  class BodyRegisterPostRegisterPost(BaseModel):
sweatstack/schemas.py CHANGED
@@ -2,7 +2,7 @@ from enum import Enum
2
2
  from typing import List, Union
3
3
 
4
4
  from .openapi_schemas import (
5
- ActivityDetails, ActivitySummary, Metric, Scope, Sport,
5
+ ActivityDetails, ActivitySummary, BackfillStatus, Metric, Scope, Sport,
6
6
  TraceDetails, UserInfoResponse, UserSummary
7
7
  )
8
8
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sweatstack
3
- Version: 0.46.0
3
+ Version: 0.47.0
4
4
  Summary: The official Python client for SweatStack
5
5
  Author-email: Aart Goossens <aart@gssns.io>
6
6
  Requires-Python: >=3.9
@@ -1,17 +1,17 @@
1
1
  sweatstack/__init__.py,sha256=tiVfgKlswRPaDMEy0gA7u8rveqEYZTA_kyB9lJ3J6Sc,21
2
2
  sweatstack/cli.py,sha256=N1NWOgEZR2yaJvIXxo9qvp_jFlypZYb0nujpbVNYQ6A,720
3
- sweatstack/client.py,sha256=bea8IY72fH93PES-dKMbNaB02k_GepRDzVMxEQuBI4g,46966
3
+ sweatstack/client.py,sha256=31iiErq1nWk-zFgApTN_iCWn7an7bB3VOBygGZgAOJ8,49517
4
4
  sweatstack/constants.py,sha256=fGO6ksOv5HeISv9lHRoYm4besW1GTveXS8YD3K0ljg0,41
5
5
  sweatstack/ipython_init.py,sha256=OtBB9dQvyLXklD4kA2x1swaVtU9u73fG4V4-zz4YRAg,139
6
6
  sweatstack/jupyterlab_oauth2_startup.py,sha256=YcjXvzeZ459vL_dCkFi1IxX_RNAu80ZX9rwa0OXJfTM,1023
7
- sweatstack/openapi_schemas.py,sha256=GaA16d2jAryI1xsxAmRPzyyqhE4jvVKiF6omyIAK4bA,46028
7
+ sweatstack/openapi_schemas.py,sha256=VvquBdbssdB9D1KeJYQCx51hy1Df4SS0PjzGWXcUaew,46221
8
8
  sweatstack/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- sweatstack/schemas.py,sha256=cYefSktNv9VJoq4PQ5SrUvZRRwB637xNOaj7kjzjocM,3667
9
+ sweatstack/schemas.py,sha256=44sXrNCYR0qtDsH8sUKR8df9zpcVA-TimkinXJBesOg,3683
10
10
  sweatstack/streamlit.py,sha256=_PER03s0dYu5eF1MZdewPDqSvYHqMr0lZLu_EnGit3Y,13257
11
11
  sweatstack/sweatshell.py,sha256=MYLNcWbOdceqKJ3S0Pe8dwHXEeYsGJNjQoYUXpMTftA,333
12
12
  sweatstack/utils.py,sha256=AwHRdC1ziOZ5o9RBIB21Uxm-DoClVRAJSVvgsmSmvps,1801
13
13
  sweatstack/Sweat Stack examples/Getting started.ipynb,sha256=k2hiSffWecoQ0VxjdpDcgFzBXDQiYEebhnAYlu8cgX8,6335204
14
- sweatstack-0.46.0.dist-info/METADATA,sha256=RLthZaCIu9YSePVaHuDfPX9TI5KpJX2Td0i0rm4TrYg,852
15
- sweatstack-0.46.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
- sweatstack-0.46.0.dist-info/entry_points.txt,sha256=kCzOUQI3dqbTpEYqtgYDeiKFaqaA7BMlV6D24BMzCFU,208
17
- sweatstack-0.46.0.dist-info/RECORD,,
14
+ sweatstack-0.47.0.dist-info/METADATA,sha256=UuRlROP5y90JVM6eopj0PklP0WvdkB1TWxy6RwALrS0,852
15
+ sweatstack-0.47.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
+ sweatstack-0.47.0.dist-info/entry_points.txt,sha256=kCzOUQI3dqbTpEYqtgYDeiKFaqaA7BMlV6D24BMzCFU,208
17
+ sweatstack-0.47.0.dist-info/RECORD,,