sweatstack 0.31.1__py3-none-any.whl → 0.33.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 +42 -28
- {sweatstack-0.31.1.dist-info → sweatstack-0.33.0.dist-info}/METADATA +1 -1
- {sweatstack-0.31.1.dist-info → sweatstack-0.33.0.dist-info}/RECORD +5 -5
- {sweatstack-0.31.1.dist-info → sweatstack-0.33.0.dist-info}/WHEEL +0 -0
- {sweatstack-0.31.1.dist-info → sweatstack-0.33.0.dist-info}/entry_points.txt +0 -0
sweatstack/client.py
CHANGED
|
@@ -8,6 +8,7 @@ import time
|
|
|
8
8
|
import urllib
|
|
9
9
|
import webbrowser
|
|
10
10
|
from datetime import date, datetime
|
|
11
|
+
from enum import Enum
|
|
11
12
|
from functools import wraps
|
|
12
13
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
13
14
|
from importlib.metadata import version
|
|
@@ -20,7 +21,7 @@ import pandas as pd
|
|
|
20
21
|
|
|
21
22
|
from .constants import DEFAULT_URL
|
|
22
23
|
from .schemas import (
|
|
23
|
-
ActivityDetails, ActivitySummary, Sport, TraceDetails, UserSummary
|
|
24
|
+
ActivityDetails, ActivitySummary, Metric, Sport, TraceDetails, UserSummary
|
|
24
25
|
)
|
|
25
26
|
from .utils import decode_jwt_body, make_dataframe_streamlit_compatible
|
|
26
27
|
|
|
@@ -114,7 +115,7 @@ class OAuth2Mixin:
|
|
|
114
115
|
data=token_data,
|
|
115
116
|
)
|
|
116
117
|
try:
|
|
117
|
-
|
|
118
|
+
self._raise_for_status(response)
|
|
118
119
|
except httpx.HTTPStatusError as e:
|
|
119
120
|
raise Exception(f"SweatStack Python login failed. Please try again.") from e
|
|
120
121
|
token_response = response.json()
|
|
@@ -141,7 +142,7 @@ class DelegationMixin:
|
|
|
141
142
|
"/api/v1/oauth/delegated-token",
|
|
142
143
|
json={"sub": user_id},
|
|
143
144
|
)
|
|
144
|
-
|
|
145
|
+
self._raise_for_status(response)
|
|
145
146
|
|
|
146
147
|
return response.json()
|
|
147
148
|
|
|
@@ -155,7 +156,7 @@ class DelegationMixin:
|
|
|
155
156
|
response = client.get(
|
|
156
157
|
"/api/v1/oauth/principal-token",
|
|
157
158
|
)
|
|
158
|
-
|
|
159
|
+
self._raise_for_status(response)
|
|
159
160
|
return response.json()
|
|
160
161
|
|
|
161
162
|
def switch_back(self):
|
|
@@ -206,7 +207,7 @@ class Client(OAuth2Mixin, DelegationMixin):
|
|
|
206
207
|
},
|
|
207
208
|
)
|
|
208
209
|
|
|
209
|
-
|
|
210
|
+
self._raise_for_status(response)
|
|
210
211
|
return response.json()["access_token"]
|
|
211
212
|
|
|
212
213
|
def _check_token_expiry(self, token: str) -> str:
|
|
@@ -286,6 +287,15 @@ class Client(OAuth2Mixin, DelegationMixin):
|
|
|
286
287
|
with httpx.Client(base_url=self.url, headers=headers) as client:
|
|
287
288
|
yield client
|
|
288
289
|
|
|
290
|
+
def _raise_for_status(self, response: httpx.Response):
|
|
291
|
+
if response.status_code == 422:
|
|
292
|
+
raise ValueError(response.json())
|
|
293
|
+
else:
|
|
294
|
+
response.raise_for_status()
|
|
295
|
+
|
|
296
|
+
def _enums_to_strings(self, values: list[Enum | str]) -> list[str]:
|
|
297
|
+
return [value.value if isinstance(value, Enum) else value for value in values]
|
|
298
|
+
|
|
289
299
|
def _get_activities_generator(
|
|
290
300
|
self,
|
|
291
301
|
*,
|
|
@@ -306,8 +316,7 @@ class Client(OAuth2Mixin, DelegationMixin):
|
|
|
306
316
|
if end is not None:
|
|
307
317
|
params["end"] = end.isoformat()
|
|
308
318
|
if sports is not None:
|
|
309
|
-
sports =
|
|
310
|
-
params["sports"] = sports
|
|
319
|
+
params["sports"] = self._enums_to_strings(sports)
|
|
311
320
|
if tags is not None:
|
|
312
321
|
params["tags"] = tags
|
|
313
322
|
|
|
@@ -317,7 +326,7 @@ class Client(OAuth2Mixin, DelegationMixin):
|
|
|
317
326
|
url="/api/v1/activities/",
|
|
318
327
|
params=params,
|
|
319
328
|
)
|
|
320
|
-
|
|
329
|
+
self._raise_for_status(response)
|
|
321
330
|
activities = response.json()
|
|
322
331
|
for activity in activities:
|
|
323
332
|
yield ActivitySummary.model_validate(activity)
|
|
@@ -383,7 +392,7 @@ class Client(OAuth2Mixin, DelegationMixin):
|
|
|
383
392
|
def get_activity(self, activity_id: str) -> ActivityDetails:
|
|
384
393
|
with self._http_client() as client:
|
|
385
394
|
response = client.get(url=f"/api/v1/activities/{activity_id}")
|
|
386
|
-
|
|
395
|
+
self._raise_for_status(response)
|
|
387
396
|
return ActivityDetails.model_validate(response.json())
|
|
388
397
|
|
|
389
398
|
def get_activity_data(
|
|
@@ -400,8 +409,7 @@ class Client(OAuth2Mixin, DelegationMixin):
|
|
|
400
409
|
url=f"/api/v1/activities/{activity_id}/data",
|
|
401
410
|
params=params,
|
|
402
411
|
)
|
|
403
|
-
|
|
404
|
-
response.raise_for_status()
|
|
412
|
+
self._raise_for_status(response)
|
|
405
413
|
|
|
406
414
|
df = pd.read_parquet(BytesIO(response.content))
|
|
407
415
|
return self._postprocess_dataframe(df)
|
|
@@ -409,9 +417,10 @@ class Client(OAuth2Mixin, DelegationMixin):
|
|
|
409
417
|
def get_activity_mean_max(
|
|
410
418
|
self,
|
|
411
419
|
activity_id: str,
|
|
412
|
-
metric:
|
|
420
|
+
metric: Literal[Metric.power, Metric.speed] | Literal["power", "speed"],
|
|
413
421
|
adaptive_sampling: bool = False,
|
|
414
422
|
) -> pd.DataFrame:
|
|
423
|
+
metric = self._enums_to_strings([metric])[0]
|
|
415
424
|
with self._http_client() as client:
|
|
416
425
|
response = client.get(
|
|
417
426
|
url=f"/api/v1/activities/{activity_id}/mean-max",
|
|
@@ -420,13 +429,13 @@ class Client(OAuth2Mixin, DelegationMixin):
|
|
|
420
429
|
"adaptive_sampling": adaptive_sampling,
|
|
421
430
|
},
|
|
422
431
|
)
|
|
423
|
-
|
|
432
|
+
self._raise_for_status(response)
|
|
424
433
|
df = pd.read_parquet(BytesIO(response.content))
|
|
425
434
|
return self._postprocess_dataframe(df)
|
|
426
435
|
|
|
427
436
|
def get_latest_activity_data(
|
|
428
437
|
self,
|
|
429
|
-
sport: Sport | None = None,
|
|
438
|
+
sport: Sport | str | None = None,
|
|
430
439
|
adaptive_sampling_on: Literal["power", "speed"] | None = None,
|
|
431
440
|
) -> pd.DataFrame:
|
|
432
441
|
activity = self.get_latest_activity(sport=sport)
|
|
@@ -434,8 +443,8 @@ class Client(OAuth2Mixin, DelegationMixin):
|
|
|
434
443
|
|
|
435
444
|
def get_latest_activity_mean_max(
|
|
436
445
|
self,
|
|
437
|
-
metric:
|
|
438
|
-
sport: Sport | None = None,
|
|
446
|
+
metric: Literal[Metric.power, Metric.speed] | Literal["power", "speed"],
|
|
447
|
+
sport: Sport | str | None = None,
|
|
439
448
|
adaptive_sampling: bool = False,
|
|
440
449
|
) -> pd.DataFrame:
|
|
441
450
|
activity = self.get_latest_activity(sport=sport)
|
|
@@ -444,11 +453,11 @@ class Client(OAuth2Mixin, DelegationMixin):
|
|
|
444
453
|
def get_longitudinal_data(
|
|
445
454
|
self,
|
|
446
455
|
*,
|
|
447
|
-
sport: Sport | None = None,
|
|
456
|
+
sport: Sport | str | None = None,
|
|
448
457
|
sports: list[Sport | str] | None = None,
|
|
449
458
|
start: date | str,
|
|
450
459
|
end: date | str | None = None,
|
|
451
|
-
metrics: list[str] | None = None,
|
|
460
|
+
metrics: list[Metric | str] | None = None,
|
|
452
461
|
adaptive_sampling_on: Literal["power", "speed"] | None = None,
|
|
453
462
|
) -> pd.DataFrame:
|
|
454
463
|
if sport and sports:
|
|
@@ -458,6 +467,9 @@ class Client(OAuth2Mixin, DelegationMixin):
|
|
|
458
467
|
elif sports is None:
|
|
459
468
|
sports = []
|
|
460
469
|
|
|
470
|
+
sports = self._enums_to_strings(sports)
|
|
471
|
+
metrics = self._enums_to_strings(metrics)
|
|
472
|
+
|
|
461
473
|
params = {
|
|
462
474
|
"sports": sports,
|
|
463
475
|
"start": start,
|
|
@@ -474,7 +486,7 @@ class Client(OAuth2Mixin, DelegationMixin):
|
|
|
474
486
|
url="/api/v1/activities/longitudinal-data",
|
|
475
487
|
params=params,
|
|
476
488
|
)
|
|
477
|
-
|
|
489
|
+
self._raise_for_status(response)
|
|
478
490
|
|
|
479
491
|
df = pd.read_parquet(BytesIO(response.content))
|
|
480
492
|
return self._postprocess_dataframe(df)
|
|
@@ -483,10 +495,13 @@ class Client(OAuth2Mixin, DelegationMixin):
|
|
|
483
495
|
self,
|
|
484
496
|
*,
|
|
485
497
|
sport: Sport | str,
|
|
486
|
-
metric:
|
|
498
|
+
metric: Literal[Metric.power, Metric.speed] | Literal["power", "speed"],
|
|
487
499
|
date: date | str | None = None,
|
|
488
500
|
window_days: int | None = None,
|
|
489
501
|
) -> pd.DataFrame:
|
|
502
|
+
sport = self._enums_to_strings([sport])[0]
|
|
503
|
+
metric = self._enums_to_strings([metric])[0]
|
|
504
|
+
|
|
490
505
|
params = {
|
|
491
506
|
"sport": sport,
|
|
492
507
|
"metric": metric,
|
|
@@ -501,7 +516,7 @@ class Client(OAuth2Mixin, DelegationMixin):
|
|
|
501
516
|
url="/api/v1/activities/longitudinal-mean-max",
|
|
502
517
|
params=params,
|
|
503
518
|
)
|
|
504
|
-
|
|
519
|
+
self._raise_for_status(response)
|
|
505
520
|
|
|
506
521
|
df = pd.read_parquet(BytesIO(response.content))
|
|
507
522
|
return self._postprocess_dataframe(df)
|
|
@@ -526,8 +541,7 @@ class Client(OAuth2Mixin, DelegationMixin):
|
|
|
526
541
|
if end is not None:
|
|
527
542
|
params["end"] = end.isoformat()
|
|
528
543
|
if sports is not None:
|
|
529
|
-
sports =
|
|
530
|
-
params["sports"] = sports
|
|
544
|
+
params["sports"] = self._enums_to_strings(sports)
|
|
531
545
|
if tags is not None:
|
|
532
546
|
params["tags"] = tags
|
|
533
547
|
|
|
@@ -537,7 +551,7 @@ class Client(OAuth2Mixin, DelegationMixin):
|
|
|
537
551
|
url="/api/v1/traces/",
|
|
538
552
|
params=params,
|
|
539
553
|
)
|
|
540
|
-
|
|
554
|
+
self._raise_for_status(response)
|
|
541
555
|
traces = response.json()
|
|
542
556
|
for trace in traces:
|
|
543
557
|
yield TraceDetails.model_validate(trace)
|
|
@@ -636,7 +650,7 @@ class Client(OAuth2Mixin, DelegationMixin):
|
|
|
636
650
|
"tags": tags,
|
|
637
651
|
},
|
|
638
652
|
)
|
|
639
|
-
|
|
653
|
+
self._raise_for_status(response)
|
|
640
654
|
return TraceDetails.model_validate(response.json())
|
|
641
655
|
|
|
642
656
|
def get_sports(self, only_root: bool = False) -> list[Sport]:
|
|
@@ -645,7 +659,7 @@ class Client(OAuth2Mixin, DelegationMixin):
|
|
|
645
659
|
url="/api/v1/profile/sports/",
|
|
646
660
|
params={"only_root": only_root},
|
|
647
661
|
)
|
|
648
|
-
|
|
662
|
+
self._raise_for_status(response)
|
|
649
663
|
return [Sport(sport) for sport in response.json()]
|
|
650
664
|
|
|
651
665
|
def get_tags(self) -> list[str]:
|
|
@@ -653,7 +667,7 @@ class Client(OAuth2Mixin, DelegationMixin):
|
|
|
653
667
|
response = client.get(
|
|
654
668
|
url="/api/v1/profile/tags/",
|
|
655
669
|
)
|
|
656
|
-
|
|
670
|
+
self._raise_for_status(response)
|
|
657
671
|
return response.json()
|
|
658
672
|
|
|
659
673
|
def get_users(self) -> list[UserSummary]:
|
|
@@ -661,7 +675,7 @@ class Client(OAuth2Mixin, DelegationMixin):
|
|
|
661
675
|
response = client.get(
|
|
662
676
|
url="/api/v1/users/",
|
|
663
677
|
)
|
|
664
|
-
|
|
678
|
+
self._raise_for_status(response)
|
|
665
679
|
return [UserSummary.model_validate(user) for user in response.json()]
|
|
666
680
|
|
|
667
681
|
_default_client = Client()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
sweatstack/__init__.py,sha256=tiVfgKlswRPaDMEy0gA7u8rveqEYZTA_kyB9lJ3J6Sc,21
|
|
2
2
|
sweatstack/cli.py,sha256=N1NWOgEZR2yaJvIXxo9qvp_jFlypZYb0nujpbVNYQ6A,720
|
|
3
|
-
sweatstack/client.py,sha256=
|
|
3
|
+
sweatstack/client.py,sha256=NpZjqDION-cets4B87brCL3sFMBAuNiP9_HKc9l6Cy4,24898
|
|
4
4
|
sweatstack/constants.py,sha256=fGO6ksOv5HeISv9lHRoYm4besW1GTveXS8YD3K0ljg0,41
|
|
5
5
|
sweatstack/ipython_init.py,sha256=zBGUlMFkdpLvsNpOpwrNaKRUpUZhzaICvH8ODJgMPcI,229
|
|
6
6
|
sweatstack/jupyterlab_oauth2_startup.py,sha256=eZ6xi0Sa4hO4vUanimq0SqjduHtiywCURSDNWk_I-7s,1200
|
|
@@ -11,7 +11,7 @@ sweatstack/streamlit.py,sha256=gsgiIDW-INGTvF24ANnX5LJ17ZxnvXx95sjSmtcTlnY,8062
|
|
|
11
11
|
sweatstack/sweatshell.py,sha256=MYLNcWbOdceqKJ3S0Pe8dwHXEeYsGJNjQoYUXpMTftA,333
|
|
12
12
|
sweatstack/utils.py,sha256=3K97OSWQy5KZ97QiBbW4Kx1wDGxwyA96ZWpwIbkcQZc,2090
|
|
13
13
|
sweatstack/Sweat Stack examples/Getting started.ipynb,sha256=k2hiSffWecoQ0VxjdpDcgFzBXDQiYEebhnAYlu8cgX8,6335204
|
|
14
|
-
sweatstack-0.
|
|
15
|
-
sweatstack-0.
|
|
16
|
-
sweatstack-0.
|
|
17
|
-
sweatstack-0.
|
|
14
|
+
sweatstack-0.33.0.dist-info/METADATA,sha256=ULQomRjnvxecWkP2Rg3B6VruKNM26_yfuXZ1jQsTWwI,775
|
|
15
|
+
sweatstack-0.33.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
16
|
+
sweatstack-0.33.0.dist-info/entry_points.txt,sha256=kCzOUQI3dqbTpEYqtgYDeiKFaqaA7BMlV6D24BMzCFU,208
|
|
17
|
+
sweatstack-0.33.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|