otf-api 0.6.4__py3-none-any.whl → 0.7.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.
Files changed (43) hide show
  1. otf_api/__init__.py +1 -9
  2. otf_api/api.py +76 -111
  3. otf_api/auth.py +11 -10
  4. otf_api/exceptions.py +18 -0
  5. otf_api/models/__init__.py +25 -32
  6. otf_api/models/base.py +1 -16
  7. otf_api/models/{responses/body_composition_list.py → body_composition_list.py} +3 -14
  8. otf_api/models/book_class.py +89 -0
  9. otf_api/models/bookings.py +119 -0
  10. otf_api/models/cancel_booking.py +49 -0
  11. otf_api/models/{responses/challenge_tracker_detail.py → challenge_tracker_detail.py} +12 -13
  12. otf_api/models/classes.py +80 -0
  13. otf_api/models/enums.py +87 -0
  14. otf_api/models/{responses/favorite_studios.py → favorite_studios.py} +17 -19
  15. otf_api/models/{responses/lifetime_stats.py → lifetime_stats.py} +0 -18
  16. otf_api/models/{responses/member_detail.py → member_detail.py} +16 -19
  17. otf_api/models/{responses/member_purchases.py → member_purchases.py} +10 -10
  18. otf_api/models/mixins.py +44 -0
  19. otf_api/models/{responses/out_of_studio_workout_history.py → out_of_studio_workout_history.py} +3 -3
  20. otf_api/models/{responses/performance_summary_list.py → performance_summary_list.py} +2 -5
  21. otf_api/models/studio_detail.py +109 -0
  22. otf_api/models/{responses/studio_services.py → studio_services.py} +2 -2
  23. otf_api/models/{responses/telemetry.py → telemetry.py} +1 -1
  24. {otf_api-0.6.4.dist-info → otf_api-0.7.1.dist-info}/METADATA +3 -3
  25. otf_api-0.7.1.dist-info/RECORD +36 -0
  26. otf_api/models/responses/__init__.py +0 -57
  27. otf_api/models/responses/book_class.py +0 -407
  28. otf_api/models/responses/bookings.py +0 -160
  29. otf_api/models/responses/cancel_booking.py +0 -95
  30. otf_api/models/responses/classes.py +0 -148
  31. otf_api/models/responses/enums.py +0 -23
  32. otf_api/models/responses/studio_detail.py +0 -113
  33. otf_api-0.6.4.dist-info/RECORD +0 -35
  34. /otf_api/models/{responses/challenge_tracker_content.py → challenge_tracker_content.py} +0 -0
  35. /otf_api/models/{responses/latest_agreement.py → latest_agreement.py} +0 -0
  36. /otf_api/models/{responses/member_membership.py → member_membership.py} +0 -0
  37. /otf_api/models/{responses/performance_summary_detail.py → performance_summary_detail.py} +0 -0
  38. /otf_api/models/{responses/telemetry_hr_history.py → telemetry_hr_history.py} +0 -0
  39. /otf_api/models/{responses/telemetry_max_hr.py → telemetry_max_hr.py} +0 -0
  40. /otf_api/models/{responses/total_classes.py → total_classes.py} +0 -0
  41. {otf_api-0.6.4.dist-info → otf_api-0.7.1.dist-info}/AUTHORS.md +0 -0
  42. {otf_api-0.6.4.dist-info → otf_api-0.7.1.dist-info}/LICENSE +0 -0
  43. {otf_api-0.6.4.dist-info → otf_api-0.7.1.dist-info}/WHEEL +0 -0
otf_api/models/base.py CHANGED
@@ -4,19 +4,4 @@ from pydantic import BaseModel, ConfigDict
4
4
 
5
5
 
6
6
  class OtfItemBase(BaseModel):
7
- model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True, extra="allow")
8
-
9
-
10
- class OtfListBase(BaseModel):
11
- model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True, extra="allow")
12
- collection_field: ClassVar[str] = "data"
13
-
14
- @property
15
- def collection(self) -> list[OtfItemBase]:
16
- return getattr(self, self.collection_field)
17
-
18
- def to_json(self, **kwargs) -> str:
19
- kwargs.setdefault("indent", 4)
20
- kwargs.setdefault("exclude_none", True)
21
-
22
- return self.model_dump_json(**kwargs)
7
+ model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True, extra="ignore")
@@ -3,9 +3,9 @@ from datetime import datetime
3
3
  from enum import Enum
