sweatstack 0.38.0__tar.gz → 0.40.1__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 (27) hide show
  1. {sweatstack-0.38.0 → sweatstack-0.40.1}/PKG-INFO +1 -1
  2. {sweatstack-0.38.0 → sweatstack-0.40.1}/pyproject.toml +1 -1
  3. {sweatstack-0.38.0 → sweatstack-0.40.1}/src/sweatstack/client.py +13 -14
  4. {sweatstack-0.38.0 → sweatstack-0.40.1}/src/sweatstack/openapi_schemas.py +8 -1
  5. {sweatstack-0.38.0 → sweatstack-0.40.1}/src/sweatstack/streamlit.py +5 -5
  6. {sweatstack-0.38.0 → sweatstack-0.40.1}/.gitignore +0 -0
  7. {sweatstack-0.38.0 → sweatstack-0.40.1}/.python-version +0 -0
  8. {sweatstack-0.38.0 → sweatstack-0.40.1}/DEVELOPMENT.md +0 -0
  9. {sweatstack-0.38.0 → sweatstack-0.40.1}/Makefile +0 -0
  10. {sweatstack-0.38.0 → sweatstack-0.40.1}/README.md +0 -0
  11. {sweatstack-0.38.0 → sweatstack-0.40.1}/playground/.ipynb_checkpoints/Untitled-checkpoint.ipynb +0 -0
  12. {sweatstack-0.38.0 → sweatstack-0.40.1}/playground/README.md +0 -0
  13. {sweatstack-0.38.0 → sweatstack-0.40.1}/playground/Sweat Stack examples/Getting started.ipynb +0 -0
  14. {sweatstack-0.38.0 → sweatstack-0.40.1}/playground/Untitled.ipynb +0 -0
  15. {sweatstack-0.38.0 → sweatstack-0.40.1}/playground/hello.py +0 -0
  16. {sweatstack-0.38.0 → sweatstack-0.40.1}/playground/pyproject.toml +0 -0
  17. {sweatstack-0.38.0 → sweatstack-0.40.1}/src/sweatstack/Sweat Stack examples/Getting started.ipynb +0 -0
  18. {sweatstack-0.38.0 → sweatstack-0.40.1}/src/sweatstack/__init__.py +0 -0
  19. {sweatstack-0.38.0 → sweatstack-0.40.1}/src/sweatstack/cli.py +0 -0
  20. {sweatstack-0.38.0 → sweatstack-0.40.1}/src/sweatstack/constants.py +0 -0
  21. {sweatstack-0.38.0 → sweatstack-0.40.1}/src/sweatstack/ipython_init.py +0 -0
  22. {sweatstack-0.38.0 → sweatstack-0.40.1}/src/sweatstack/jupyterlab_oauth2_startup.py +0 -0
  23. {sweatstack-0.38.0 → sweatstack-0.40.1}/src/sweatstack/py.typed +0 -0
  24. {sweatstack-0.38.0 → sweatstack-0.40.1}/src/sweatstack/schemas.py +0 -0
  25. {sweatstack-0.38.0 → sweatstack-0.40.1}/src/sweatstack/sweatshell.py +0 -0
  26. {sweatstack-0.38.0 → sweatstack-0.40.1}/src/sweatstack/utils.py +0 -0
  27. {sweatstack-0.38.0 → sweatstack-0.40.1}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sweatstack
3
- Version: 0.38.0
3
+ Version: 0.40.1
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,6 +1,6 @@
1
1
  [project]
2
2
  name = "sweatstack"
3
- version = "0.38.0"
3
+ version = "0.40.1"
4
4
  description = "The official Python client for SweatStack"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -444,7 +444,7 @@ class Client(OAuth2Mixin, DelegationMixin):
444
444
  tags: list[str] | None = None,
445
445
  limit: int = 100,
446
446
  as_dataframe: bool = False,
447
- ) -> Generator[ActivitySummary, None, None] | pd.DataFrame:
447
+ ) -> list[ActivitySummary] | pd.DataFrame:
448
448
  """Gets a list of activities based on specified filters.
449
449
 
450
450
  Args:
@@ -456,28 +456,27 @@ class Client(OAuth2Mixin, DelegationMixin):
456
456
  as_dataframe: Whether to return results as a pandas DataFrame. Defaults to False.
457
457
 
458
458
  Returns:
459
- Either a generator yielding ActivitySummary objects or a pandas DataFrame containing
459
+ Either a list of ActivitySummary objects or a pandas DataFrame containing
460
460
  the activities data, depending on the value of as_dataframe.
461
461
 
462
462
  Raises:
463
463
  HTTPStatusError: If the API request fails.
464
464
  """
465
- generator = self._get_activities_generator(
465
+ activities = list(self._get_activities_generator(
466
466
  start=start,
467
467
  end=end,
468
468
  sports=sports,
469
469
  tags=tags,
470
470
  limit=limit,
471
- )
471
+ ))
472
472
  if as_dataframe:
473
- df = pd.DataFrame([activity.model_dump() for activity in generator])
474
- df = df.set_index(df["start"].rename("timestamp"))
473
+ df = pd.DataFrame([activity.model_dump() for activity in activities])
475
474
  df = self._normalize_dataframe_column(df, "summary")
476
475
  df = self._normalize_dataframe_column(df, "laps")
477
476
  df = self._normalize_dataframe_column(df, "traces")
478
477
  return self._postprocess_dataframe(df)
479
478
  else:
480
- return generator
479
+ return activities
481
480
 
