otf-api 0.8.2__py3-none-any.whl → 0.9.1__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 +7 -4
- otf_api/api.py +699 -480
- otf_api/auth/__init__.py +4 -0
- otf_api/auth/auth.py +234 -0
- otf_api/auth/user.py +66 -0
- otf_api/auth/utils.py +129 -0
- otf_api/exceptions.py +38 -5
- otf_api/filters.py +97 -0
- otf_api/logging.py +19 -0
- otf_api/models/__init__.py +27 -38
- otf_api/models/body_composition_list.py +47 -50
- otf_api/models/bookings.py +63 -87
- otf_api/models/challenge_tracker_content.py +42 -21
- otf_api/models/challenge_tracker_detail.py +68 -48
- otf_api/models/classes.py +53 -62
- otf_api/models/enums.py +108 -30
- otf_api/models/lifetime_stats.py +59 -45
- otf_api/models/member_detail.py +95 -115
- otf_api/models/member_membership.py +18 -17
- otf_api/models/member_purchases.py +21 -127
- otf_api/models/mixins.py +37 -33
- otf_api/models/notifications.py +17 -0
- otf_api/models/out_of_studio_workout_history.py +22 -31
- otf_api/models/performance_summary_detail.py +47 -42
- otf_api/models/performance_summary_list.py +19 -37
- otf_api/models/studio_detail.py +51 -98
- otf_api/models/studio_services.py +27 -48
- otf_api/models/telemetry.py +14 -5
- otf_api/utils.py +134 -0
- {otf_api-0.8.2.dist-info → otf_api-0.9.1.dist-info}/METADATA +21 -10
- otf_api-0.9.1.dist-info/RECORD +35 -0
- {otf_api-0.8.2.dist-info → otf_api-0.9.1.dist-info}/WHEEL +1 -1
- otf_api/auth.py +0 -316
- otf_api/models/book_class.py +0 -89
- otf_api/models/cancel_booking.py +0 -49
- otf_api/models/favorite_studios.py +0 -106
- otf_api/models/latest_agreement.py +0 -21
- otf_api/models/telemetry_hr_history.py +0 -34
- otf_api/models/telemetry_max_hr.py +0 -13
- otf_api/models/total_classes.py +0 -8
- otf_api-0.8.2.dist-info/AUTHORS.md +0 -9
- otf_api-0.8.2.dist-info/RECORD +0 -36
- {otf_api-0.8.2.dist-info → otf_api-0.9.1.dist-info}/LICENSE +0 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
otf_api/__init__.py,sha256=dGgyclSo165V_hZAl7Z5E06WQrs5NjO6fXtPcbo3RIg,204
|
2
|
+
otf_api/api.py,sha256=Ah6bheezsV-RLxPLGKZqqW77gotWkznLYmEeRhEwJaY,47006
|
3
|
+
otf_api/auth/__init__.py,sha256=Pu6ugbEXhgjJEpYKr7K2CqEac53Wq9FRF3F16GLQ_ww,133
|
4
|
+
otf_api/auth/auth.py,sha256=m9qzUn0hdAo6AZHm2Mn054nLqUWPxEyP3y3DOBVkvYA,9010
|
5
|
+
otf_api/auth/user.py,sha256=HgggfneeVMnKFWSUMnlikfZyC__HE8vVaKSpBS4_sIg,2256
|
6
|
+
otf_api/auth/utils.py,sha256=jUH_A1-DU3KfY-XrymCuQoud79o9qfu5P9Ws9QA2aWs,3211
|
7
|
+
otf_api/exceptions.py,sha256=mt0k0-2oNcpNhboYzkywpt535W6bHjx6gro7P3PpYzo,1463
|
8
|
+
otf_api/filters.py,sha256=fk2bFGi3srjS96qZlaDx-ARZRaj93NUTUdMJ01TX420,3702
|
9
|
+
otf_api/logging.py,sha256=PRZpCaJ1F1Xya3L9Efkt3mKS5_QNr3sXjEUERSxYjvE,563
|
10
|
+
otf_api/models/__init__.py,sha256=czt_OYsU4igeudRAi8kqOQro6fjNrbE9aLr--FA76Sg,1588
|
11
|
+
otf_api/models/base.py,sha256=KJlIxl_sRj6f-g5vKYPw4yV6fGDk-fwZ93EO0JGPYMw,202
|
12
|
+
otf_api/models/body_composition_list.py,sha256=jGdR-9ScvIOtULJNB99aYh2INk2ihoHAnTWtbQCIea4,12202
|
13
|
+
otf_api/models/bookings.py,sha256=8zgLnQ40ci0c7AimGeoAI-Raw8d2byvKoXkJXyW1xUE,4260
|
14
|
+
otf_api/models/challenge_tracker_content.py,sha256=zukbda38DloiBHW9HUEKQnumGhgVqpjjbxo7G4bDIc0,2630
|
15
|
+
otf_api/models/challenge_tracker_detail.py,sha256=KDfLRDjr8quVhDPVj9x_tuLnkJPua5y8C3WtGIVzXZw,4356
|
16
|
+
otf_api/models/classes.py,sha256=DZ7oo_MNK7m3ul2gK929maGBksekG9RtLzYa-Di8skk,3005
|
17
|
+
otf_api/models/enums.py,sha256=qXpnJwUyR5fZxr_z1VIlgo_uOGFdGNqoaITVpiA2z4E,4214
|
18
|
+
otf_api/models/lifetime_stats.py,sha256=sQw0oaT5uSlCTZyMSEEhYJg7IdOzuxelTI3mzDBHNls,3055
|
19
|
+
otf_api/models/member_detail.py,sha256=CDDZg3Ow07U57yRqbm3i-BVha0cvCNOZ8QhN0pliU_A,6598
|
20
|
+
otf_api/models/member_membership.py,sha256=jZwHzwtVyMUr8dWGlFbMYj9qveCbiOblWW5szXDUFFo,1338
|
21
|
+
otf_api/models/member_purchases.py,sha256=Ne7ByEbGTqTJhuEyCgywWe8I3nc-D46qw09up7ys38s,1627
|
22
|
+
otf_api/models/mixins.py,sha256=VR5EeweySHhzaiqqnCr853Cpe1uK97cxY0IFEdf5T8w,2262
|
23
|
+
otf_api/models/notifications.py,sha256=AkmIfiIiU6wox_7puyenbhCX10SFvBD5eBAovcurRgY,833
|
24
|
+
otf_api/models/out_of_studio_workout_history.py,sha256=El5i0K2Td_sMReyfUKP-Iv1L1WgRx0ijjjnHzYvlCeY,1703
|
25
|
+
otf_api/models/performance_summary_detail.py,sha256=HmtAJIKkekYy2rJOQBxfQGsKJxuFZm7gP4Dauae-PqM,2711
|
26
|
+
otf_api/models/performance_summary_list.py,sha256=jv28jZJEOXOZtghhHhNR-4BPVBY3TpyiwHNKmnoTRcA,1692
|
27
|
+
otf_api/models/studio_detail.py,sha256=4HZXP6khjuFs7J7lokr3rMEDDpCwcb7OYJVJvzgly7U,3639
|
28
|
+
otf_api/models/studio_services.py,sha256=aGLQMQmjGVpI6YxzAl-mcp3Y9cHPXuH9dIqrl6E-78E,1665
|
29
|
+
otf_api/models/telemetry.py,sha256=_g-wtJDbaXENk1DqH0bbpJUsGepWDYraRIsIvpvDBU8,2531
|
30
|
+
otf_api/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
31
|
+
otf_api/utils.py,sha256=Af_Xg2QLoFqRzHOj4LtCIIqpRsdih-7HW1rJ8tUo8tE,3937
|
32
|
+
otf_api-0.9.1.dist-info/LICENSE,sha256=UaPT9ynYigC3nX8n22_rC37n-qmTRKLFaHrtUwF9ktE,1071
|
33
|
+
otf_api-0.9.1.dist-info/METADATA,sha256=SPUE5dzWt65jmeoSiWbCY-Tb84vO-x6kOg91kKlvlmg,2296
|
34
|
+
otf_api-0.9.1.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
35
|
+
otf_api-0.9.1.dist-info/RECORD,,
|
otf_api/auth.py
DELETED
@@ -1,316 +0,0 @@
|
|
1
|
-
import typing
|
2
|
-
from datetime import datetime, timedelta
|
3
|
-
from logging import getLogger
|
4
|
-
from typing import Any
|
5
|
-
|
6
|
-
import jwt
|
7
|
-
from pycognito import AWSSRP, Cognito, MFAChallengeException
|
8
|
-
from pycognito.exceptions import TokenVerificationException
|
9
|
-
from pydantic import Field
|
10
|
-
from pydantic.config import ConfigDict
|
11
|
-
|
12
|
-
from otf_api.models.base import OtfItemBase
|
13
|
-
|
14
|
-
if typing.TYPE_CHECKING:
|
15
|
-
from boto3.session import Session
|
16
|
-
from botocore.config import Config
|
17
|
-
|
18
|
-
LOGGER = getLogger(__name__)
|
19
|
-
CLIENT_ID = "1457d19r0pcjgmp5agooi0rb1b" # from otlive
|
20
|
-
USER_POOL_ID = "us-east-1_dYDxUeyL1"
|
21
|
-
|
22
|
-
|
23
|
-
class OtfCognito(Cognito):
|
24
|
-
_device_key: str | None = None
|
25
|
-
|
26
|
-
def __init__(
|
27
|
-
self,
|
28
|
-
user_pool_id: str,
|
29
|
-
client_id: str,
|
30
|
-
user_pool_region: str | None = None,
|
31
|
-
username: str | None = None,
|
32
|
-
id_token: str | None = None,
|
33
|
-
refresh_token: str | None = None,
|
34
|
-
access_token: str | None = None,
|
35
|
-
client_secret: str | None = None,
|
36
|
-
access_key: str | None = None,
|
37
|
-
secret_key: str | None = None,
|
38
|
-
session: "Session|None" = None,
|
39
|
-
botocore_config: "Config|None" = None,
|
40
|
-
boto3_client_kwargs: dict[str, Any] | None = None,
|
41
|
-
device_key: str | None = None,
|
42
|
-
):
|
43
|
-
super().__init__(
|
44
|
-
user_pool_id,
|
45
|
-
client_id,
|
46
|
-
user_pool_region=user_pool_region,
|
47
|
-
username=username,
|
48
|
-
id_token=id_token,
|
49
|
-
refresh_token=refresh_token,
|
50
|
-
access_token=access_token,
|
51
|
-
client_secret=client_secret,
|
52
|
-
access_key=access_key,
|
53
|
-
secret_key=secret_key,
|
54
|
-
session=session,
|
55
|
-
botocore_config=botocore_config,
|
56
|
-
boto3_client_kwargs=boto3_client_kwargs,
|
57
|
-
)
|
58
|
-
self.device_key = device_key
|
59
|
-
|
60
|
-
@property
|
61
|
-
def device_key(self) -> str | None:
|
62
|
-
return self._device_key
|
63
|
-
|
64
|
-
@device_key.setter
|
65
|
-
def device_key(self, value: str | None):
|
66
|
-
if not value:
|
67
|
-
if self._device_key:
|
68
|
-
LOGGER.debug("Clearing device key")
|
69
|
-
self._device_key = value
|
70
|
-
return
|
71
|
-
|
72
|
-
redacted_value = value[:4] + "*" * (len(value) - 8) + value[-4:]
|
73
|
-
LOGGER.debug(f"Setting device key: {redacted_value}")
|
74
|
-
self._device_key = value
|
75
|
-
|
76
|
-
def _set_tokens(self, tokens: dict[str, Any]):
|
77
|
-
"""Set the tokens and device metadata from the response.
|
78
|
-
|
79
|
-
Args:
|
80
|
-
tokens (dict): The response from the Cognito service.
|
81
|
-
"""
|
82
|
-
super()._set_tokens(tokens)
|
83
|
-
|
84
|
-
if new_metadata := tokens["AuthenticationResult"].get("NewDeviceMetadata"):
|
85
|
-
self.device_key = new_metadata["DeviceKey"]
|
86
|
-
|
87
|
-
def authenticate(self, password: str, client_metadata: dict[str, Any] | None = None, device_key: str | None = None):
|
88
|
-
"""
|
89
|
-
Authenticate the user using the SRP protocol. Overridden to add `confirm_device` call.
|
90
|
-
|
91
|
-
Args:
|
92
|
-
password (str): The user's password
|
93
|
-
client_metadata (dict, optional): Any additional client metadata to send to Cognito
|
94
|
-
"""
|
95
|
-
aws = AWSSRP(
|
96
|
-
username=self.username,
|
97
|
-
password=password,
|
98
|
-
pool_id=self.user_pool_id,
|
99
|
-
client_id=self.client_id,
|
100
|
-
client=self.client,
|
101
|
-
client_secret=self.client_secret,
|
102
|
-
)
|
103
|
-
try:
|
104
|
-
tokens = aws.authenticate_user(client_metadata=client_metadata)
|
105
|
-
except MFAChallengeException as mfa_challenge:
|
106
|
-
self.mfa_tokens = mfa_challenge.get_tokens()
|
107
|
-
raise mfa_challenge
|
108
|
-
|
109
|
-
# Set the tokens and device metadata
|
110
|
-
self._set_tokens(tokens)
|
111
|
-
|
112
|
-
if not device_key:
|
113
|
-
# Confirm the device so we can use the refresh token
|
114
|
-
aws.confirm_device(tokens)
|
115
|
-
else:
|
116
|
-
self.device_key = device_key
|
117
|
-
try:
|
118
|
-
self.renew_access_token()
|
119
|
-
except TokenVerificationException:
|
120
|
-
LOGGER.error("Failed to renew access token. Confirming device.")
|
121
|
-
self.device_key = None
|
122
|
-
aws.confirm_device(tokens)
|
123
|
-
|
124
|
-
def check_token(self, renew: bool = True) -> bool:
|
125
|
-
"""
|
126
|
-
Checks the exp attribute of the access_token and either refreshes
|
127
|
-
the tokens by calling the renew_access_tokens method or does nothing
|
128
|
-
:param renew: bool indicating whether to refresh on expiration
|
129
|
-
:return: bool indicating whether access_token has expired
|
130
|
-
"""
|
131
|
-
if not self.access_token:
|
132
|
-
raise AttributeError("Access Token Required to Check Token")
|
133
|
-
now = datetime.now() # noqa
|
134
|
-
dec_access_token = jwt.decode(self.access_token, options={"verify_signature": False})
|
135
|
-
|
136
|
-
exp = datetime.fromtimestamp(dec_access_token["exp"]) # noqa
|
137
|
-
if now > exp - timedelta(minutes=15):
|
138
|
-
expired = True
|
139
|
-
if renew:
|
140
|
-
self.renew_access_token()
|
141
|
-
else:
|
142
|
-
expired = False
|
143
|
-
return expired
|
144
|
-
|
145
|
-
def renew_access_token(self):
|
146
|
-
"""Sets a new access token on the User using the cached refresh token and device metadata."""
|
147
|
-
auth_params = {"REFRESH_TOKEN": self.refresh_token}
|
148
|
-
self._add_secret_hash(auth_params, "SECRET_HASH")
|
149
|
-
|
150
|
-
if self.device_key:
|
151
|
-
LOGGER.debug("Using device key for refresh token")
|
152
|
-
auth_params["DEVICE_KEY"] = self.device_key
|
153
|
-
|
154
|
-
refresh_response = self.client.initiate_auth(
|
155
|
-
ClientId=self.client_id, AuthFlow="REFRESH_TOKEN_AUTH", AuthParameters=auth_params
|
156
|
-
)
|
157
|
-
self._set_tokens(refresh_response)
|
158
|
-
|
159
|
-
@classmethod
|
160
|
-
def from_token(
|
161
|
-
cls, access_token: str, id_token: str, refresh_token: str | None = None, device_key: str | None = None
|
162
|
-
) -> "OtfCognito":
|
163
|
-
"""Create an OtfCognito instance from an id token.
|
164
|
-
|
165
|
-
Args:
|
166
|
-
access_token (str): The access token.
|
167
|
-
id_token (str): The id token.
|
168
|
-
refresh_token (str, optional): The refresh token. Defaults to None.
|
169
|
-
device_key (str, optional): The device key. Defaults
|
170
|
-
|
171
|
-
Returns:
|
172
|
-
OtfCognito: The user instance
|
173
|
-
"""
|
174
|
-
cognito = OtfCognito(
|
175
|
-
USER_POOL_ID,
|
176
|
-
CLIENT_ID,
|
177
|
-
access_token=access_token,
|
178
|
-
id_token=id_token,
|
179
|
-
refresh_token=refresh_token,
|
180
|
-
device_key=device_key,
|
181
|
-
)
|
182
|
-
cognito.verify_tokens()
|
183
|
-
cognito.check_token()
|
184
|
-
return cognito
|
185
|
-
|
186
|
-
@classmethod
|
187
|
-
def login(cls, username: str, password: str) -> "OtfCognito":
|
188
|
-
"""Create an OtfCognito instance from a username and password.
|
189
|
-
|
190
|
-
Args:
|
191
|
-
username (str): The username to login with.
|
192
|
-
password (str): The password to login with.
|
193
|
-
|
194
|
-
Returns:
|
195
|
-
OtfCognito: The logged in user.
|
196
|
-
"""
|
197
|
-
cognito_user = OtfCognito(USER_POOL_ID, CLIENT_ID, username=username)
|
198
|
-
cognito_user.authenticate(password)
|
199
|
-
cognito_user.check_token()
|
200
|
-
return cognito_user
|
201
|
-
|
202
|
-
|
203
|
-
class IdClaimsData(OtfItemBase):
|
204
|
-
sub: str
|
205
|
-
email_verified: bool
|
206
|
-
iss: str
|
207
|
-
cognito_username: str = Field(alias="cognito:username")
|
208
|
-
given_name: str
|
209
|
-
locale: str
|
210
|
-
home_studio_id: str = Field(alias="custom:home_studio_id")
|
211
|
-
aud: str
|
212
|
-
event_id: str
|
213
|
-
token_use: str
|
214
|
-
auth_time: int
|
215
|
-
exp: int
|
216
|
-
is_migration: str = Field(alias="custom:isMigration")
|
217
|
-
iat: int
|
218
|
-
family_name: str
|
219
|
-
email: str
|
220
|
-
koji_person_id: str = Field(alias="custom:koji_person_id")
|
221
|
-
|
222
|
-
@property
|
223
|
-
def member_uuid(self) -> str:
|
224
|
-
return self.cognito_username
|
225
|
-
|
226
|
-
@property
|
227
|
-
def full_name(self) -> str:
|
228
|
-
return f"{self.given_name} {self.family_name}"
|
229
|
-
|
230
|
-
|
231
|
-
class AccessClaimsData(OtfItemBase):
|
232
|
-
sub: str
|
233
|
-
device_key: str
|
234
|
-
iss: str
|
235
|
-
client_id: str
|
236
|
-
event_id: str
|
237
|
-
token_use: str
|
238
|
-
scope: str
|
239
|
-
auth_time: int
|
240
|
-
exp: int
|
241
|
-
iat: int
|
242
|
-
jti: str
|
243
|
-
username: str
|
244
|
-
|
245
|
-
@property
|
246
|
-
def member_uuid(self) -> str:
|
247
|
-
return self.username
|
248
|
-
|
249
|
-
|
250
|
-
class OtfUser(OtfItemBase):
|
251
|
-
model_config = ConfigDict(arbitrary_types_allowed=True)
|
252
|
-
cognito: OtfCognito
|
253
|
-
|
254
|
-
def __init__(
|
255
|
-
self,
|
256
|
-
username: str | None = None,
|
257
|
-
password: str | None = None,
|
258
|
-
id_token: str | None = None,
|
259
|
-
access_token: str | None = None,
|
260
|
-
refresh_token: str | None = None,
|
261
|
-
device_key: str | None = None,
|
262
|
-
cognito: OtfCognito | None = None,
|
263
|
-
):
|
264
|
-
"""Create a User instance.
|
265
|
-
|
266
|
-
Args:
|
267
|
-
username (str, optional): The username to login with. Defaults to None.
|
268
|
-
password (str, optional): The password to login with. Defaults to None.
|
269
|
-
id_token (str, optional): The id token. Defaults to None.
|
270
|
-
access_token (str, optional): The access token. Defaults to None.
|
271
|
-
refresh_token (str, optional): The refresh token. Defaults to None.
|
272
|
-
device_key (str, optional): The device key. Defaults to None.
|
273
|
-
cognito (OtfCognito, optional): A Cognito instance. Defaults to None.
|
274
|
-
|
275
|
-
Raises:
|
276
|
-
ValueError: Must provide either username and password or id token
|
277
|
-
|
278
|
-
|
279
|
-
"""
|
280
|
-
if cognito:
|
281
|
-
cognito = cognito
|
282
|
-
elif username and password:
|
283
|
-
cognito = OtfCognito.login(username, password)
|
284
|
-
elif access_token and id_token:
|
285
|
-
cognito = OtfCognito.from_token(access_token, id_token, refresh_token, device_key)
|
286
|
-
else:
|
287
|
-
raise ValueError("Must provide either username and password or id token.")
|
288
|
-
|
289
|
-
super().__init__(cognito=cognito)
|
290
|
-
|
291
|
-
@property
|
292
|
-
def member_id(self) -> str:
|
293
|
-
return self.id_claims_data.cognito_username
|
294
|
-
|
295
|
-
@property
|
296
|
-
def member_uuid(self) -> str:
|
297
|
-
return self.access_claims_data.sub
|
298
|
-
|
299
|
-
@property
|
300
|
-
def access_claims_data(self) -> AccessClaimsData:
|
301
|
-
return AccessClaimsData(**self.cognito.access_claims)
|
302
|
-
|
303
|
-
@property
|
304
|
-
def id_claims_data(self) -> IdClaimsData:
|
305
|
-
return IdClaimsData(**self.cognito.id_claims)
|
306
|
-
|
307
|
-
def get_tokens(self) -> dict[str, str]:
|
308
|
-
return {
|
309
|
-
"id_token": self.cognito.id_token,
|
310
|
-
"access_token": self.cognito.access_token,
|
311
|
-
"refresh_token": self.cognito.refresh_token,
|
312
|
-
}
|
313
|
-
|
314
|
-
@property
|
315
|
-
def device_key(self):
|
316
|
-
return self.cognito.device_key
|
otf_api/models/book_class.py
DELETED
@@ -1,89 +0,0 @@
|
|
1
|
-
from collections.abc import Hashable
|
2
|
-
from datetime import datetime
|
3
|
-
from typing import Any
|
4
|
-
|
5
|
-
from pydantic import Field
|
6
|
-
|
7
|
-
from otf_api.models.base import OtfItemBase
|
8
|
-
|
9
|
-
|
10
|
-
class Class(OtfItemBase):
|
11
|
-
class_id: int = Field(None, alias="classId")
|
12
|
-
class_uuid: str = Field(None, alias="classUUId")
|
13
|
-
mbo_studio_id: int | None = Field(None, alias="mboStudioId")
|
14
|
-
mbo_class_id: int | None = Field(None, alias="mboClassId")
|
15
|
-
mbo_class_schedule_id: int | None = Field(None, alias="mboClassScheduleId")
|
16
|
-
mbo_program_id: int | None = Field(None, alias="mboProgramId")
|
17
|
-
studio_id: int | None = Field(None, alias="studioId")
|
18
|
-
coach_id: int | None = Field(None, alias="coachId")
|
19
|
-
location_id: int | None = Field(None, alias="locationId")
|
20
|
-
name: str | None = None
|
21
|
-
description: str | None = None
|
22
|
-
program_name: str | None = Field(None, alias="programName")
|
23
|
-
program_schedule_type: str | None = Field(None, alias="programScheduleType")
|
24
|
-
program_cancel_offset: int | None = Field(None, alias="programCancelOffset")
|
25
|
-
max_capacity: int | None = Field(None, alias="maxCapacity")
|
26
|
-
total_booked: int | None = Field(None, alias="totalBooked")
|
27
|
-
web_capacity: int | None = Field(None, alias="webCapacity")
|
28
|
-
web_booked: int | None = Field(None, alias="webBooked")
|
29
|
-
total_booked_waitlist: int | None = Field(None, alias="totalBookedWaitlist")
|
30
|
-
start_date_time: datetime | None = Field(None, alias="startDateTime")
|
31
|
-
end_date_time: datetime | None = Field(None, alias="endDateTime")
|
32
|
-
is_cancelled: bool | None = Field(None, alias="isCancelled")
|
33
|
-
substitute: bool | None = None
|
34
|
-
is_active: bool | None = Field(None, alias="isActive")
|
35
|
-
is_waitlist_available: bool | None = Field(None, alias="isWaitlistAvailable")
|
36
|
-
is_enrolled: bool | None = Field(None, alias="isEnrolled")
|
37
|
-
is_hide_cancel: bool | None = Field(None, alias="isHideCancel")
|
38
|
-
is_available: bool | None = Field(None, alias="isAvailable")
|
39
|
-
room_number: int | None = Field(None, alias="roomNumber")
|
40
|
-
created_by: str | None = Field(None, alias="createdBy")
|
41
|
-
created_date: datetime | None = Field(None, alias="createdDate")
|
42
|
-
updated_by: str | None = Field(None, alias="updatedBy")
|
43
|
-
updated_date: datetime | None = Field(None, alias="updatedDate")
|
44
|
-
is_deleted: bool | None = Field(None, alias="isDeleted")
|
45
|
-
studio: dict[Hashable, Any] | None = Field(None, exclude=True)
|
46
|
-
location: dict[Hashable, Any] | None = Field(None, exclude=True)
|
47
|
-
coach: dict[Hashable, Any] | None = Field(None, exclude=True)
|
48
|
-
attributes: dict[str, Any] | None = Field(None, exclude=True)
|
49
|
-
|
50
|
-
|
51
|
-
class SavedBooking(OtfItemBase):
|
52
|
-
class_booking_id: int = Field(..., alias="classBookingId")
|
53
|
-
class_booking_uuid: str = Field(..., alias="classBookingUUId")
|
54
|
-
studio_id: int | None = Field(None, alias="studioId")
|
55
|
-
class_id: int | None = Field(None, alias="classId")
|
56
|
-
is_intro: bool | None = Field(None, alias="isIntro")
|
57
|
-
member_id: int | None = Field(None, alias="memberId")
|
58
|
-
mbo_member_id: str | None = Field(None, alias="mboMemberId")
|
59
|
-
mbo_class_id: int | None = Field(None, alias="mboClassId")
|
60
|
-
mbo_visit_id: int | None = Field(None, alias="mboVisitId")
|
61
|
-
mbo_waitlist_entry_id: int | None = Field(None, alias="mboWaitlistEntryId")
|
62
|
-
mbo_sync_message: str | None = Field(None, alias="mboSyncMessage")
|
63
|
-
status: str | None = None
|
64
|
-
booked_date: datetime | None = Field(None, alias="bookedDate")
|
65
|
-
checked_in_date: datetime | None = Field(None, alias="checkedInDate")
|
66
|
-
cancelled_date: datetime | None = Field(None, alias="cancelledDate")
|
67
|
-
created_by: str | None = Field(None, alias="createdBy")
|
68
|
-
created_date: datetime | None = Field(None, alias="createdDate")
|
69
|
-
updated_by: str | None = Field(None, alias="updatedBy")
|
70
|
-
updated_date: datetime | None = Field(None, alias="updatedDate")
|
71
|
-
is_deleted: bool | None = Field(None, alias="isDeleted")
|
72
|
-
member: dict[Hashable, Any] | None = Field(None, exclude=True)
|
73
|
-
otf_class: Class = Field(..., alias="class")
|
74
|
-
custom_data: Any | None = Field(None, alias="customData", exclude=True)
|
75
|
-
attributes: dict[str, Any] | None = Field(None, exclude=True)
|
76
|
-
|
77
|
-
|
78
|
-
class BookClass(OtfItemBase):
|
79
|
-
saved_bookings: list[SavedBooking] = Field(None, alias="savedBookings")
|
80
|
-
mbo_response: list[dict[Hashable, Any]] | Any | None = Field(None, alias="mboResponse", exclude=True)
|
81
|
-
|
82
|
-
@property
|
83
|
-
def booking(self) -> SavedBooking:
|
84
|
-
return self.saved_bookings[0]
|
85
|
-
|
86
|
-
@property
|
87
|
-
def booking_uuid(self) -> str:
|
88
|
-
"""Returns the booking UUID for the class. This can be used to cancel the class."""
|
89
|
-
return self.booking.class_booking_uuid
|
otf_api/models/cancel_booking.py
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
from collections.abc import Hashable
|
2
|
-
from datetime import datetime
|
3
|
-
from typing import Any
|
4
|
-
|
5
|
-
from pydantic import Field
|
6
|
-
|
7
|
-
from otf_api.models.base import OtfItemBase
|
8
|
-
|
9
|
-
|
10
|
-
class Class(OtfItemBase):
|
11
|
-
class_uuid: str = Field(..., alias="classUUId")
|
12
|
-
name: str | None = None
|
13
|
-
description: str | None = None
|
14
|
-
start_date_time: datetime | None = Field(None, alias="startDateTime")
|
15
|
-
end_date_time: datetime | None = Field(None, alias="endDateTime")
|
16
|
-
is_available: bool | None = Field(None, alias="isAvailable")
|
17
|
-
is_cancelled: bool | None = Field(None, alias="isCancelled")
|
18
|
-
total_booked: int | None = Field(None, alias="totalBooked")
|
19
|
-
mbo_class_id: int | None = Field(None, alias="mboClassId")
|
20
|
-
mbo_studio_id: int | None = Field(None, alias="mboStudioId")
|
21
|
-
studio: dict[Hashable, Any] | None = None
|
22
|
-
coach: dict[Hashable, Any] | None = None
|
23
|
-
|
24
|
-
|
25
|
-
class CancelBooking(OtfItemBase):
|
26
|
-
class_booking_id: int = Field(..., alias="classBookingId")
|
27
|
-
class_booking_uuid: str = Field(..., alias="classBookingUUId")
|
28
|
-
otf_class: Class = Field(..., alias="class")
|
29
|
-
|
30
|
-
studio_id: int | None = Field(None, alias="studioId")
|
31
|
-
class_id: int | None = Field(None, alias="classId")
|
32
|
-
is_intro: bool | None = Field(None, alias="isIntro")
|
33
|
-
member_id: int | None = Field(None, alias="memberId")
|
34
|
-
mbo_member_id: str | None = Field(None, alias="mboMemberId")
|
35
|
-
mbo_class_id: int | None = Field(None, alias="mboClassId")
|
36
|
-
mbo_visit_id: int | None = Field(None, alias="mboVisitId")
|
37
|
-
mbo_waitlist_entry_id: int | None = Field(None, alias="mboWaitlistEntryId")
|
38
|
-
mbo_sync_message: str | None = Field(None, alias="mboSyncMessage")
|
39
|
-
status: str | None = None
|
40
|
-
booked_date: datetime | None = Field(None, alias="bookedDate")
|
41
|
-
checked_in_date: datetime | None = Field(None, alias="checkedInDate")
|
42
|
-
cancelled_date: datetime | None = Field(None, alias="cancelledDate")
|
43
|
-
created_by: str | None = Field(None, alias="createdBy")
|
44
|
-
created_date: datetime | None = Field(None, alias="createdDate")
|
45
|
-
updated_by: str | None = Field(None, alias="updatedBy")
|
46
|
-
updated_date: datetime | None = Field(None, alias="updatedDate")
|
47
|
-
is_deleted: bool | None = Field(None, alias="isDeleted")
|
48
|
-
member: dict[Hashable, Any] | None = None
|
49
|
-
continue_retry: bool | None = Field(None, alias="continueRetry")
|
@@ -1,106 +0,0 @@
|
|
1
|
-
from datetime import datetime
|
2
|
-
|
3
|
-
from pydantic import Field
|
4
|
-
|
5
|
-
from otf_api.models.base import OtfItemBase
|
6
|
-
|
7
|
-
|
8
|
-
class Location(OtfItemBase):
|
9
|
-
location_id: int = Field(..., alias="locationId")
|
10
|
-
location_uuid: str = Field(..., alias="locationUUId")
|
11
|
-
studio_id: int = Field(..., alias="studioId")
|
12
|
-
mbo_location_id: int = Field(..., alias="mboLocationId")
|
13
|
-
mbo_studio_id: int = Field(..., alias="mboStudioId")
|
14
|
-
latitude: float
|
15
|
-
longitude: float
|
16
|
-
address1: str
|
17
|
-
address2: str | None = None
|
18
|
-
city: str
|
19
|
-
state: str
|
20
|
-
phone: str
|
21
|
-
postal_code: str = Field(..., alias="postalCode")
|
22
|
-
|
23
|
-
|
24
|
-
class StudioLocation(OtfItemBase):
|
25
|
-
bill_to_address: str = Field(..., alias="billToAddress")
|
26
|
-
bill_to_address2: str = Field(..., alias="billToAddress2")
|
27
|
-
bill_to_city: str = Field(..., alias="billToCity")
|
28
|
-
bill_to_state: str = Field(..., alias="billToState")
|
29
|
-
bill_to_postal_code: str = Field(..., alias="billToPostalCode")
|
30
|
-
bill_to_region: str = Field(..., alias="billToRegion")
|
31
|
-
bill_to_country_id: int = Field(..., alias="billToCountryId")
|
32
|
-
bill_to_country: str = Field(..., alias="billToCountry")
|
33
|
-
ship_to_address: str = Field(..., alias="shipToAddress")
|
34
|
-
ship_to_address2: str | None = Field(None, alias="shipToAddress2")
|
35
|
-
ship_to_city: str = Field(..., alias="shipToCity")
|
36
|
-
ship_to_state: str = Field(..., alias="shipToState")
|
37
|
-
ship_to_postal_code: str = Field(..., alias="shipToPostalCode")
|
38
|
-
ship_to_region: str = Field(..., alias="shipToRegion")
|
39
|
-
ship_to_country_id: int = Field(..., alias="shipToCountryId")
|
40
|
-
ship_to_country: str = Field(..., alias="shipToCountry")
|
41
|
-
physical_address: str = Field(..., alias="physicalAddress")
|
42
|
-
physical_address2: str | None = Field(None, alias="physicalAddress2")
|
43
|
-
physical_city: str = Field(..., alias="physicalCity")
|
44
|
-
physical_state: str = Field(..., alias="physicalState")
|
45
|
-
physical_postal_code: str = Field(..., alias="physicalPostalCode")
|
46
|
-
physical_region: str = Field(..., alias="physicalRegion")
|
47
|
-
physical_country_id: int = Field(..., alias="physicalCountryId")
|
48
|
-
physical_country: str = Field(..., alias="physicalCountry")
|
49
|
-
phone_number: str = Field(..., alias="phoneNumber")
|
50
|
-
latitude: str
|
51
|
-
longitude: str
|
52
|
-
|
53
|
-
|
54
|
-
class FavoriteStudio(OtfItemBase):
|
55
|
-
studio_id: int = Field(..., alias="studioId")
|
56
|
-
studio_uuid: str = Field(..., alias="studioUUId")
|
57
|
-
mbo_studio_id: int = Field(..., alias="mboStudioId")
|
58
|
-
studio_name: str = Field(..., alias="studioName")
|
59
|
-
area_id: int | None = Field(None, alias="areaId")
|
60
|
-
market_id: int | None = Field(None, alias="marketId")
|
61
|
-
state_id: int | None = Field(None, alias="stateId")
|
62
|
-
studio_physical_location_id: int = Field(..., alias="studioPhysicalLocationId")
|
63
|
-
studio_number: str = Field(..., alias="studioNumber")
|
64
|
-
description: str | None = None
|
65
|
-
studio_version: str | None = Field(None, alias="studioVersion")
|
66
|
-
studio_token: str = Field(..., alias="studioToken")
|
67
|
-
studio_status: str = Field(..., alias="studioStatus")
|
68
|
-
open_date: datetime = Field(..., alias="openDate")
|
69
|
-
studio_type_id: int = Field(..., alias="studioTypeId")
|
70
|
-
pos_type_id: int | None = Field(None, alias="posTypeId")
|
71
|
-
logo_url: str | None = Field(None, alias="logoUrl")
|
72
|
-
page_color1: str | None = Field(None, alias="pageColor1")
|
73
|
-
page_color2: str | None = Field(None, alias="pageColor2")
|
74
|
-
page_color3: str | None = Field(None, alias="pageColor3")
|
75
|
-
page_color4: str | None = Field(None, alias="pageColor4")
|
76
|
-
accepts_visa_master_card: bool = Field(..., alias="acceptsVisaMasterCard")
|
77
|
-
accepts_american_express: bool = Field(..., alias="acceptsAmericanExpress")
|
78
|
-
accepts_discover: bool = Field(..., alias="acceptsDiscover")
|
79
|
-
accepts_ach: bool = Field(..., alias="acceptsACH")
|
80
|
-
sms_package_enabled: bool | None = Field(None, alias="smsPackageEnabled")
|
81
|
-
allows_dashboard_access: bool | None = Field(None, alias="allowsDashboardAccess")
|
82
|
-
pricing_level: str | None = Field(None, alias="pricingLevel")
|
83
|
-
contact_email: str = Field(..., alias="contactEmail")
|
84
|
-
time_zone: str = Field(..., alias="timeZone")
|
85
|
-
environment: str
|
86
|
-
allows_cr_waitlist: bool = Field(..., alias="allowsCRWaitlist")
|
87
|
-
cr_waitlist_flag_last_updated: datetime = Field(..., alias="crWaitlistFlagLastUpdated")
|
88
|
-
is_integrated: bool = Field(..., alias="isIntegrated")
|
89
|
-
created_by: str = Field(..., alias="createdBy")
|
90
|
-
created_date: datetime = Field(..., alias="createdDate")
|
91
|
-
updated_date: datetime = Field(..., alias="updatedDate")
|
92
|
-
is_deleted: bool = Field(..., alias="isDeleted")
|
93
|
-
locations: list[Location]
|
94
|
-
studio_location: StudioLocation = Field(..., alias="studioLocation")
|
95
|
-
|
96
|
-
|
97
|
-
class FavoriteStudioList(OtfItemBase):
|
98
|
-
studios: list[FavoriteStudio]
|
99
|
-
|
100
|
-
@property
|
101
|
-
def studio_ids(self) -> list[int]:
|
102
|
-
return [studio.studio_id for studio in self.studios]
|
103
|
-
|
104
|
-
@property
|
105
|
-
def studio_uuids(self) -> list[str]:
|
106
|
-
return [studio.studio_uuid for studio in self.studios]
|
@@ -1,21 +0,0 @@
|
|
1
|
-
from datetime import datetime
|
2
|
-
|
3
|
-
from pydantic import Field
|
4
|
-
|
5
|
-
from otf_api.models.base import OtfItemBase
|
6
|
-
|
7
|
-
|
8
|
-
class LatestAgreement(OtfItemBase):
|
9
|
-
file_url: str = Field(..., alias="fileUrl")
|
10
|
-
agreement_id: int = Field(..., alias="agreementId")
|
11
|
-
agreement_uuid: str = Field(..., alias="agreementUUId")
|
12
|
-
agreement_datetime: datetime = Field(..., alias="agreementDatetime")
|
13
|
-
agreement_type_id: int = Field(..., alias="agreementTypeId")
|
14
|
-
platform: None
|
15
|
-
locale: str
|
16
|
-
version: str
|
17
|
-
created_by: str = Field(..., alias="createdBy")
|
18
|
-
created_date: datetime = Field(..., alias="createdDate")
|
19
|
-
updated_by: str = Field(..., alias="updatedBy")
|
20
|
-
updated_date: datetime = Field(..., alias="updatedDate")
|
21
|
-
is_deleted: bool = Field(..., alias="isDeleted")
|
@@ -1,34 +0,0 @@
|
|
1
|
-
from pydantic import Field
|
2
|
-
|
3
|
-
from otf_api.models.base import OtfItemBase
|
4
|
-
|
5
|
-
|
6
|
-
class MaxHr(OtfItemBase):
|
7
|
-
type: str
|
8
|
-
value: int
|
9
|
-
|
10
|
-
|
11
|
-
class Zone(OtfItemBase):
|
12
|
-
start_bpm: int = Field(..., alias="startBpm")
|
13
|
-
end_bpm: int = Field(..., alias="endBpm")
|
14
|
-
|
15
|
-
|
16
|
-
class Zones(OtfItemBase):
|
17
|
-
gray: Zone
|
18
|
-
blue: Zone
|
19
|
-
green: Zone
|
20
|
-
orange: Zone
|
21
|
-
red: Zone
|
22
|
-
|
23
|
-
|
24
|
-
class HistoryItem(OtfItemBase):
|
25
|
-
max_hr: MaxHr = Field(..., alias="maxHr")
|
26
|
-
zones: Zones
|
27
|
-
change_from_previous: int = Field(..., alias="changeFromPrevious")
|
28
|
-
change_bucket: str = Field(..., alias="changeBucket")
|
29
|
-
assigned_at: str = Field(..., alias="assignedAt")
|
30
|
-
|
31
|
-
|
32
|
-
class TelemetryHrHistory(OtfItemBase):
|
33
|
-
member_uuid: str = Field(..., alias="memberUuid")
|
34
|
-
history: list[HistoryItem]
|
@@ -1,13 +0,0 @@
|
|
1
|
-
from pydantic import Field
|
2
|
-
|
3
|
-
from otf_api.models.base import OtfItemBase
|
4
|
-
|
5
|
-
|
6
|
-
class MaxHr(OtfItemBase):
|
7
|
-
type: str
|
8
|
-
value: int
|
9
|
-
|
10
|
-
|
11
|
-
class TelemetryMaxHr(OtfItemBase):
|
12
|
-
member_uuid: str = Field(..., alias="memberUuid")
|
13
|
-
max_hr: MaxHr = Field(..., alias="maxHr")
|