otf-api 0.2.1__py3-none-any.whl → 0.3.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.
Files changed (49) hide show
  1. otf_api/__init__.py +12 -67
  2. otf_api/api.py +794 -36
  3. otf_api/cli/__init__.py +4 -0
  4. otf_api/cli/_utilities.py +60 -0
  5. otf_api/cli/app.py +177 -0
  6. otf_api/cli/bookings.py +231 -0
  7. otf_api/cli/prompts.py +162 -0
  8. otf_api/models/__init__.py +4 -8
  9. otf_api/models/auth.py +18 -12
  10. otf_api/models/base.py +205 -2
  11. otf_api/models/responses/__init__.py +6 -14
  12. otf_api/models/responses/body_composition_list.py +304 -0
  13. otf_api/models/responses/book_class.py +405 -0
  14. otf_api/models/responses/bookings.py +211 -37
  15. otf_api/models/responses/cancel_booking.py +93 -0
  16. otf_api/models/responses/challenge_tracker_content.py +6 -6
  17. otf_api/models/responses/challenge_tracker_detail.py +6 -6
  18. otf_api/models/responses/classes.py +205 -7
  19. otf_api/models/responses/enums.py +0 -35
  20. otf_api/models/responses/favorite_studios.py +5 -5
  21. otf_api/models/responses/latest_agreement.py +2 -2
  22. otf_api/models/responses/lifetime_stats.py +92 -0
  23. otf_api/models/responses/member_detail.py +17 -12
  24. otf_api/models/responses/member_membership.py +2 -2
  25. otf_api/models/responses/member_purchases.py +9 -9
  26. otf_api/models/responses/out_of_studio_workout_history.py +4 -4
  27. otf_api/models/responses/performance_summary_detail.py +1 -1
  28. otf_api/models/responses/performance_summary_list.py +13 -13
  29. otf_api/models/responses/studio_detail.py +10 -10
  30. otf_api/models/responses/studio_services.py +8 -8
  31. otf_api/models/responses/telemetry.py +6 -6
  32. otf_api/models/responses/telemetry_hr_history.py +6 -6
  33. otf_api/models/responses/telemetry_max_hr.py +3 -3
  34. otf_api/models/responses/total_classes.py +2 -2
  35. otf_api/models/responses/workouts.py +4 -4
  36. otf_api-0.3.0.dist-info/METADATA +55 -0
  37. otf_api-0.3.0.dist-info/RECORD +42 -0
  38. otf_api-0.3.0.dist-info/entry_points.txt +3 -0
  39. otf_api/__version__.py +0 -1
  40. otf_api/classes_api.py +0 -44
  41. otf_api/member_api.py +0 -380
  42. otf_api/performance_api.py +0 -54
  43. otf_api/studios_api.py +0 -96
  44. otf_api/telemetry_api.py +0 -95
  45. otf_api-0.2.1.dist-info/METADATA +0 -284
  46. otf_api-0.2.1.dist-info/RECORD +0 -38
  47. {otf_api-0.2.1.dist-info → otf_api-0.3.0.dist-info}/AUTHORS.md +0 -0
  48. {otf_api-0.2.1.dist-info → otf_api-0.3.0.dist-info}/LICENSE +0 -0
  49. {otf_api-0.2.1.dist-info → otf_api-0.3.0.dist-info}/WHEEL +0 -0
@@ -1,13 +1,89 @@
1
1
  from datetime import datetime
2
+ from enum import Enum
3
+ from typing import ClassVar
2
4
 
5
+ from inflection import humanize
3
6
  from pydantic import Field
7
+ from rich.style import Style
8
+ from rich.styled import Styled
9
+ from rich.table import Table
4
10
 
5
- from otf_api.models.base import OtfBaseModel
11
+ from otf_api.models.base import OtfItemBase, OtfListBase
12
+ from otf_api.models.responses.classes import OtfClassTimeMixin
6
13
 
7
- from .enums import BookingStatus, StudioStatus
8
14
 
15
+ class StudioStatus(str, Enum):
16
+ OTHER = "OTHER"
17
+ ACTIVE = "Active"
18
+ INACTIVE = "Inactive"
19
+ COMING_SOON = "Coming Soon"
20
+ TEMP_CLOSED = "Temporarily Closed"
21
+ PERM_CLOSED = "Permanently Closed"
9
22
 
