This commit is contained in:
Iliyan Angelov
2025-09-14 23:24:25 +03:00
commit c67067a2a4
71311 changed files with 6800714 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
from allauth.socialaccount.providers.base import ProviderAccount
from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider
class TrainingPeaksAccount(ProviderAccount):
def get_profile_url(self):
return "https://app.trainingpeaks.com"
def get_avatar_url(self):
return None
def to_str(self):
name = (
self.account.extra_data.get("FirstName")
+ " "
+ self.account.extra_data.get("LastName")
)
if name != " ":
return name
return super(TrainingPeaksAccount, self).to_str()
class TrainingPeaksProvider(OAuth2Provider):
id = "trainingpeaks"
name = "TrainingPeaks"
account_class = TrainingPeaksAccount
def extract_uid(self, data):
return str(data["Id"])
def extract_common_fields(self, data):
extra_common = super(TrainingPeaksProvider, self).extract_common_fields(data)
firstname = data.get("FirstName")
lastname = data.get("LastName")
# fallback username as there is actually no Username in response
username = firstname.strip().lower() + "." + lastname.strip().lower()
name = " ".join(part for part in (firstname, lastname) if part)
extra_common.update(
username=data.get("username", username),
email=data.get("Email"),
first_name=firstname,
last_name=lastname,
name=name.strip(),
)
return extra_common
def get_default_scope(self):
return ["athlete:profile"]
provider_classes = [TrainingPeaksProvider]

View File

@@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
"""
Run just this suite:
python manage.py test allauth.socialaccount.providers.trainingpeaks.tests.TrainingPeaksTests
"""
from __future__ import unicode_literals
from collections import namedtuple
from django.test.utils import override_settings
from allauth.socialaccount.tests import OAuth2TestsMixin
from allauth.tests import MockedResponse, TestCase
from .provider import TrainingPeaksProvider
from .views import TrainingPeaksOAuth2Adapter
class TrainingPeaksTests(OAuth2TestsMixin, TestCase):
provider_id = TrainingPeaksProvider.id
def get_mocked_response(self):
return MockedResponse(
200,
"""{
"Id": 123456,
"FirstName": "John",
"LastName": "Doe",
"Email": "user@example.com",
"DateOfBirth": "1986-02-01T00:00:00",
"CoachedBy": 987654,
"Weight": 87.5223617553711
}""",
) # noqa
def get_login_response_json(self, with_refresh_token=True):
rtoken = ""
if with_refresh_token:
rtoken = ',"refresh_token": "testrf"'
return (
"""{
"access_token" : "testac",
"token_type" : "bearer",
"expires_in" : 600,
"scope": "scopes granted"
%s }"""
% rtoken
)
def test_default_use_sandbox_uri(self):
adapter = TrainingPeaksOAuth2Adapter(None)
self.assertTrue(".sandbox." in adapter.authorize_url)
self.assertTrue(".sandbox." in adapter.access_token_url)
self.assertTrue(".sandbox." in adapter.profile_url)
@override_settings(
SOCIALACCOUNT_PROVIDERS={"trainingpeaks": {"USE_PRODUCTION": True}}
)
def test_use_production_uri(self):
adapter = TrainingPeaksOAuth2Adapter(None)
self.assertFalse(".sandbox." in adapter.authorize_url)
self.assertFalse(".sandbox." in adapter.access_token_url)
self.assertFalse(".sandbox." in adapter.profile_url)
def test_scope_from_default(self):
Request = namedtuple("request", ["GET"])
mock_request = Request(GET={})
scope = self.provider.get_scope(mock_request)
self.assertTrue("athlete:profile" in scope)
@override_settings(
SOCIALACCOUNT_PROVIDERS={
"trainingpeaks": {"SCOPE": ["athlete:profile", "workouts", "workouts:wod"]}
}
)
def test_scope_from_settings(self):
Request = namedtuple("request", ["GET"])
mock_request = Request(GET={})
scope = self.provider.get_scope(mock_request)
for item in ("athlete:profile", "workouts", "workouts:wod"):
self.assertTrue(item in scope)

View File

@@ -0,0 +1,6 @@
from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns
from .provider import TrainingPeaksProvider
urlpatterns = default_urlpatterns(TrainingPeaksProvider)

View File

@@ -0,0 +1,58 @@
import requests
from allauth.socialaccount import app_settings
from allauth.socialaccount.providers.oauth2.views import (
OAuth2Adapter,
OAuth2CallbackView,
OAuth2LoginView,
)
from .provider import TrainingPeaksProvider
class TrainingPeaksOAuth2Adapter(OAuth2Adapter):
# https://github.com/TrainingPeaks/PartnersAPI/wiki/OAuth
provider_id = TrainingPeaksProvider.id
def get_settings(self):
"""Provider settings"""
return app_settings.PROVIDERS.get(self.provider_id, {})
def get_hostname(self):
"""Return hostname depending on sandbox setting"""
settings = self.get_settings()
if settings.get("USE_PRODUCTION"):
return "trainingpeaks.com"
return "sandbox.trainingpeaks.com"
@property
def access_token_url(self):
return "https://oauth." + self.get_hostname() + "/oauth/token"
@property
def authorize_url(self):
return "https://oauth." + self.get_hostname() + "/OAuth/Authorize"
@property
def profile_url(self):
return "https://api." + self.get_hostname() + "/v1/athlete/profile"
@property
def api_hostname(self):
"""Return https://api.hostname.tld"""
return "https://api." + self.get_hostname()
# https://oauth.sandbox.trainingpeaks.com/oauth/deauthorize
scope_delimiter = " "
def complete_login(self, request, app, token, **kwargs):
headers = {"Authorization": "Bearer {0}".format(token.token)}
response = requests.get(self.profile_url, headers=headers)
response.raise_for_status()
extra_data = response.json()
return self.get_provider().sociallogin_from_response(request, extra_data)
oauth2_login = OAuth2LoginView.adapter_view(TrainingPeaksOAuth2Adapter)
oauth2_callback = OAuth2CallbackView.adapter_view(TrainingPeaksOAuth2Adapter)