4
4
 
5
5
  import pint
6
- from pydantic import BaseModel, ConfigDict, Field, field_validator
6
+ from pydantic import BaseModel, Field, field_validator
7
7
 
8
- from otf_api.models.base import OtfItemBase, OtfListBase
8
+ from otf_api.models.base import OtfItemBase
9
9
 
10
10
  ureg = pint.UnitRegistry()
11
11
 
@@ -101,7 +101,6 @@ def get_body_fat_percent_dividers_female(age: int) -> list[float]:
101
101
 
102
102
 
103
103
  class LeanBodyMass(OtfItemBase):
104
- model_config: ConfigDict = ConfigDict(extra="ignore")
105
104
  left_arm: float = Field(..., alias="lbmOfLeftArm")
106
105
  left_leg: float = Field(..., alias="lbmOfLeftLeg")
107
106
  right_arm: float = Field(..., alias="lbmOfRightArm")
@@ -110,7 +109,6 @@ class LeanBodyMass(OtfItemBase):
110
109
 
111
110
 
112
111
  class LeanBodyMassPercent(OtfItemBase):
113
- model_config: ConfigDict = ConfigDict(extra="ignore")
114
112
  left_arm: float = Field(..., alias="lbmPercentOfLeftArm")
115
113
  left_leg: float = Field(..., alias="lbmPercentOfLeftLeg")
116
114
  right_arm: float = Field(..., alias="lbmPercentOfRightArm")
@@ -119,7 +117,6 @@ class LeanBodyMassPercent(OtfItemBase):
119
117
 
120
118
 
121
119
  class BodyFatMass(OtfItemBase):
122
- model_config: ConfigDict = ConfigDict(extra="ignore")
123
120
  control: float = Field(..., alias="bfmControl")
124
121
  left_arm: float = Field(..., alias="bfmOfLeftArm")
125
122
  left_leg: float = Field(..., alias="bfmOfLeftLeg")
@@ -129,7 +126,6 @@ class BodyFatMass(OtfItemBase):
129
126
 
130
127
 
131
128
  class BodyFatMassPercent(OtfItemBase):
132
- model_config: ConfigDict = ConfigDict(extra="ignore")
133
129
  left_arm: float = Field(..., alias="bfmPercentOfLeftArm")
134
130
  left_leg: float = Field(..., alias="bfmPercentOfLeftLeg")
135
131
  right_arm: float = Field(..., alias="bfmPercentOfRightArm")
@@ -138,7 +134,6 @@ class BodyFatMassPercent(OtfItemBase):
138
134
 
139
135
 
140
136
  class TotalBodyWeight(OtfItemBase):
141
- model_config: ConfigDict = ConfigDict(extra="ignore")
142
137
  right_arm: float = Field(..., alias="tbwOfRightArm")
143
138
  left_arm: float = Field(..., alias="tbwOfLeftArm")
144
139
  trunk: float = Field(..., alias="tbwOfTrunk")
@@ -147,7 +142,6 @@ class TotalBodyWeight(OtfItemBase):
147
142
 
148
143
 
149
144
  class IntraCellularWater(OtfItemBase):
150
- model_config: ConfigDict = ConfigDict(extra="ignore")
151
145
  right_arm: float = Field(..., alias="icwOfRightArm")
152
146
  left_arm: float = Field(..., alias="icwOfLeftArm")
153
147
  trunk: float = Field(..., alias="icwOfTrunk")
@@ -156,7 +150,6 @@ class IntraCellularWater(OtfItemBase):
156
150
 
157
151
 
158
152
  class ExtraCellularWater(OtfItemBase):
159
- model_config: ConfigDict = ConfigDict(extra="ignore")
160
153
  right_arm: float = Field(..., alias="ecwOfRightArm")
161
154
  left_arm: float = Field(..., alias="ecwOfLeftArm")
162
155
  trunk: float = Field(..., alias="ecwOfTrunk")
@@ -165,7 +158,6 @@ class ExtraCellularWater(OtfItemBase):
165
158
 
166
159
 
167
160
  class ExtraCellularWaterOverTotalBodyWater(OtfItemBase):
168
- model_config: ConfigDict = ConfigDict(extra="ignore")
169
161
  right_arm: float = Field(..., alias="ecwOverTBWOfRightArm")