10
- class Location(OtfBaseModel):
23
+ @classmethod
24
+ def all_statuses(cls) -> list[str]:
25
+ return list(cls.__members__.values())
26
+
27
+
28
+ class BookingStatus(str, Enum):
29
+ CheckedIn = "Checked In"
30
+ CancelCheckinPending = "Cancel Checkin Pending"
31
+ CancelCheckinRequested = "Cancel Checkin Requested"
32
+ Cancelled = "Cancelled"
33
+ LateCancelled = "Late Cancelled"
34
+ Booked = "Booked"
35
+ Waitlisted = "Waitlisted"
36
+ CheckinPending = "Checkin Pending"
37
+ CheckinRequested = "Checkin Requested"
38
+ CheckinCancelled = "Checkin Cancelled"
39
+
40
+ @classmethod
41
+ def get_from_key_insensitive(cls, key: str) -> "BookingStatus":
42
+ lcase_to_actual = {item.lower(): item for item in cls._member_map_}
43
+ val = cls.__members__.get(lcase_to_actual[key.lower()])
44
+ if not val:
45
+ raise ValueError(f"Invalid BookingStatus: {key}")
46
+ return val
47
+
48
+ @classmethod
49
+ def get_case_insensitive(cls, value: str) -> str:
50
+ lcase_to_actual = {item.value.lower(): item.value for item in cls}
51
+ return lcase_to_actual[value.lower()]
52
+
53
+ @classmethod
54
+ def all_statuses(cls) -> list[str]:
55
+ return list(cls.__members__.values())
56
+
57
+
58
+ class BookingStatusCli(str, Enum):
59
+ """Flipped enum so that the CLI does not have values with spaces"""
60
+
61
+ CheckedIn = "CheckedIn"
62
+ CancelCheckinPending = "CancelCheckinPending"
63
+ CancelCheckinRequested = "CancelCheckinRequested"
64
+ Cancelled = "Cancelled"
65
+ LateCancelled = "LateCancelled"
66
+ Booked = "Booked"
67
+ Waitlisted = "Waitlisted"
68
+ CheckinPending = "CheckinPending"
69
+ CheckinRequested = "CheckinRequested"
70
+ CheckinCancelled = "CheckinCancelled"
71
+
72
+ @classmethod
73
+ def to_standard_case_insensitive(cls, key: str) -> BookingStatus:
74
+ lcase_to_actual = {item.lower(): item for item in cls._member_map_}
75
+ val = cls.__members__.get(lcase_to_actual[key.lower()])
76
+ if not val:
77
+ raise ValueError(f"Invalid BookingStatus: {key}")
78
+ return BookingStatus(val)
79
+
80
+ @classmethod
81
+ def get_case_insensitive(cls, value: str) -> str:
82
+ lcase_to_actual = {item.value.lower(): item.value for item in cls}
83
+ return lcase_to_actual[value.lower()]
84
+
85
+
86
+ class Location(OtfItemBase):
11
87
  address_one: str = Field(alias="address1")
12
88
  address_two: str | None = Field(alias="address2")
13
89
  city: str
@@ -21,30 +97,30 @@ class Location(OtfBaseModel):
21
97
  state: str | None = None
22
98
 
23
99
 
24
- class Coach(OtfBaseModel):
100
+ class Coach(OtfItemBase):
25
101
  coach_uuid: str = Field(alias="coachUUId")
26
102
  name: str
27
103
  first_name: str = Field(alias="firstName")
28
104
  last_name: str = Field(alias="lastName")
29
- image_url: str = Field(alias="imageUrl")
30
- profile_picture_url: str | None = Field(None, alias="profilePictureUrl")
105
+ image_url: str = Field(alias="imageUrl", exclude=True)
106
+ profile_picture_url: str | None = Field(None, alias="profilePictureUrl", exclude=True)
31
107
 
32
108
 
33
- class Currency(OtfBaseModel):
109
+ class Currency(OtfItemBase):
34
110
  currency_alphabetic_code: str = Field(alias="currencyAlphabeticCode")
