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/logging.py
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
import logging
|
2
|
-
import os
|
3
|
-
|
4
|
-
LOG_LEVEL = os.getenv("OTF_LOG_LEVEL", "INFO")
|
5
|
-
|
6
|
-
LOG_FMT = "{asctime} - {module}.{funcName}:{lineno} - {levelname} - {message}"
|
7
|
-
DATE_FMT = "%Y-%m-%d %H:%M:%S%z"
|
8
|
-
|
9
|
-
logger = logging.getLogger("otf_api")
|
10
|
-
|
11
|
-
# 2) Set the logger level to INFO (or whatever you need).
|
12
|
-
logger.setLevel(LOG_LEVEL)
|
13
|
-
|
14
|
-
# 3) Create a handler (e.g., console) and set its formatter.
|
15
|
-
handler = logging.StreamHandler()
|
16
|
-
handler.setFormatter(logging.Formatter(fmt=LOG_FMT, datefmt=DATE_FMT, style="{"))
|
17
|
-
|
18
|
-
# 4) Add this handler to your package logger.
|
19
|
-
logger.addHandler(handler)
|
otf_api/models/bookings.py
DELETED
@@ -1,109 +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.enums import BookingStatus
|
7
|
-
from otf_api.models.studio_detail import StudioDetail
|
8
|
-
|
9
|
-
|
10
|
-
class Coach(OtfItemBase):
|
11
|
-
coach_uuid: str = Field(alias="coachUUId")
|
12
|
-
first_name: str | None = Field(None, alias="firstName")
|
13
|
-
last_name: str | None = Field(None, alias="lastName")
|
14
|
-
|
15
|
-
# unused fields
|
16
|
-
name: str = Field(exclude=True, repr=False)
|
17
|
-
|
18
|
-
@property
|
19
|
-
def full_name(self) -> str:
|
20
|
-
return f"{self.first_name} {self.last_name}"
|
21
|
-
|
22
|
-
|
23
|
-
class OtfClass(OtfItemBase):
|
24
|
-
class_uuid: str = Field(alias="classUUId")
|
25
|
-
name: str
|
26
|
-
starts_at: datetime = Field(alias="startDateTime", description="Start time in local timezone")
|
27
|
-
ends_at: datetime = Field(alias="endDateTime", description="End time in local timezone")
|
28
|
-
is_available: bool = Field(alias="isAvailable")
|
29
|
-
is_cancelled: bool = Field(alias="isCancelled")
|
30
|
-
studio: StudioDetail
|
31
|
-
coach: Coach
|
32
|
-
|
33
|
-
# unused fields
|
34
|
-
coach_id: int | None = Field(None, alias="coachId", exclude=True, repr=False, description="Not used by API")
|
35
|
-
description: str | None = Field(None, exclude=True, repr=False)
|
36
|
-
program_name: str | None = Field(None, alias="programName", exclude=True, repr=False)
|
37
|
-
virtual_class: bool | None = Field(None, alias="virtualClass", exclude=True, repr=False)
|
38
|
-
|
39
|
-
@property
|
40
|
-
def coach_name(self) -> str:
|
41
|
-
"""Shortcut to get the coach's name, to be compatible with new BookingV2Class"""
|
42
|
-
return self.coach.first_name or ""
|
43
|
-
|
44
|
-
def __str__(self) -> str:
|
45
|
-
starts_at_str = self.starts_at.strftime("%a %b %d, %I:%M %p")
|
46
|
-
return f"Class: {starts_at_str} {self.name} - {self.coach.first_name}"
|
47
|
-
|
48
|
-
|
49
|
-
class Booking(OtfItemBase):
|
50
|
-
booking_uuid: str = Field(alias="classBookingUUId", description="ID used to cancel the booking")
|
51
|
-
is_intro: bool = Field(alias="isIntro")
|
52
|
-
status: BookingStatus
|
53
|
-
booked_date: datetime | None = Field(None, alias="bookedDate")
|
54
|
-
checked_in_date: datetime | None = Field(None, alias="checkedInDate")
|
55
|
-
cancelled_date: datetime | None = Field(None, alias="cancelledDate")
|
56
|
-
created_date: datetime = Field(alias="createdDate")
|
57
|
-
updated_date: datetime = Field(alias="updatedDate")
|
58
|
-
is_deleted: bool = Field(alias="isDeleted")
|
59
|
-
waitlist_position: int | None = Field(None, alias="waitlistPosition")
|
60
|
-
otf_class: OtfClass = Field(alias="class")
|
61
|
-
is_home_studio: bool | None = Field(None, description="Custom helper field to determine if at home studio")
|
62
|
-
|
63
|
-
# unused fields
|
64
|
-
class_booking_id: int = Field(alias="classBookingId", exclude=True, repr=False, description="Not used by API")
|
65
|
-
class_id: int = Field(alias="classId", exclude=True, repr=False, description="Not used by API")
|
66
|
-
created_by: str = Field(alias="createdBy", exclude=True, repr=False)
|
67
|
-
mbo_class_id: int | None = Field(None, alias="mboClassId", exclude=True, repr=False, description="MindBody attr")
|
68
|
-
mbo_member_id: str | None = Field(None, alias="mboMemberId", exclude=True, repr=False, description="MindBody attr")
|
69
|
-
mbo_sync_message: str | None = Field(
|
70
|
-
None, alias="mboSyncMessage", exclude=True, repr=False, description="MindBody attr"
|
71
|
-
)
|
72
|
-
mbo_visit_id: int | None = Field(None, alias="mboVisitId", exclude=True, repr=False, description="MindBody attr")
|
73
|
-
mbo_waitlist_entry_id: int | None = Field(None, alias="mboWaitlistEntryId", exclude=True, repr=False)
|
74
|
-
member_id: int = Field(alias="memberId", exclude=True, repr=False, description="Not used by API")
|
75
|
-
studio_id: int = Field(alias="studioId", exclude=True, repr=False, description="Not used by API")
|
76
|
-
updated_by: str = Field(alias="updatedBy", exclude=True, repr=False)
|
77
|
-
|
78
|
-
@property
|
79
|
-
def studio_uuid(self) -> str:
|
80
|
-
"""Shortcut to get the studio UUID"""
|
81
|
-
return self.otf_class.studio.studio_uuid
|
82
|
-
|
83
|
-
@property
|
84
|
-
def class_uuid(self) -> str:
|
85
|
-
"""Shortcut to get the class UUID"""
|
86
|
-
return self.otf_class.class_uuid
|
87
|
-
|
88
|
-
@property
|
89
|
-
def starts_at(self) -> datetime:
|
90
|
-
"""Shortcut to get the class start time"""
|
91
|
-
return self.otf_class.starts_at
|
92
|
-
|
93
|
-
@property
|
94
|
-
def ends_at(self) -> datetime:
|
95
|
-
"""Shortcut to get the class end time"""
|
96
|
-
return self.otf_class.ends_at
|
97
|
-
|
98
|
-
@property
|
99
|
-
def id_value(self) -> str:
|
100
|
-
"""Returns the booking_uuid, to be compatible with new BookingV2 model"""
|
101
|
-
return self.booking_uuid
|
102
|
-
|
103
|
-
def __str__(self) -> str:
|
104
|
-
starts_at_str = self.otf_class.starts_at.strftime("%a %b %d, %I:%M %p")
|
105
|
-
class_name = self.otf_class.name
|
106
|
-
coach_name = self.otf_class.coach.name
|
107
|
-
booked_str = self.status.value
|
108
|
-
|
109
|
-
return f"Booking: {starts_at_str} {class_name} - {coach_name} ({booked_str})"
|
@@ -1,59 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
These models represent the data returned by the challenge tracker endpoint.
|
3
|
-
|
4
|
-
I believe these are used by the app to populate the list of challenges, programs,
|
5
|
-
and benchmarks that a member can compete in.
|
6
|
-
|
7
|
-
The actual data for *your* participation in these challenges is returned by a different
|
8
|
-
endpoint.
|
9
|
-
"""
|
10
|
-
|
11
|
-
from pydantic import Field
|
12
|
-
|
13
|
-
from otf_api.models.base import OtfItemBase
|
14
|
-
from otf_api.models.enums import EquipmentType
|
15
|
-
|
16
|
-
|
17
|
-
class Year(OtfItemBase):
|
18
|
-
year: int | None = Field(None, alias="Year")
|
19
|
-
is_participated: bool | None = Field(None, alias="IsParticipated")
|
20
|
-
in_progress: bool | None = Field(None, alias="InProgress")
|
21
|
-
|
22
|
-
|
23
|
-
class Program(OtfItemBase):
|
24
|
-
"""A program represents multi-day/week challenges that members can participate in."""
|
25
|
-
|
26
|
-
# NOTE: These ones do seem to match the ChallengeType enums in the OTF app.
|
27
|
-
# Leaving them as int for now though in case older data or other user's
|
28
|
-
# data doesn't match up.
|
29
|
-
challenge_category_id: int | None = Field(None, alias="ChallengeCategoryId")
|
30
|
-
challenge_sub_category_id: int | None = Field(None, alias="ChallengeSubCategoryId")
|
31
|
-
challenge_name: str | None = Field(None, alias="ChallengeName")
|
32
|
-
years: list[Year] = Field(default_factory=list, alias="Years")
|
33
|
-
|
34
|
-
|
35
|
-
class Challenge(OtfItemBase):
|
36
|
-
"""A challenge represents a single day or event that members can participate in."""
|
37
|
-
|
38
|
-
# NOTE: The challenge category/subcategory ids here do not seem to be at
|
39
|
-
# all related to the ChallengeType enums or the few SubCategory enums I've
|
40
|
-
# been able to puzzle out. I haven't been able to link them to any code
|
41
|
-
# in the OTF app. Due to that, they are being excluded from the model for now.
|
42
|
-
challenge_category_id: int | None = Field(None, alias="ChallengeCategoryId")
|
43
|
-
challenge_sub_category_id: int | None = Field(None, alias="ChallengeSubCategoryId")
|
44
|
-
challenge_name: str | None = Field(None, alias="ChallengeName")
|
45
|
-
years: list[Year] = Field(default_factory=list, alias="Years")
|
46
|
-
|
47
|
-
|
48
|
-
class Benchmark(OtfItemBase):
|
49
|
-
"""A benchmark represents a specific workout that members can participate in."""
|
50
|
-
|
51
|
-
equipment_id: EquipmentType | None = Field(None, alias="EquipmentId")
|
52
|
-
equipment_name: str | None = Field(None, alias="EquipmentName")
|
53
|
-
years: list[Year] = Field(default_factory=list, alias="Years")
|
54
|
-
|
55
|
-
|
56
|
-
class ChallengeTracker(OtfItemBase):
|
57
|
-
programs: list[Program] = Field(default_factory=list, alias="Programs")
|
58
|
-
challenges: list[Challenge] = Field(default_factory=list, alias="Challenges")
|
59
|
-
benchmarks: list[Benchmark] = Field(default_factory=list, alias="Benchmarks")
|
@@ -1,88 +0,0 @@
|
|
1
|
-
from datetime import datetime
|
2
|
-
from typing import Any
|
3
|
-
|
4
|
-
from pydantic import Field
|
5
|
-
|
6
|
-
from otf_api.models.base import OtfItemBase
|
7
|
-
from otf_api.models.enums import EquipmentType
|
8
|
-
|
9
|
-
|
10
|
-
class MetricEntry(OtfItemBase):
|
11
|
-
title: str | None = Field(None, alias="Title")
|
12
|
-
equipment_id: EquipmentType | None = Field(None, alias="EquipmentId")
|
13
|
-
entry_type: str | None = Field(None, alias="EntryType")
|
14
|
-
metric_key: str | None = Field(None, alias="MetricKey")
|
15
|
-
min_value: str | None = Field(None, alias="MinValue")
|
16
|
-
max_value: str | None = Field(None, alias="MaxValue")
|
17
|
-
|
18
|
-
|
19
|
-
class BenchmarkHistory(OtfItemBase):
|
20
|
-
studio_name: str | None = Field(None, alias="StudioName")
|
21
|
-
equipment_id: EquipmentType | None = Field(None, alias="EquipmentId")
|
22
|
-
class_time: datetime | None = Field(None, alias="ClassTime")
|
23
|
-
challenge_sub_category_id: int | None = Field(None, alias="ChallengeSubCategoryId")
|
24
|
-
weight_lbs: int | None = Field(None, alias="WeightLBS")
|
25
|
-
class_name: str | None = Field(None, alias="ClassName")
|
26
|
-
coach_name: str | None = Field(None, alias="CoachName")
|
27
|
-
result: float | str | None = Field(None, alias="Result")
|
28
|
-
workout_type_id: int | None = Field(None, alias="WorkoutTypeId", exclude=True, repr=False)
|
29
|
-
workout_id: int | None = Field(None, alias="WorkoutId", exclude=True, repr=False)
|
30
|
-
linked_challenges: list[Any] | None = Field(None, alias="LinkedChallenges", exclude=True, repr=False)
|
31
|
-
|
32
|
-
date_created: datetime | None = Field(
|
33
|
-
None,
|
34
|
-
alias="DateCreated",
|
35
|
-
exclude=True,
|
36
|
-
repr=False,
|
37
|
-
description="When the entry was created in database, not useful to users",
|
38
|
-
)
|
39
|
-
date_updated: datetime | None = Field(
|
40
|
-
None,
|
41
|
-
alias="DateUpdated",
|
42
|
-
exclude=True,
|
43
|
-
repr=False,
|
44
|
-
description="When the entry was updated in database, not useful to users",
|
45
|
-
)
|
46
|
-
class_id: int | None = Field(None, alias="ClassId", exclude=True, repr=False, description="Not used by API")
|
47
|
-
substitute_id: int | None = Field(
|
48
|
-
None, alias="SubstituteId", exclude=True, repr=False, description="Not used by API, also always seems to be 0"
|
49
|
-
)
|
50
|
-
|
51
|
-
|
52
|
-
class ChallengeHistory(OtfItemBase):
|
53
|
-
studio_name: str | None = Field(None, alias="StudioName")
|
54
|
-
start_date: datetime | None = Field(None, alias="StartDate")
|
55
|
-
end_date: datetime | None = Field(None, alias="EndDate")
|
56
|
-
total_result: float | str | None = Field(None, alias="TotalResult")
|
57
|
-
is_finished: bool | None = Field(None, alias="IsFinished")
|
58
|
-
benchmark_histories: list[BenchmarkHistory] = Field(default_factory=list, alias="BenchmarkHistories")
|
59
|
-
|
60
|
-
challenge_id: int | None = Field(None, alias="ChallengeId", exclude=True, repr=False, description="Not used by API")
|
61
|
-
studio_id: int | None = Field(None, alias="StudioId", exclude=True, repr=False, description="Not used by API")
|
62
|
-
challenge_objective: str | None = Field(
|
63
|
-
None, alias="ChallengeObjective", exclude=True, repr=False, description="Always the string 'None'"
|
64
|
-
)
|
65
|
-
|
66
|
-
|
67
|
-
class Goal(OtfItemBase):
|
68
|
-
goal: int | None = Field(None, alias="Goal")
|
69
|
-
goal_period: str | None = Field(None, alias="GoalPeriod")
|
70
|
-
overall_goal: int | None = Field(None, alias="OverallGoal")
|
71
|
-
overall_goal_period: str | None = Field(None, alias="OverallGoalPeriod")
|
72
|
-
min_overall: int | None = Field(None, alias="MinOverall")
|
73
|
-
min_overall_period: str | None = Field(None, alias="MinOverallPeriod")
|
74
|
-
|
75
|
-
|
76
|
-
class FitnessBenchmark(OtfItemBase):
|
77
|
-
challenge_category_id: int | None = Field(None, alias="ChallengeCategoryId")
|
78
|
-
challenge_sub_category_id: int | None = Field(None, alias="ChallengeSubCategoryId")
|
79
|
-
equipment_id: EquipmentType = Field(None, alias="EquipmentId")
|
80
|
-
equipment_name: str | None = Field(None, alias="EquipmentName")
|
81
|
-
metric_entry: MetricEntry | None = Field(None, alias="MetricEntry")
|
82
|
-
challenge_name: str | None = Field(None, alias="ChallengeName")
|
83
|
-
best_record: float | str | None = Field(None, alias="BestRecord")
|
84
|
-
last_record: float | str | None = Field(None, alias="LastRecord")
|
85
|
-
previous_record: float | str | None = Field(None, alias="PreviousRecord")
|
86
|
-
unit: str | None = Field(None, alias="Unit")
|
87
|
-
goals: Goal | None = Field(None, alias="Goals")
|
88
|
-
challenge_histories: list[ChallengeHistory] = Field(default_factory=list, alias="ChallengeHistories")
|
otf_api/models/classes.py
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
from datetime import datetime
|
2
|
-
|
3
|
-
from pydantic import AliasPath, Field
|
4
|
-
|
5
|
-
from otf_api.models.base import OtfItemBase
|
6
|
-
from otf_api.models.enums import ClassType, DoW
|
7
|
-
from otf_api.models.studio_detail import StudioDetail
|
8
|
-
|
9
|
-
|
10
|
-
class OtfClass(OtfItemBase):
|
11
|
-
class_uuid: str = Field(alias="ot_base_class_uuid", description="The OTF class UUID")
|
12
|
-
class_id: str | None = Field(None, alias="id", description="Matches new booking endpoint class id")
|
13
|
-
|
14
|
-
name: str | None = Field(None, description="The name of the class")
|
15
|
-
class_type: ClassType = Field(alias="type")
|
16
|
-
coach: str | None = Field(None, validation_alias=AliasPath("coach", "first_name"))
|
17
|
-
ends_at: datetime = Field(
|
18
|
-
alias="ends_at_local",
|
19
|
-
description="The end time of the class. Reflects local time, but the object does not have a timezone.",
|
20
|
-
)
|
21
|
-
starts_at: datetime = Field(
|
22
|
-
alias="starts_at_local",
|
23
|
-
description="The start time of the class. Reflects local time, but the object does not have a timezone.",
|
24
|
-
)
|
25
|
-
studio: StudioDetail
|
26
|
-
|
27
|
-
# capacity/status fields
|
28
|
-
booking_capacity: int | None = None
|
29
|
-
full: bool | None = None
|
30
|
-
max_capacity: int | None = None
|
31
|
-
waitlist_available: bool | None = None
|
32
|
-
waitlist_size: int | None = Field(None, description="The number of people on the waitlist")
|
33
|
-
is_booked: bool | None = Field(None, description="Custom helper field to determine if class is already booked")
|
34
|
-
is_cancelled: bool | None = Field(None, alias="canceled")
|
35
|
-
is_home_studio: bool | None = Field(None, description="Custom helper field to determine if at home studio")
|
36
|
-
|
37
|
-
created_at: datetime | None = Field(None, exclude=True, repr=False)
|
38
|
-
ends_at_utc: datetime | None = Field(None, alias="ends_at", exclude=True, repr=False)
|
39
|
-
mbo_class_description_id: str | None = Field(None, exclude=True, repr=False, description="MindBody attr")
|
40
|
-
mbo_class_id: str | None = Field(None, exclude=True, repr=False, description="MindBody attr")
|
41
|
-
mbo_class_schedule_id: str | None = Field(None, exclude=True, repr=False, description="MindBody attr")
|
42
|
-
starts_at_utc: datetime | None = Field(None, alias="starts_at", exclude=True, repr=False)
|
43
|
-
updated_at: datetime | None = Field(None, exclude=True, repr=False)
|
44
|
-
|
45
|
-
@property
|
46
|
-
def day_of_week(self) -> DoW:
|
47
|
-
dow = self.starts_at.strftime("%A")
|
48
|
-
return DoW(dow)
|
49
|
-
|
50
|
-
def __str__(self) -> str:
|
51
|
-
starts_at_str = self.starts_at.strftime("%a %b %d, %I:%M %p")
|
52
|
-
booked_str = ""
|
53
|
-
if self.is_booked:
|
54
|
-
booked_str = "Booked"
|
55
|
-
elif self.has_availability:
|
56
|
-
booked_str = "Available"
|
57
|
-
elif self.waitlist_available:
|
58
|
-
booked_str = "Waitlist Available"
|
59
|
-
else:
|
60
|
-
booked_str = "Full"
|
61
|
-
return f"Class: {starts_at_str} {self.name} - {self.coach} ({booked_str})"
|
62
|
-
|
63
|
-
@property
|
64
|
-
def has_availability(self) -> bool:
|
65
|
-
return not self.full
|
66
|
-
|
67
|
-
@property
|
68
|
-
def day_of_week_enum(self) -> DoW:
|
69
|
-
dow = self.starts_at.strftime("%A").upper()
|
70
|
-
return DoW(dow)
|
otf_api/models/lifetime_stats.py
DELETED
@@ -1,78 +0,0 @@
|
|
1
|
-
from typing import Generic, TypeVar
|
2
|
-
|
3
|
-
from pydantic import Field
|
4
|
-
|
5
|
-
from otf_api.models.base import OtfItemBase
|
6
|
-
from otf_api.models.enums import StatsTime
|
7
|
-
|
8
|
-
T = TypeVar("T", bound=OtfItemBase)
|
9
|
-
|
10
|
-
|
11
|
-
class OutStudioMixin:
|
12
|
-
walking_distance: float | None = Field(None, alias="walkingDistance")
|
13
|
-
running_distance: float | None = Field(None, alias="runningDistance")
|
14
|
-
cycling_distance: float | None = Field(None, alias="cyclingDistance")
|
15
|
-
|
16
|
-
|
17
|
-
class InStudioMixin:
|
18
|
-
treadmill_distance: float | None = Field(None, alias="treadmillDistance")
|
19
|
-
treadmill_elevation_gained: float | None = Field(None, alias="treadmillElevationGained")
|
20
|
-
rower_distance: float | None = Field(None, alias="rowerDistance")
|
21
|
-
rower_watt: float | None = Field(None, alias="rowerWatt")
|
22
|
-
|
23
|
-
|
24
|
-
class BaseStatsData(OtfItemBase):
|
25
|
-
calories: float | None = None
|
26
|
-
splat_point: float | None = Field(None, alias="splatPoint")
|
27
|
-
total_black_zone: float | None = Field(None, alias="totalBlackZone")
|
28
|
-
total_blue_zone: float | None = Field(None, alias="totalBlueZone")
|
29
|
-
total_green_zone: float | None = Field(None, alias="totalGreenZone")
|
30
|
-
total_orange_zone: float | None = Field(None, alias="totalOrangeZone")
|
31
|
-
total_red_zone: float | None = Field(None, alias="totalRedZone")
|
32
|
-
workout_duration: float | None = Field(None, alias="workoutDuration")
|
33
|
-
step_count: float | None = Field(None, alias="stepCount")
|
34
|
-
|
35
|
-
|
36
|
-
class InStudioStatsData(InStudioMixin, BaseStatsData):
|
37
|
-
pass
|
38
|
-
|
39
|
-
|
40
|
-
class OutStudioStatsData(OutStudioMixin, BaseStatsData):
|
41
|
-
pass
|
42
|
-
|
43
|
-
|
44
|
-
class AllStatsData(OutStudioMixin, InStudioMixin, BaseStatsData):
|
45
|
-
pass
|
46
|
-
|
47
|
-
|
48
|
-
class TimeStats(OtfItemBase, Generic[T]):
|
49
|
-
last_year: T = Field(..., alias="lastYear")
|
50
|
-
this_year: T = Field(..., alias="thisYear")
|
51
|
-
last_month: T = Field(..., alias="lastMonth")
|
52
|
-
this_month: T = Field(..., alias="thisMonth")
|
53
|
-
last_week: T = Field(..., alias="lastWeek")
|
54
|
-
this_week: T = Field(..., alias="thisWeek")
|
55
|
-
all_time: T = Field(..., alias="allTime")
|
56
|
-
|
57
|
-
def get_by_time(self, stats_time: StatsTime) -> T:
|
58
|
-
match stats_time:
|
59
|
-
case StatsTime.LastYear:
|
60
|
-
return self.last_year
|
61
|
-
case StatsTime.ThisYear:
|
62
|
-
return self.this_year
|
63
|
-
case StatsTime.LastMonth:
|
64
|
-
return self.last_month
|
65
|
-
case StatsTime.ThisMonth:
|
66
|
-
return self.this_month
|
67
|
-
case StatsTime.LastWeek:
|
68
|
-
return self.last_week
|
69
|
-
case StatsTime.ThisWeek:
|
70
|
-
return self.this_week
|
71
|
-
case StatsTime.AllTime:
|
72
|
-
return self.all_time
|
73
|
-
|
74
|
-
|
75
|
-
class StatsResponse(OtfItemBase):
|
76
|
-
all_stats: TimeStats[AllStatsData] = Field(..., alias="allStats")
|
77
|
-
in_studio: TimeStats[InStudioStatsData] = Field(..., alias="inStudio")
|
78
|
-
out_studio: TimeStats[OutStudioStatsData] = Field(..., alias="outStudio")
|
otf_api/models/member_detail.py
DELETED
@@ -1,121 +0,0 @@
|
|
1
|
-
from datetime import date, datetime
|
2
|
-
|
3
|
-
from pydantic import Field, field_validator
|
4
|
-
|
5
|
-
from otf_api.models.base import OtfItemBase
|
6
|
-
from otf_api.models.mixins import AddressMixin
|
7
|
-
from otf_api.models.studio_detail import StudioDetail
|
8
|
-
|
9
|
-
|
10
|
-
class Address(AddressMixin, OtfItemBase):
|
11
|
-
member_address_uuid: str | None = Field(None, alias="memberAddressUUId", exclude=True, repr=False)
|
12
|
-
type: str | None = None
|
13
|
-
|
14
|
-
|
15
|
-
class MemberProfile(OtfItemBase):
|
16
|
-
unit_of_measure: str | None = Field(None, alias="unitOfMeasure")
|
17
|
-
max_hr_type: str | None = Field(None, alias="maxHrType")
|
18
|
-
manual_max_hr: int | None = Field(None, alias="manualMaxHr")
|
19
|
-
formula_max_hr: int | None = Field(None, alias="formulaMaxHr")
|
20
|
-
automated_hr: int | None = Field(None, alias="automatedHr")
|
21
|
-
|
22
|
-
member_profile_uuid: str | None = Field(None, alias="memberProfileUUId", exclude=True, repr=False)
|
23
|
-
member_optin_flow_type_id: int | None = Field(None, alias="memberOptinFlowTypeId", exclude=True, repr=False)
|
24
|
-
|
25
|
-
|
26
|
-
class MemberClassSummary(OtfItemBase):
|
27
|
-
total_classes_booked: int | None = Field(None, alias="totalClassesBooked")
|
28
|
-
total_classes_attended: int | None = Field(None, alias="totalClassesAttended")
|
29
|
-
total_intro_classes: int | None = Field(None, alias="totalIntro")
|
30
|
-
total_ot_live_classes_booked: int | None = Field(None, alias="totalOTLiveClassesBooked")
|
31
|
-
total_ot_live_classes_attended: int | None = Field(None, alias="totalOTLiveClassesAttended")
|
32
|
-
total_classes_used_hrm: int | None = Field(None, alias="totalClassesUsedHRM")
|
33
|
-
total_studios_visited: int | None = Field(None, alias="totalStudiosVisited")
|
34
|
-
first_visit_date: date | None = Field(None, alias="firstVisitDate")
|
35
|
-
last_class_visited_date: date | None = Field(None, alias="lastClassVisitedDate")
|
36
|
-
last_class_booked_date: date | None = Field(None, alias="lastClassBookedDate")
|
37
|
-
|
38
|
-
last_class_studio_visited: int | None = Field(None, alias="lastClassStudioVisited", exclude=True, repr=False)
|
39
|
-
|
40
|
-
|
41
|
-
class MemberDetail(OtfItemBase):
|
42
|
-
member_uuid: str = Field(..., alias="memberUUId")
|
43
|
-
cognito_id: str = Field(
|
44
|
-
...,
|
45
|
-
alias="cognitoId",
|
46
|
-
exclude=True,
|
47
|
-
repr=False,
|
48
|
-
description="Cognito user ID, not necessary for end users. Also on OtfUser object.",
|
49
|
-
)
|
50
|
-
|
51
|
-
home_studio: StudioDetail
|
52
|
-
profile: MemberProfile = Field(..., alias="memberProfile")
|
53
|
-
class_summary: MemberClassSummary | None = Field(None, alias="memberClassSummary")
|
54
|
-
addresses: list[Address] | None = Field(default_factory=list)
|
55
|
-
|
56
|
-
studio_display_name: str | None = Field(
|
57
|
-
None, alias="userName", description="The value that is displayed on tread/rower tablets and OTBeat screens"
|
58
|
-
)
|
59
|
-
first_name: str | None = Field(None, alias="firstName")
|
60
|
-
last_name: str | None = Field(None, alias="lastName")
|
61
|
-
email: str | None = Field(None, alias="email")
|
62
|
-
phone_number: str | None = Field(None, alias="phoneNumber")
|
63
|
-
birth_day: date | None = Field(None, alias="birthDay")
|
64
|
-
gender: str | None = Field(None, alias="gender")
|
65
|
-
locale: str | None = Field(None, alias="locale")
|
66
|
-
weight: int | None = Field(None, alias="weight")
|
67
|
-
weight_units: str | None = Field(None, alias="weightMeasure")
|
68
|
-
height: int | None = Field(None, alias="height")
|
69
|
-
height_units: str | None = Field(None, alias="heightMeasure")
|
70
|
-
|
71
|
-
# unused fields - leaving these in for now in case someone finds a purpose for them
|
72
|
-
# but they will potentially (likely?) be removed in the future
|
73
|
-
|
74
|
-
# mbo fields
|
75
|
-
mbo_id: str | None = Field(None, alias="mboId", exclude=True, repr=False, description="MindBody attr")
|
76
|
-
mbo_status: str | None = Field(None, alias="mboStatus", exclude=True, repr=False, description="MindBody attr")
|
77
|
-
mbo_studio_id: int | None = Field(None, alias="mboStudioId", exclude=True, repr=False, description="MindBody attr")
|
78
|
-
mbo_unique_id: int | None = Field(None, alias="mboUniqueId", exclude=True, repr=False, description="MindBody attr")
|
79
|
-
|
80
|
-
# ids
|
81
|
-
created_by: str | None = Field(None, alias="createdBy", exclude=True, repr=False)
|
82
|
-
home_studio_id: int | None = Field(
|
83
|
-
None, alias="homeStudioId", exclude=True, repr=False, description="Not used by API"
|
84
|
-
)
|
85
|
-
member_id: int | None = Field(None, alias="memberId", exclude=True, repr=False, description="Not used by API")
|
86
|
-
otf_acs_id: str | None = Field(None, alias="otfAcsId", exclude=True, repr=False)
|
87
|
-
updated_by: str | None = Field(None, alias="updatedBy", exclude=True, repr=False)
|
88
|
-
|
89
|
-
# unused address/member detail fields
|
90
|
-
created_date: datetime | None = Field(None, alias="createdDate", exclude=True, repr=False)
|
91
|
-
updated_date: datetime | None = Field(None, alias="updatedDate", exclude=True, repr=False)
|
92
|
-
|
93
|
-
address_line1: str | None = Field(None, alias="addressLine1", exclude=True, repr=False)
|
94
|
-
address_line2: str | None = Field(None, alias="addressLine2", exclude=True, repr=False)
|
95
|
-
alternate_emails: None = Field(None, alias="alternateEmails", exclude=True, repr=False)
|
96
|
-
cc_last4: str | None = Field(None, alias="ccLast4", exclude=True, repr=False)
|
97
|
-
cc_type: str | None = Field(None, alias="ccType", exclude=True, repr=False)
|
98
|
-
city: str | None = Field(None, exclude=True, repr=False)
|
99
|
-
home_phone: str | None = Field(None, alias="homePhone", exclude=True, repr=False)
|
100
|
-
intro_neccessary: bool | None = Field(None, alias="introNeccessary", exclude=True, repr=False)
|
101
|
-
is_deleted: bool | None = Field(None, alias="isDeleted", exclude=True, repr=False)
|
102
|
-
is_member_verified: bool | None = Field(None, alias="isMemberVerified", exclude=True, repr=False)
|
103
|
-
lead_prospect: bool | None = Field(None, alias="leadProspect", exclude=True, repr=False)
|
104
|
-
max_hr: int | None = Field(
|
105
|
-
None, alias="maxHr", exclude=True, repr=False, description="Also found in member_profile"
|
106
|
-
)
|
107
|
-
online_signup: None = Field(None, alias="onlineSignup", exclude=True, repr=False)
|
108
|
-
phone_type: None = Field(None, alias="phoneType", exclude=True, repr=False)
|
109
|
-
postal_code: str | None = Field(None, alias="postalCode", exclude=True, repr=False)
|
110
|
-
state: str | None = Field(None, exclude=True, repr=False)
|
111
|
-
work_phone: str | None = Field(None, alias="workPhone", exclude=True, repr=False)
|
112
|
-
year_imported: int | None = Field(None, alias="yearImported", exclude=True, repr=False)
|
113
|
-
|
114
|
-
@field_validator("birth_day")
|
115
|
-
@classmethod
|
116
|
-
def validate_birth_day(cls, value: date | str | None, **_kwargs) -> date | None:
|
117
|
-
if value is None:
|
118
|
-
return value
|
119
|
-
if not isinstance(value, date):
|
120
|
-
return datetime.strptime(value, "%Y-%m-%d").date()
|
121
|
-
return value
|
@@ -1,26 +0,0 @@
|
|
1
|
-
from datetime import datetime
|
2
|
-
|
3
|
-
from pydantic import Field
|
4
|
-
|
5
|
-
from otf_api.models.base import OtfItemBase
|
6
|
-
|
7
|
-
|
8
|
-
class MemberMembership(OtfItemBase):
|
9
|
-
payment_date: datetime | None = Field(None, alias="paymentDate")
|
10
|
-
active_date: datetime | None = Field(None, alias="activeDate")
|
11
|
-
expiration_date: datetime | None = Field(None, alias="expirationDate")
|
12
|
-
current: bool | None = None
|
13
|
-
count: int | None = None
|
14
|
-
remaining: int | None = None
|
15
|
-
name: str | None = None
|
16
|
-
updated_date: datetime | None = Field(None, alias="updatedDate")
|
17
|
-
created_date: datetime | None = Field(None, alias="createdDate")
|
18
|
-
is_deleted: bool | None = Field(None, alias="isDeleted")
|
19
|
-
|
20
|
-
member_membership_id: int | None = Field(None, alias="memberMembershipId", exclude=True, repr=False)
|
21
|
-
member_membership_uuid: str | None = Field(None, alias="memberMembershipUUId", exclude=True, repr=False)
|
22
|
-
membership_id: int | None = Field(None, alias="membershipId", exclude=True, repr=False)
|
23
|
-
member_id: int | None = Field(None, alias="memberId", exclude=True, repr=False)
|
24
|
-
mbo_description_id: str | None = Field(None, alias="mboDescriptionId", exclude=True, repr=False)
|
25
|
-
created_by: str | None = Field(None, alias="createdBy", exclude=True, repr=False)
|
26
|
-
updated_by: str | None = Field(None, alias="updatedBy", exclude=True, repr=False)
|
@@ -1,29 +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 MemberPurchase(OtfItemBase):
|
10
|
-
purchase_uuid: str = Field(..., alias="memberPurchaseUUId")
|
11
|
-
name: str | None = None
|
12
|
-
price: str | None = None
|
13
|
-
purchase_date_time: datetime | None = Field(None, alias="memberPurchaseDateTime")
|
14
|
-
purchase_type: str | None = Field(None, alias="memberPurchaseType")
|
15
|
-
status: str | None = None
|
16
|
-
quantity: int | None = None
|
17
|
-
studio: StudioDetail = Field(..., exclude=True, repr=False)
|
18
|
-
|
19
|
-
member_fee_id: int | None = Field(None, alias="memberFeeId", exclude=True, repr=False)
|
20
|
-
member_id: int | None = Field(..., alias="memberId", exclude=True, repr=False)
|
21
|
-
member_membership_id: int | None = Field(None, alias="memberMembershipId", exclude=True, repr=False)
|
22
|
-
member_purchase_id: int | None = Field(..., alias="memberPurchaseId", exclude=True, repr=False)
|
23
|
-
member_service_id: int | None = Field(None, alias="memberServiceId", exclude=True, repr=False)
|
24
|
-
pos_contract_id: int | None = Field(None, alias="posContractId", exclude=True, repr=False)
|
25
|
-
pos_description_id: int | None = Field(None, alias="posDescriptionId", exclude=True, repr=False)
|
26
|
-
pos_pmt_ref_no: int | None = Field(None, alias="posPmtRefNo", exclude=True, repr=False)
|
27
|
-
pos_product_id: int | None = Field(..., alias="posProductId", exclude=True, repr=False)
|
28
|
-
pos_sale_id: int | None = Field(..., alias="posSaleId", exclude=True, repr=False)
|
29
|
-
studio_id: int | None = Field(..., alias="studioId", exclude=True, repr=False)
|
otf_api/models/notifications.py
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
from pydantic import Field
|
2
|
-
|
3
|
-
from otf_api.models.base import OtfItemBase
|
4
|
-
|
5
|
-
|
6
|
-
class SmsNotificationSettings(OtfItemBase):
|
7
|
-
is_promotional_sms_opt_in: bool | None = Field(None, alias="isPromotionalSmsOptIn")
|
8
|
-
is_transactional_sms_opt_in: bool | None = Field(None, alias="isTransactionalSmsOptIn")
|
9
|
-
is_promotional_phone_opt_in: bool | None = Field(None, alias="isPromotionalPhoneOptIn")
|
10
|
-
is_transactional_phone_opt_in: bool | None = Field(None, alias="isTransactionalPhoneOptIn")
|
11
|
-
|
12
|
-
|
13
|
-
class EmailNotificationSettings(OtfItemBase):
|
14
|
-
is_system_email_opt_in: bool | None = Field(None, alias="isSystemEmailOptIn")
|
15
|
-
is_promotional_email_opt_in: bool | None = Field(None, alias="isPromotionalEmailOptIn")
|
16
|
-
is_transactional_email_opt_in: bool | None = Field(None, alias="isTransactionalEmailOptIn")
|
17
|
-
email: str | None = None
|
@@ -1,32 +0,0 @@
|
|
1
|
-
from datetime import datetime
|
2
|
-
|
3
|
-
from pydantic import AliasPath, Field
|
4
|
-
|
5
|
-
from otf_api.models.base import OtfItemBase
|
6
|
-
|
7
|
-
|
8
|
-
class OutOfStudioWorkoutHistory(OtfItemBase):
|
9
|
-
member_uuid: str = Field(..., alias="memberUUId")
|
10
|
-
workout_uuid: str = Field(..., alias="workoutUUId")
|
11
|
-
|
12
|
-
workout_date: datetime | None = Field(None, alias="workoutDate")
|
13
|
-
start_time: datetime | None = Field(None, alias="startTime")
|
14
|
-
end_time: datetime | None = Field(None, alias="endTime")
|
15
|
-
duration: float | None = None
|
16
|
-
duration_unit: str | None = Field(None, alias="durationUnit")
|
17
|
-
total_calories: int | None = Field(None, alias="totalCalories")
|
18
|
-
hr_percent_max: int | None = Field(None, alias="hrPercentMax")
|
19
|
-
distance_unit: str | None = Field(None, alias="distanceUnit")
|
20
|
-
total_distance: float | None = Field(None, alias="totalDistance")
|
21
|
-
splat_points: int | None = Field(None, alias="splatPoints")
|
22
|
-
target_heart_rate: int | None = Field(None, alias="targetHeartRate")
|
23
|
-
total_steps: int | None = Field(None, alias="totalSteps")
|
24
|
-
has_detailed_data: bool | None = Field(None, alias="hasDetailedData")
|
25
|
-
avg_heartrate: int | None = Field(None, alias="avgHeartrate")
|
26
|
-
max_heartrate: int | None = Field(None, alias="maxHeartrate")
|
27
|
-
workout_type: str | None = Field(None, validation_alias=AliasPath("workoutType", "displayName"))
|
28
|
-
red_zone_seconds: int | None = Field(None, alias="redZoneSeconds")
|
29
|
-
orange_zone_seconds: int | None = Field(None, alias="orangeZoneSeconds")
|
30
|
-
green_zone_seconds: int | None = Field(None, alias="greenZoneSeconds")
|
31
|
-
blue_zone_seconds: int | None = Field(None, alias="blueZoneSeconds")
|
32
|
-
grey_zone_seconds: int | None = Field(None, alias="greyZoneSeconds")
|