From 5e023a0e075de5abe0df5e4cada5a8ad636b6281 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 12 Jul 2020 10:11:52 +0200 Subject: [PATCH 1/3] gets strava owner id thru api --- rowers/models.py | 1 + rowers/stravastuff.py | 25 ++++++++++++++++++++++++ rowers/tests/mocks.py | 10 ++++++++++ rowers/tests/testdata/strava_athlete.txt | 1 + 4 files changed, 37 insertions(+) create mode 100644 rowers/tests/testdata/strava_athlete.txt diff --git a/rowers/models.py b/rowers/models.py index 2a317423..b676f4a7 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -849,6 +849,7 @@ class Rower(models.Model): max_length=30, choices=stravatypes, verbose_name="Export Workouts to Strava as") + strava_owner_id = models.BigIntegerField(default=0) strava_auto_export = models.BooleanField(default=False) strava_auto_import = models.BooleanField(default=False) diff --git a/rowers/stravastuff.py b/rowers/stravastuff.py index f0cbf6e2..36097d9c 100644 --- a/rowers/stravastuff.py +++ b/rowers/stravastuff.py @@ -71,6 +71,8 @@ def get_token(code): return imports_get_token(code, oauth_data) def strava_open(user): + if user.rower.strava_owner_id == 0: + strava_owner_id = set_strava_athlete_id(user) return imports_open(user, oauth_data) def do_refresh_token(refreshtoken): @@ -95,6 +97,29 @@ def rower_strava_token_refresh(user): def make_authorization_url(request): return imports_make_authorization_url(oauth_data) +def set_strava_athlete_id(user): + r = Rower.objects.get(user=user) + if (r.stravatoken == '') or (r.stravatoken is None): + s = "Token doesn't exist. Need to authorize" + return custom_exception_handler(401,s) + elif (r.stravatokenexpirydate is None or timezone.now()+timedelta(seconds=3599)>r.stravatokenexpirydate): + s = "Token expired. Needs to refresh." + return custom_exception_handler(401,s) + else: + authorizationstring = str('Bearer ' + r.stravatoken) + headers = {'Authorization': authorizationstring, + 'user-agent': 'sanderroosendaal', + 'Content-Type': 'application/json'} + url = "https://www.strava.com/api/v3/athlete" + + response = requests.get(url,headers=headers,params={}) + + r.strava_owner_id = response.json()['id'] + r.save() + + return response.json()['id'] + + # Get list of workouts available on Strava def get_strava_workout_list(user,limit_n=0): r = Rower.objects.get(user=user) diff --git a/rowers/tests/mocks.py b/rowers/tests/mocks.py index 5fb400a5..08e90110 100644 --- a/rowers/tests/mocks.py +++ b/rowers/tests/mocks.py @@ -606,6 +606,8 @@ def mocked_requests(*args, **kwargs): uastrokesjson = json.load(open('rowers/tests/testdata/uastrokes.txt','r')) uauserjson = json.load(open('rowers/tests/testdata/uauser.txt','r')) + stravaathletejson = json.load(open('rowers/tests/testdata/strava_athlete.txt')) + class MockResponse: def __init__(self, json_data, status_code): self.json_data = json_data @@ -660,6 +662,10 @@ def mocked_requests(*args, **kwargs): c2workoutlistregex = '.*?concept2\.com\/api\/users\/me\/results\?page=\d' c2workoutlisttester = re.compile(c2workoutlistregex) + stravaathleteregex = '.*?strava\.com\/api\/v3\/athlete$' + stravaathletetester = re.compile(stravaathleteregex) + + stravaworkoutlistregex = '.*?strava\.com\/api\/v3\/athlete\/activities' stravaworkoutlisttester = re.compile(stravaworkoutlistregex) @@ -703,6 +709,10 @@ def mocked_requests(*args, **kwargs): tpuploadregex = '.*?trainingpeaks\.com\/v1\/file' tpuploadtester = re.compile(tpuploadregex) + if stravaathletetester.match(args[0]): + json_data = stravaathletejson + return MockResponse(json_data,200) + if polartester.match(args[0]): json_data = polar_json return MockResponse(json_data,200) diff --git a/rowers/tests/testdata/strava_athlete.txt b/rowers/tests/testdata/strava_athlete.txt new file mode 100644 index 00000000..0108efef --- /dev/null +++ b/rowers/tests/testdata/strava_athlete.txt @@ -0,0 +1 @@ +{"id": 47155909, "username": null, "resource_state": 2, "firstname": "Rowsandall", "lastname": "Testing", "city": "Zliv", "state": "Jiho\u010desk\u00fd kraj", "country": "Czechia", "sex": "M", "premium": false, "summit": false, "created_at": "2019-10-06T06:59:54Z", "updated_at": "2020-05-15T11:52:33Z", "badge_type_id": 0, "profile_medium": "avatar/athlete/medium.png", "profile": "avatar/athlete/large.png", "friend": null, "follower": null} \ No newline at end of file From c4c1a02794a69799365626bc7b08b35b2abb6a5a Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 12 Jul 2020 10:45:40 +0200 Subject: [PATCH 2/3] adding webhook processing for strava - does not do anything useful yet --- rowers/stravastuff.py | 21 +++++++++++++++++++++ rowers/urls.py | 1 + rowers/views/importviews.py | 13 +++++++++++++ 3 files changed, 35 insertions(+) diff --git a/rowers/stravastuff.py b/rowers/stravastuff.py index 36097d9c..587d7c3a 100644 --- a/rowers/stravastuff.py +++ b/rowers/stravastuff.py @@ -43,6 +43,8 @@ except ImportError: from rowers.imports import * +webhookverification = "kudos_to_rowing" + headers = {'Accept': 'application/json', 'Api-Key': STRAVA_CLIENT_ID, 'Content-Type': 'application/json', @@ -97,6 +99,25 @@ def rower_strava_token_refresh(user): def make_authorization_url(request): return imports_make_authorization_url(oauth_data) +def strava_establish_push(): + url = "https://www.strava.com/api/v3/push_subscriptions" + post_data = { + 'client_id': STRAVA_CLIENT_ID, + 'client_secret': STRAVA_CLIENT_SECRET, + 'callback_url': 'http://localhost:8000/rowers/strava/webhooks/', + 'verify_token': webhookverification, + } + headers = {'user-agent': 'sanderroosendaal', + 'Accept': 'application/json', + 'Content-Type': oauth_data['content_type']} + + response = requests.post(url,data=post_data) + + print(response.json()) + + return response.status_code + + def set_strava_athlete_id(user): r = Rower.objects.get(user=user) if (r.stravatoken == '') or (r.stravatoken is None): diff --git a/rowers/urls.py b/rowers/urls.py index 6e74b0df..5e53e52f 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -417,6 +417,7 @@ urlpatterns = [ re_path(r'^workout/(?P\b[0-9A-Fa-f]+\b)/delete/$',login_required( views.WorkoutDelete.as_view()), name='workout_delete'), + re_path(r'^strava/webhooks/',views.strava_webhook_view,name='strava_webhook_view'), re_path(r'^garmin/summaries/',views.garmin_summaries_view,name='garmin_summaries_view'), re_path(r'^garmin/files/',views.garmin_newfiles_ping,name='garmin_newfiles_ping'), re_path(r'^garmin/activities/',views.garmin_details_view,name='garmin_details_view'), diff --git a/rowers/views/importviews.py b/rowers/views/importviews.py index d1981dc9..a0149a41 100644 --- a/rowers/views/importviews.py +++ b/rowers/views/importviews.py @@ -1008,6 +1008,19 @@ def workout_stravaimport_view(request,message="",userid=0): return HttpResponse(res) +# for Strava webhook request validation +def strava_webhook_view(request): + if request.method == 'GET': + challenge = request.GET.get('hub.challenge') + verificationtoken = request.GET.get('hub.verify_token') + if verificationtoken != stravastuff.webhookverification: + return HttpResponse(status=403) + data = {"hub.challenge":challenge} + return JSONResponse(data) + + # POST - does nothing so far + return HttpResponse(status=200) + # For push notifications from Garmin @csrf_exempt def garmin_summaries_view(request): From 06bfcd6460b4f0e48996edc798e94e7bb2ed6cf6 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 12 Jul 2020 10:51:58 +0200 Subject: [PATCH 3/3] webhook link --- rowers/stravastuff.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rowers/stravastuff.py b/rowers/stravastuff.py index 587d7c3a..e46649d5 100644 --- a/rowers/stravastuff.py +++ b/rowers/stravastuff.py @@ -33,7 +33,8 @@ from rowers.tasks import handle_strava_sync from rowsandall_app.settings import ( C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, - STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET + STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET, + SITE_URL ) try: @@ -44,6 +45,7 @@ except ImportError: from rowers.imports import * webhookverification = "kudos_to_rowing" +webhooklink = SITE_URL+'/rowers/strava/webhooks/' headers = {'Accept': 'application/json', 'Api-Key': STRAVA_CLIENT_ID, @@ -104,7 +106,7 @@ def strava_establish_push(): post_data = { 'client_id': STRAVA_CLIENT_ID, 'client_secret': STRAVA_CLIENT_SECRET, - 'callback_url': 'http://localhost:8000/rowers/strava/webhooks/', + 'callback_url': webhooklink, 'verify_token': webhookverification, } headers = {'user-agent': 'sanderroosendaal',