35
111
 
36
112
 
37
- class DefaultCurrency(OtfBaseModel):
113
+ class DefaultCurrency(OtfItemBase):
38
114
  currency_id: int = Field(alias="currencyId")
39
115
  currency: Currency
40
116
 
41
117
 
42
- class StudioLocationCountry(OtfBaseModel):
118
+ class StudioLocationCountry(OtfItemBase):
43
119
  country_currency_code: str = Field(alias="countryCurrencyCode")
44
120
  default_currency: DefaultCurrency = Field(alias="defaultCurrency")
45
121
 
46
122
 
47
- class StudioLocation(OtfBaseModel):
123
+ class StudioLocation(OtfItemBase):
48
124
  latitude: float = Field(alias="latitude")
49
125
  longitude: float = Field(alias="longitude")
50
126
  phone_number: str = Field(alias="phoneNumber")
@@ -53,33 +129,33 @@ class StudioLocation(OtfBaseModel):
53
129
  physical_address2: str | None = Field(alias="physicalAddress2")
54
130
  physical_state: str = Field(alias="physicalState")
55
131
  physical_postal_code: str = Field(alias="physicalPostalCode")
56
- physical_region: str = Field(alias="physicalRegion")
57
- physical_country_id: int = Field(alias="physicalCountryId")
132
+ physical_region: str = Field(alias="physicalRegion", exclude=True)
133
+ physical_country_id: int = Field(alias="physicalCountryId", exclude=True)
58
134
  physical_country: str = Field(alias="physicalCountry")
59
- country: StudioLocationCountry = Field(alias="country")
135
+ country: StudioLocationCountry = Field(alias="country", exclude=True)
60
136
 
61
137
 
62
- class Studio(OtfBaseModel):
138
+ class Studio(OtfItemBase):
63
139
  studio_uuid: str = Field(alias="studioUUId")
64
140
  studio_name: str = Field(alias="studioName")
65
- description: str
66
- contact_email: str = Field(alias="contactEmail")
141
+ description: str | None = None
142
+ contact_email: str = Field(alias="contactEmail", exclude=True)
67
143
  status: StudioStatus
68
- logo_url: str | None = Field(alias="logoUrl")
144
+ logo_url: str | None = Field(alias="logoUrl", exclude=True)
69
145
  time_zone: str = Field(alias="timeZone")
70
- mbo_studio_id: int = Field(alias="mboStudioId")
146
+ mbo_studio_id: int = Field(alias="mboStudioId", exclude=True)
71
147
  studio_id: int = Field(alias="studioId")
72
148
  allows_cr_waitlist: bool | None = Field(None, alias="allowsCRWaitlist")
73
- cr_waitlist_flag_last_updated: datetime = Field(alias="crWaitlistFlagLastUpdated")
74
- studio_location: StudioLocation = Field(alias="studioLocation")
149
+ cr_waitlist_flag_last_updated: datetime = Field(alias="crWaitlistFlagLastUpdated", exclude=True)
150
+ studio_location: StudioLocation = Field(alias="studioLocation", exclude=True)
75
151
 
76
152
 
77
- class OtfClass(OtfBaseModel):
153
+ class OtfClass(OtfItemBase, OtfClassTimeMixin):
78
154
  class_uuid: str = Field(alias="classUUId")
79
155
  name: str
80
- description: str
81
- start_date_time: datetime = Field(alias="startDateTime")
82
- end_date_time: datetime = Field(alias="endDateTime")
156
+ description: str | None = Field(None, exclude=True)
157
+ starts_at_local: datetime = Field(alias="startDateTime")
158
+ ends_at_local: datetime = Field(alias="endDateTime")
83
159
  is_available: bool = Field(alias="isAvailable")
84
160
  is_cancelled: bool = Field(alias="isCancelled")
85
161
  program_name: str = Field(alias="programName")
@@ -89,42 +165,140 @@ class OtfClass(OtfBaseModel):
89
165
  location: Location
90
166
  virtual_class: bool | None = Field(None, alias="virtualClass")
91
167
 