170
162
  left_arm: float = Field(..., alias="ecwOverTBWOfLeftArm")
171
163
  trunk: float = Field(..., alias="ecwOverTBWOfTrunk")
@@ -200,9 +192,6 @@ class BodyCompositionData(OtfItemBase):
200
192
  basal_metabolic_rate: float = Field(..., alias="bmr")
201
193
  in_body_type: str = Field(..., alias="inBodyType")
202
194
 
203
- body_fat_mass: float = Field(..., alias="bfm")
204
- skeletal_muscle_mass: float = Field(..., alias="smm")
205
-
206
195
  # excluded because they are only useful for end result of calculations
207
196
  body_fat_mass_dividers: list[float] = Field(..., alias="bfmGraphScale", exclude=True)
208
197
  body_fat_mass_plot_point: float = Field(..., alias="pfatnew", exclude=True)
@@ -302,5 +291,5 @@ class BodyCompositionData(OtfItemBase):
302
291
  )
303
292
 
304
293
 
305
- class BodyCompositionList(OtfListBase):
294
+ class BodyCompositionList(OtfItemBase):
306
295
  data: list[BodyCompositionData]
@@ -0,0 +1,89 @@
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
@@ -0,0 +1,119 @@
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
+ from otf_api.models.enums import BookingStatus, StudioStatus
9
+ from otf_api.models.mixins import OtfClassTimeMixin
10
+
11
+
12
+ class Location(OtfItemBase):
13
+ address_one: str | None = Field(None, alias="address1")
14
+ address_two: str | None = Field(alias="address2")
15
+ city: str | None = None
16
+ country: str | None = None
17
+ distance: float | None = None
18
+ location_name: str | None = Field(None, alias="locationName")
19
+ latitude: float | None = Field(None, alias="latitude")
20
+ longitude: float | None = Field(None, alias="longitude")
21
+ phone_number: str | None = Field(None, alias="phone")
22
+ postal_code: str | None = Field(None, alias="postalCode")
23
+ state: str | None = None
24
+
25
+
26
+ class Coach(OtfItemBase):
27
+ coach_uuid: str = Field(alias="coachUUId")
28
+ name: str
29
+ first_name: str | None = Field(None, alias="firstName")
30
+ last_name: str | None = Field(None, alias="lastName")
31
+ image_url: str | None = Field(None, alias="imageUrl", exclude=True)
32
+ profile_picture_url: str | None = Field(None, alias="profilePictureUrl", exclude=True)
33
+
34
+
35
+ class StudioLocation(OtfItemBase):
36
+ latitude: float | None = Field(None, alias="latitude")
37
+ longitude: float | None = Field(None, alias="longitude")
38
+ phone_number: str | None = Field(None, alias="phoneNumber")
39
+ physical_city: str | None = Field(None, alias="physicalCity")
40
+ physical_address: str | None = Field(None, alias="physicalAddress")
41
+ physical_address2: str | None = Field(None, alias="physicalAddress2")
42
+ physical_state: str | None = Field(None, alias="physicalState")
43
+ physical_postal_code: str | None = Field(None, alias="physicalPostalCode")
44
+ physical_region: str | None = Field(None, alias="physicalRegion", exclude=True)
45
+ physical_country_id: int | None = Field(None, alias="physicalCountryId", exclude=True)
46
+ physical_country: str | None = Field(None, alias="physicalCountry")
47
+ country: dict[Hashable, Any] | None = Field(None, alias="country", exclude=True)
48
+
49
+
50
+ class Studio(OtfItemBase):
51
+ studio_uuid: str = Field(alias="studioUUId")
52
+ studio_name: str = Field(alias="studioName")
53
+ studio_id: int = Field(alias="studioId")
54
+ description: str | None = None
55
+ contact_email: str | None = Field(None, alias="contactEmail", exclude=True)
56
+ status: StudioStatus | None = None
57
+ logo_url: str | None = Field(None, alias="logoUrl", exclude=True)
58
+ time_zone: str = Field(alias="timeZone")
59
+ mbo_studio_id: int | None = Field(None, alias="mboStudioId", exclude=True)
60
+ allows_cr_waitlist: bool | None = Field(None, alias="allowsCRWaitlist")
61
+ cr_waitlist_flag_last_updated: datetime | None = Field(None, alias="crWaitlistFlagLastUpdated", exclude=True)
62
+ studio_location: StudioLocation | None = Field(None, alias="studioLocation", exclude=True)
63
+
64
+
65
+ class OtfClass(OtfItemBase, OtfClassTimeMixin):
66
+ class_uuid: str = Field(alias="classUUId")
67
+ name: str
68
+ description: str | None = Field(None, exclude=True)
69
+ starts_at_local: datetime = Field(alias="startDateTime")
70
+ ends_at_local: datetime = Field(alias="endDateTime")
71
+ is_available: bool = Field(alias="isAvailable")
72
+ is_cancelled: bool = Field(alias="isCancelled")
73
+ program_name: str | None = Field(None, alias="programName")
74
+ coach_id: int | None = Field(None, alias="coachId")
75
+ studio: Studio | None = None
76
+ coach: Coach | None = None
77
+ location: Location | None = None
78
+ virtual_class: bool | None = Field(None, alias="virtualClass")
79
+
80
+
81
+ class Member(OtfItemBase):
82
+ member_uuid: str = Field(alias="memberUUId")
83
+ first_name: str = Field(alias="firstName")
84
+ last_name: str = Field(alias="lastName")
85
+ email: str | None = None
86
+ phone_number: str | None = Field(None, alias="phoneNumber")
87
+ gender: str | None = None
88
+ cc_last_4: str | None = Field(None, alias="ccLast4", exclude=True)
89
+
90
+
91
+ class Booking(OtfItemBase):
92
+ class_booking_id: int = Field(alias="classBookingId")
93
+ class_booking_uuid: str = Field(alias="classBookingUUId", description="ID used to cancel the booking")
94
+ studio_id: int = Field(alias="studioId")
95
+ class_id: int = Field(alias="classId")
96
+ is_intro: bool = Field(alias="isIntro")
97
+ member_id: int = Field(alias="memberId")
98
+ mbo_member_id: str | None = Field(None, alias="mboMemberId", exclude=True)
99
+ mbo_class_id: int | None = Field(None, alias="mboClassId", exclude=True)
100
+ mbo_visit_id: int | None = Field(None, alias="mboVisitId", exclude=True)
101
+ mbo_waitlist_entry_id: int | None = Field(None, alias="mboWaitlistEntryId", exclude=True)
102
+ mbo_sync_message: str | None = Field(None, alias="mboSyncMessage", exclude=True)
103
+ status: BookingStatus
104
+ booked_date: datetime | None = Field(None, alias="bookedDate")
105
+ checked_in_date: datetime | None = Field(None, alias="checkedInDate")
106
+ cancelled_date: datetime | None = Field(None, alias="cancelledDate")
107
+ created_by: str = Field(alias="createdBy", exclude=True)
108
+ created_date: datetime = Field(alias="createdDate")
109
+ updated_by: str = Field(alias="updatedBy", exclude=True)
110
+ updated_date: datetime = Field(alias="updatedDate")
111
+ is_deleted: bool = Field(alias="isDeleted")
112
+ member: Member | None = Field(None, exclude=True)
113
+ waitlist_position: int | None = Field(None, alias="waitlistPosition")
114
+ otf_class: OtfClass = Field(alias="class")
115
+ is_home_studio: bool | None = Field(None, description="Custom helper field to determine if at home studio")
116
+
117
+
118
+ class BookingList(OtfItemBase):
119
+ bookings: list[Booking]
@@ -0,0 +1,49 @@
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,9 +1,9 @@
1
1
  from datetime import datetime
