From c0860b8cccb775a64accebb472593dd99b532d7e Mon Sep 17 00:00:00 2001 From: Jessica Smith <12jessicasmith34@gmail.com> Date: Tue, 21 Jan 2025 17:07:39 -0600 Subject: [PATCH 1/2] update notification endpoints and add models --- README.md | 2 +- src/otf_api/api.py | 96 ++++++++++++++++++++++++++--- src/otf_api/models/__init__.py | 3 + src/otf_api/models/notifications.py | 17 +++++ 4 files changed, 109 insertions(+), 9 deletions(-) create mode 100644 src/otf_api/models/notifications.py diff --git a/README.md b/README.md index 2376294..c8769bd 100644 --- a/README.md +++ b/README.md @@ -27,4 +27,4 @@ otf = Otf() otf = Otf(user=OtfUser(,)) -``` \ No newline at end of file +``` diff --git a/src/otf_api/api.py b/src/otf_api/api.py index 5a93d18..b36cc6e 100644 --- a/src/otf_api/api.py +++ b/src/otf_api/api.py @@ -1007,14 +1007,58 @@ def get_telemetry(self, performance_summary_id: str, max_data_points: int = 120) res = self._telemetry_request("GET", path, params=params) return models.Telemetry(**res) - def get_sms_notification_settings(self): + def get_sms_notification_settings(self) -> models.SmsNotificationSettings: + """Get the member's SMS notification settings. + + Returns: + SmsNotificationSettings: The member's SMS notification settings. + """ res = self._default_request("GET", url="/sms/v1/preferences", params={"phoneNumber": self.member.phone_number}) - return res["data"] + return models.SmsNotificationSettings(**res["data"]) + + def update_sms_notification_settings( + self, promotional_enabled: bool | None = None, transactional_enabled: bool | None = None + ) -> models.SmsNotificationSettings: + """Update the member's SMS notification settings. Arguments not provided will be left unchanged. + + Args: + promotional_enabled (bool | None): Whether to enable promotional SMS notifications. + transactional_enabled (bool | None): Whether to enable transactional SMS notifications. + + Returns: + SmsNotificationSettings: The updated SMS notification settings. - def update_sms_notification_settings(self, promotional_enabled: bool, transactional_enabled: bool): + Warning: + --- + This endpoint seems to accept almost anything, converting values to truthy/falsey and + updating the settings accordingly. The one error I've gotten is with -1 + + ``` + ERROR - Response: + { + "code": "ER_WARN_DATA_OUT_OF_RANGE", + "message": "An unexpected server error occurred, please try again.", + "details": [ + { + "message": "ER_WARN_DATA_OUT_OF_RANGE: Out of range value for column 'IsPromotionalSMSOptIn' at row 1", + "additionalInfo": "" + } + ] + } + ``` + """ url = "/sms/v1/preferences" + current_settings = self.get_sms_notification_settings() + + promotional_enabled = ( + promotional_enabled if promotional_enabled is not None else current_settings.is_promotional_sms_opt_in + ) + transactional_enabled = ( + transactional_enabled if transactional_enabled is not None else current_settings.is_transactional_sms_opt_in + ) + body = { "promosms": promotional_enabled, "source": "OTF", @@ -1022,11 +1066,45 @@ def update_sms_notification_settings(self, promotional_enabled: bool, transactio "phoneNumber": self.member.phone_number, } - res = self._default_request("POST", url, json=body) + self._default_request("POST", url, json=body) - return res["data"] + # the response returns nothing useful, so we just query the settings again + new_settings = self.get_sms_notification_settings() + return new_settings + + def get_email_notification_settings(self) -> models.EmailNotificationSettings: + """Get the member's email notification settings. + + Returns: + EmailNotificationSettings: The member's email notification settings. + """ + res = self._default_request("GET", url="/otfmailing/v2/preferences", params={"email": self.member.email}) + + return models.EmailNotificationSettings(**res["data"]) + + def update_email_notification_settings( + self, promotional_enabled: bool | None = None, transactional_enabled: bool | None = None + ) -> models.EmailNotificationSettings: + """Update the member's email notification settings. Arguments not provided will be left unchanged. + + Args: + promotional_enabled (bool | None): Whether to enable promotional email notifications. + transactional_enabled (bool | None): Whether to enable transactional email notifications. + + Returns: + EmailNotificationSettings: The updated email notification settings. + """ + current_settings = self.get_email_notification_settings() + + promotional_enabled = ( + promotional_enabled if promotional_enabled is not None else current_settings.is_promotional_email_opt_in + ) + transactional_enabled = ( + transactional_enabled + if transactional_enabled is not None + else current_settings.is_transactional_email_opt_in + ) - def update_email_notification_settings(self, promotional_enabled: bool, transactional_enabled: bool): body = { "promotionalEmail": promotional_enabled, "source": "OTF", @@ -1034,9 +1112,11 @@ def update_email_notification_settings(self, promotional_enabled: bool, transact "email": self.member.email, } - res = self._default_request("POST", "/otfmailing/v2/preferences", json=body) + self._default_request("POST", "/otfmailing/v2/preferences", json=body) - return res["data"] + # the response returns nothing useful, so we just query the settings again + new_settings = self.get_email_notification_settings() + return new_settings def update_member_name(self, first_name: str | None = None, last_name: str | None = None) -> models.MemberDetail: """Update the member's name. Will return the original member details if no names are provided. diff --git a/src/otf_api/models/__init__.py b/src/otf_api/models/__init__.py index 09b07ec..eb3ebee 100644 --- a/src/otf_api/models/__init__.py +++ b/src/otf_api/models/__init__.py @@ -8,6 +8,7 @@ from .member_detail import MemberDetail from .member_membership import MemberMembership from .member_purchases import MemberPurchase +from .notifications import EmailNotificationSettings, SmsNotificationSettings from .out_of_studio_workout_history import OutOfStudioWorkoutHistory from .performance_summary_detail import PerformanceSummaryDetail from .performance_summary_list import PerformanceSummaryEntry @@ -24,6 +25,7 @@ "ChallengeTracker", "ClassType", "DoW", + "EmailNotificationSettings", "EquipmentType", "FitnessBenchmark", "LatestAgreement", @@ -34,6 +36,7 @@ "OutOfStudioWorkoutHistory", "PerformanceSummaryDetail", "PerformanceSummaryEntry", + "SmsNotificationSettings", "StatsResponse", "StatsTime", "StudioDetail", diff --git a/src/otf_api/models/notifications.py b/src/otf_api/models/notifications.py new file mode 100644 index 0000000..f5f4d89 --- /dev/null +++ b/src/otf_api/models/notifications.py @@ -0,0 +1,17 @@ +from pydantic import Field + +from otf_api.models.base import OtfItemBase + + +class SmsNotificationSettings(OtfItemBase): + is_promotional_sms_opt_in: bool | None = Field(None, alias="isPromotionalSmsOptIn") + is_transactional_sms_opt_in: bool | None = Field(None, alias="isTransactionalSmsOptIn") + is_promotional_phone_opt_in: bool | None = Field(None, alias="isPromotionalPhoneOptIn") + is_transactional_phone_opt_in: bool | None = Field(None, alias="isTransactionalPhoneOptIn") + + +class EmailNotificationSettings(OtfItemBase): + is_system_email_opt_in: bool | None = Field(None, alias="isSystemEmailOptIn") + is_promotional_email_opt_in: bool | None = Field(None, alias="isPromotionalEmailOptIn") + is_transactional_email_opt_in: bool | None = Field(None, alias="isTransactionalEmailOptIn") + email: str | None = None From 61ba22b1e535b461f9fc6a7f6ca37155a507bbed Mon Sep 17 00:00:00 2001 From: Jessica Smith <12jessicasmith34@gmail.com> Date: Tue, 21 Jan 2025 17:08:10 -0600 Subject: [PATCH 2/2] bump --- .bumpversion.toml | 2 +- pyproject.toml | 2 +- src/otf_api/__init__.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.bumpversion.toml b/.bumpversion.toml index 73a0916..9b16d9b 100644 --- a/.bumpversion.toml +++ b/.bumpversion.toml @@ -1,5 +1,5 @@ [tool.bumpversion] -current_version = "0.9.0" +current_version = "0.9.1" parse = "(?P\\d+)\\.(?P\\d+)\\.(?P\\d+)(?:-(?Pdev)(?P0|[1-9]\\d*))?" diff --git a/pyproject.toml b/pyproject.toml index 660bcb4..44d7d9c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "otf-api" -version = "0.9.0" +version = "0.9.1" description = "Python OrangeTheory Fitness API Client" authors = ["Jessica Smith "] license = "MIT" diff --git a/src/otf_api/__init__.py b/src/otf_api/__init__.py index 22adf23..fcca50f 100644 --- a/src/otf_api/__init__.py +++ b/src/otf_api/__init__.py @@ -4,7 +4,7 @@ from otf_api import models from otf_api.auth import OtfUser -__version__ = "0.9.0" +__version__ = "0.9.1" __all__ = ["Otf", "OtfUser", "models"]