168
+ @classmethod
169
+ def attr_to_column_header(cls, attr: str) -> str:
170
+ attr_map = {k: humanize(k) for k in cls.model_fields}
171
+ overrides = {
172
+ "day_of_week": "Class DoW",
173
+ "date": "Class Date",
174
+ "time": "Class Time",
175
+ "duration": "Class Duration",
176
+ "name": "Class Name",
177
+ "is_home_studio": "Home Studio",
178
+ "is_booked": "Booked",
179
+ }
180
+
181
+ attr_map.update(overrides)
182
+
183
+ return attr_map.get(attr, attr)
184
+
92
185
 
93
- class Member(OtfBaseModel):
186
+ class Member(OtfItemBase):
94
187
  member_uuid: str = Field(alias="memberUUId")
95
188
  first_name: str = Field(alias="firstName")
96
189
  last_name: str = Field(alias="lastName")
97
190
  email: str
98
191
  phone_number: str = Field(alias="phoneNumber")
99
192
  gender: str
100
- cc_last_4: str = Field(alias="ccLast4")
193
+ cc_last_4: str = Field(alias="ccLast4", exclude=True)
101
194
 
102
195
 
103
- class Booking(OtfBaseModel):
196
+ class Booking(OtfItemBase):
104
197
  class_booking_id: int = Field(alias="classBookingId")
105
198
  class_booking_uuid: str = Field(alias="classBookingUUId")
106
199
  studio_id: int = Field(alias="studioId")
107
200
  class_id: int = Field(alias="classId")
108
201
  is_intro: bool = Field(alias="isIntro")
109
202
  member_id: int = Field(alias="memberId")
110
- mbo_member_id: str = Field(alias="mboMemberId")
111
- mbo_class_id: int = Field(alias="mboClassId")
112
- mbo_visit_id: int | None = Field(None, alias="mboVisitId")
113
- mbo_waitlist_entry_id: int | None = Field(alias="mboWaitlistEntryId")
114
- mbo_sync_message: str | None = Field(alias="mboSyncMessage")
203
+ mbo_member_id: str = Field(alias="mboMemberId", exclude=True)
204
+ mbo_class_id: int = Field(alias="mboClassId", exclude=True)
205
+ mbo_visit_id: int | None = Field(None, alias="mboVisitId", exclude=True)
206
+ mbo_waitlist_entry_id: int | None = Field(alias="mboWaitlistEntryId", exclude=True)
207
+ mbo_sync_message: str | None = Field(alias="mboSyncMessage", exclude=True)
115
208
  status: BookingStatus
116
209
  booked_date: datetime | None = Field(None, alias="bookedDate")
117
210
  checked_in_date: datetime | None = Field(alias="checkedInDate")
118
211
  cancelled_date: datetime | None = Field(alias="cancelledDate")
119
- created_by: str = Field(alias="createdBy")
212
+ created_by: str = Field(alias="createdBy", exclude=True)
120
213
  created_date: datetime = Field(alias="createdDate")
121
- updated_by: str = Field(alias="updatedBy")
214
+ updated_by: str = Field(alias="updatedBy", exclude=True)
122
215
  updated_date: datetime = Field(alias="updatedDate")
123
216
  is_deleted: bool = Field(alias="isDeleted")
124
- member: Member
217
+ member: Member = Field(exclude=True)
125
218
  waitlist_position: int | None = Field(None, alias="waitlistPosition")
126
- class_: OtfClass | None = Field(None, alias="class")
219
+ otf_class: OtfClass = Field(alias="class")
220
+ is_home_studio: bool | None = Field(None, description="Custom helper field to determine if at home studio")
127
221
 
222
+ @property
223
+ def id_val(self) -> str:
224
+ return self.class_booking_id
128
225
 