2
- from typing import Any, ClassVar
2
+ from typing import Any
3
3
 
4
4
  from pydantic import Field
5
5
 
6
- from otf_api.models.base import OtfItemBase, OtfListBase
6
+ from otf_api.models.base import OtfItemBase
7
7
 
8
8
 
9
9
  class MetricEntry(OtfItemBase):
@@ -22,17 +22,17 @@ class BenchmarkHistory(OtfItemBase):
22
22
  date_created: datetime = Field(..., alias="DateCreated")
23
23
  date_updated: datetime = Field(..., alias="DateUpdated")
24
24
  class_time: datetime = Field(..., alias="ClassTime")
25
- challenge_sub_category_id: int | None = Field(..., alias="ChallengeSubCategoryId")
25
+ challenge_sub_category_id: int | None = Field(None, alias="ChallengeSubCategoryId")
26
26
  class_id: int = Field(..., alias="ClassId")
27
- substitute_id: int | None = Field(..., alias="SubstituteId")
27
+ substitute_id: int | None = Field(None, alias="SubstituteId")
28
28
  weight_lbs: int = Field(..., alias="WeightLBS")
29
29
  class_name: str = Field(..., alias="ClassName")
30
30
  coach_name: str = Field(..., alias="CoachName")
31
- coach_image_url: str = Field(..., alias="CoachImageUrl")
32
- workout_type_id: int | None = Field(..., alias="WorkoutTypeId")
33
- workout_id: int | None = Field(..., alias="WorkoutId")
34
- linked_challenges: list[Any] = Field(
35
- ..., alias="LinkedChallenges"
31
+ coach_image_url: str | None = Field(None, alias="CoachImageUrl", exclude=True)
32
+ workout_type_id: int | None = Field(None, alias="WorkoutTypeId")
33
+ workout_id: int | None = Field(None, alias="WorkoutId")
34
+ linked_challenges: list[Any] | None = Field(
35
+ None, alias="LinkedChallenges", exclude=True
36
36
  ) # not sure what this will be, never seen it before
37
37
 
38
38
 
@@ -50,7 +50,7 @@ class ChallengeHistory(OtfItemBase):
50
50
 
51
51
  class ChallengeTrackerDetail(OtfItemBase):
52
52
  challenge_category_id: int = Field(..., alias="ChallengeCategoryId")
53
- challenge_sub_category_id: int | None = Field(..., alias="ChallengeSubCategoryId")
53
+ challenge_sub_category_id: int | None = Field(None, alias="ChallengeSubCategoryId")
54
54
  equipment_id: int = Field(..., alias="EquipmentId")
55
55
  equipment_name: str = Field(..., alias="EquipmentName")
56
56
  metric_entry: MetricEntry = Field(..., alias="MetricEntry")
@@ -59,11 +59,10 @@ class ChallengeTrackerDetail(OtfItemBase):
59
59
  best_record: float | str = Field(..., alias="BestRecord")
60
60
  last_record: float | str = Field(..., alias="LastRecord")
61
61
  previous_record: float | str = Field(..., alias="PreviousRecord")
62
- unit: str | None = Field(..., alias="Unit")
62
+ unit: str | None = Field(None, alias="Unit")
63
63
  goals: None = Field(..., alias="Goals")
64
64
  challenge_histories: list[ChallengeHistory] = Field(..., alias="ChallengeHistories")
65
65
 
66
66
 
67
- class ChallengeTrackerDetailList(OtfListBase):
68
- collection_field: ClassVar[str] = "details"
67
+ class ChallengeTrackerDetailList(OtfItemBase):
69
68
  details: list[ChallengeTrackerDetail]
@@ -0,0 +1,80 @@
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.enums import DoW
7
+ from otf_api.models.mixins import OtfClassTimeMixin
8
+
9
+
10
+ class Address(OtfItemBase):
11
+ line1: str
12
+ city: str
13
+ state: str
14
+ country: str
15
+ postal_code: str
16
+
17
+
18
+ class Studio(OtfItemBase):
19
+ id: str
20
+ name: str
21
+ mbo_studio_id: str
22
+ time_zone: str
23
+ currency_code: str
24
+ address: Address
25
+ phone_number: str
26
+ latitude: float
27
+ longitude: float
28
+
29
+
30
+ class Coach(OtfItemBase):
31
+ mbo_staff_id: str
32
+ first_name: str
33
+ image_url: str | None = None
34
+
35
+
36
+ class OtfClass(OtfItemBase, OtfClassTimeMixin):
37
+ id: str
38
+ ot_class_uuid: str = Field(
39
+ alias="ot_base_class_uuid",
40
+ description="The OTF class UUID, this is what shows in a booking response and how you can book a class.",
41
+ )
42
+ starts_at: datetime
43
+ starts_at_local: datetime
44
+ ends_at: datetime
45
+ ends_at_local: datetime
46
+ name: str
47
+ type: str
48
+ studio: Studio
49
+ coach: Coach
50
+ max_capacity: int
51
+ booking_capacity: int
52
+ waitlist_size: int
53
+ full: bool
54
+ waitlist_available: bool
55
+ canceled: bool
56
+ mbo_class_id: str
57
+ mbo_class_schedule_id: str
58
+ mbo_class_description_id: str
59
+ created_at: datetime
60
+ updated_at: datetime
61
+ is_home_studio: bool | None = Field(None, description="Custom helper field to determine if at home studio")
62
+ is_booked: bool | None = Field(None, description="Custom helper field to determine if class is already booked")
63
+
64
+ @property
65
+ def has_availability(self) -> bool:
66
+ return not self.full
67
+
68
+ @property
69
+ def day_of_week_enum(self) -> DoW:
70
+ dow = self.starts_at_local.strftime("%A")
71
+ return DoW.get_case_insensitive(dow)
72
+
73
+ @property
74
+ def actual_class_uuid(self) -> str:
75
+ """The UUID used to book the class"""
76
+ return self.ot_class_uuid
77
+
78
+
79
+ class OtfClassList(OtfItemBase):
80
+ classes: list[OtfClass]
@@ -0,0 +1,87 @@
1
+ from enum import Enum
2
+
3
+
4
+ class StudioStatus(str, Enum):
5
+ OTHER = "OTHER"
6
+ ACTIVE = "Active"
7
+ INACTIVE = "Inactive"
8
+ COMING_SOON = "Coming Soon"
9
+ TEMP_CLOSED = "Temporarily Closed"
10
+ PERM_CLOSED = "Permanently Closed"
11
+
12
+
13
+ class BookingStatus(str, Enum):
14
+ CheckedIn = "Checked In"
15
+ CancelCheckinPending = "Cancel Checkin Pending"
16
+ CancelCheckinRequested = "Cancel Checkin Requested"
17
+ Cancelled = "Cancelled"
18
+ LateCancelled = "Late Cancelled"
19
+ Booked = "Booked"
20
+ Waitlisted = "Waitlisted"
21
+ CheckinPending = "Checkin Pending"
22
+ CheckinRequested = "Checkin Requested"
23
+ CheckinCancelled = "Checkin Cancelled"
24
+
25
+
26
+ class DoW(str, Enum):
27
+ MONDAY = "monday"
28
+ TUESDAY = "tuesday"
29
+ WEDNESDAY = "wednesday"
30
+ THURSDAY = "thursday"
31
+ FRIDAY = "friday"
32
+ SATURDAY = "saturday"
33
+ SUNDAY = "sunday"
34
+
35
+ @classmethod
36
+ def get_case_insensitive(cls, value: str) -> "DoW":
37
+ lcase_to_actual = {item.value.lower(): item for item in cls}
38
+ return lcase_to_actual[value.lower()]
39
+
40
+
41
+ class ClassType(str, Enum):
42
+ ORANGE_60_MIN_2G = "Orange 60 Min 2G"
43
+ TREAD_50 = "Tread 50"
44
+ STRENGTH_50 = "Strength 50"
45
+ ORANGE_3G = "Orange 3G"
46
+ ORANGE_60_TORNADO = "Orange 60 - Tornado"
47
+ ORANGE_TORNADO = "Orange Tornado"
48
+ ORANGE_90_MIN_3G = "Orange 90 Min 3G"
49
+ VIP_CLASS = "VIP Class"
50
+ OTHER = "Other"
51
+
52
+ @classmethod
53
+ def get_case_insensitive(cls, value: str) -> str:
54
+ lcase_to_actual = {item.value.lower(): item.value for item in cls}
55
+ return lcase_to_actual[value.lower()]
56
+
57
+
58
+ class StatsTime(str, Enum):
59
+ LastYear = "lastYear"
60
+ ThisYear = "thisYear"
61
+ LastMonth = "lastMonth"
62
+ ThisMonth = "thisMonth"
63
+ LastWeek = "lastWeek"
64
+ ThisWeek = "thisWeek"
65
+ AllTime = "allTime"
66
+
67
+
68
+ class EquipmentType(int, Enum):
69
+ Treadmill = 2
70
+ Strider = 3
71
+ Rower = 4
72
+ Bike = 5
73
+ WeightFloor = 6
74
+ PowerWalker = 7
75
+
76
+
77
+ class ChallengeType(int, Enum):
78
+ Other = 0
79
+ DriTri = 2
80
+ MarathonMonth = 5
81
+ HellWeek = 52
82
+ Mayhem = 58
83
+ TwelveDaysOfFitness = 63
84
+ Transformation = 64
85
+ RemixInSix = 65
86
+ Push = 66
87
+ BackAtIt = 84