otf-api 0.12.0__py3-none-any.whl → 0.13.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.
- otf_api/__init__.py +35 -3
- otf_api/api/__init__.py +3 -0
- otf_api/api/_compat.py +77 -0
- otf_api/api/api.py +80 -0
- otf_api/api/bookings/__init__.py +3 -0
- otf_api/api/bookings/booking_api.py +541 -0
- otf_api/api/bookings/booking_client.py +112 -0
- otf_api/api/client.py +203 -0
- otf_api/api/members/__init__.py +3 -0
- otf_api/api/members/member_api.py +187 -0
- otf_api/api/members/member_client.py +112 -0
- otf_api/api/studios/__init__.py +3 -0
- otf_api/api/studios/studio_api.py +173 -0
- otf_api/api/studios/studio_client.py +120 -0
- otf_api/api/utils.py +307 -0
- otf_api/api/workouts/__init__.py +3 -0
- otf_api/api/workouts/workout_api.py +333 -0
- otf_api/api/workouts/workout_client.py +140 -0
- otf_api/auth/__init__.py +1 -1
- otf_api/auth/auth.py +155 -89
- otf_api/auth/user.py +5 -17
- otf_api/auth/utils.py +27 -2
- otf_api/cache.py +132 -0
- otf_api/exceptions.py +18 -6
- otf_api/models/__init__.py +25 -21
- otf_api/models/bookings/__init__.py +23 -0
- otf_api/models/bookings/bookings.py +134 -0
- otf_api/models/{bookings_v2.py → bookings/bookings_v2.py} +72 -31
- otf_api/models/bookings/classes.py +124 -0
- otf_api/models/{enums.py → bookings/enums.py} +7 -81
- otf_api/{filters.py → models/bookings/filters.py} +39 -11
- otf_api/models/{ratings.py → bookings/ratings.py} +2 -6
- otf_api/models/members/__init__.py +5 -0
- otf_api/models/members/member_detail.py +149 -0
- otf_api/models/members/member_membership.py +26 -0
- otf_api/models/members/member_purchases.py +29 -0
- otf_api/models/members/notifications.py +17 -0
- otf_api/models/mixins.py +48 -1
- otf_api/models/studios/__init__.py +5 -0
- otf_api/models/studios/enums.py +11 -0
- otf_api/models/studios/studio_detail.py +93 -0
- otf_api/models/studios/studio_services.py +36 -0
- otf_api/models/workouts/__init__.py +31 -0
- otf_api/models/{body_composition_list.py → workouts/body_composition_list.py} +140 -71
- otf_api/models/workouts/challenge_tracker_content.py +50 -0
- otf_api/models/workouts/challenge_tracker_detail.py +99 -0
- otf_api/models/workouts/enums.py +70 -0
- otf_api/models/workouts/lifetime_stats.py +96 -0
- otf_api/models/workouts/out_of_studio_workout_history.py +32 -0
- otf_api/models/{performance_summary.py → workouts/performance_summary.py} +19 -5
- otf_api/models/workouts/telemetry.py +88 -0
- otf_api/models/{workout.py → workouts/workout.py} +34 -20
- {otf_api-0.12.0.dist-info → otf_api-0.13.0.dist-info}/METADATA +4 -2
- otf_api-0.13.0.dist-info/RECORD +59 -0
- {otf_api-0.12.0.dist-info → otf_api-0.13.0.dist-info}/WHEEL +1 -1
- otf_api/api.py +0 -1682
- otf_api/logging.py +0 -19
- otf_api/models/bookings.py +0 -109
- otf_api/models/challenge_tracker_content.py +0 -59
- otf_api/models/challenge_tracker_detail.py +0 -88
- otf_api/models/classes.py +0 -70
- otf_api/models/lifetime_stats.py +0 -78
- otf_api/models/member_detail.py +0 -121
- otf_api/models/member_membership.py +0 -26
- otf_api/models/member_purchases.py +0 -29
- otf_api/models/notifications.py +0 -17
- otf_api/models/out_of_studio_workout_history.py +0 -32
- otf_api/models/studio_detail.py +0 -71
- otf_api/models/studio_services.py +0 -36
- otf_api/models/telemetry.py +0 -84
- otf_api/utils.py +0 -164
- otf_api-0.12.0.dist-info/RECORD +0 -38
- {otf_api-0.12.0.dist-info → otf_api-0.13.0.dist-info}/licenses/LICENSE +0 -0
- {otf_api-0.12.0.dist-info → otf_api-0.13.0.dist-info}/top_level.txt +0 -0
@@ -22,15 +22,27 @@ class HeartRate(OtfItemBase):
|
|
22
22
|
|
23
23
|
|
24
24
|
class PerformanceMetric(OtfItemBase):
|
25
|
-
display_value: time | float
|
25
|
+
display_value: time | float | None
|
26
26
|
display_unit: str
|
27
27
|
metric_value: float
|
28
28
|
|
29
|
+
def __str__(self) -> str:
|
30
|
+
"""Return a string representation of the PerformanceMetric."""
|
31
|
+
return f"{self.display_value} {self.display_unit}"
|
32
|
+
|
29
33
|
@field_validator("display_value", mode="before")
|
30
34
|
@classmethod
|
31
|
-
def convert_to_time_format(cls, value) -> time | float:
|
35
|
+
def convert_to_time_format(cls, value: str | None | float | int) -> time | float | None:
|
36
|
+
"""Convert display_value to a time object if it is in the format of HH:MM:SS or MM:SS.
|
37
|
+
|
38
|
+
Args:
|
39
|
+
value (str | None | float | int): The value to convert.
|
40
|
+
|
41
|
+
Returns:
|
42
|
+
time | float: The converted value, or the original value if it is not in the expected format.
|
43
|
+
"""
|
32
44
|
if not value:
|
33
|
-
return
|
45
|
+
return None
|
34
46
|
|
35
47
|
if isinstance(value, float | int):
|
36
48
|
return value
|
@@ -73,8 +85,10 @@ class PerformanceSummary(OtfItemBase):
|
|
73
85
|
You likely want to use the `Workout` model and `get_workouts` method instead.
|
74
86
|
"""
|
75
87
|
|
76
|
-
performance_summary_id: str = Field(
|
77
|
-
|
88
|
+
performance_summary_id: str = Field(
|
89
|
+
..., validation_alias="id", description="Unique identifier for this performance summary"
|
90
|
+
)
|
91
|
+
class_history_uuid: str = Field(..., validation_alias="id", description="Same as performance_summary_id")
|
78
92
|
ratable: bool | None = None
|
79
93
|
|
80
94
|
calories_burned: int | None = Field(None, validation_alias=AliasPath("details", "calories_burned"))
|
@@ -0,0 +1,88 @@
|
|
1
|
+
from datetime import datetime, timedelta
|
2
|
+
from typing import Any
|
3
|
+
|
4
|
+
from pydantic import AliasPath, Field, field_serializer
|
5
|
+
|
6
|
+
from otf_api.models.base import OtfItemBase
|
7
|
+
|
8
|
+
|
9
|
+
class Zone(OtfItemBase):
|
10
|
+
start_bpm: int = Field(..., validation_alias="startBpm")
|
11
|
+
end_bpm: int = Field(..., validation_alias="endBpm")
|
12
|
+
|
13
|
+
|
14
|
+
class Zones(OtfItemBase):
|
15
|
+
gray: Zone
|
16
|
+
blue: Zone
|
17
|
+
green: Zone
|
18
|
+
orange: Zone
|
19
|
+
red: Zone
|
20
|
+
|
21
|
+
|
22
|
+
class TreadData(OtfItemBase):
|
23
|
+
tread_speed: float = Field(..., validation_alias="treadSpeed")
|
24
|
+
tread_incline: float = Field(..., validation_alias="treadIncline")
|
25
|
+
agg_tread_distance: int = Field(..., validation_alias="aggTreadDistance")
|
26
|
+
|
27
|
+
|
28
|
+
class RowData(OtfItemBase):
|
29
|
+
row_speed: float = Field(..., validation_alias="rowSpeed")
|
30
|
+
row_pps: float = Field(..., validation_alias="rowPps")
|
31
|
+
row_spm: float = Field(..., validation_alias="rowSpm")
|
32
|
+
agg_row_distance: int = Field(..., validation_alias="aggRowDistance")
|
33
|
+
row_pace: int = Field(..., validation_alias="rowPace")
|
34
|
+
|
35
|
+
|
36
|
+
class TelemetryItem(OtfItemBase):
|
37
|
+
relative_timestamp: int = Field(..., validation_alias="relativeTimestamp")
|
38
|
+
hr: int | None = None
|
39
|
+
agg_splats: int = Field(..., validation_alias="aggSplats")
|
40
|
+
agg_calories: int = Field(..., validation_alias="aggCalories")
|
41
|
+
timestamp: datetime | None = Field(
|
42
|
+
None,
|
43
|
+
init_var=False,
|
44
|
+
description="The timestamp of the telemetry item, calculated from the class start time and relative timestamp.",
|
45
|
+
)
|
46
|
+
tread_data: TreadData | None = Field(None, validation_alias="treadData")
|
47
|
+
row_data: RowData | None = Field(None, validation_alias="rowData")
|
48
|
+
|
49
|
+
|
50
|
+
class Telemetry(OtfItemBase):
|
51
|
+
member_uuid: str = Field(..., validation_alias="memberUuid")
|
52
|
+
performance_summary_id: str = Field(
|
53
|
+
...,
|
54
|
+
validation_alias="classHistoryUuid",
|
55
|
+
description="The ID of the performance summary this telemetry item belongs to.",
|
56
|
+
)
|
57
|
+
class_history_uuid: str = Field(
|
58
|
+
..., validation_alias="classHistoryUuid", description="The same as performance_summary_id."
|
59
|
+
)
|
60
|
+
class_start_time: datetime | None = Field(None, validation_alias="classStartTime")
|
61
|
+
max_hr: int | None = Field(None, validation_alias="maxHr")
|
62
|
+
zones: Zones
|
63
|
+
window_size: int | None = Field(None, validation_alias="windowSize")
|
64
|
+
telemetry: list[TelemetryItem] = Field(default_factory=list)
|
65
|
+
|
66
|
+
def __init__(self, **data: dict[str, Any]):
|
67
|
+
super().__init__(**data)
|
68
|
+
for telem in self.telemetry:
|
69
|
+
if self.class_start_time is None:
|
70
|
+
continue
|
71
|
+
|
72
|
+
telem.timestamp = self.class_start_time + timedelta(seconds=telem.relative_timestamp)
|
73
|
+
|
74
|
+
@field_serializer("telemetry", when_used="json")
|
75
|
+
def reduce_telemetry_list(self, value: list[TelemetryItem]) -> list[TelemetryItem]:
|
76
|
+
"""Reduces the telemetry list to only include the first 10 items."""
|
77
|
+
if len(value) > 10:
|
78
|
+
return value[:5] + value[-5:]
|
79
|
+
return value
|
80
|
+
|
81
|
+
|
82
|
+
class TelemetryHistoryItem(OtfItemBase):
|
83
|
+
max_hr_type: str | None = Field(None, validation_alias=AliasPath("maxHr", "type"))
|
84
|
+
max_hr_value: int | None = Field(None, validation_alias=AliasPath("maxHr", "value"))
|
85
|
+
zones: Zones | None = None
|
86
|
+
change_from_previous: int | None = Field(None, validation_alias="changeFromPrevious")
|
87
|
+
change_bucket: str | None = Field(None, validation_alias="changeBucket")
|
88
|
+
assigned_at: datetime | None = Field(None, validation_alias="assignedAt")
|
@@ -1,21 +1,15 @@
|
|
1
|
+
from typing import Any, Literal
|
2
|
+
|
1
3
|
from pydantic import AliasPath, Field
|
2
4
|
|
3
5
|
from otf_api.models.base import OtfItemBase
|
4
|
-
from otf_api.models.
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
)
|
12
|
-
from otf_api.models.performance_summary import HeartRate, Rower, Treadmill
|
13
|
-
from otf_api.models.telemetry import Telemetry
|
14
|
-
|
15
|
-
|
16
|
-
class Workout(OtfItemBase):
|
17
|
-
"""Represents a workout - this combines the performance summary, data from the new bookings endpoint, and
|
18
|
-
telemetry data.
|
6
|
+
from otf_api.models.bookings import BookingV2, BookingV2Class, BookingV2Studio, BookingV2Workout, Rating
|
7
|
+
from otf_api.models.mixins import ApiMixin
|
8
|
+
from otf_api.models.workouts import HeartRate, Rower, Telemetry, Treadmill, ZoneTimeMinutes
|
9
|
+
|
10
|
+
|
11
|
+
class Workout(ApiMixin, OtfItemBase):
|
12
|
+
"""Represents a workout - combines the performance summary, data from the new bookings endpoint, and telemetry data.
|
19
13
|
|
20
14
|
The final product contains all the performance summary data, the detailed data over time, as well as the class,
|
21
15
|
coach, studio, and rating data from the new endpoint.
|
@@ -23,8 +17,10 @@ class Workout(OtfItemBase):
|
|
23
17
|
This should match the data that is shown in the OTF app after a workout.
|
24
18
|
"""
|
25
19
|
|
26
|
-
performance_summary_id: str = Field(
|
27
|
-
|
20
|
+
performance_summary_id: str = Field(
|
21
|
+
..., validation_alias="id", description="Unique identifier for this performance summary"
|
22
|
+
)
|
23
|
+
class_history_uuid: str = Field(..., validation_alias="id", description="Same as performance_summary_id")
|
28
24
|
booking_id: str = Field(..., description="The booking id for the new bookings endpoint.")
|
29
25
|
class_uuid: str | None = Field(
|
30
26
|
None, description="Used by the ratings endpoint - seems to fall off after a few months"
|
@@ -72,10 +68,28 @@ class Workout(OtfItemBase):
|
|
72
68
|
data["class_rating"] = v2_booking.class_rating
|
73
69
|
data["coach_rating"] = v2_booking.coach_rating
|
74
70
|
|
75
|
-
telemetry = data.get("telemetry")
|
76
|
-
if telemetry and
|
71
|
+
telemetry: dict[str, Any] | None = data.get("telemetry")
|
72
|
+
if telemetry and "maxHr" in telemetry:
|
77
73
|
# max_hr seems to be left out of the heart rate data - it has peak_hr but they do not match
|
78
74
|
# so if we have telemetry data, we can get the max_hr from there
|
79
|
-
data["details"]["heart_rate"]["max_hr"] = telemetry
|
75
|
+
data["details"]["heart_rate"]["max_hr"] = telemetry["maxHr"]
|
80
76
|
|
81
77
|
super().__init__(**data)
|
78
|
+
|
79
|
+
def rate(self, class_rating: Literal[0, 1, 2, 3], coach_rating: Literal[0, 1, 2, 3]) -> None:
|
80
|
+
"""Rate the class and coach for this workout.
|
81
|
+
|
82
|
+
The class rating must be 0, 1, 2, or 3. 0 is the same as dismissing the prompt to rate the class/coach. 1 - 3\
|
83
|
+
is a range from bad to good.
|
84
|
+
|
85
|
+
Args:
|
86
|
+
class_rating (Literal[0, 1, 2, 3]): Rating for the class.
|
87
|
+
coach_rating (Literal[0, 1, 2, 3]): Rating for the coach.
|
88
|
+
|
89
|
+
Raises:
|
90
|
+
ValueError: If the API instance is not set.
|
91
|
+
AlreadyRatedError: If the performance summary is already rated.
|
92
|
+
ClassNotRatableError: If the performance summary is not rateable.
|
93
|
+
"""
|
94
|
+
self.raise_if_api_not_set()
|
95
|
+
self._api.workouts.rate_class_from_workout(self, class_rating=class_rating, coach_rating=coach_rating)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: otf-api
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.13.0
|
4
4
|
Summary: Python OrangeTheory Fitness API Client
|
5
5
|
Author-email: Jessica Smith <j.smith.git1@gmail.com>
|
6
6
|
License-Expression: MIT
|
@@ -9,7 +9,6 @@ Classifier: Development Status :: 4 - Beta
|
|
9
9
|
Classifier: Intended Audience :: Developers
|
10
10
|
Classifier: Programming Language :: Python :: 3.11
|
11
11
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
12
|
-
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
13
12
|
Classifier: Topic :: Software Development :: Libraries
|
14
13
|
Classifier: Topic :: Internet :: WWW/HTTP
|
15
14
|
Classifier: Operating System :: OS Independent
|
@@ -27,6 +26,9 @@ Requires-Dist: yarl<2,>=1.18.3
|
|
27
26
|
Requires-Dist: tenacity<10,>=9.0.0
|
28
27
|
Requires-Dist: cachetools>=5.5.0
|
29
28
|
Requires-Dist: pendulum>=3.1.0
|
29
|
+
Requires-Dist: diskcache>=5.6.3
|
30
|
+
Requires-Dist: platformdirs>=4.3.6
|
31
|
+
Requires-Dist: packaging>=24.2
|
30
32
|
Dynamic: license-file
|
31
33
|
|
32
34
|
Simple API client for interacting with the OrangeTheory Fitness APIs.
|
@@ -0,0 +1,59 @@
|
|
1
|
+
otf_api/__init__.py,sha256=mpjME4x_GP5hUzbQ99qMPuZ4pzbQNrYXLAXqnGNsXcw,1150
|
2
|
+
otf_api/cache.py,sha256=m4xUBhaS0MkcZypon1jjfhJzZPRQBE5Fto4_RMReXZ0,4311
|
3
|
+
otf_api/exceptions.py,sha256=b6ZdH1dtYyUfXSupdVGGni6d66qqhzD0SGzyuty89gM,2174
|
4
|
+
otf_api/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
+
otf_api/api/__init__.py,sha256=YC6bwYiAN6BSE7sqbxr2l34vwSdZPMGDv3wwPIQ_S3o,51
|
6
|
+
otf_api/api/_compat.py,sha256=vWMC6kC3tK9qoXwnU5AgbQie-dhYAkkUI-hmq_4nf7Q,3512
|
7
|
+
otf_api/api/api.py,sha256=yda8RObOMd_lBWbqlAAUZ3dTwGhdEYsfGQJDiphp6Eo,2633
|
8
|
+
otf_api/api/client.py,sha256=o533H-nYweRcosBEYmm3U-7ivkpGDF7jElM9iIhK7E8,8157
|
9
|
+
otf_api/api/utils.py,sha256=mNDUrz6d8b8KqTaUr5fGGGejy6bq9HHLs9LS8ibbfW4,9926
|
10
|
+
otf_api/api/bookings/__init__.py,sha256=ocHSZXV4nnkZhjpjBX76iKHCQ21_ZL5hV4YKgUR0Wwg,62
|
11
|
+
otf_api/api/bookings/booking_api.py,sha256=s4GWT1MYUY0-EjXucj_8rcNfRP6o-sFGfVsmY3RJMdM,22087
|
12
|
+
otf_api/api/bookings/booking_client.py,sha256=qYaEomexGWP2N_WdWePMN7BR8aIKEpO71g5J0KaVfAc,4244
|
13
|
+
otf_api/api/members/__init__.py,sha256=6mkeMiATRsNQTYQ37P7k6SWf9RZ9T5QY9-_r1sS1-vY,59
|
14
|
+
otf_api/api/members/member_api.py,sha256=bpSnUWrLbrqkIcwhzaPonkWTa9-gLPdadzE4ec_meT0,7224
|
15
|
+
otf_api/api/members/member_client.py,sha256=d6v6vexmtiedw1-lXLMobbN8tTmRT-zHq7VzdJb5wDE,4528
|
16
|
+
otf_api/api/studios/__init__.py,sha256=vPir509hQOJL7uJ4yBCOHgooXVnXs6N5RlWs1tN54Uk,59
|
17
|
+
otf_api/api/studios/studio_api.py,sha256=qggdaTGeYtrE1q5oV8ozCqAC5Xv6DtwVG5dHzSB1q74,6781
|
18
|
+
otf_api/api/studios/studio_client.py,sha256=KWc4CF-2387WDUFYpMtIUm3KyPj1JdelCn7RH9L1FBI,4707
|
19
|
+
otf_api/api/workouts/__init__.py,sha256=GPb2cEsAxoaiJIP8Finrk9x9vUeL9NXA9iz2eya9HYk,62
|
20
|
+
otf_api/api/workouts/workout_api.py,sha256=h65nEt2-2mYV33tiVkv2N6L7NoEJ_naViw-IrMnEn8E,13207
|
21
|
+
otf_api/api/workouts/workout_client.py,sha256=MvEWbhPXywpQMSdsBSGCYBIR65TJjdqgb7T0Bc8H8D0,6762
|
22
|
+
otf_api/auth/__init__.py,sha256=uTvFTNQVvfogTN_-4D3bRSundAf2enUJ5ji6PwO0xm8,104
|
23
|
+
otf_api/auth/auth.py,sha256=67-RXDla3rVm5vmlyPEOqPOF5kecJiEznVmq0gSz3M0,16448
|
24
|
+
otf_api/auth/user.py,sha256=OChRG0EhZ63vEtT_Sg3NmXWmOD1vZcILuHhRXlwaG0I,2156
|
25
|
+
otf_api/auth/utils.py,sha256=YBxqg2h59u4V1ij5kgqJDyKh0gtgOlXrSZdpAKSdelY,3954
|
26
|
+
otf_api/models/__init__.py,sha256=P0IjxRUPIfzUI-e3VDMpipJeWr6BlypWAED2ovnZw-w,1545
|
27
|
+
otf_api/models/base.py,sha256=KJlIxl_sRj6f-g5vKYPw4yV6fGDk-fwZ93EO0JGPYMw,202
|
28
|
+
otf_api/models/mixins.py,sha256=mY9ufoi3QJM5H7KMU9q7grgYuZp85xLs8ie2aLikvi8,4164
|
29
|
+
otf_api/models/bookings/__init__.py,sha256=-EtaVW9qF5dmWvSlkq693vkxc3bX6d7dWOgcnNNfpV4,642
|
30
|
+
otf_api/models/bookings/bookings.py,sha256=YlocSxdOEECxcCsVkEYAXHFE3EmMTUBxXgSFFxskaLk,5681
|
31
|
+
otf_api/models/bookings/bookings_v2.py,sha256=Jd9zh4E3AEWUyxLTISq2j2I_firKCfV5IQzNdkc7iK4,7965
|
32
|
+
otf_api/models/bookings/classes.py,sha256=k5cOdMCdDyyXgwBnMrLV9gV_CYE02L8rKewTz8Vv7mg,5068
|
33
|
+
otf_api/models/bookings/enums.py,sha256=z9nSciWjvnpKDTHwj496aTKGgREvAeu-h6RKBAf-Tm0,3140
|
34
|
+
otf_api/models/bookings/filters.py,sha256=quorUTo9_PR03JMv2m2qhXKhGtb_URLOu92Lj2Y6IMQ,4866
|
35
|
+
otf_api/models/bookings/ratings.py,sha256=QHUQQoDmT1sLVil1QJsqP1QKFWfSypg7B2RzyQeCMng,914
|
36
|
+
otf_api/models/members/__init__.py,sha256=umzYnnZvRDcX7qVq3t5IRjxxtmTe4F75JjZJm5923gA,199
|
37
|
+
otf_api/models/members/member_detail.py,sha256=ACcfomS2hz7i-8521jUBRGYO3oTwpMMrIP9wcTHFOgY,8039
|
38
|
+
otf_api/models/members/member_membership.py,sha256=pz-lHD6T60-rh1StRlCJd0L-g3PPkiFIeRVv1P-ebjY,1481
|
39
|
+
otf_api/models/members/member_purchases.py,sha256=oEHMRJCFeU8A39fcY0RDDUimedP4aLjh4Ueoz5te0eo,1775
|
40
|
+
otf_api/models/members/notifications.py,sha256=7RONios8H1WnzRKuy3E7s5j6MXSwKk60b5NkjB6Biok,910
|
41
|
+
otf_api/models/studios/__init__.py,sha256=2mMRnwFdlnE6Mubcrh7K35x86jEINGnSPa7oYHETN8Q,210
|
42
|
+
otf_api/models/studios/enums.py,sha256=I5B4iyHUvcBfn0C3pMwnriAZxx8-fhupDlyrD_jSZa8,258
|
43
|
+
otf_api/models/studios/studio_detail.py,sha256=cinktnXq3AH0BnSR1W4kXrIkDw8bg0IOXO4_sS1FCHc,4979
|
44
|
+
otf_api/models/studios/studio_services.py,sha256=Y7INy_9-UhIM1g5EQjmhlJtdJouSvYIvG8GIa4H3UpU,1835
|
45
|
+
otf_api/models/workouts/__init__.py,sha256=picQOBsx0JhkgQlZwQq_vQShC5ZepJWpvSTO3N1K_RA,1003
|
46
|
+
otf_api/models/workouts/body_composition_list.py,sha256=E5xDqP647QEC8seKBBDeFaTod-mL4alXn-tP83RtvIg,14930
|
47
|
+
otf_api/models/workouts/challenge_tracker_content.py,sha256=kP57mYsLiDfuSVRJswESi-XXCL7BBQfULEXEiOM78SI,2459
|
48
|
+
otf_api/models/workouts/challenge_tracker_detail.py,sha256=v-1WbOnBtuUyxllxlzUSuhtcBr3hV8PldI_6-SZCPQI,4920
|
49
|
+
otf_api/models/workouts/enums.py,sha256=El_H3l6XLxO27Xo23YheR6LPvIjfTUaeK0rBpQvRHpc,1540
|
50
|
+
otf_api/models/workouts/lifetime_stats.py,sha256=WBDqdcJ4Bz2YXg1WOijeJgs7LKcix_JgZ5lkwsOpGiI,3819
|
51
|
+
otf_api/models/workouts/out_of_studio_workout_history.py,sha256=-BKp-MnIp_5-U2KWy6nv58H6flgcbdOGsSbz5cHq20w,1945
|
52
|
+
otf_api/models/workouts/performance_summary.py,sha256=R1p-g1L4XWWEiBYaGtX5JFvoS3Z6bzQrsPIfTQHDqyM,3464
|
53
|
+
otf_api/models/workouts/telemetry.py,sha256=rKsmLbsOVUxUXH439zxxIEWYGTohKNemqrtMamAl5cA,3465
|
54
|
+
otf_api/models/workouts/workout.py,sha256=_KIR9fLI4LgUlS02Q5SNauHd4aOqJI9H4uaFKf-xJOU,4580
|
55
|
+
otf_api-0.13.0.dist-info/licenses/LICENSE,sha256=UaPT9ynYigC3nX8n22_rC37n-qmTRKLFaHrtUwF9ktE,1071
|
56
|
+
otf_api-0.13.0.dist-info/METADATA,sha256=-x6Y4z5eG5ECRWfbS_jmbFr3oo6eNrKdrNMeDkMniuM,2162
|
57
|
+
otf_api-0.13.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
58
|
+
otf_api-0.13.0.dist-info/top_level.txt,sha256=KAhYg1X2YG0LkTuVRhUV1I_AReNZUVNdEan7cp0pEE4,8
|
59
|
+
otf_api-0.13.0.dist-info/RECORD,,
|