129
- class BookingList(OtfBaseModel):
226
+ @property
227
+ def sidebar_data(self) -> Table:
228
+ data = {
229
+ "date": self.otf_class.date,
230
+ "class_name": self.otf_class.name,
231
+ "description": self.otf_class.description,
232
+ "class_id": self.id_val,
233
+ "studio_address": self.otf_class.studio.studio_location.physical_address,
234
+ "coach_name": self.otf_class.coach.name,
235
+ }
236
+
237
+ table = Table(expand=True, show_header=False, show_footer=False)
238
+ table.add_column("Key", style="cyan", ratio=1)
239
+ table.add_column("Value", style="magenta", ratio=2)
240
+
241
+ for key, value in data.items():
242
+ if value is False:
243
+ table.add_row(key, Styled(str(value), style="red"))
244
+ else:
245
+ table.add_row(key, str(value))
246
+
247
+ return table
248
+
249
+ def get_style(self, is_selected: bool = False) -> Style:
250
+ style = super().get_style(is_selected)
251
+ if self.status == BookingStatus.Cancelled:
252
+ style = Style(color="red")
253
+ elif self.status == BookingStatus.Waitlisted:
254
+ style = Style(color="yellow")
255
+ elif self.status == BookingStatus.CheckedIn and is_selected:
256
+ style = Style(color="blue", strike=True)
257
+ elif self.status == BookingStatus.CheckedIn:
258
+ style = Style(color="grey58")
259
+
260
+ return style
261
+
262
+ @classmethod
263
+ def attr_to_column_header(cls, attr: str) -> str:
264
+ if attr.startswith("otf_class"):
265
+ return OtfClass.attr_to_column_header(attr.split(".")[-1])
266
+
267
+ attr_map = {k: humanize(k) for k in cls.model_fields}
268
+ overrides = {
269
+ "day_of_week": "Class DoW",
270
+ "date": "Class Date",
271
+ "time": "Class Time",
272
+ "duration": "Class Duration",
273
+ "name": "Class Name",
274
+ "is_home_studio": "Home Studio",
275
+ "is_booked": "Booked",
276
+ }
277
+
278
+ attr_map.update(overrides)
279
+
280
+ return attr_map.get(attr, attr)
281
+
282
+
283
+ class BookingList(OtfListBase):
284
+ collection_field: ClassVar[str] = "bookings"
130
285
  bookings: list[Booking]
