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
otf_api/models/studio_detail.py
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
from datetime import datetime
|
2
|
-
|
3
|
-
from pydantic import AliasChoices, AliasPath, Field
|
4
|
-
|
5
|
-
from otf_api.models.base import OtfItemBase
|
6
|
-
from otf_api.models.enums import StudioStatus
|
7
|
-
from otf_api.models.mixins import AddressMixin
|
8
|
-
|
9
|
-
|
10
|
-
class StudioLocation(AddressMixin, OtfItemBase):
|
11
|
-
phone_number: str | None = Field(None, validation_alias=AliasChoices("phone", "phoneNumber"))
|
12
|
-
latitude: float | None = Field(None, validation_alias=AliasChoices("latitude"))
|
13
|
-
longitude: float | None = Field(None, validation_alias=AliasChoices("longitude"))
|
14
|
-
|
15
|
-
physical_region: str | None = Field(None, alias="physicalRegion", exclude=True, repr=False)
|
16
|
-
physical_country_id: int | None = Field(None, alias="physicalCountryId", exclude=True, repr=False)
|
17
|
-
|
18
|
-
|
19
|
-
class StudioDetail(OtfItemBase):
|
20
|
-
studio_uuid: str = Field(..., alias="studioUUId", description="The OTF studio UUID")
|
21
|
-
|
22
|
-
contact_email: str | None = Field(None, alias="contactEmail")
|
23
|
-
distance: float | None = Field(
|
24
|
-
None,
|
25
|
-
description="Distance from latitude and longitude provided to `search_studios_by_geo` method,\
|
26
|
-
NULL if that method was not used",
|
27
|
-
exclude=True,
|
28
|
-
repr=False,
|
29
|
-
)
|
30
|
-
location: StudioLocation = Field(..., alias="studioLocation", default_factory=StudioLocation) # type: ignore
|
31
|
-
name: str | None = Field(None, alias="studioName")
|
32
|
-
status: StudioStatus | None = Field(
|
33
|
-
None, alias="studioStatus", description="Active, Temporarily Closed, Coming Soon"
|
34
|
-
)
|
35
|
-
time_zone: str | None = Field(None, alias="timeZone")
|
36
|
-
|
37
|
-
# flags
|
38
|
-
accepts_ach: bool | None = Field(None, alias="acceptsAch", exclude=True, repr=False)
|
39
|
-
accepts_american_express: bool | None = Field(None, alias="acceptsAmericanExpress", exclude=True, repr=False)
|
40
|
-
accepts_discover: bool | None = Field(None, alias="acceptsDiscover", exclude=True, repr=False)
|
41
|
-
accepts_visa_master_card: bool | None = Field(None, alias="acceptsVisaMasterCard", exclude=True, repr=False)
|
42
|
-
allows_cr_waitlist: bool | None = Field(None, alias="allowsCrWaitlist", exclude=True, repr=False)
|
43
|
-
allows_dashboard_access: bool | None = Field(None, alias="allowsDashboardAccess", exclude=True, repr=False)
|
44
|
-
is_crm: bool | None = Field(None, validation_alias=AliasPath("studioProfiles", "isCrm"), exclude=True, repr=False)
|
45
|
-
is_integrated: bool | None = Field(
|
46
|
-
None, alias="isIntegrated", exclude=True, repr=False, description="Always 'True'"
|
47
|
-
)
|
48
|
-
is_mobile: bool | None = Field(None, alias="isMobile", exclude=True, repr=False)
|
49
|
-
is_otbeat: bool | None = Field(None, alias="isOtbeat", exclude=True, repr=False)
|
50
|
-
is_web: bool | None = Field(None, validation_alias=AliasPath("studioProfiles", "isWeb"), exclude=True, repr=False)
|
51
|
-
sms_package_enabled: bool | None = Field(None, alias="smsPackageEnabled", exclude=True, repr=False)
|
52
|
-
|
53
|
-
# misc
|
54
|
-
studio_id: int | None = Field(None, alias="studioId", description="Not used by API", exclude=True, repr=False)
|
55
|
-
mbo_studio_id: int | None = Field(None, alias="mboStudioId", exclude=True, repr=False, description="MindBody attr")
|
56
|
-
open_date: datetime | None = Field(None, alias="openDate", exclude=True, repr=False)
|
57
|
-
pricing_level: str | None = Field(
|
58
|
-
None, alias="pricingLevel", exclude=True, repr=False, description="Pro, Legacy, Accelerate, or empty"
|
59
|
-
)
|
60
|
-
re_open_date: datetime | None = Field(None, alias="reOpenDate", exclude=True, repr=False)
|
61
|
-
studio_number: str | None = Field(None, alias="studioNumber", exclude=True, repr=False)
|
62
|
-
studio_physical_location_id: int | None = Field(None, alias="studioPhysicalLocationId", exclude=True, repr=False)
|
63
|
-
studio_token: str | None = Field(None, alias="studioToken", exclude=True, repr=False)
|
64
|
-
studio_type_id: int | None = Field(None, alias="studioTypeId", exclude=True, repr=False)
|
65
|
-
|
66
|
-
@classmethod
|
67
|
-
def create_empty_model(cls, studio_uuid: str) -> "StudioDetail":
|
68
|
-
"""Create an empty model with the given studio_uuid."""
|
69
|
-
|
70
|
-
# pylance doesn't know that the rest of the fields default to None, so we use type: ignore
|
71
|
-
return StudioDetail(studioUUId=studio_uuid, studioName="Studio Not Found", studioStatus="Unknown") # type: ignore
|
@@ -1,36 +0,0 @@
|
|
1
|
-
from datetime import datetime
|
2
|
-
|
3
|
-
from pydantic import Field
|
4
|
-
|
5
|
-
from otf_api.models.base import OtfItemBase
|
6
|
-
from otf_api.models.studio_detail import StudioDetail
|
7
|
-
|
8
|
-
|
9
|
-
class StudioService(OtfItemBase):
|
10
|
-
studio: StudioDetail = Field(..., exclude=True, repr=False)
|
11
|
-
service_uuid: str = Field(..., alias="serviceUUId")
|
12
|
-
name: str | None = None
|
13
|
-
price: str | None = None
|
14
|
-
qty: int | None = None
|
15
|
-
online_price: str | None = Field(None, alias="onlinePrice")
|
16
|
-
tax_rate: str | None = Field(None, alias="taxRate")
|
17
|
-
current: bool | None = None
|
18
|
-
is_deleted: bool | None = Field(None, alias="isDeleted")
|
19
|
-
created_date: datetime | None = Field(None, alias="createdDate")
|
20
|
-
updated_date: datetime | None = Field(None, alias="updatedDate")
|
21
|
-
|
22
|
-
# unused fields
|
23
|
-
|
24
|
-
# ids
|
25
|
-
mbo_program_id: int | None = Field(None, alias="mboProgramId", exclude=True, repr=False)
|
26
|
-
mbo_description_id: str | None = Field(None, alias="mboDescriptionId", exclude=True, repr=False)
|
27
|
-
mbo_product_id: int | None = Field(None, alias="mboProductId", exclude=True, repr=False)
|
28
|
-
service_id: int | None = Field(None, alias="serviceId", exclude=True, repr=False)
|
29
|
-
studio_id: int | None = Field(None, alias="studioId", exclude=True, repr=False)
|
30
|
-
created_by: str | None = Field(None, alias="createdBy", exclude=True, repr=False)
|
31
|
-
updated_by: str | None = Field(None, alias="updatedBy", exclude=True, repr=False)
|
32
|
-
|
33
|
-
# flags
|
34
|
-
is_web: bool | None = Field(None, alias="isWeb", exclude=True, repr=False)
|
35
|
-
is_crm: bool | None = Field(None, alias="isCrm", exclude=True, repr=False)
|
36
|
-
is_mobile: bool | None = Field(None, alias="isMobile", exclude=True, repr=False)
|
otf_api/models/telemetry.py
DELETED
@@ -1,84 +0,0 @@
|
|
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(..., alias="startBpm")
|
11
|
-
end_bpm: int = Field(..., 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(..., alias="treadSpeed")
|
24
|
-
tread_incline: float = Field(..., alias="treadIncline")
|
25
|
-
agg_tread_distance: int = Field(..., alias="aggTreadDistance")
|
26
|
-
|
27
|
-
|
28
|
-
class RowData(OtfItemBase):
|
29
|
-
row_speed: float = Field(..., alias="rowSpeed")
|
30
|
-
row_pps: float = Field(..., alias="rowPps")
|
31
|
-
row_spm: float = Field(..., alias="rowSpm")
|
32
|
-
agg_row_distance: int = Field(..., alias="aggRowDistance")
|
33
|
-
row_pace: int = Field(..., alias="rowPace")
|
34
|
-
|
35
|
-
|
36
|
-
class TelemetryItem(OtfItemBase):
|
37
|
-
relative_timestamp: int = Field(..., alias="relativeTimestamp")
|
38
|
-
hr: int | None = None
|
39
|
-
agg_splats: int = Field(..., alias="aggSplats")
|
40
|
-
agg_calories: int = Field(..., 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, alias="treadData")
|
47
|
-
row_data: RowData | None = Field(None, alias="rowData")
|
48
|
-
|
49
|
-
|
50
|
-
class Telemetry(OtfItemBase):
|
51
|
-
member_uuid: str = Field(..., alias="memberUuid")
|
52
|
-
performance_summary_id: str = Field(
|
53
|
-
..., alias="classHistoryUuid", description="The ID of the performance summary this telemetry item belongs to."
|
54
|
-
)
|
55
|
-
class_history_uuid: str = Field(..., alias="classHistoryUuid", description="The same as performance_summary_id.")
|
56
|
-
class_start_time: datetime | None = Field(None, alias="classStartTime")
|
57
|
-
max_hr: int | None = Field(None, alias="maxHr")
|
58
|
-
zones: Zones
|
59
|
-
window_size: int | None = Field(None, alias="windowSize")
|
60
|
-
telemetry: list[TelemetryItem] = Field(default_factory=list)
|
61
|
-
|
62
|
-
def __init__(self, **data: dict[str, Any]):
|
63
|
-
super().__init__(**data)
|
64
|
-
for telem in self.telemetry:
|
65
|
-
if self.class_start_time is None:
|
66
|
-
continue
|
67
|
-
|
68
|
-
telem.timestamp = self.class_start_time + timedelta(seconds=telem.relative_timestamp)
|
69
|
-
|
70
|
-
@field_serializer("telemetry", when_used="json")
|
71
|
-
def reduce_telemetry_list(self, value: list[TelemetryItem]) -> list[TelemetryItem]:
|
72
|
-
"""Reduces the telemetry list to only include the first 10 items."""
|
73
|
-
if len(value) > 10:
|
74
|
-
return value[:5] + value[-5:]
|
75
|
-
return value
|
76
|
-
|
77
|
-
|
78
|
-
class TelemetryHistoryItem(OtfItemBase):
|
79
|
-
max_hr_type: str | None = Field(None, validation_alias=AliasPath("maxHr", "type"))
|
80
|
-
max_hr_value: int | None = Field(None, validation_alias=AliasPath("maxHr", "value"))
|
81
|
-
zones: Zones | None = None
|
82
|
-
change_from_previous: int | None = Field(None, alias="changeFromPrevious")
|
83
|
-
change_bucket: str | None = Field(None, alias="changeBucket")
|
84
|
-
assigned_at: datetime | None = Field(None, alias="assignedAt")
|
otf_api/utils.py
DELETED
@@ -1,164 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
import typing
|
3
|
-
from datetime import date, datetime, time
|
4
|
-
from logging import getLogger
|
5
|
-
from pathlib import Path
|
6
|
-
from typing import Any
|
7
|
-
|
8
|
-
import attrs
|
9
|
-
|
10
|
-
if typing.TYPE_CHECKING:
|
11
|
-
from otf_api import models
|
12
|
-
|
13
|
-
LOGGER = getLogger(__name__)
|
14
|
-
|
15
|
-
MIN_TIME = datetime.min.time()
|
16
|
-
|
17
|
-
|
18
|
-
def get_booking_uuid(booking_or_uuid: "str | models.Booking") -> str:
|
19
|
-
from otf_api.models.bookings import Booking
|
20
|
-
|
21
|
-
if isinstance(booking_or_uuid, str):
|
22
|
-
return booking_or_uuid
|
23
|
-
|
24
|
-
if isinstance(booking_or_uuid, Booking):
|
25
|
-
return booking_or_uuid.booking_uuid
|
26
|
-
|
27
|
-
raise ValueError(f"Expected Booking or str, got {type(booking_or_uuid)}")
|
28
|
-
|
29
|
-
|
30
|
-
def get_booking_id(booking_or_id: "str | models.BookingV2") -> str:
|
31
|
-
from otf_api.models.bookings_v2 import BookingV2
|
32
|
-
|
33
|
-
if isinstance(booking_or_id, str):
|
34
|
-
return booking_or_id
|
35
|
-
|
36
|
-
if isinstance(booking_or_id, BookingV2):
|
37
|
-
return booking_or_id.booking_id
|
38
|
-
|
39
|
-
raise ValueError(f"Expected BookingV2 or str, got {type(booking_or_id)}")
|
40
|
-
|
41
|
-
|
42
|
-
def get_class_uuid(class_or_uuid: "str | models.OtfClass | models.BookingV2Class") -> str:
|
43
|
-
if isinstance(class_or_uuid, str):
|
44
|
-
return class_or_uuid
|
45
|
-
|
46
|
-
if hasattr(class_or_uuid, "class_uuid"):
|
47
|
-
class_uuid = getattr(class_or_uuid, "class_uuid", None)
|
48
|
-
if class_uuid:
|
49
|
-
return class_uuid
|
50
|
-
raise ValueError("Class does not have a class_uuid")
|
51
|
-
|
52
|
-
raise ValueError(f"Expected OtfClass, BookingV2Class, or str, got {type(class_or_uuid)}")
|
53
|
-
|
54
|
-
|
55
|
-
def ensure_list(obj: list | Any | None) -> list:
|
56
|
-
if obj is None:
|
57
|
-
return []
|
58
|
-
if not isinstance(obj, list):
|
59
|
-
return [obj]
|
60
|
-
return obj
|
61
|
-
|
62
|
-
|
63
|
-
def ensure_datetime(date_str: str | datetime | None, combine_with: time = MIN_TIME) -> datetime | None:
|
64
|
-
if not date_str:
|
65
|
-
return None
|
66
|
-
|
67
|
-
if isinstance(date_str, str):
|
68
|
-
return datetime.fromisoformat(date_str)
|
69
|
-
|
70
|
-
if isinstance(date_str, datetime):
|
71
|
-
return date_str
|
72
|
-
|
73
|
-
if isinstance(date_str, date):
|
74
|
-
return datetime.combine(date_str, combine_with)
|
75
|
-
|
76
|
-
raise ValueError(f"Expected str or datetime, got {type(date_str)}")
|
77
|
-
|
78
|
-
|
79
|
-
def ensure_date(date_str: str | date | None) -> date | None:
|
80
|
-
if not date_str:
|
81
|
-
return None
|
82
|
-
|
83
|
-
if isinstance(date_str, str):
|
84
|
-
return datetime.fromisoformat(date_str).date()
|
85
|
-
|
86
|
-
if isinstance(date_str, datetime):
|
87
|
-
return date_str.date()
|
88
|
-
|
89
|
-
return date_str
|
90
|
-
|
91
|
-
|
92
|
-
@attrs.define
|
93
|
-
class CacheableData:
|
94
|
-
"""Represents a cacheable data object, with methods to read and write to cache."""
|
95
|
-
|
96
|
-
name: str
|
97
|
-
cache_dir: Path
|
98
|
-
|
99
|
-
def __attrs_post_init__(self):
|
100
|
-
self.cache_path.parent.mkdir(parents=True, exist_ok=True)
|
101
|
-
|
102
|
-
@property
|
103
|
-
def cache_path(self) -> Path:
|
104
|
-
"""The path to the cache file."""
|
105
|
-
return self.cache_dir.expanduser().joinpath(f"{self.name}_cache.json")
|
106
|
-
|
107
|
-
def get_cached_data(self, keys: list[str] | None = None) -> dict[str, str]:
|
108
|
-
"""Reads the cache file and returns the data if it exists and is valid.
|
109
|
-
|
110
|
-
Returns:
|
111
|
-
dict[str, str]: The cached data, or an empty dictionary if the cache is invalid or missing.
|
112
|
-
"""
|
113
|
-
LOGGER.debug(f"Loading {self.name} from cache ({self.cache_path})")
|
114
|
-
try:
|
115
|
-
if not self.cache_path.exists():
|
116
|
-
return {}
|
117
|
-
|
118
|
-
if self.cache_path.stat().st_size == 0:
|
119
|
-
return {}
|
120
|
-
|
121
|
-
data: dict[str, str] = json.loads(self.cache_path.read_text())
|
122
|
-
if not keys:
|
123
|
-
return data
|
124
|
-
|
125
|
-
if set(data.keys()).issuperset(set(keys)):
|
126
|
-
return {k: v for k, v in data.items() if k in keys}
|
127
|
-
raise ValueError(f"Data must contain all keys: {keys}")
|
128
|
-
except Exception:
|
129
|
-
LOGGER.exception(f"Failed to read {self.cache_path.name}")
|
130
|
-
return {}
|
131
|
-
|
132
|
-
def write_to_cache(self, data: dict[str, str]) -> None:
|
133
|
-
"""Writes the data to the cache file."""
|
134
|
-
LOGGER.debug(f"Writing {self.name} to cache ({self.cache_path})")
|
135
|
-
|
136
|
-
# double check everything exists
|
137
|
-
if not self.cache_path.parent.exists():
|
138
|
-
self.cache_path.parent.mkdir(parents=True, exist_ok=True)
|
139
|
-
|
140
|
-
if not self.cache_path.exists():
|
141
|
-
self.cache_path.touch()
|
142
|
-
|
143
|
-
existing_data = self.get_cached_data()
|
144
|
-
data = {**existing_data, **data}
|
145
|
-
|
146
|
-
self.cache_path.write_text(json.dumps(data, indent=4, default=str))
|
147
|
-
|
148
|
-
def clear_cache(self, keys: list[str] | None = None) -> None:
|
149
|
-
"""Deletes the cache file if it exists."""
|
150
|
-
if not self.cache_path.exists():
|
151
|
-
return
|
152
|
-
|
153
|
-
if not keys:
|
154
|
-
self.cache_path.unlink()
|
155
|
-
LOGGER.debug(f"{self.name} cache deleted")
|
156
|
-
return
|
157
|
-
|
158
|
-
assert isinstance(keys, list), "Keys must be a list"
|
159
|
-
|
160
|
-
data = self.get_cached_data()
|
161
|
-
for key in keys:
|
162
|
-
data.pop(key, None)
|
163
|
-
|
164
|
-
self.write_to_cache(data)
|
otf_api-0.12.0.dist-info/RECORD
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
otf_api/__init__.py,sha256=QbShJl50bmebZLa_dNvs9PAoexI6pHGY1d_3endsC7M,205
|
2
|
-
otf_api/api.py,sha256=EtdFB-ZYQAP3b5Ghz-uNQIm_p6wr7Yf8fyJyL6iEBc0,67554
|
3
|
-
otf_api/exceptions.py,sha256=GISekwF5dPt0Ol0WCU55kE5ODc5VxicNEEhmlguuE0U,1815
|
4
|
-
otf_api/filters.py,sha256=fk2bFGi3srjS96qZlaDx-ARZRaj93NUTUdMJ01TX420,3702
|
5
|
-
otf_api/logging.py,sha256=PRZpCaJ1F1Xya3L9Efkt3mKS5_QNr3sXjEUERSxYjvE,563
|
6
|
-
otf_api/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
-
otf_api/utils.py,sha256=Yr_BKzQ28ARzHN5FvMkVIgZ1_3icZKaqFzs4SDv6ugw,4879
|
8
|
-
otf_api/auth/__init__.py,sha256=PuhtiZ02GVP8zVSn1O-fhaYm7JM_tq5yUFATk_-8upk,132
|
9
|
-
otf_api/auth/auth.py,sha256=5cPELC9Soif5knDDHm55ii1OMEPkJlGUphAdbOEmaRo,13278
|
10
|
-
otf_api/auth/user.py,sha256=XlK3nbqJA4fF5UFmw2tt0eAle4QOQd6trnW72QrBsx4,2681
|
11
|
-
otf_api/auth/utils.py,sha256=vc2pEyU-3yKdv0mR6r_zSHZyMw92j5UeIN_kzcb6TeE,2909
|
12
|
-
otf_api/models/__init__.py,sha256=MSinaMQaBTGscL-YRKJ3axFiItQ1HoH62wC2xdaBMgk,1876
|
13
|
-
otf_api/models/base.py,sha256=KJlIxl_sRj6f-g5vKYPw4yV6fGDk-fwZ93EO0JGPYMw,202
|
14
|
-
otf_api/models/body_composition_list.py,sha256=jGdR-9ScvIOtULJNB99aYh2INk2ihoHAnTWtbQCIea4,12202
|
15
|
-
otf_api/models/bookings.py,sha256=fnHZmN2F8fVSWS_tco3IwjO9SJbbxzuIs2WJcCC0gNs,4781
|
16
|
-
otf_api/models/bookings_v2.py,sha256=PFSlORNZF45TRvgLwewbVGoiOd3oDG1YGvvFMtXtbpo,6533
|
17
|
-
otf_api/models/challenge_tracker_content.py,sha256=5Ucu1n4W15v1rzhoXNvAD9tCSg3JTUiR92HHiDAxRec,2597
|
18
|
-
otf_api/models/challenge_tracker_detail.py,sha256=c2Ds7Kv2-VaPtxoXSUTI5zrmU1A1dcSaM1UIolwSVxU,4323
|
19
|
-
otf_api/models/classes.py,sha256=aKV6LGEh0YiPxyOaoMD0gaQOSHqs69cYHhP9H_2p_jY,3051
|
20
|
-
otf_api/models/enums.py,sha256=6P7wOYwvZkBLfNckKaNWv6Po94yg6lq0qyNNNOSqEFg,4633
|
21
|
-
otf_api/models/lifetime_stats.py,sha256=qpPCJuL68KhEbsIZ6cTx1dhh7RzDH5xWEAnLIsq8mZE,2853
|
22
|
-
otf_api/models/member_detail.py,sha256=UzUttKuF_P3bvzEy_ZXBwDVwj52JtpDhGuPuAtQqT0I,6611
|
23
|
-
otf_api/models/member_membership.py,sha256=jZwHzwtVyMUr8dWGlFbMYj9qveCbiOblWW5szXDUFFo,1338
|
24
|
-
otf_api/models/member_purchases.py,sha256=Ne7ByEbGTqTJhuEyCgywWe8I3nc-D46qw09up7ys38s,1627
|
25
|
-
otf_api/models/mixins.py,sha256=RBuAvAN1lYQpas0brACBof-6R4EqwGJL8HWHt41eQNg,2368
|
26
|
-
otf_api/models/notifications.py,sha256=AkmIfiIiU6wox_7puyenbhCX10SFvBD5eBAovcurRgY,833
|
27
|
-
otf_api/models/out_of_studio_workout_history.py,sha256=Kjkb8HW7k0qGMW3rAKXxZQju4MYglmmSRUdRx6FW_MQ,1714
|
28
|
-
otf_api/models/performance_summary.py,sha256=vUQJum2lW6zHnYMOvdWwClWwrIwef6WojfKqyXUxTno,2914
|
29
|
-
otf_api/models/ratings.py,sha256=RVsOGqx_eaB5i60pMRNR4xqYkQZhwRrLeSvmcFEDTgw,934
|
30
|
-
otf_api/models/studio_detail.py,sha256=2gq0A27NOZGz_PTBvsB-dkzm01nYc9FHmx1NON6xp6U,4187
|
31
|
-
otf_api/models/studio_services.py,sha256=aGLQMQmjGVpI6YxzAl-mcp3Y9cHPXuH9dIqrl6E-78E,1665
|
32
|
-
otf_api/models/telemetry.py,sha256=PQ_CbADW5-t-U2iEQJGugNy-c4rD0q76TfyIqeFnTho,3170
|
33
|
-
otf_api/models/workout.py,sha256=P3xVTvcYrm_RdU6qi3Xm2BXTxxvhvF0dgoEcODY41AA,3678
|
34
|
-
otf_api-0.12.0.dist-info/licenses/LICENSE,sha256=UaPT9ynYigC3nX8n22_rC37n-qmTRKLFaHrtUwF9ktE,1071
|
35
|
-
otf_api-0.12.0.dist-info/METADATA,sha256=jmck88-0JUoy5Y8oezk2KnBJhJ63jxgwZX2xZu7OoKA,2145
|
36
|
-
otf_api-0.12.0.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
37
|
-
otf_api-0.12.0.dist-info/top_level.txt,sha256=KAhYg1X2YG0LkTuVRhUV1I_AReNZUVNdEan7cp0pEE4,8
|
38
|
-
otf_api-0.12.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|