diff --git a/rowers/rp3stuff.py b/rowers/rp3stuff.py index 02569b48..6369b71e 100644 --- a/rowers/rp3stuff.py +++ b/rowers/rp3stuff.py @@ -21,7 +21,6 @@ from rowsandall_app.settings import ( RP3_CLIENT_ID, RP3_CLIENT_KEY, RP3_REDIRECT_URI, RP3_CLIENT_SECRET ) -tpapilocation = "https://api.trainingpeaks.com" from celery import Celery,app from django_rq import job @@ -45,6 +44,8 @@ oauth_data = { from rowers.rower_rules import is_workout_user +graphql_url = "https://rp3rowing-app.com/graphql" + # Checks if user has UnderArmour token, renews them if they are expired def rp3_open(user): @@ -73,8 +74,6 @@ def get_token(code): data=post_data,verify=False, ) - print(response.json()) - try: token_json = response.json() @@ -93,124 +92,23 @@ def make_authorization_url(request): return imports_make_authorization_url(oauth_data) -def getidfromresponse(response): - t = json.loads(response.text) +def get_rp3_workout_list(user): + r = Rower.objects.get(user=user) - links = t["_links"] + auth_token = rp3_open(user) - id = links["self"][0]["id"] + headers = {'Authorization': 'Bearer ' + auth_token } - return int(id) - -def createtpworkoutdata(w): - filename = w.csvfilename - row = rowingdata(csvfile=filename) - tcxfilename = filename[:-4]+'.tcx' - try: - newnotes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com' - except TypeError: - newnotes = 'from '+w.workoutsource+' via rowsandall.com' - - row.exporttotcx(tcxfilename,notes=newnotes) - - return tcxfilename - - -def rp3_check(access_token): - headers = { - "Content-Type": "application/json", - 'Accept': 'application/json', - 'authorization': 'Bearer %s' % access_token - } - - resp = requests.post(tpapilocation+"/v1/info/version", - headers=headers,verify=False) - - return resp - -def uploadactivity(access_token,filename,description='', - name='Rowsandall.com workout'): - - data_gz = BytesIO() - with open(filename,'rb') as inF: - s = inF.read() - with gzip.GzipFile(fileobj=data_gz,mode="w") as gzf: - gzf.write(s) - - - headers = { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'Authorization': 'Bearer %s' % access_token - } - - - - data = { - "UploadClient": "rowsandall", - "Filename": filename, - "SetWorkoutPublic": True, - "Title":name, - "Type": "rowing", - "Comment": description, - "Data": base64.b64encode(data_gz.getvalue()).decode("ascii") + get_workouts_list = """{ + workouts{ + id + executed_at } +}""" + response = requests.post( + url=graphql_url, + headers=headers, + json={'query': get_workouts_list} + ) - resp = requests.post(tpapilocation+"/v1/file", - data = json.dumps(data), - headers=headers,verify=False) - - if resp.status_code != 200: - return 0,resp.reason,resp.status_code,headers - else: - return resp.json()[0]["Id"],"ok",200,"" - - return 0,0,0,0 - - -def workout_rp3_upload(user,w): - message = "Uploading to TrainingPeaks" - tpid = 0 - r = w.user - - thetoken = rp3_open(r.user) - - # need some code if token doesn't refresh - - - if (is_workout_user(user,w)): - tcxfile = createtpworkoutdata(w) - if tcxfile: - res,reason,status_code,headers = uploadactivity( - thetoken,tcxfile, - name=w.name - ) - if res == 0: - message = "Upload to TrainingPeaks failed with status code "+str(status_code)+": "+reason - w.tpid = -1 - try: - os.remove(tcxfile) - except WindowsError: - pass - - return message,tpid - - else: # res != 0 - w.uploadedtotp = res - tpid = res - w.save() - os.remove(tcxfile) - return 'Successfully synchronized to TrainingPeaks',tpid - - else: # no tcxfile - message = "Upload to TrainingPeaks failed" - w.uploadedtotp = -1 - tpid = -1 - w.save() - return message,tpid - else: # not allowed to upload - message = "You are not allowed to export this workout to TP" - tpid = 0 - return message,tpid - - return message,tpid + return response diff --git a/rowers/templates/menu_workouts.html b/rowers/templates/menu_workouts.html index dfc2f0cf..aea1c84f 100644 --- a/rowers/templates/menu_workouts.html +++ b/rowers/templates/menu_workouts.html @@ -16,7 +16,7 @@  Compare {% endif %} - +
  •  Upload
  • @@ -35,7 +35,7 @@
  • - +
    • Concept2
    • Strava
    • @@ -43,6 +43,7 @@
    • SportTracks
    • MapMyFitness
    • Polar
    • +
    • RP3
  • @@ -64,7 +65,7 @@ {% if member == rower %} • {% else %} -   +   {% endif %} {{ member.user.first_name }} {{ member.user.last_name }} @@ -85,7 +86,7 @@ {% for tteam in teams %}
  • - + {% if tteam == team %} • {% else %} diff --git a/rowers/templates/rp3_list_import.html b/rowers/templates/rp3_list_import.html new file mode 100644 index 00000000..49b6ff61 --- /dev/null +++ b/rowers/templates/rp3_list_import.html @@ -0,0 +1,43 @@ +{% extends "newbase.html" %} +{% load staticfiles %} +{% load rowerfilters %} + +{% block title %}Workouts{% endblock %} + +{% block main %} +

    Available on RP3

    +{% if workouts %} +
      +
    • + + + + + + + + + + {% for workout in workouts %} + + + + + + + {% endfor %} + +
      Import Date New
      + Import{{ workout|lookup:'starttime' }}{{ workout|lookup:'new' }}
      +
    • +
    +{% else %} +

    + No workouts found +

    + {% endif %} + {% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} +{% endblock %} diff --git a/rowers/urls.py b/rowers/urls.py index d33b5393..44201c87 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -537,6 +537,8 @@ urlpatterns = [ re_path(r'^workout/c2list/(?P\d+)/$',views.workout_c2import_view,name='workout_c2import_view'), re_path(r'^workout/c2list/user/(?P\d+)/$',views.workout_c2import_view,name='workout_c2import_view'), re_path(r'^workout/c2list/(?P\d+)/user/(?P\d+)/$',views.workout_c2import_view,name='workout_c2import_view'), + re_path(r'^workout/rp3import/$',views.workout_rp3import_view,name='workout_rp3import_view'), + re_path(r'^workout/rp3import/user/(?P\d+)/$',views.workout_rp3import_view,name='workout_rp3import_view'), re_path(r'^workout/stravaimport/$',views.workout_stravaimport_view,name='workout_stravaimport_view'), re_path(r'^workout/stravaimport/user/(?P\d+)/$',views.workout_stravaimport_view,name='workout_stravaimport_view'), re_path(r'^workout/c2import/all/$',views.workout_getc2workout_all,name='workout_getc2workout_all'), diff --git a/rowers/views/importviews.py b/rowers/views/importviews.py index de8994f8..00086950 100644 --- a/rowers/views/importviews.py +++ b/rowers/views/importviews.py @@ -964,6 +964,78 @@ def rower_process_testcallback(request): return HttpResponse(text) +@login_required() +@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) +def workout_rp3import_view(request,userid=0): + r = getrequestrower(request,userid=userid) + + try: + thetoken = rp3stuff.rp3_open(request.user) + except NoTokenError: + url = reverse('rower_rp3_authorize') + return HttpResponseRedirect(url) + + res = rp3stuff.get_rp3_workout_list(request.user) + + if (res.status_code != 200): + if (res.status_code == 401): + r = getrower(request.user) + 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" + messages.error(request,message) + url = reverse('workouts_view') + return HttpResponseRedirect(url) + + workouts_list = pd.json_normalize(res.json()['data']['workouts']) + + + rp3ids = workouts_list['id'].values + + knownrp3ids = uniqify([ + w.uploadedtorp3 for w in Workout.objects.filter(user=r) + ]) + + newids = [rp3id for rp3id in rp3ids if not rp3id in knownrp3ids] + + workouts = [] + + for key,data in workouts_list.iterrows(): + i = data['id'] + if i in knownrp3ids: + nnn = '' + else: + nnn = 'NEW' + + s = data['executed_at'] + + keys = ['id','starttime','new'] + values = [i,s,nnn] + + res = dict(zip(keys,values)) + + workouts.append(res) + + breadcrumbs = [ + { + 'url':'/rowers/list-workouts/', + 'name':'Workouts' + }, + { + 'url':reverse('workout_stravaimport_view'), + 'name':'Strava' + }, + ] + + return render(request,'rp3_list_import.html', + { + 'workouts':workouts, + 'rower':r, + 'active':'nav-workouts', + 'breadcrumbs':breadcrumbs, + 'teams':get_my_teams(request.user) + }) # The page where you select which Strava workout to import