286
+
287
+ @staticmethod
288
+ def show_bookings_columns() -> list[str]:
289
+ return [
290
+ "otf_class.day_of_week",
291
+ "otf_class.date",
292
+ "otf_class.time",
293
+ "otf_class.duration",
294
+ "otf_class.name",
295
+ "status",
296
+ "otf_class.studio.studio_name",
297
+ "is_home_studio",
298
+ ]
299
+
300
+ def to_table(self, columns: list[str] | None = None) -> Table:
301
+ if not columns:
302
+ columns = self.show_bookings_columns()
303
+
304
+ return super().to_table(columns)
@@ -0,0 +1,93 @@
1
+ from datetime import datetime
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+
6
+ class Studio(BaseModel):
7
+ studio_uuid: str = Field(..., alias="studioUUId")
8
+ studio_name: str = Field(..., alias="studioName")
9
+ description: str
10
+ contact_email: str = Field(..., alias="contactEmail")
11
+ status: str
12
+ logo_url: str = Field(..., alias="logoUrl")
13
+ time_zone: str = Field(..., alias="timeZone")
14
+ mbo_studio_id: int = Field(..., alias="mboStudioId")
15
+ studio_id: int = Field(..., alias="studioId")
16
+ allows_cr_waitlist: bool = Field(..., alias="allowsCRWaitlist")
17
+ cr_waitlist_flag_last_updated: datetime = Field(..., alias="crWaitlistFlagLastUpdated")
18
+
19
+
20
+ class Coach(BaseModel):
21
+ coach_uuid: str = Field(..., alias="coachUUId")
22
+ name: str
23
+ first_name: str = Field(..., alias="firstName")
24
+ last_name: str = Field(..., alias="lastName")
25
+ mbo_coach_id: int = Field(..., alias="mboCoachId")
26
+
27
+
28
+ class Class(BaseModel):
29
+ class_uuid: str = Field(..., alias="classUUId")
30
+ name: str
31
+ description: str
32
+ start_date_time: datetime = Field(..., alias="startDateTime")
33
+ end_date_time: datetime = Field(..., alias="endDateTime")
34
+ is_available: bool = Field(..., alias="isAvailable")
35
+ is_cancelled: bool = Field(..., alias="isCancelled")
36
+ total_booked: int = Field(..., alias="totalBooked")
37
+ mbo_class_id: int = Field(..., alias="mboClassId")
38
+ mbo_studio_id: int = Field(..., alias="mboStudioId")
39
+ studio: Studio
40
+ coach: Coach
41
+
42
+
43
+ class HomeStudio(BaseModel):
44
+ studio_uuid: str = Field(..., alias="studioUUId")
45
+ studio_name: str = Field(..., alias="studioName")
46
+ description: str
47
+ contact_email: str = Field(..., alias="contactEmail")
48
+ status: str
49
+ logo_url: str = Field(..., alias="logoUrl")
50
+ time_zone: str = Field(..., alias="timeZone")
51
+ mbo_studio_id: int = Field(..., alias="mboStudioId")
52
+ studio_id: int = Field(..., alias="studioId")
53
+ allows_cr_waitlist: bool = Field(..., alias="allowsCRWaitlist")
54
+ cr_waitlist_flag_last_updated: datetime = Field(..., alias="crWaitlistFlagLastUpdated")
55
+
56
+
57
+ class Member(BaseModel):
58
+ member_id: int = Field(..., alias="memberId")
59
+ member_uuid: str = Field(..., alias="memberUUId")
60
+ email: str
61
+ phone_number: str = Field(..., alias="phoneNumber")
62
+ first_name: str = Field(..., alias="firstName")
63
+ last_name: str = Field(..., alias="lastName")
64
+ mbo_id: str = Field(..., alias="mboId")
65
+ cc_last4: str = Field(..., alias="ccLast4")
66
+ mbo_studio_id: int = Field(..., alias="mboStudioId")
67
+ home_studio: HomeStudio = Field(..., alias="homeStudio")
68
+
69
+
70
+ class CancelBooking(BaseModel):
71
+ class_booking_id: int = Field(..., alias="classBookingId")
72
+ class_booking_uuid: str = Field(..., alias="classBookingUUId")
73
+ studio_id: int = Field(..., alias="studioId")
74
+ class_id: int = Field(..., alias="classId")
75
+ is_intro: bool = Field(..., alias="isIntro")
76
+ member_id: int = Field(..., alias="memberId")
77
+ mbo_member_id: str = Field(..., alias="mboMemberId")
78
+ mbo_class_id: int = Field(..., alias="mboClassId")
79
+ mbo_visit_id: int = Field(..., alias="mboVisitId")
80
+ mbo_waitlist_entry_id: None = Field(..., alias="mboWaitlistEntryId")
81
+ mbo_sync_message: str = Field(..., alias="mboSyncMessage")
82
+ status: str
83
+ booked_date: datetime = Field(..., alias="bookedDate")
84
+ checked_in_date: None = Field(..., alias="checkedInDate")
85
+ cancelled_date: datetime = Field(..., alias="cancelledDate")
86
+ created_by: str = Field(..., alias="createdBy")
87
+ created_date: datetime = Field(..., alias="createdDate")
88
+ updated_by: str = Field(..., alias="updatedBy")
89
+ updated_date: datetime = Field(..., alias="updatedDate")
90
+ is_deleted: bool = Field(..., alias="isDeleted")
91
+ otf_class: Class = Field(..., alias="class")
92
+ member: Member
93
+ continue_retry: bool = Field(..., alias="continueRetry")
@@ -1,15 +1,15 @@
1
1
  from pydantic import Field
2
2
 
3
- from otf_api.models.base import OtfBaseModel
3
+ from otf_api.models.base import OtfItemBase
4
4
 
5
5
 
6
- class Year(OtfBaseModel):
6
+ class Year(OtfItemBase):
7
7
  year: str = Field(..., alias="Year")
8
8
  is_participated: bool = Field(..., alias="IsParticipated")
9
9
  in_progress: bool = Field(..., alias="InProgress")
10
10
 
11
11
 
12
- class Program(OtfBaseModel):
12
+ class Program(OtfItemBase):
13
13
  challenge_category_id: int = Field(..., alias="ChallengeCategoryId")
14
14
  challenge_sub_category_id: int = Field(..., alias="ChallengeSubCategoryId")
15
15
  challenge_name: str = Field(..., alias="ChallengeName")
