diff --git a/examples/challenge_tracker_examples.py b/examples/challenge_tracker_examples.py index 8131142..7981dc1 100644 --- a/examples/challenge_tracker_examples.py +++ b/examples/challenge_tracker_examples.py @@ -52,7 +52,7 @@ def main(): # challenge tracker details are detailed information about specific challenges # this endpoint takes an equipment type and a challenge type as arguments tread_challenge_details = otf.get_challenge_tracker_detail(EquipmentType.Treadmill, ChallengeType.Other) - print(tread_challenge_details.details[0].model_dump_json(indent=4)) + print(tread_challenge_details[0].model_dump_json(indent=4)) """ { diff --git a/examples/class_bookings_examples.py b/examples/class_bookings_examples.py index 65dd06b..3eeb9ac 100644 --- a/examples/class_bookings_examples.py +++ b/examples/class_bookings_examples.py @@ -12,12 +12,13 @@ def main(): otf = Otf(user=OtfUser(USERNAME, PASSWORD)) - resp = otf.get_bookings(start_date=datetime.today().date()) - print(resp.model_dump_json(indent=4)) + bookings = otf.get_bookings(start_date=datetime.today().date()) + for b in bookings: + print(b.model_dump_json(indent=4)) studios = otf.search_studios_by_geo(40.7831, 73.9712, distance=100) - studio_uuids = [studio.studio_uuid for studio in studios.studios] + studio_uuids = [studio.studio_uuid for studio in studios] cf = ClassFilter( day_of_week=[DoW.TUESDAY, DoW.THURSDAY], @@ -28,7 +29,7 @@ def main(): classes = otf.get_classes(studio_uuids=studio_uuids, filters=[cf, cf2]) - print(classes.classes[0].model_dump_json(indent=4)) + print(classes[0].model_dump_json(indent=4)) """ { @@ -62,7 +63,7 @@ def main(): bookings = otf.get_bookings() print("Latest Upcoming Class:") - print(bookings.bookings[-1].model_dump_json(indent=4)) + print(bookings[-1].model_dump_json(indent=4)) """ { diff --git a/examples/studio_examples.py b/examples/studio_examples.py index 3c67779..8a3f59f 100644 --- a/examples/studio_examples.py +++ b/examples/studio_examples.py @@ -15,7 +15,7 @@ def main(): # but you'll generally just need the first 3 # same as with classes, you can leave it blank and get the studios within 50 miles of your home studio studios_by_geo = otf.search_studios_by_geo() - print(studios_by_geo.studios[0].model_dump_json(indent=4)) + print(studios_by_geo[0].model_dump_json(indent=4)) """ { @@ -71,7 +71,8 @@ def main(): print("Studio Services") services = otf.get_studio_services(studio_detail.studio_uuid) - print(services.model_dump_json(indent=4)) + for svc in services: + print(svc.model_dump_json(indent=4)) faves = otf.get_favorite_studios() @@ -79,7 +80,7 @@ def main(): otf.add_favorite_studio(otf.home_studio_uuid) faves = otf.get_favorite_studios() - print(faves.model_dump_json(indent=4)) + print(faves[0].model_dump()) if __name__ == "__main__": diff --git a/examples/workout_examples.py b/examples/workout_examples.py index 161df40..a573a4e 100644 --- a/examples/workout_examples.py +++ b/examples/workout_examples.py @@ -14,12 +14,12 @@ def main(): print(resp.model_dump_json(indent=4)) resp = otf.get_body_composition_list() - print(resp.data[0].model_dump_json(indent=4)) + print(resp[0].model_dump_json(indent=4)) # performance summaries are historical records of your performance in workouts # `get_performance_summaries` takes a limit (default of 30) and returns a list of summaries data_list = otf.get_performance_summaries() - print(data_list.summaries[0].model_dump_json(indent=4)) + print(data_list[0].model_dump_json(indent=4)) """ { "performance_summary_id": "29dd97f4-3418-4247-b35c-37eabc5e17f3", @@ -50,7 +50,7 @@ def main(): # you can get detailed information about a specific performance summary by calling `get_performance_summary` # which takes a performance_summary_id as an argument - data = otf.get_performance_summary(data_list.summaries[0].id) + data = otf.get_performance_summary(data_list[0].id) print(data.model_dump_json(indent=4)) """ @@ -110,7 +110,7 @@ def main(): # this endpoint takes a class_history_uuid, as well as a number of max data points - if you do not pass # this value it will attempt to return enough data points for 30 second intervals - telemetry = otf.get_telemetry(performance_summary_id=data_list.summaries[0].id) + telemetry = otf.get_telemetry(performance_summary_id=data_list[0].id) telemetry.telemetry = telemetry.telemetry[:2] print(telemetry.model_dump_json(indent=4)) diff --git a/src/otf_api/api.py b/src/otf_api/api.py index 82c7ecb..b2a459a 100644 --- a/src/otf_api/api.py +++ b/src/otf_api/api.py @@ -134,7 +134,7 @@ def get_classes( filters: list[filters.ClassFilter] | filters.ClassFilter | None = None, exclude_cancelled: bool = True, exclude_unbookable: bool = True, - ) -> models.OtfClassList: + ) -> list[models.OtfClass]: """Get the classes for the user. Returns a list of classes that are available for the user, based on the studio UUIDs provided. If no studio @@ -154,7 +154,7 @@ def get_classes( True. Returns: - OtfClassList: The classes for the user. + list[OtfClass]: The classes for the user. """ if not studio_uuids: @@ -170,40 +170,40 @@ def get_classes( studio_uuids.append(self.home_studio_uuid) classes_resp = self._classes_request("GET", "/v1/classes", params={"studio_ids": studio_uuids}) - classes_list = models.OtfClassList(classes=classes_resp["items"]) + classes = [models.OtfClass(**c) for c in classes_resp["items"]] if exclude_cancelled: - classes_list.classes = [c for c in classes_list.classes if not c.is_cancelled] + classes = [c for c in classes if not c.is_cancelled] - for otf_class in classes_list.classes: + for otf_class in classes: otf_class.is_home_studio = otf_class.studio.studio_uuid == self.home_studio_uuid if exclude_unbookable: # this endpoint returns classes that the `book_class` endpoint will reject, this filters them out max_date = datetime.today().date() + timedelta(days=29) - classes_list.classes = [c for c in classes_list.classes if c.starts_at.date() <= max_date] + classes = [c for c in classes if c.starts_at.date() <= max_date] - booking_resp = self.get_bookings(status=models.BookingStatus.Booked) - booked_classes = {b.otf_class.class_uuid for b in booking_resp.bookings} + bookings = self.get_bookings(status=models.BookingStatus.Booked) + booked_classes = {b.otf_class.class_uuid for b in bookings} - for otf_class in classes_list.classes: + for otf_class in classes: otf_class.is_booked = otf_class.class_uuid in booked_classes if limit: - classes_list.classes = classes_list.classes[:limit] + classes = classes[:limit] # apply date filters if start_date: if not isinstance(start_date, date | datetime): raise ValueError("start_date must be a date or datetime object") start_date = start_date.date() if isinstance(start_date, datetime) else start_date - classes_list.classes = [c for c in classes_list.classes if c.starts_at.date() >= start_date] + classes = [c for c in classes if c.starts_at.date() >= start_date] if end_date: if not isinstance(end_date, date | datetime): raise ValueError("end_date must be a date or datetime object") end_date = end_date.date() if isinstance(end_date, datetime) else end_date - classes_list.classes = [c for c in classes_list.classes if c.starts_at.date() <= end_date] + classes = [c for c in classes if c.starts_at.date() <= end_date] # apply provided filters if filters: @@ -213,13 +213,13 @@ def get_classes( filters = [filters] for f in filters: - filtered_classes.extend(f.filter_classes(classes_list.classes)) + filtered_classes.extend(f.filter_classes(classes)) # remove duplicates - classes_list.classes = list({c.class_uuid: c for c in filtered_classes}.values()) - classes_list.classes = sorted(classes_list.classes, key=lambda x: (x.starts_at, x.name)) + classes = list({c.class_uuid: c for c in filtered_classes}.values()) + classes = sorted(classes, key=lambda x: (x.starts_at, x.name)) - return classes_list + return classes def get_booking(self, booking_uuid: str) -> models.Booking: """Get a specific booking by booking_uuid. @@ -260,7 +260,7 @@ def get_booking_from_class(self, otf_class: str | models.OtfClass) -> models.Boo all_bookings = self.get_bookings(exclude_cancelled=False, exclude_checkedin=False) - if booking := all_bookings.get_booking_from_class_uuid(class_uuid): + if booking := next((b for b in all_bookings if b.otf_class.class_uuid == class_uuid), None): return booking raise exc.BookingNotFoundError(f"Booking for class {class_uuid} not found.") @@ -327,13 +327,13 @@ def _check_for_booking_conflicts(self, otf_class: models.OtfClass) -> None: found, a ConflictingBookingError is raised. """ - all_bookings = self.get_bookings(start_date=otf_class.starts_at.date(), end_date=otf_class.starts_at.date()) - if not all_bookings.bookings: + bookings = self.get_bookings(start_date=otf_class.starts_at.date(), end_date=otf_class.starts_at.date()) + if not bookings: return booking_map: dict[tuple[datetime, datetime], models.Booking] = {} - for booking in all_bookings.bookings: + for booking in bookings: booking_map[(booking.otf_class.starts_at, booking.otf_class.ends_at)] = booking for (booking_start, booking_end), booking in booking_map.items(): @@ -382,10 +382,9 @@ def get_bookings( start_date: date | str | None = None, end_date: date | str | None = None, status: models.BookingStatus | None = None, - limit: int | None = None, exclude_cancelled: bool = True, exclude_checkedin: bool = True, - ) -> models.BookingList: + ) -> list[models.Booking]: """Get the member's bookings. Args: @@ -393,13 +392,11 @@ def get_bookings( end_date (date | str | None): The end date for the bookings. Default is None. status (BookingStatus | None): The status of the bookings to get. Default is None, which includes\ all statuses. Only a single status can be provided. - limit (int | None): The maximum number of bookings to return. Default is None, which returns all\ - bookings. exclude_cancelled (bool): Whether to exclude cancelled bookings. Default is True. exclude_checkedin (bool): Whether to exclude checked-in bookings. Default is True. Returns: - BookingList: The member's bookings. + list[Booking]: The member's bookings. Warning: --- @@ -438,28 +435,28 @@ def get_bookings( params = {"startDate": start_date, "endDate": end_date, "statuses": status_value} - res = self._default_request("GET", f"/member/members/{self.member_uuid}/bookings", params=params) - - bookings = res["data"][:limit] if limit else res["data"] + bookings = self._default_request("GET", f"/member/members/{self.member_uuid}/bookings", params=params)["data"] - data = models.BookingList(bookings=bookings) - data.bookings = sorted(data.bookings, key=lambda x: x.otf_class.starts_at) + # add studio details for each booking, instead of using the different studio model returned by this endpoint + studio_uuids = {b["class"]["studio"]["studioUUId"] for b in bookings} + studios = {studio_uuid: self.get_studio_detail(studio_uuid) for studio_uuid in studio_uuids} - for booking in data.bookings: - if not booking.otf_class: - continue + for b in bookings: + b["class"]["studio"] = studios[b["class"]["studio"]["studioUUId"]] + b["is_home_studio"] = b["class"]["studio"].studio_uuid == self.home_studio_uuid - booking.is_home_studio = booking.studio_uuid == self.home_studio_uuid + bookings = [models.Booking(**b) for b in bookings] + bookings = sorted(bookings, key=lambda x: x.otf_class.starts_at) if exclude_cancelled: - data.bookings = [b for b in data.bookings if b.status != models.BookingStatus.Cancelled] + bookings = [b for b in bookings if b.status != models.BookingStatus.Cancelled] if exclude_checkedin: - data.bookings = [b for b in data.bookings if b.status != models.BookingStatus.CheckedIn] + bookings = [b for b in bookings if b.status != models.BookingStatus.CheckedIn] - return data + return bookings - def _get_bookings_old(self, status: models.BookingStatus | None = None) -> models.BookingList: + def _get_bookings_old(self, status: models.BookingStatus | None = None) -> list[models.Booking]: """Get the member's bookings. Args: @@ -467,7 +464,7 @@ def _get_bookings_old(self, status: models.BookingStatus | None = None) -> model all statuses. Only a single status can be provided. Returns: - BookingList: The member's bookings. + list[Booking]: The member's bookings. Raises: ValueError: If an unaccepted status is provided. @@ -506,7 +503,7 @@ def _get_bookings_old(self, status: models.BookingStatus | None = None) -> model "GET", f"/member/members/{self.member_uuid}/bookings", params={"status": status_value} ) - return models.BookingList(bookings=res["data"]) + return [models.Booking(**b) for b in res["data"]] def get_member_detail( self, include_addresses: bool = True, include_class_summary: bool = True, include_credit_card: bool = False @@ -558,14 +555,14 @@ def get_member_membership(self) -> models.MemberMembership: data = self._default_request("GET", f"/member/members/{self.member_uuid}/memberships") return models.MemberMembership(**data["data"]) - def get_member_purchases(self) -> models.MemberPurchaseList: + def get_member_purchases(self) -> list[models.MemberPurchase]: """Get the member's purchases, including monthly subscriptions and class packs. Returns: - MemberPurchaseList: The member's purchases. + list[MemberPurchase]: The member's purchases. """ data = self._default_request("GET", f"/member/members/{self.member_uuid}/purchases") - return models.MemberPurchaseList(data=data["data"]) + return [models.MemberPurchase(**purchase) for purchase in data["data"]] def get_member_lifetime_stats( self, select_time: models.StatsTime = models.StatsTime.AllTime @@ -603,21 +600,21 @@ def get_latest_agreement(self) -> models.LatestAgreement: data = self._default_request("GET", "/member/agreements/9d98fb27-0f00-4598-ad08-5b1655a59af6") return models.LatestAgreement(**data["data"]) - def get_out_of_studio_workout_history(self) -> models.OutOfStudioWorkoutHistoryList: + def get_out_of_studio_workout_history(self) -> list[models.OutOfStudioWorkoutHistory]: """Get the member's out of studio workout history. Returns: - OutOfStudioWorkoutHistoryList: The member's out of studio workout history. + list[OutOfStudioWorkoutHistory]: The member's out of studio workout history. """ data = self._default_request("GET", f"/member/members/{self.member_uuid}/out-of-studio-workout") - return models.OutOfStudioWorkoutHistoryList(workouts=data["data"]) + return [models.OutOfStudioWorkoutHistory(**workout) for workout in data["data"]] - def get_favorite_studios(self) -> models.StudioDetailList: + def get_favorite_studios(self) -> list[models.StudioDetail]: """Get the member's favorite studios. Returns: - StudioDetailList: The member's favorite studios. + list[StudioDetail]: The member's favorite studios. """ data = self._default_request("GET", f"/member/members/{self.member_uuid}/favorite-studios") studio_uuids = [studio["studioUUId"] for studio in data["data"]] @@ -628,12 +625,7 @@ def get_favorite_studios(self) -> models.StudioDetailList: # it's slower, but it's more consistent - all_studio_details: list[models.StudioDetail] = [] - - for studio_uuid in studio_uuids: - all_studio_details.append(self.get_studio_detail(studio_uuid)) - - return models.StudioDetailList(studios=all_studio_details) + return [self.get_studio_detail(studio_uuid) for studio_uuid in studio_uuids] def add_favorite_studio(self, studio_uuids: list[str] | str) -> list[models.StudioDetail]: """Add a studio to the member's favorite studios. @@ -677,7 +669,7 @@ def remove_favorite_studio(self, studio_uuids: list[str] | str) -> None: body = {"studioUUIds": studio_uuids} self._default_request("DELETE", "/mobile/v1/members/favorite-studios", json=body) - def get_studio_services(self, studio_uuid: str | None = None) -> models.StudioServiceList: + def get_studio_services(self, studio_uuid: str | None = None) -> list[models.StudioService]: """Get the services available at a specific studio. If no studio UUID is provided, the member's home studio will be used. @@ -685,7 +677,7 @@ def get_studio_services(self, studio_uuid: str | None = None) -> models.StudioSe studio_uuid (str, optional): The studio UUID to get services for. Returns: - StudioServiceList: The services available at the studio. + list[StudioService]: The services available at the studio. """ studio_uuid = studio_uuid or self.home_studio_uuid data = self._default_request("GET", f"/member/studios/{studio_uuid}/services") @@ -694,7 +686,7 @@ def get_studio_services(self, studio_uuid: str | None = None) -> models.StudioSe for d in data["data"]: d["studio_uuid"] = studio_uuid - return models.StudioServiceList(studio_uuid=studio_uuid, data=data["data"]) + return [models.StudioService(**d) for d in data["data"]] @functools.cache def get_studio_detail(self, studio_uuid: str | None = None) -> models.StudioDetail: @@ -722,7 +714,7 @@ def get_studios_by_geo( distance: float = 500, page_index: int = 1, page_size: int = 100, - ) -> models.StudioDetailList: + ) -> list[models.StudioDetail]: """Alias for search_studios_by_geo.""" return self.search_studios_by_geo(latitude, longitude, distance, page_index, page_size) @@ -734,7 +726,7 @@ def search_studios_by_geo( distance: float = 50, page_index: int = 1, page_size: int = 100, - ) -> models.StudioDetailList: + ) -> list[models.StudioDetail]: """Search for studios by geographic location. Args: @@ -745,19 +737,19 @@ def search_studios_by_geo( page_size (int, optional): Number of results per page. Defaults to 100. Returns: - StudioDetailList: List of studios that match the search criteria. + list[StudioDetail]: List of studios that match the search criteria. """ latitude = latitude or self.home_studio.location.latitude longitude = longitude or self.home_studio.location.longitude return self._get_studios_by_geo(latitude, longitude, distance, page_index, page_size) - def _get_all_studios(self) -> models.StudioDetailList: + def _get_all_studios(self) -> list[models.StudioDetail]: """Gets all studios. Marked as private to avoid random users calling it. Useful for testing and validating models. Returns: - StudioDetailList: List of studios that match the search criteria. + list[StudioDetail]: List of studios that match the search criteria. """ return self._get_studios_by_geo(None, None, 500, 1, 100) @@ -769,7 +761,7 @@ def _get_studios_by_geo( distance: float = 50, page_index: int = 1, page_size: int = 100, - ): + ) -> list[models.StudioDetail]: """Searches for studios by geographic location. Used by search_studios_by_geo and get_all_studios.""" path = "/mobile/v1/studios" @@ -808,7 +800,7 @@ def _get_studios_by_geo( params["pageIndex"] += 1 - return models.StudioDetailList(studios=list(all_results.values())) + return [models.StudioDetail(**studio) for studio in all_results.values()] def get_total_classes(self) -> models.TotalClasses: """Get the member's total classes. This is a simple object reflecting the total number of classes attended, @@ -820,15 +812,14 @@ def get_total_classes(self) -> models.TotalClasses: data = self._default_request("GET", "/mobile/v1/members/classes/summary") return models.TotalClasses(**data["data"]) - def get_body_composition_list(self) -> models.BodyCompositionList: + def get_body_composition_list(self) -> list[models.BodyCompositionData]: """Get the member's body composition list. Returns: - Any: The member's body composition list. + list[BodyCompositionData]: The member's body composition list. """ data = self._default_request("GET", f"/member/members/{self.member.cognito_id}/body-composition") - - return models.BodyCompositionList(data=data["data"]) + return [models.BodyCompositionData(**item) for item in data["data"]] def get_challenge_tracker_content(self) -> models.ChallengeTrackerContent: """Get the member's challenge tracker content. @@ -844,7 +835,7 @@ def get_challenge_tracker_detail( equipment_id: models.EquipmentType, challenge_type_id: models.ChallengeType, challenge_sub_type_id: int = 0, - ): + ) -> list[models.ChallengeTrackerDetail]: """Get the member's challenge tracker details. Args: @@ -853,7 +844,7 @@ def get_challenge_tracker_detail( challenge_sub_type_id (int): The challenge sub type ID. Default is 0. Returns: - ChallengeTrackerDetailList: The member's challenge tracker details. + list[ChallengeTrackerDetail]: The member's challenge tracker details. Notes: --- @@ -867,8 +858,7 @@ def get_challenge_tracker_detail( } data = self._default_request("GET", f"/challenges/v3/member/{self.member_uuid}/benchmarks", params=params) - - return models.ChallengeTrackerDetailList(details=data["Dto"]) + return [models.ChallengeTrackerDetail(**item) for item in data["Dto"]] def get_challenge_tracker_participation(self, challenge_type_id: models.ChallengeType) -> Any: """Get the member's participation in a challenge. @@ -893,14 +883,14 @@ def get_challenge_tracker_participation(self, challenge_type_id: models.Challeng ) return data - def get_performance_summaries(self, limit: int = 30) -> models.PerformanceSummaryList: + def get_performance_summaries(self, limit: int = 30) -> list[models.PerformanceSummaryEntry]: """Get a list of performance summaries for the authenticated user. Args: limit (int): The maximum number of performance summaries to return. Defaults to 30. Returns: - PerformanceSummaryList: A list of performance summaries. + list[PerformanceSummaryEntry]: A list of performance summaries. Developer Notes: --- @@ -909,7 +899,7 @@ def get_performance_summaries(self, limit: int = 30) -> models.PerformanceSummar """ res = self._performance_summary_request("GET", "/v1/performance-summaries", params={"limit": limit}) - return models.PerformanceSummaryList(summaries=res["items"]) + return [models.PerformanceSummaryEntry(**item) for item in res["items"]] def get_performance_summary(self, performance_summary_id: str) -> models.PerformanceSummaryDetail: """Get a detailed performance summary for a given workout. @@ -925,21 +915,21 @@ def get_performance_summary(self, performance_summary_id: str) -> models.Perform res = self._performance_summary_request("GET", path) return models.PerformanceSummaryDetail(**res) - def get_hr_history(self) -> models.TelemetryHrHistory: + def get_hr_history(self) -> list[models.HistoryItem]: """Get the heartrate history for the user. Returns a list of history items that contain the max heartrate, start/end bpm for each zone, the change from the previous, the change bucket, and the assigned at time. Returns: - TelemetryHrHistory: The heartrate history for the user. + list[HistoryItem]: The heartrate history for the user. """ path = "/v1/physVars/maxHr/history" params = {"memberUuid": self.member_uuid} res = self._telemetry_request("GET", path, params=params) - return models.TelemetryHrHistory(**res) + return [models.HistoryItem(**item) for item in res["items"]] def get_max_hr(self) -> models.TelemetryMaxHr: """Get the max heartrate for the user. diff --git a/src/otf_api/models/__init__.py b/src/otf_api/models/__init__.py index 91706a7..bd4e5e6 100644 --- a/src/otf_api/models/__init__.py +++ b/src/otf_api/models/__init__.py @@ -1,56 +1,53 @@ -from .body_composition_list import BodyCompositionList +from .body_composition_list import BodyCompositionData from .book_class import BookClass -from .bookings import Booking, BookingList +from .bookings import Booking from .cancel_booking import CancelBooking from .challenge_tracker_content import ChallengeTrackerContent -from .challenge_tracker_detail import ChallengeTrackerDetailList -from .classes import OtfClass, OtfClassList +from .challenge_tracker_detail import ChallengeTrackerDetail +from .classes import OtfClass from .enums import BookingStatus, ChallengeType, ClassType, DoW, EquipmentType, StatsTime, StudioStatus from .latest_agreement import LatestAgreement from .lifetime_stats import StatsResponse from .member_detail import MemberDetail from .member_membership import MemberMembership -from .member_purchases import MemberPurchaseList -from .out_of_studio_workout_history import OutOfStudioWorkoutHistoryList +from .member_purchases import MemberPurchase +from .out_of_studio_workout_history import OutOfStudioWorkoutHistory from .performance_summary_detail import PerformanceSummaryDetail -from .performance_summary_list import PerformanceSummaryList -from .studio_detail import StudioDetail, StudioDetailList -from .studio_services import StudioServiceList +from .performance_summary_list import PerformanceSummaryEntry +from .studio_detail import StudioDetail +from .studio_services import StudioService from .telemetry import Telemetry -from .telemetry_hr_history import TelemetryHrHistory +from .telemetry_hr_history import HistoryItem from .telemetry_max_hr import TelemetryMaxHr from .total_classes import TotalClasses __all__ = [ - "BodyCompositionList", + "BodyCompositionData", "BookClass", "Booking", - "BookingList", "BookingStatus", "CancelBooking", "ChallengeTrackerContent", - "ChallengeTrackerDetailList", + "ChallengeTrackerDetail", "ChallengeType", "ClassType", "DoW", "EquipmentType", + "HistoryItem", "LatestAgreement", "MemberDetail", "MemberMembership", - "MemberPurchaseList", + "MemberPurchase", "OtfClass", - "OtfClassList", - "OutOfStudioWorkoutHistoryList", + "OutOfStudioWorkoutHistory", "PerformanceSummaryDetail", - "PerformanceSummaryList", + "PerformanceSummaryEntry", "StatsResponse", "StatsTime", "StudioDetail", - "StudioDetailList", - "StudioServiceList", + "StudioService", "StudioStatus", "Telemetry", - "TelemetryHrHistory", "TelemetryMaxHr", - "TotalClasses", + "TotalClasses" ] diff --git a/src/otf_api/models/body_composition_list.py b/src/otf_api/models/body_composition_list.py index 83d443d..e661f13 100644 --- a/src/otf_api/models/body_composition_list.py +++ b/src/otf_api/models/body_composition_list.py @@ -293,14 +293,14 @@ def body_fat_percent_relative_descriptor(self) -> BodyFatPercentIndicator: ) -class BodyCompositionList(OtfItemBase): - data: list[BodyCompositionData] +# class BodyCompositionList(OtfItemBase): +# data: list[BodyCompositionData] - def __len__(self) -> int: - return len(self.data) +# def __len__(self) -> int: +# return len(self.data) - def __iter__(self): - return iter(self.data) +# def __iter__(self): +# return iter(self.data) - def __getitem__(self, item) -> BodyCompositionData: - return self.data[item] +# def __getitem__(self, item) -> BodyCompositionData: +# return self.data[item] diff --git a/src/otf_api/models/bookings.py b/src/otf_api/models/bookings.py index 98d0edb..d73fc0e 100644 --- a/src/otf_api/models/bookings.py +++ b/src/otf_api/models/bookings.py @@ -1,42 +1,10 @@ from datetime import datetime -from pydantic import AliasPath, Field +from pydantic import Field from otf_api.models.base import OtfItemBase -from otf_api.models.enums import BookingStatus, StudioStatus -from otf_api.models.mixins import AddressMixin, PhoneLongitudeLatitudeMixin - - -class CountryCurrency(OtfItemBase): - country_currency_code: str = Field(..., alias="countryCurrencyCode") - currency_id: int | None = Field(None, alias=AliasPath("defaultCurrency", "currencyId")) - currency_alphabetic_code: str | None = Field(None, alias=AliasPath("defaultCurrency", "currencyAlphabeticCode")) - - -class Location(PhoneLongitudeLatitudeMixin, AddressMixin): - distance: float | None = Field(None, alias="distance", exclude=True, repr=False) - location_name: str | None = Field(None, alias="locationName", exclude=True, repr=False) - - -class StudioLocation(PhoneLongitudeLatitudeMixin, AddressMixin): - physical_region: str | None = Field(None, alias="physicalRegion", exclude=True, repr=False) - physical_country_id: int | None = Field(None, alias="physicalCountryId", exclude=True, repr=False) - country_currency: CountryCurrency | None = Field(None, alias="country_currency", exclude=True, repr=False) - - -class Studio(OtfItemBase): - studio_uuid: str = Field(alias="studioUUId") - - contact_email: str | None = Field(None, alias="contactEmail") - description: str | None = None - status: StudioStatus | None = None - location: StudioLocation | None = Field(None, alias="studioLocation", exclude=True, repr=False) - name: str = Field(alias="studioName") - time_zone: str = Field(alias="timeZone") - - # unused fields - mbo_studio_id: int | None = Field(None, alias="mboStudioId", exclude=True, repr=False, description="MindBody attr") - studio_id: int = Field(alias="studioId", exclude=True, repr=False, description="Not used by API") +from otf_api.models.enums import BookingStatus +from otf_api.models.studio_detail import StudioDetail class Coach(OtfItemBase): @@ -61,13 +29,12 @@ class OtfClass(OtfItemBase): ends_at: datetime = Field(alias="endDateTime", description="End time in local timezone") is_available: bool = Field(alias="isAvailable") is_cancelled: bool = Field(alias="isCancelled") - studio: Studio + studio: StudioDetail coach: Coach # unused fields coach_id: int | None = Field(None, alias="coachId", exclude=True, repr=False, description="Not used by API") description: str | None = Field(None, exclude=True, repr=False) - location: Location | None = Field(None, exclude=True, repr=False) program_name: str | None = Field(None, alias="programName", exclude=True, repr=False) virtual_class: bool | None = Field(None, alias="virtualClass", exclude=True, repr=False) @@ -82,16 +49,6 @@ def ends_at_local(self) -> datetime: return self.ends_at -class Member(OtfItemBase): - member_uuid: str = Field(alias="memberUUId") - first_name: str = Field(alias="firstName") - last_name: str = Field(alias="lastName") - email: str | None = None - phone_number: str | None = Field(None, alias="phoneNumber") - gender: str | None = None - cc_last_4: str | None = Field(None, alias="ccLast4", exclude=True, repr=False) - - class Booking(OtfItemBase): booking_uuid: str = Field(alias="classBookingUUId", description="ID used to cancel the booking") is_intro: bool = Field(alias="isIntro") @@ -118,7 +75,6 @@ class Booking(OtfItemBase): mbo_visit_id: int | None = Field(None, alias="mboVisitId", exclude=True, repr=False, description="MindBody attr") mbo_waitlist_entry_id: int | None = Field(None, alias="mboWaitlistEntryId", exclude=True, repr=False) member_id: int = Field(alias="memberId", exclude=True, repr=False, description="Not used by API") - member: Member | None = Field(None, exclude=True, repr=False, description="Slimmed down member object") studio_id: int = Field(alias="studioId", exclude=True, repr=False, description="Not used by API") updated_by: str = Field(alias="updatedBy", exclude=True, repr=False) @@ -149,22 +105,3 @@ def __str__(self) -> str: booked_str = self.status.value return f"Booking: {starts_at_str} {class_name} - {coach_name} ({booked_str})" - - -class BookingList(OtfItemBase): - bookings: list[Booking] - - def __len__(self) -> int: - return len(self.bookings) - - def __iter__(self): - return iter(self.bookings) - - def get_booking_from_class_uuid(self, class_uuid: str) -> Booking | None: - for booking in self.bookings: - if booking.otf_class.class_uuid == class_uuid: - return booking - return None - - def __getitem__(self, item) -> Booking: - return self.bookings[item] diff --git a/src/otf_api/models/challenge_tracker_detail.py b/src/otf_api/models/challenge_tracker_detail.py index 9973963..c248580 100644 --- a/src/otf_api/models/challenge_tracker_detail.py +++ b/src/otf_api/models/challenge_tracker_detail.py @@ -73,14 +73,14 @@ class ChallengeTrackerDetail(OtfItemBase): challenge_histories: list[ChallengeHistory] = Field(..., alias="ChallengeHistories") -class ChallengeTrackerDetailList(OtfItemBase): - details: list[ChallengeTrackerDetail] +# class ChallengeTrackerDetailList(OtfItemBase): +# details: list[ChallengeTrackerDetail] - def __iter__(self): - return iter(self.details) +# def __iter__(self): +# return iter(self.details) - def __len__(self): - return len(self.details) +# def __len__(self): +# return len(self.details) - def __getitem__(self, item) -> ChallengeTrackerDetail: - return self.details[item] +# def __getitem__(self, item) -> ChallengeTrackerDetail: +# return self.details[item] diff --git a/src/otf_api/models/classes.py b/src/otf_api/models/classes.py index 7b4d24c..5bd5892 100644 --- a/src/otf_api/models/classes.py +++ b/src/otf_api/models/classes.py @@ -90,16 +90,3 @@ def has_availability(self) -> bool: def day_of_week_enum(self) -> DoW: dow = self.starts_at.strftime("%A").upper() return DoW(dow) - - -class OtfClassList(OtfItemBase): - classes: list[OtfClass] - - def __len__(self) -> int: - return len(self.classes) - - def __iter__(self): - return iter(self.classes) - - def __getitem__(self, item) -> OtfClass: - return self.classes[item] diff --git a/src/otf_api/models/member_purchases.py b/src/otf_api/models/member_purchases.py index 53ba09c..ce4f7a5 100644 --- a/src/otf_api/models/member_purchases.py +++ b/src/otf_api/models/member_purchases.py @@ -109,14 +109,14 @@ class MemberPurchase(OtfItemBase): studio: Studio -class MemberPurchaseList(OtfItemBase): - data: list[MemberPurchase] +# class MemberPurchaseList(OtfItemBase): +# data: list[MemberPurchase] - def __iter__(self): - return iter(self.data) +# def __iter__(self): +# return iter(self.data) - def __len__(self): - return len(self.data) +# def __len__(self): +# return len(self.data) - def __getitem__(self, item) -> MemberPurchase: - return self.data[item] +# def __getitem__(self, item) -> MemberPurchase: +# return self.data[item] diff --git a/src/otf_api/models/out_of_studio_workout_history.py b/src/otf_api/models/out_of_studio_workout_history.py index ea1f1a6..b46b06c 100644 --- a/src/otf_api/models/out_of_studio_workout_history.py +++ b/src/otf_api/models/out_of_studio_workout_history.py @@ -37,14 +37,14 @@ class OutOfStudioWorkoutHistory(OtfItemBase): max_heartrate: int = Field(..., alias="maxHeartrate") -class OutOfStudioWorkoutHistoryList(OtfItemBase): - workouts: list[OutOfStudioWorkoutHistory] +# class OutOfStudioWorkoutHistoryList(OtfItemBase): +# workouts: list[OutOfStudioWorkoutHistory] - def __iter__(self): - return iter(self.workouts) +# def __iter__(self): +# return iter(self.workouts) - def __len__(self): - return len(self.workouts) +# def __len__(self): +# return len(self.workouts) - def __getitem__(self, item) -> OutOfStudioWorkoutHistory: - return self.workouts[item] +# def __getitem__(self, item) -> OutOfStudioWorkoutHistory: +# return self.workouts[item] diff --git a/src/otf_api/models/performance_summary_list.py b/src/otf_api/models/performance_summary_list.py index d05df08..e5f9c2c 100644 --- a/src/otf_api/models/performance_summary_list.py +++ b/src/otf_api/models/performance_summary_list.py @@ -64,14 +64,14 @@ class PerformanceSummaryEntry(OtfItemBase): ratings: Ratings | None = None -class PerformanceSummaryList(OtfItemBase): - summaries: list[PerformanceSummaryEntry] +# class PerformanceSummaryList(OtfItemBase): +# summaries: list[PerformanceSummaryEntry] - def __iter__(self): - return iter(self.summaries) +# def __iter__(self): +# return iter(self.summaries) - def __len__(self): - return len(self.summaries) +# def __len__(self): +# return len(self.summaries) - def __getitem__(self, item) -> PerformanceSummaryEntry: - return self.summaries[item] +# def __getitem__(self, item) -> PerformanceSummaryEntry: +# return self.summaries[item] diff --git a/src/otf_api/models/studio_detail.py b/src/otf_api/models/studio_detail.py index 85a8e5d..df79287 100644 --- a/src/otf_api/models/studio_detail.py +++ b/src/otf_api/models/studio_detail.py @@ -60,16 +60,3 @@ class StudioDetail(OtfItemBase): studio_physical_location_id: int | None = Field(None, alias="studioPhysicalLocationId", exclude=True, repr=False) studio_token: str | None = Field(None, alias="studioToken", exclude=True, repr=False) studio_type_id: int | None = Field(None, alias="studioTypeId", exclude=True, repr=False) - - -class StudioDetailList(OtfItemBase): - studios: list[StudioDetail] - - def __len__(self) -> int: - return len(self.studios) - - def __iter__(self): - return iter(self.studios) - - def __getitem__(self, item) -> StudioDetail: - return self.studios[item] diff --git a/src/otf_api/models/studio_services.py b/src/otf_api/models/studio_services.py index 02bfdaa..519ab8b 100644 --- a/src/otf_api/models/studio_services.py +++ b/src/otf_api/models/studio_services.py @@ -52,17 +52,3 @@ class StudioService(OtfItemBase): updated_date: datetime = Field(..., alias="updatedDate") is_deleted: bool = Field(..., alias="isDeleted") studio: Studio - - -class StudioServiceList(OtfItemBase): - studio_uuid: str = Field(..., description="The OTF studio UUID") - data: list[StudioService] - - def __len__(self) -> int: - return len(self.data) - - def __iter__(self): - return iter(self.data) - - def __getitem__(self, item) -> StudioService: - return self.data[item] diff --git a/src/otf_api/models/telemetry_hr_history.py b/src/otf_api/models/telemetry_hr_history.py index 5e99d85..d163cee 100644 --- a/src/otf_api/models/telemetry_hr_history.py +++ b/src/otf_api/models/telemetry_hr_history.py @@ -29,6 +29,6 @@ class HistoryItem(OtfItemBase): assigned_at: str = Field(..., alias="assignedAt") -class TelemetryHrHistory(OtfItemBase): - member_uuid: str = Field(..., alias="memberUuid") - history: list[HistoryItem] +# class TelemetryHrHistory(OtfItemBase): +# member_uuid: str = Field(..., alias="memberUuid") +# history: list[HistoryItem]