Skip to content

Commit

Permalink
Merge pull request #72 from NodeJSmith/feature/update_notification_se…
Browse files Browse the repository at this point in the history
…ttings_endpoints_and_models

Feature/update notification settings endpoints and models
  • Loading branch information
NodeJSmith authored Jan 21, 2025
2 parents 4680f05 + 61ba22b commit 7da1d58
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 12 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tool.bumpversion]
current_version = "0.9.0"
current_version = "0.9.1"

parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(?:-(?P<dev_l>dev)(?P<dev>0|[1-9]\\d*))?"

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ otf = Otf()

otf = Otf(user=OtfUser(<email_address>,<password>))

```
```
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -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 <j.smith.git1@gmail.com>"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion src/otf_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
96 changes: 88 additions & 8 deletions src/otf_api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1007,36 +1007,116 @@ 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",
"transactionalsms": transactional_enabled,
"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",
"transactionalEmail": transactional_enabled,
"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.
Expand Down
3 changes: 3 additions & 0 deletions src/otf_api/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -24,6 +25,7 @@
"ChallengeTracker",
"ClassType",
"DoW",
"EmailNotificationSettings",
"EquipmentType",
"FitnessBenchmark",
"LatestAgreement",
Expand All @@ -34,6 +36,7 @@
"OutOfStudioWorkoutHistory",
"PerformanceSummaryDetail",
"PerformanceSummaryEntry",
"SmsNotificationSettings",
"StatsResponse",
"StatsTime",
"StudioDetail",
Expand Down
17 changes: 17 additions & 0 deletions src/otf_api/models/notifications.py
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 7da1d58

Please sign in to comment.