otf-api 0.12.1__tar.gz → 0.13.1__tar.gz
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-0.12.1/src/otf_api.egg-info → otf_api-0.13.1}/PKG-INFO +4 -2
- {otf_api-0.12.1 → otf_api-0.13.1}/pyproject.toml +17 -19
- otf_api-0.13.1/src/otf_api/__init__.py +42 -0
- otf_api-0.13.1/src/otf_api/api/__init__.py +3 -0
- otf_api-0.13.1/src/otf_api/api/_compat.py +77 -0
- otf_api-0.13.1/src/otf_api/api/api.py +80 -0
- otf_api-0.13.1/src/otf_api/api/bookings/__init__.py +3 -0
- otf_api-0.13.1/src/otf_api/api/bookings/booking_api.py +541 -0
- otf_api-0.13.1/src/otf_api/api/bookings/booking_client.py +112 -0
- otf_api-0.13.1/src/otf_api/api/client.py +203 -0
- otf_api-0.13.1/src/otf_api/api/members/__init__.py +3 -0
- otf_api-0.13.1/src/otf_api/api/members/member_api.py +187 -0
- otf_api-0.13.1/src/otf_api/api/members/member_client.py +112 -0
- otf_api-0.13.1/src/otf_api/api/studios/__init__.py +3 -0
- otf_api-0.13.1/src/otf_api/api/studios/studio_api.py +174 -0
- otf_api-0.13.1/src/otf_api/api/studios/studio_client.py +120 -0
- otf_api-0.13.1/src/otf_api/api/utils.py +307 -0
- otf_api-0.13.1/src/otf_api/api/workouts/__init__.py +3 -0
- otf_api-0.13.1/src/otf_api/api/workouts/workout_api.py +333 -0
- otf_api-0.13.1/src/otf_api/api/workouts/workout_client.py +140 -0
- otf_api-0.13.1/src/otf_api/auth/__init__.py +4 -0
- {otf_api-0.12.1 → otf_api-0.13.1}/src/otf_api/auth/auth.py +155 -89
- {otf_api-0.12.1 → otf_api-0.13.1}/src/otf_api/auth/user.py +5 -17
- {otf_api-0.12.1 → otf_api-0.13.1}/src/otf_api/auth/utils.py +27 -2
- otf_api-0.13.1/src/otf_api/cache.py +132 -0
- {otf_api-0.12.1 → otf_api-0.13.1}/src/otf_api/exceptions.py +18 -6
- otf_api-0.13.1/src/otf_api/models/__init__.py +68 -0
- otf_api-0.13.1/src/otf_api/models/bookings/__init__.py +23 -0
- otf_api-0.13.1/src/otf_api/models/bookings/bookings.py +134 -0
- {otf_api-0.12.1/src/otf_api/models → otf_api-0.13.1/src/otf_api/models/bookings}/bookings_v2.py +72 -31
- otf_api-0.13.1/src/otf_api/models/bookings/classes.py +124 -0
- {otf_api-0.12.1/src/otf_api/models → otf_api-0.13.1/src/otf_api/models/bookings}/enums.py +7 -81
- {otf_api-0.12.1/src/otf_api → otf_api-0.13.1/src/otf_api/models/bookings}/filters.py +39 -11
- {otf_api-0.12.1/src/otf_api/models → otf_api-0.13.1/src/otf_api/models/bookings}/ratings.py +2 -6
- otf_api-0.13.1/src/otf_api/models/members/__init__.py +5 -0
- otf_api-0.13.1/src/otf_api/models/members/member_detail.py +149 -0
- otf_api-0.13.1/src/otf_api/models/members/member_membership.py +26 -0
- otf_api-0.13.1/src/otf_api/models/members/member_purchases.py +29 -0
- otf_api-0.13.1/src/otf_api/models/members/notifications.py +17 -0
- {otf_api-0.12.1 → otf_api-0.13.1}/src/otf_api/models/mixins.py +48 -1
- otf_api-0.13.1/src/otf_api/models/studios/__init__.py +5 -0
- otf_api-0.13.1/src/otf_api/models/studios/enums.py +11 -0
- otf_api-0.13.1/src/otf_api/models/studios/studio_detail.py +93 -0
- otf_api-0.13.1/src/otf_api/models/studios/studio_services.py +36 -0
- otf_api-0.13.1/src/otf_api/models/workouts/__init__.py +31 -0
- {otf_api-0.12.1/src/otf_api/models → otf_api-0.13.1/src/otf_api/models/workouts}/body_composition_list.py +140 -71
- otf_api-0.13.1/src/otf_api/models/workouts/challenge_tracker_content.py +50 -0
- otf_api-0.13.1/src/otf_api/models/workouts/challenge_tracker_detail.py +99 -0
- otf_api-0.13.1/src/otf_api/models/workouts/enums.py +70 -0
- otf_api-0.13.1/src/otf_api/models/workouts/lifetime_stats.py +96 -0
- otf_api-0.13.1/src/otf_api/models/workouts/out_of_studio_workout_history.py +32 -0
- {otf_api-0.12.1/src/otf_api/models → otf_api-0.13.1/src/otf_api/models/workouts}/performance_summary.py +19 -5
- otf_api-0.13.1/src/otf_api/models/workouts/telemetry.py +88 -0
- {otf_api-0.12.1/src/otf_api/models → otf_api-0.13.1/src/otf_api/models/workouts}/workout.py +34 -20
- {otf_api-0.12.1 → otf_api-0.13.1/src/otf_api.egg-info}/PKG-INFO +4 -2
- otf_api-0.13.1/src/otf_api.egg-info/SOURCES.txt +63 -0
- {otf_api-0.12.1 → otf_api-0.13.1}/src/otf_api.egg-info/requires.txt +3 -0
- {otf_api-0.12.1 → otf_api-0.13.1}/tests/test_filters.py +1 -2
- otf_api-0.12.1/src/otf_api/__init__.py +0 -10
- otf_api-0.12.1/src/otf_api/api.py +0 -1682
- otf_api-0.12.1/src/otf_api/auth/__init__.py +0 -4
- otf_api-0.12.1/src/otf_api/logging.py +0 -19
- otf_api-0.12.1/src/otf_api/models/__init__.py +0 -64
- otf_api-0.12.1/src/otf_api/models/bookings.py +0 -109
- otf_api-0.12.1/src/otf_api/models/challenge_tracker_content.py +0 -59
- otf_api-0.12.1/src/otf_api/models/challenge_tracker_detail.py +0 -88
- otf_api-0.12.1/src/otf_api/models/classes.py +0 -70
- otf_api-0.12.1/src/otf_api/models/lifetime_stats.py +0 -78
- otf_api-0.12.1/src/otf_api/models/member_detail.py +0 -121
- otf_api-0.12.1/src/otf_api/models/member_membership.py +0 -26
- otf_api-0.12.1/src/otf_api/models/member_purchases.py +0 -29
- otf_api-0.12.1/src/otf_api/models/notifications.py +0 -17
- otf_api-0.12.1/src/otf_api/models/out_of_studio_workout_history.py +0 -32
- otf_api-0.12.1/src/otf_api/models/studio_detail.py +0 -71
- otf_api-0.12.1/src/otf_api/models/studio_services.py +0 -36
- otf_api-0.12.1/src/otf_api/models/telemetry.py +0 -84
- otf_api-0.12.1/src/otf_api/utils.py +0 -164
- otf_api-0.12.1/src/otf_api.egg-info/SOURCES.txt +0 -42
- {otf_api-0.12.1 → otf_api-0.13.1}/LICENSE +0 -0
- {otf_api-0.12.1 → otf_api-0.13.1}/README.md +0 -0
- {otf_api-0.12.1 → otf_api-0.13.1}/setup.cfg +0 -0
- {otf_api-0.12.1 → otf_api-0.13.1}/src/otf_api/models/base.py +0 -0
- {otf_api-0.12.1 → otf_api-0.13.1}/src/otf_api/py.typed +0 -0
- {otf_api-0.12.1 → otf_api-0.13.1}/src/otf_api.egg-info/dependency_links.txt +0 -0
- {otf_api-0.12.1 → otf_api-0.13.1}/src/otf_api.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: otf-api
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.13.1
|
4
4
|
Summary: Python OrangeTheory Fitness API Client
|
5
5
|
Author-email: Jessica Smith <j.smith.git1@gmail.com>
|
6
6
|
License-Expression: MIT
|
@@ -9,7 +9,6 @@ Classifier: Development Status :: 4 - Beta
|
|
9
9
|
Classifier: Intended Audience :: Developers
|
10
10
|
Classifier: Programming Language :: Python :: 3.11
|
11
11
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
12
|
-
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
13
12
|
Classifier: Topic :: Software Development :: Libraries
|
14
13
|
Classifier: Topic :: Internet :: WWW/HTTP
|
15
14
|
Classifier: Operating System :: OS Independent
|
@@ -27,6 +26,9 @@ Requires-Dist: yarl<2,>=1.18.3
|
|
27
26
|
Requires-Dist: tenacity<10,>=9.0.0
|
28
27
|
Requires-Dist: cachetools>=5.5.0
|
29
28
|
Requires-Dist: pendulum>=3.1.0
|
29
|
+
Requires-Dist: diskcache>=5.6.3
|
30
|
+
Requires-Dist: platformdirs>=4.3.6
|
31
|
+
Requires-Dist: packaging>=24.2
|
30
32
|
Dynamic: license-file
|
31
33
|
|
32
34
|
Simple API client for interacting with the OrangeTheory Fitness APIs.
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "otf-api"
|
3
|
-
version = "0.
|
3
|
+
version = "0.13.1"
|
4
4
|
description = "Python OrangeTheory Fitness API Client"
|
5
5
|
authors = [{ name = "Jessica Smith", email = "j.smith.git1@gmail.com" }]
|
6
6
|
requires-python = ">=3.11"
|
@@ -11,7 +11,6 @@ classifiers = [
|
|
11
11
|
"Intended Audience :: Developers",
|
12
12
|
"Programming Language :: Python :: 3.11",
|
13
13
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
14
|
-
"Topic :: Software Development :: Libraries :: Application Frameworks",
|
15
14
|
"Topic :: Software Development :: Libraries",
|
16
15
|
"Topic :: Internet :: WWW/HTTP",
|
17
16
|
"Operating System :: OS Independent",
|
@@ -28,6 +27,9 @@ dependencies = [
|
|
28
27
|
"tenacity>=9.0.0,<10",
|
29
28
|
"cachetools>=5.5.0",
|
30
29
|
"pendulum>=3.1.0",
|
30
|
+
"diskcache>=5.6.3",
|
31
|
+
"platformdirs>=4.3.6",
|
32
|
+
"packaging>=24.2",
|
31
33
|
]
|
32
34
|
|
33
35
|
[project.urls]
|
@@ -47,23 +49,19 @@ dev = [
|
|
47
49
|
"twine==5.1.1",
|
48
50
|
]
|
49
51
|
docs = [
|
50
|
-
"
|
51
|
-
"
|
52
|
-
"
|
53
|
-
"
|
54
|
-
"
|
55
|
-
"
|
56
|
-
"
|
57
|
-
"
|
58
|
-
"
|
59
|
-
"
|
60
|
-
"
|
61
|
-
"
|
62
|
-
"
|
63
|
-
"mkdocstrings-python==1.10.3",
|
64
|
-
"pkginfo<1.11",
|
65
|
-
"setuptools>=70.0.0,<71",
|
66
|
-
"virtualenv>=20.26.2,<21",
|
52
|
+
"autodoc-pydantic>=2.2.0",
|
53
|
+
"furo>=2024.8.6",
|
54
|
+
"sphinx==8.3.0",
|
55
|
+
"sphinx-autobuild==2024.10.3",
|
56
|
+
"sphinx-autodoc-typehints==3.2.0",
|
57
|
+
"sphinx-autodoc2==0.5.0",
|
58
|
+
"sphinx-basic-ng==1.0.0b2",
|
59
|
+
"sphinxcontrib-applehelp==2.0.0",
|
60
|
+
"sphinxcontrib-devhelp==2.0.0",
|
61
|
+
"sphinxcontrib-htmlhelp==2.1.0",
|
62
|
+
"sphinxcontrib-jsmath==1.0.1",
|
63
|
+
"sphinxcontrib-qthelp==2.0.0",
|
64
|
+
"sphinxcontrib-serializinghtml==2.0.0",
|
67
65
|
]
|
68
66
|
|
69
67
|
[tool.uv]
|
@@ -0,0 +1,42 @@
|
|
1
|
+
"""Unofficial Orangetheory API client.
|
2
|
+
|
3
|
+
This software is not affiliated with, endorsed by, or supported by Orangetheory Fitness.
|
4
|
+
Use it at your own risk. It may break at any time if Orangetheory changes their services.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import logging
|
8
|
+
import os
|
9
|
+
|
10
|
+
from otf_api import models
|
11
|
+
from otf_api.api import Otf
|
12
|
+
from otf_api.auth import OtfUser
|
13
|
+
|
14
|
+
LOG_LEVEL = os.getenv("OTF_LOG_LEVEL", "INFO").upper()
|
15
|
+
LOG_LEVEL_NUM = getattr(logging, LOG_LEVEL, logging.INFO)
|
16
|
+
LOG_FMT = "{asctime} - {module}.{funcName}:{lineno} - {levelname} - {message}"
|
17
|
+
DATE_FMT = "%Y-%m-%d %H:%M:%S%z"
|
18
|
+
|
19
|
+
|
20
|
+
def _setup_logging() -> None:
|
21
|
+
logger = logging.getLogger("otf_api")
|
22
|
+
|
23
|
+
if logger.handlers:
|
24
|
+
return # Already set up
|
25
|
+
|
26
|
+
# 2) Set the logger level to INFO (or whatever you need).
|
27
|
+
logger.setLevel(LOG_LEVEL_NUM)
|
28
|
+
|
29
|
+
# 3) Create a handler (e.g., console) and set its formatter.
|
30
|
+
handler = logging.StreamHandler()
|
31
|
+
handler.setFormatter(logging.Formatter(fmt=LOG_FMT, datefmt=DATE_FMT, style="{"))
|
32
|
+
|
33
|
+
# 4) Add this handler to your package logger.
|
34
|
+
logger.addHandler(handler)
|
35
|
+
|
36
|
+
|
37
|
+
_setup_logging()
|
38
|
+
|
39
|
+
__version__ = "0.13.1"
|
40
|
+
|
41
|
+
|
42
|
+
__all__ = ["Otf", "OtfUser", "models"]
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# ruff: noqa
|
2
|
+
|
3
|
+
|
4
|
+
import warnings
|
5
|
+
|
6
|
+
|
7
|
+
_LEGACY_METHOD_MAP = {
|
8
|
+
"book_class": "bookings.book_class",
|
9
|
+
"book_class_new": "bookings.book_class_new",
|
10
|
+
"cancel_booking": "bookings.cancel_booking",
|
11
|
+
"cancel_booking_new": "bookings.cancel_booking_new",
|
12
|
+
"get_booking": "bookings.get_booking",
|
13
|
+
"get_booking_from_class": "bookings.get_booking_from_class",
|
14
|
+
"get_booking_from_class_new": "bookings.get_booking_from_class_new",
|
15
|
+
"get_booking_new": "bookings.get_booking_new",
|
16
|
+
"get_bookings": "bookings.get_bookings",
|
17
|
+
"get_bookings_new": "bookings.get_bookings_new",
|
18
|
+
"get_bookings_new_by_date": "bookings.get_bookings_new_by_date",
|
19
|
+
"get_classes": "bookings.get_classes",
|
20
|
+
"get_historical_bookings": "bookings.get_historical_bookings",
|
21
|
+
"rate_class": "bookings.rate_class",
|
22
|
+
"get_email_notification_settings": "members.get_email_notification_settings",
|
23
|
+
"get_member_detail": "members.get_member_detail",
|
24
|
+
"get_member_membership": "members.get_member_membership",
|
25
|
+
"get_member_purchases": "members.get_member_purchases",
|
26
|
+
"get_sms_notification_settings": "members.get_sms_notification_settings",
|
27
|
+
"update_email_notification_settings": "members.update_email_notification_settings",
|
28
|
+
"update_member_name": "members.update_member_name",
|
29
|
+
"update_sms_notification_settings": "members.update_sms_notification_settings",
|
30
|
+
"add_favorite_studio": "studios.add_favorite_studio",
|
31
|
+
"get_favorite_studios": "studios.get_favorite_studios",
|
32
|
+
"get_studio_detail": "studios.get_studio_detail",
|
33
|
+
"get_studio_services": "studios.get_studio_services",
|
34
|
+
"get_studios_by_geo": "studios.get_studios_by_geo",
|
35
|
+
"remove_favorite_studio": "studios.remove_favorite_studio",
|
36
|
+
"search_studios_by_geo": "studios.search_studios_by_geo",
|
37
|
+
"get_benchmarks": "workouts.get_benchmarks",
|
38
|
+
"get_benchmarks_by_challenge_category": "workouts.get_benchmarks_by_challenge_category",
|
39
|
+
"get_benchmarks_by_equipment": "workouts.get_benchmarks_by_equipment",
|
40
|
+
"get_body_composition_list": "workouts.get_body_composition_list",
|
41
|
+
"get_challenge_tracker": "workouts.get_challenge_tracker",
|
42
|
+
"get_challenge_tracker_detail": "workouts.get_challenge_tracker_detail",
|
43
|
+
"get_hr_history": "workouts.get_hr_history",
|
44
|
+
"get_member_lifetime_stats_in_studio": "workouts.get_member_lifetime_stats_in_studio",
|
45
|
+
"get_member_lifetime_stats_out_of_studio": "workouts.get_member_lifetime_stats_out_of_studio",
|
46
|
+
"get_out_of_studio_workout_history": "workouts.get_out_of_studio_workout_history",
|
47
|
+
"get_performance_summary": "workouts.get_performance_summary",
|
48
|
+
"get_telemetry": "workouts.get_telemetry",
|
49
|
+
"get_workout_from_booking": "workouts.get_workout_from_booking",
|
50
|
+
"get_workouts": "workouts.get_workouts",
|
51
|
+
"rate_class_from_workout": "workouts.rate_class_from_workout",
|
52
|
+
}
|
53
|
+
|
54
|
+
|
55
|
+
def _generate_legacy_method(old_name, new_path):
|
56
|
+
def method(self, *args, **kwargs):
|
57
|
+
warnings.warn(
|
58
|
+
f"`Otf.{old_name}()` is deprecated. Use `Otf.{new_path}()` instead.", DeprecationWarning, stacklevel=2
|
59
|
+
)
|
60
|
+
|
61
|
+
# Resolve nested attributes like 'bookings.cancel'
|
62
|
+
target = self
|
63
|
+
for attr in new_path.split("."):
|
64
|
+
target = getattr(target, attr)
|
65
|
+
|
66
|
+
return target(*args, **kwargs)
|
67
|
+
|
68
|
+
method.__name__ = old_name
|
69
|
+
return method
|
70
|
+
|
71
|
+
|
72
|
+
class _LegacyCompatMixin:
|
73
|
+
pass
|
74
|
+
|
75
|
+
|
76
|
+
for legacy_name, new_path in _LEGACY_METHOD_MAP.items():
|
77
|
+
setattr(_LegacyCompatMixin, legacy_name, _generate_legacy_method(legacy_name, new_path))
|
@@ -0,0 +1,80 @@
|
|
1
|
+
from otf_api import models
|
2
|
+
from otf_api.api._compat import _LegacyCompatMixin
|
3
|
+
from otf_api.auth import OtfUser
|
4
|
+
|
5
|
+
from .bookings import BookingApi
|
6
|
+
from .client import OtfClient
|
7
|
+
from .members import MemberApi
|
8
|
+
from .studios import StudioApi
|
9
|
+
from .workouts import WorkoutApi
|
10
|
+
|
11
|
+
# TODO: clean up docs and turn on autodoc when we get rig of _LegacyCompatMixin
|
12
|
+
|
13
|
+
|
14
|
+
class Otf(_LegacyCompatMixin):
|
15
|
+
"""The main OTF API client.
|
16
|
+
|
17
|
+
This class handles serialization and enrichment of data from the OTF API. The actual requests to the OTF API are\
|
18
|
+
handled by separate client classes. This class provides methods to get bookings, classes, member details, and more.
|
19
|
+
It also provides methods to book and cancel classes, get member stats, and manage favorite studios.
|
20
|
+
|
21
|
+
It is designed to be used with an authenticated user, which can be provided as an `OtfUser` object. If no user is\
|
22
|
+
provided, the `OtfClient` class will attempt to use cached credentials, environment variables, or prompt the user\
|
23
|
+
for credentials.
|
24
|
+
"""
|
25
|
+
|
26
|
+
bookings: BookingApi
|
27
|
+
members: MemberApi
|
28
|
+
workouts: WorkoutApi
|
29
|
+
studios: StudioApi
|
30
|
+
|
31
|
+
def __init__(self, user: OtfUser | None = None):
|
32
|
+
"""Initialize the OTF API client.
|
33
|
+
|
34
|
+
Args:
|
35
|
+
user (OtfUser): The user to authenticate as.
|
36
|
+
"""
|
37
|
+
client = OtfClient(user)
|
38
|
+
|
39
|
+
self.bookings = BookingApi(self, client)
|
40
|
+
self.members = MemberApi(self, client)
|
41
|
+
self.workouts = WorkoutApi(self, client)
|
42
|
+
self.studios = StudioApi(self, client)
|
43
|
+
|
44
|
+
self._member: models.MemberDetail | None = None
|
45
|
+
|
46
|
+
@property
|
47
|
+
def member_uuid(self) -> str:
|
48
|
+
"""Get the member UUID."""
|
49
|
+
return self.member.member_uuid
|
50
|
+
|
51
|
+
@property
|
52
|
+
def member(self) -> models.MemberDetail:
|
53
|
+
"""Get the member details.
|
54
|
+
|
55
|
+
This will lazily load the member details if they have not been loaded yet.
|
56
|
+
"""
|
57
|
+
if self._member is None:
|
58
|
+
self._member = self.members.get_member_detail()
|
59
|
+
return self._member
|
60
|
+
|
61
|
+
@property
|
62
|
+
def home_studio(self) -> models.StudioDetail:
|
63
|
+
"""Get the home studio details."""
|
64
|
+
return self.member.home_studio
|
65
|
+
|
66
|
+
@property
|
67
|
+
def home_studio_uuid(self) -> str:
|
68
|
+
"""Get the home studio UUID."""
|
69
|
+
return self.home_studio.studio_uuid
|
70
|
+
|
71
|
+
def refresh_member(self) -> models.MemberDetail:
|
72
|
+
"""Refresh the member details.
|
73
|
+
|
74
|
+
This will reload the member details from the OTF API.
|
75
|
+
|
76
|
+
Returns:
|
77
|
+
MemberDetail: The refreshed member details.
|
78
|
+
"""
|
79
|
+
self._member = self.members.get_member_detail()
|
80
|
+
return self._member
|