From 4c7da56c10df13c6be1db62f9c5893e8f6392f05 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 17 Mar 2021 20:35:19 +0100 Subject: [PATCH] adding rp3 auto import --- rowers/management/commands/processemail.py | 4 ++ rowers/models.py | 3 + rowers/rp3stuff.py | 72 +++++++++++++++++----- rowers/tasks.py | 8 ++- rowers/templates/rp3_list_import.html | 1 + rowers/urls.py | 1 + rowers/views/importviews.py | 22 ++++++- rowers/views/statements.py | 1 + 8 files changed, 94 insertions(+), 18 deletions(-) diff --git a/rowers/management/commands/processemail.py b/rowers/management/commands/processemail.py index a14cf6b2..feb15a12 100644 --- a/rowers/management/commands/processemail.py +++ b/rowers/management/commands/processemail.py @@ -207,6 +207,10 @@ class Command(BaseCommand): if user_is_not_basic(r.user): c2stuff.get_c2_workouts(r) + rowers = Rower.objects.filter(rp3_auto_import=True) + for r in rowers: + if user_is_not_basic(r.user): + res = rp3stuff.get_rp3_workouts(r) messages = Message.objects.filter(mailbox_id = workoutmailbox.id) message_ids = [m.id for m in messages] diff --git a/rowers/models.py b/rowers/models.py index 362fc604..ab28bf6c 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -995,6 +995,8 @@ class Rower(models.Model): rp3refreshtoken = models.TextField(default='',max_length=1000, blank=True,null=True) + rp3_auto_import = models.BooleanField(default=False) + trainingpeaks_auto_export = models.BooleanField(default=False) polartoken = models.CharField(default='',max_length=1000,blank=True,null=True) @@ -3836,6 +3838,7 @@ class RowerExportForm(ModelForm): 'strava_auto_import', 'strava_auto_delete', 'trainingpeaks_auto_export', + 'rp3_auto_import' ] # Simple form to set rower's Functional Threshold Power diff --git a/rowers/rp3stuff.py b/rowers/rp3stuff.py index 3b733b88..a4f9e99e 100644 --- a/rowers/rp3stuff.py +++ b/rowers/rp3stuff.py @@ -12,16 +12,24 @@ import gzip import base64 from io import BytesIO +import django_rq +queue = django_rq.get_queue('default') +queuelow = django_rq.get_queue('low') +queuehigh = django_rq.get_queue('high') + +from rowers.utils import myqueue from rowsandall_app.settings import ( C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET, RP3_CLIENT_ID, RP3_CLIENT_SECRET, RP3_REDIRECT_URI,RP3_CLIENT_KEY, - RP3_CLIENT_ID, RP3_CLIENT_KEY, RP3_REDIRECT_URI, RP3_CLIENT_SECRET, + RP3_CLIENT_ID, RP3_CLIENT_KEY, RP3_REDIRECT_URI, RP3_CLIENT_SECRET, UPLOAD_SERVICE_URL, UPLOAD_SERVICE_SECRET ) +from rowers.tasks import handle_rp3_async_workout + from celery import Celery,app from django_rq import job @@ -60,14 +68,14 @@ def do_refresh_token(refreshtoken): def get_token(code): client_auth = requests.auth.HTTPBasicAuth(RP3_CLIENT_KEY, RP3_CLIENT_SECRET) post_data = { - "client_id":RP3_CLIENT_KEY, + "client_id":RP3_CLIENT_KEY, "grant_type": "authorization_code", "code": code, "redirect_uri":RP3_REDIRECT_URI, - "client_secret": RP3_CLIENT_SECRET, + "client_secret": RP3_CLIENT_SECRET, } headers = { - 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Type': 'application/x-www-form-urlencoded', } response = requests.post( @@ -107,13 +115,49 @@ def get_rp3_workout_list(user): } }""" response = requests.post( - url=graphql_url, - headers=headers, - json={'query': get_workouts_list} - ) + url=graphql_url, + headers=headers, + json={'query': get_workouts_list} + ) return response +def get_rp3_workouts(rower,do_async=True): + try: + auth_token = rp3_open(rower.user) + except NoTokenError: + return 0 + + res = get_rp3_workout_list(rower.user) + + if (res.status_code != 200): + return 0 + + workouts_list = pd.json_normalize(res.json()['data']['workouts']) + rp3ids = workouts_list['id'].values + workouts_list.set_index('id',inplace=True) + + knownrp3ids = uniqify([ + w.uploadedtorp3 for w in Workout.objects.filter(user=rower) + ]) + + newids = [rp3id for rp3id in rp3ids if not rp3id in knownrp3ids] + + for id in newids: + startdatetime = workouts_list.loc[id,'executed_at'] + + job = myqueue( + queuehigh, + handle_rp3_async_workout, + rower.user.id, + auth_token, + id, + startdatetime, + 10, + ) + + return 1 + def download_rp3_file(url,auth_token,filename): headers = {'Authorization': 'Bearer ' + auth_token } @@ -129,11 +173,11 @@ def get_rp3_workout_token(workout_id,auth_token,waittime=3,max_attempts=20): headers = {'Authorization': 'Bearer ' + auth_token } get_download_link = """{ - download(workout_id: """ + str(workout_id) + """, type:csv){ - id - status - link - } + download(workout_id: """ + str(workout_id) + """, type:csv){ + id + status + link + } }""" have_link = False @@ -144,7 +188,7 @@ def get_rp3_workout_token(workout_id,auth_token,waittime=3,max_attempts=20): url=graphql_url, headers=headers, json={'query': get_download_link} - ) + ) if response.status_code != 200: have_link = True diff --git a/rowers/tasks.py b/rowers/tasks.py index f22a3aab..c841185f 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -2872,7 +2872,7 @@ def add2(x, y,debug=False,**kwargs): graphql_url = "https://rp3rowing-app.com/graphql" @app.task -def handle_rp3_async_workout(userid,rp3token,rp3id,startdatetime,debug=False,**kwargs): +def handle_rp3_async_workout(userid,rp3token,rp3id,startdatetime,max_attempts,debug=False,**kwargs): headers = {'Authorization': 'Bearer ' + rp3token } get_download_link = """{ @@ -2886,7 +2886,7 @@ def handle_rp3_async_workout(userid,rp3token,rp3id,startdatetime,debug=False,**k have_link = False download_url = '' counter = 0 - max_attempts = 20 + #max_attempts = 20 waittime = 3 while not have_link: response = requests.post( @@ -2895,6 +2895,7 @@ def handle_rp3_async_workout(userid,rp3token,rp3id,startdatetime,debug=False,**k json={'query': get_download_link} ) + if response.status_code != 200: have_link = True @@ -2915,6 +2916,7 @@ def handle_rp3_async_workout(userid,rp3token,rp3id,startdatetime,debug=False,**k return 0 filename = 'media/RP3Import_'+str(rp3id)+'.csv' + res = requests.get(download_url,headers=headers) if not startdatetime: @@ -2933,7 +2935,7 @@ def handle_rp3_async_workout(userid,rp3token,rp3id,startdatetime,debug=False,**k 'file': filename, 'workouttype':'dynamic', 'boattype':'1x', - 'rp3id':rp3id, + 'rp3id':int(rp3id), 'startdatetime':startdatetime, } diff --git a/rowers/templates/rp3_list_import.html b/rowers/templates/rp3_list_import.html index 919e2242..5827dda7 100644 --- a/rowers/templates/rp3_list_import.html +++ b/rowers/templates/rp3_list_import.html @@ -7,6 +7,7 @@ {% block main %}