482
481
  def get_latest_activity(
483
482
  self,
@@ -502,7 +501,7 @@ class Client(OAuth2Mixin, DelegationMixin):
502
501
  StopIteration: If no activities match the filters.
503
502
  HTTPStatusError: If the API request fails.
504
503
  """
505
- return next(self.get_activities(
504
+ return next(self._get_activities_generator(
506
505
  start=start,
507
506
  end=end,
508
507
  sports=[sport] if sport is not None else None,
@@ -841,7 +840,7 @@ class Client(OAuth2Mixin, DelegationMixin):
841
840
  tags: list[str] | None = None,
842
841
  limit: int = 100,
843
842
  as_dataframe: bool = False,
844
- ) -> Generator[TraceDetails, None, None] | pd.DataFrame:
843
+ ) -> list[TraceDetails] | pd.DataFrame:
845
844
  """Gets a list of traces based on specified filters.
846
845
 
847
846
  Args:
@@ -853,23 +852,23 @@ class Client(OAuth2Mixin, DelegationMixin):
853
852
  as_dataframe: Whether to return results as a pandas DataFrame. Defaults to False.
854
853
 
855
854
  Returns:
856
- Either a generator yielding TraceDetails objects or a pandas DataFrame containing
855
+ Either a list of TraceDetails objects or a pandas DataFrame containing
857
856
  the traces data, depending on the value of as_dataframe.
858
857
 
859
858
  Raises:
860
859
  HTTPStatusError: If the API request fails.
861
860
  """
862
- generator = self._get_traces_generator(
861
+ traces = list(self._get_traces_generator(
863
862
  start=start,
864
863
  end=end,
865
864
  sports=sports,
866
865
  tags=tags,
867
866
  limit=limit,
868
- )
867
+ ))
869
868
  if not as_dataframe:
870
- return generator
869
+ return traces
871
870
 
872
- data = pd.DataFrame([trace.model_dump() for trace in generator])
871
+ data = pd.DataFrame([trace.model_dump() for trace in traces])
873
872
 
874
873
  if "activity" in data.columns:
875
874
  data = self._normalize_dataframe_column(data, "activity")
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: openapi.json
3
- # timestamp: 2025-04-08T11:14:52+00:00
3
+ # timestamp: 2025-04-11T08:42:56+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -332,6 +332,8 @@ class Lap(BaseModel):
332
332
  start: datetime = Field(..., title='Start')
333
333
  end: datetime = Field(..., title='End')
334
334
  duration: timedelta = Field(..., title='Duration')
335
+ start_local: datetime = Field(..., title='Start Local')
336
+ end_local: datetime = Field(..., title='End Local')
335
337
 
336
338
 
337
339
  class ActivityDetails(BaseModel):
@@ -347,6 +349,8 @@ class ActivityDetails(BaseModel):
347
349
  traces: Optional[List[TraceDetails]] = Field(None, title='Traces')
348
350
  distance: Optional[float] = Field(None, title='Distance')
349
351
  duration: timedelta = Field(..., title='Duration')
352
+ start_local: datetime = Field(..., title='Start Local')
353
+ end_local: datetime = Field(..., title='End Local')
350
354
 
351
355
 
352
356
  class ActivitySummary(BaseModel):
@@ -361,6 +365,8 @@ class ActivitySummary(BaseModel):
361
365
  laps: Optional[List[Lap]] = Field(None, title='Laps')
362
366
  traces: Optional[List[TraceDetails]] = Field(None, title='Traces')
363
367
  duration: timedelta = Field(..., title='Duration')
368
+ start_local: datetime = Field(..., title='Start Local')
369
+ end_local: datetime = Field(..., title='End Local')
364
370
 
365
371
 
366
372
  class TraceDetails(BaseModel):
@@ -376,6 +382,7 @@ class TraceDetails(BaseModel):
376
382
  lap: Optional[Lap] = None
377
383
  activity: Optional[ActivitySummary] = None
378
384
  sport: Optional[Sport] = None
385
+ timestamp_local: datetime = Field(..., title='Timestamp Local')
379
386
 
380
387
 
381
388
  ActivityDetails.model_rebuild()
@@ -15,7 +15,7 @@ import httpx
15
15
  from .client import Client
16
16
  from .constants import DEFAULT_URL
17
17
  from .schemas import Metric, Scope, Sport
18
- from .utils import format_sport
18
+
19
19
 
20
20
  class StreamlitAuth:
21
21
  def __init__(
@@ -253,7 +253,7 @@ class StreamlitAuth:
253
253
  selected_activity = st.selectbox(
254
254
  "Select an activity",
255
255
  activities,
256
- format_func=lambda activity: f"{activity.start.date().isoformat()} {format_sport(activity.sport)}",
256
+ format_func=lambda activity: f"{activity.start.date().isoformat()} {activity.sport.display_name()}",
257
257
  )
258
258
  return selected_activity
259
259
 
@@ -272,7 +272,7 @@ class StreamlitAuth:
272
272
  Sport or list[Sport]: The selected sport or list of sports, depending on allow_multiple.
273
273
 
274
274
  Note:
275
- Sports are displayed in a human-readable format using the format_sport function.
275
+ Sports are displayed in a human-readable format using the display_name function.
276
276
  """
277
277
  if only_available:
278
278
  sports = self.client.get_sports(only_root)
@@ -286,13 +286,13 @@ class StreamlitAuth:
286
286
  selected_sport = st.multiselect(
287
287
  "Select sports",
288
288
  sports,
289
- format_func=format_sport,
289
+ format_func=lambda sport: sport.display_name(),
290
290
  )
291
291
  else:
292
292
  selected_sport = st.selectbox(
293
293
  "Select a sport",
294
294
  sports,
295
- format_func=format_sport,
295
+ format_func=lambda sport: sport.display_name(),
296
296
  )
297
297
  return selected_sport
298
298
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes