From a7773a6f4a752650e65e896e00a60ebd67b115a9 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 17 Aug 2022 02:09:22 +0200 Subject: [PATCH 1/4] v2/file now works --- rowers/tpstuff.py | 13 ++++++++----- rowers/views/importviews.py | 7 +++++-- rowsandall_app/settings.py | 5 +++++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/rowers/tpstuff.py b/rowers/tpstuff.py index 0ac7187d..1fb29c26 100644 --- a/rowers/tpstuff.py +++ b/rowers/tpstuff.py @@ -17,7 +17,8 @@ from rowsandall_app.settings import ( C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET, TP_CLIENT_ID, TP_CLIENT_SECRET, - TP_REDIRECT_URI, TP_CLIENT_KEY,TP_API_LOCATION + TP_REDIRECT_URI, TP_CLIENT_KEY,TP_API_LOCATION, + TP_OAUTH_LOCATION, ) tpapilocation = TP_API_LOCATION @@ -50,7 +51,6 @@ def do_refresh_token(refreshtoken): # Exchange access code for long-lived access token - def get_token(code): # client_auth = requests.auth.HTTPBasicAuth(TP_CLIENT_KEY, TP_CLIENT_SECRET) post_data = { @@ -62,10 +62,13 @@ def get_token(code): } response = requests.post( - "https://oauth.trainingpeaks.com/oauth/token", + TP_OAUTH_LOCATION+"/oauth/token/", data=post_data, verify=False, ) + if response.status_code != 200: + return 0,0,0 + try: token_json = response.json() thetoken = token_json['access_token'] @@ -116,7 +119,7 @@ def tp_check(access_token): # pragma: no cover 'authorization': 'Bearer %s' % access_token } - resp = requests.post(tpapilocation+"/v1/info/version", + resp = requests.post(tpapilocation+"/v2/info/version", headers=headers, verify=False) return resp @@ -147,7 +150,7 @@ def uploadactivity(access_token, filename, description='', "Data": base64.b64encode(data_gz.getvalue()).decode("ascii") } - resp = requests.post(tpapilocation+"/v1/file", + resp = requests.post(tpapilocation+"/v2/file/synchronous", data=json.dumps(data), headers=headers, verify=False) diff --git a/rowers/views/importviews.py b/rowers/views/importviews.py index 82e13b48..c34d83d4 100644 --- a/rowers/views/importviews.py +++ b/rowers/views/importviews.py @@ -1,4 +1,7 @@ -from rowsandall_app.settings import NK_OAUTH_LOCATION, ROJABO_OAUTH_LOCATION +from rowsandall_app.settings import ( + NK_OAUTH_LOCATION, ROJABO_OAUTH_LOCATION, + TP_OAUTH_LOCATION, + ) from rowers.views.statements import * from rowers.plannedsessions import get_dates_timeperiod @@ -295,7 +298,7 @@ def rower_tp_authorize(request): # pragma: no cover "redirect_uri": TP_REDIRECT_URI, "scope": "file:write", } - url = "https://oauth.trainingpeaks.com/oauth/authorize/?" + \ + url = TP_OAUTH_LOCATION+"oauth/authorize/?" + \ urllib.parse.urlencode(params) return HttpResponseRedirect(url) diff --git a/rowsandall_app/settings.py b/rowsandall_app/settings.py index 3ee00411..ad7eb610 100644 --- a/rowsandall_app/settings.py +++ b/rowsandall_app/settings.py @@ -328,6 +328,11 @@ try: except KeyError: TP_API_LOCATION = "https://api.trainingpeaks.com" +try: + TP_OAUTH_LOCATION = CFG['tp_oauth_location'] +except KeyError: + TP_OAUTH_LOCATION = "https://oauth.trainingpeaks.com/oauth/token" + # RP3 RP3_CLIENT_ID = CFG["rp3_client_id"] RP3_CLIENT_SECRET = CFG["rp3_client_secret"] From b88aa0a246dc356941617308ee21a85c51d7560c Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 17 Aug 2022 02:44:48 +0200 Subject: [PATCH 2/4] using v3 now --- rowers/tasks.py | 19 +++++++++++++++++++ rowers/tpstuff.py | 27 ++++++++++++++++++++++++--- rowers/views/importviews.py | 5 +++++ rowers/views/statements.py | 1 + 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/rowers/tasks.py b/rowers/tasks.py index b2076664..e922f653 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -90,6 +90,8 @@ SETTINGS_NAME = settings.SETTINGS_NAME UPLOAD_SERVICE_URL = settings.UPLOAD_SERVICE_URL UPLOAD_SERVICE_SECRET = settings.UPLOAD_SERVICE_SECRET NK_API_LOCATION = settings.NK_API_LOCATION +TP_CLIENT_ID = settings.TP_CLIENT_ID +TP_CLIENT_SECRET = settings.TP_CLIENT_SECRET from requests_oauthlib import OAuth1, OAuth1Session @@ -299,6 +301,23 @@ def summaryfromsplitdata(splitdata, data, filename, sep='|', workouttype='rower' return sums, sa, results +@app.task +def check_tp_workout_id(workout, location, attempts=5, debug=False, **kwargs): + authorizationstring = str('Bearer ' + workout.user.tptoken) + headers = {'Authorization': authorizationstring, + 'user-agent': 'sanderroosendaal', + 'Content-Type': 'application/json'} + response = requests.get(location, headers=headers, params={}) + + if response.status_code == 200: + status = response.json()['Status'] + if status == 'Success': + tpid = response.json()['WorkoutIds'][0] + workout.uploadedtotp = tpid + workout.save() + + return 1 + @app.task def instroke_static(w, metric, debug=False, **kwargs): f1 = w.csvfilename[6:-4] diff --git a/rowers/tpstuff.py b/rowers/tpstuff.py index 1fb29c26..52158868 100644 --- a/rowers/tpstuff.py +++ b/rowers/tpstuff.py @@ -5,6 +5,14 @@ from django_rq import job # All the functionality needed to connect to Runkeeper from rowers.imports import * from rowers.utils import dologging +from rowers.tasks import check_tp_workout_id + +import django_rq +queue = django_rq.get_queue('default') +queuelow = django_rq.get_queue('low') +queuehigh = django_rq.get_queue('low') + +from rowers.utils import myqueue # Python import gzip @@ -150,17 +158,24 @@ def uploadactivity(access_token, filename, description='', "Data": base64.b64encode(data_gz.getvalue()).decode("ascii") } - resp = requests.post(tpapilocation+"/v2/file/synchronous", + #resp = requests.post(tpapilocation+"/v2/file/synchronous", + # data=json.dumps(data), + # headers=headers, verify=False) + + resp = requests.post(tpapilocation+"/v3/file", data=json.dumps(data), headers=headers, verify=False) - if resp.status_code != 200: # pragma: no cover + print(resp.headers['Location']) + + + if resp.status_code not in (200, 202): # pragma: no cover dologging('tp_export.log',resp.status_code) dologging('tp_export.log',resp.reason) dologging('tp_export.log',json.dumps(data)) return 0, resp.reason, resp.status_code, headers else: - return resp.json()[0]["Id"], "ok", 200, "" + return 1, "ok", 200, resp.headers return 0, 0, 0, 0 # pragma: no cover @@ -197,6 +212,12 @@ def workout_tp_upload(user, w): # pragma: no cover tpid = res w.save() os.remove(tcxfile) + + job = myqueue(queuelow, + check_tp_workout_id, + w, + headers['Location']) + return 'Successfully synchronized to TrainingPeaks', tpid else: # no tcxfile diff --git a/rowers/views/importviews.py b/rowers/views/importviews.py index c34d83d4..3a1c509a 100644 --- a/rowers/views/importviews.py +++ b/rowers/views/importviews.py @@ -52,6 +52,11 @@ def workout_tp_upload_view(request, id=0): w.uploadedtotp = res w.save() os.remove(tcxfile) + job = myqueue(queuelow, + check_tp_workout_id, + w, + headers['Location']) + messages.info(request, 'Uploaded to TrainingPeaks') else: # pragma: no cover # no tcxfile diff --git a/rowers/views/statements.py b/rowers/views/statements.py index 71d2b616..5448ff02 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -263,6 +263,7 @@ from rowers.tasks import ( handle_c2_async_workout, handle_send_email_instantplan_notification, handle_nk_async_workout, + check_tp_workout_id, ) from scipy.signal import savgol_filter From 1fec766e600ec51640ecba411aebe6bc84d6b2b9 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 17 Aug 2022 06:47:35 +0200 Subject: [PATCH 3/4] removing print statement --- rowers/tpstuff.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/rowers/tpstuff.py b/rowers/tpstuff.py index 52158868..25ef8616 100644 --- a/rowers/tpstuff.py +++ b/rowers/tpstuff.py @@ -166,9 +166,6 @@ def uploadactivity(access_token, filename, description='', data=json.dumps(data), headers=headers, verify=False) - print(resp.headers['Location']) - - if resp.status_code not in (200, 202): # pragma: no cover dologging('tp_export.log',resp.status_code) dologging('tp_export.log',resp.reason) From af3d2f4056e412f86a5f285854ecc4d827b3c038 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 17 Aug 2022 16:37:47 +0200 Subject: [PATCH 4/4] fixing tests --- rowers/tests/mocks.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/rowers/tests/mocks.py b/rowers/tests/mocks.py index a2584dc1..4e8bfc34 100644 --- a/rowers/tests/mocks.py +++ b/rowers/tests/mocks.py @@ -116,6 +116,10 @@ def mocked_session(*args, **kwargs): def __init__(self, json_data, status_code): self.json_data = json_data self.status_code = status_code + self.reason = 'mock reason' + self.headers = { + 'Location':'MockLocation', + } self.ok = True def json(self): @@ -916,6 +920,9 @@ def mocked_requests(*args, **kwargs): self.json_data = json_data self.status_code = status_code self.ok = True + self.headers = { + 'Location':'MockLocation', + } def json(self): return self.json_data @@ -1132,7 +1139,7 @@ def mocked_requests(*args, **kwargs): uauserregex = r'.*?api\.ua\.com\/v7.1\/user\/self\/' uausertester = re.compile(uauserregex) - tpuploadregex = r'.*?trainingpeaks\.com\/v1\/file' + tpuploadregex = r'.*?trainingpeaks\.com\/v3\/file' tpuploadtester = re.compile(tpuploadregex) garmindownloadregex = r'.*?garmin\.com\/mockfile?id=1' @@ -1445,6 +1452,9 @@ class MockResponse: self.json_data = json_data self.status_code = status_code self.ok = True + self.headers = { + 'Location':'MockLocation', + } def json(self): return self.json_data