Available on RP3

{% if workouts %} +

Import all New

  • diff --git a/rowers/urls.py b/rowers/urls.py index fc82f282..18cb25ff 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -569,6 +569,7 @@ urlpatterns = [ re_path(r'^workout/c2import/all/(?P\d+)/$',views.workout_getc2workout_all,name='workout_getc2workout_all'), re_path(r'^workout/rp3import/(?P\d+)/$',views.workout_getrp3importview, name='workout_getrp3importview'), + re_path(r'^workout/rp3import/all/$',views.workout_getrp3workout_all,name='workout_getrp3workout_all'), re_path(r'^workout/(?P\w+.*)import/(?P\d+)/$',views.workout_getimportview,name='workout_getimportview'), re_path(r'^workout/stravaimport/all/$',views.workout_getstravaworkout_all,name='workout_getstravaworkout_all'), re_path(r'^workout/stravaimport/next/$',views.workout_getstravaworkout_next,name='workout_getstravaworkout_next'), diff --git a/rowers/views/importviews.py b/rowers/views/importviews.py index 152bcc01..4dfc0ab4 100644 --- a/rowers/views/importviews.py +++ b/rowers/views/importviews.py @@ -1002,7 +1002,7 @@ def workout_rp3import_view(request,userid=0): if (r.stravatoken == '') or (r.stravatoken is None): s = "Token doesn't exist. Need to authorize" return HttpResponseRedirect("/rowers/me/stravaauthorize/") - message = "Something went wrong in workout_stravaimport_view" + message = "Something went wrong in workout_rp3import_view" messages.error(request,message) url = reverse('workouts_view') return HttpResponseRedirect(url) @@ -1700,6 +1700,25 @@ def workout_getc2workout_all(request,page=1,message=""): url = reverse('workouts_view') return HttpResponseRedirect(url) +@login_required() +def workout_getrp3workout_all(request): + try: + thetoken = rp3_open(request.user) + except NoTokenError: + return HttpResponseRedirect("/rowers/me/rp3authorize/") + + r = getrequestrower(request) + + result = rp3stuff.get_rp3_workouts(r,do_async=True) + + if result: + messages.info(request,'Your RP3 workouts will be imported in the coming few minutes') + else: + messages.error(request,'Your RP3 workouts import failed') + + url = reverse('workouts_view') + return HttpResponseRedirect(url) + # List of workouts available on Concept2 logbook - for import @login_required() @permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) @@ -1807,6 +1826,7 @@ def workout_getrp3importview(request,externalid): token, externalid, startdatetime, + 20, ) #id = rp3stuff.get_rp3_workout(r.user,externalid,startdatetime=startdatetime) diff --git a/rowers/views/statements.py b/rowers/views/statements.py index 93cc446c..5881d15d 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -154,6 +154,7 @@ import datetime import iso8601 import rowers.c2stuff as c2stuff from rowers.c2stuff import c2_open +from rowers.rp3stuff import rp3_open from rowers.runkeeperstuff import runkeeper_open from rowers.sporttracksstuff import sporttracks_open from rowers.tpstuff import tp_open