@@ -17,7 +17,7 @@ class Program(OtfBaseModel):
17
17
  logo_url: str = Field(..., alias="LogoUrl")
18
18
 
19
19
 
20
- class Challenge(OtfBaseModel):
20
+ class Challenge(OtfItemBase):
21
21
  challenge_category_id: int = Field(..., alias="ChallengeCategoryId")
22
22
  challenge_sub_category_id: int = Field(..., alias="ChallengeSubCategoryId")
23
23
  challenge_name: str = Field(..., alias="ChallengeName")
@@ -25,14 +25,14 @@ class Challenge(OtfBaseModel):
25
25
  logo_url: str = Field(..., alias="LogoUrl")
26
26
 
27
27
 
28
- class Benchmark(OtfBaseModel):
28
+ class Benchmark(OtfItemBase):
29
29
  equipment_id: int = Field(..., alias="EquipmentId")
30
30
  equipment_name: str = Field(..., alias="EquipmentName")
31
31
  years: list[Year] = Field(..., alias="Years")
32
32
  logo_url: str = Field(..., alias="LogoUrl")
33
33
 
34
34
 
35
- class ChallengeTrackerContent(OtfBaseModel):
35
+ class ChallengeTrackerContent(OtfItemBase):
36
36
  programs: list[Program] = Field(..., alias="Programs")
37
37
  challenges: list[Challenge] = Field(..., alias="Challenges")
38
38
  benchmarks: list[Benchmark] = Field(..., alias="Benchmarks")
@@ -3,10 +3,10 @@ from typing import Any
3
3
 
4
4
  from pydantic import Field
5
5
 
6
- from otf_api.models.base import OtfBaseModel
6
+ from otf_api.models.base import OtfItemBase
7
7
 
8
8
 
9
- class MetricEntry(OtfBaseModel):
9
+ class MetricEntry(OtfItemBase):
10
10
  title: str = Field(..., alias="Title")
11
11
  equipment_id: int = Field(..., alias="EquipmentId")
12
12
  entry_type: str = Field(..., alias="EntryType")
@@ -15,7 +15,7 @@ class MetricEntry(OtfBaseModel):
15
15
  max_value: str = Field(..., alias="MaxValue")
16
16
 
17
17
 
18
- class BenchmarkHistory(OtfBaseModel):
18
+ class BenchmarkHistory(OtfItemBase):
19
19
  studio_name: str = Field(..., alias="StudioName")
20
20
  equipment_id: int = Field(..., alias="EquipmentId")
21
21
  result: float | str = Field(..., alias="Result")
@@ -36,7 +36,7 @@ class BenchmarkHistory(OtfBaseModel):
36
36
  ) # not sure what this will be, never seen it before
37
37
 
38
38
 
39
- class ChallengeHistory(OtfBaseModel):
39
+ class ChallengeHistory(OtfItemBase):
40
40
  challenge_objective: str = Field(..., alias="ChallengeObjective")
41
41
  challenge_id: int = Field(..., alias="ChallengeId")
42
42
  studio_id: int = Field(..., alias="StudioId")
@@ -48,7 +48,7 @@ class ChallengeHistory(OtfBaseModel):
48
48
  benchmark_histories: list[BenchmarkHistory] = Field(..., alias="BenchmarkHistories")
49
49
 
50
50
 
51
- class ChallengeTrackerDetail(OtfBaseModel):
51
+ class ChallengeTrackerDetail(OtfItemBase):
52
52
  challenge_category_id: int = Field(..., alias="ChallengeCategoryId")
53
53
  challenge_sub_category_id: None = Field(..., alias="ChallengeSubCategoryId")
54
54
  equipment_id: int = Field(..., alias="EquipmentId")
@@ -64,5 +64,5 @@ class ChallengeTrackerDetail(OtfBaseModel):
64
64
  challenge_histories: list[ChallengeHistory] = Field(..., alias="ChallengeHistories")
65
65
 
66
66
 
67
- class ChallengeTrackerDetailList(OtfBaseModel):
67
+ class ChallengeTrackerDetailList(OtfItemBase):
68
68
  details: list[ChallengeTrackerDetail]