import list rp3 working
This commit is contained in:
@@ -21,7 +21,6 @@ from rowsandall_app.settings import (
|
|||||||
RP3_CLIENT_ID, RP3_CLIENT_KEY, RP3_REDIRECT_URI, RP3_CLIENT_SECRET
|
RP3_CLIENT_ID, RP3_CLIENT_KEY, RP3_REDIRECT_URI, RP3_CLIENT_SECRET
|
||||||
)
|
)
|
||||||
|
|
||||||
tpapilocation = "https://api.trainingpeaks.com"
|
|
||||||
|
|
||||||
from celery import Celery,app
|
from celery import Celery,app
|
||||||
from django_rq import job
|
from django_rq import job
|
||||||
@@ -45,6 +44,8 @@ oauth_data = {
|
|||||||
|
|
||||||
from rowers.rower_rules import is_workout_user
|
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
|
# Checks if user has UnderArmour token, renews them if they are expired
|
||||||
def rp3_open(user):
|
def rp3_open(user):
|
||||||
@@ -73,8 +74,6 @@ def get_token(code):
|
|||||||
data=post_data,verify=False,
|
data=post_data,verify=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
print(response.json())
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
token_json = response.json()
|
token_json = response.json()
|
||||||
@@ -93,124 +92,23 @@ def make_authorization_url(request):
|
|||||||
return imports_make_authorization_url(oauth_data)
|
return imports_make_authorization_url(oauth_data)
|
||||||
|
|
||||||
|
|
||||||
def getidfromresponse(response):
|
def get_rp3_workout_list(user):
|
||||||
t = json.loads(response.text)
|
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)
|
get_workouts_list = """{
|
||||||
|
workouts{
|
||||||
def createtpworkoutdata(w):
|
id
|
||||||
filename = w.csvfilename
|
executed_at
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
|
}"""
|
||||||
|
response = requests.post(
|
||||||
|
url=graphql_url,
|
||||||
|
headers=headers,
|
||||||
|
json={'query': get_workouts_list}
|
||||||
|
)
|
||||||
|
|
||||||
resp = requests.post(tpapilocation+"/v1/file",
|
return response
|
||||||
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
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<a href="/rowers/team-compare-select/team/0/">
|
<a href="/rowers/team-compare-select/team/0/">
|
||||||
<i class="fas fa-balance-scale fa-fw"></i> Compare</a>
|
<i class="fas fa-balance-scale fa-fw"></i> Compare</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/rowers/workout/upload/"><i class="fas fa-file-upload fa-fw"></i> Upload</a>
|
<a href="/rowers/workout/upload/"><i class="fas fa-file-upload fa-fw"></i> Upload</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<li class="has-children" id="imports">
|
<li class="has-children" id="imports">
|
||||||
<input type="checkbox" name ="group-1" id="group-1">
|
<input type="checkbox" name ="group-1" id="group-1">
|
||||||
<label for="group-1"><i class="fas fa-cloud-download fa-fw"></i> Import</label>
|
<label for="group-1"><i class="fas fa-cloud-download fa-fw"></i> Import</label>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li id="concept2"><a href="/rowers/workout/c2list/">Concept2</a></li>
|
<li id="concept2"><a href="/rowers/workout/c2list/">Concept2</a></li>
|
||||||
<li id="strava"><a href="/rowers/workout/stravaimport/">Strava</a></li>
|
<li id="strava"><a href="/rowers/workout/stravaimport/">Strava</a></li>
|
||||||
@@ -43,6 +43,7 @@
|
|||||||
<li id="sporttracks"><a href="/rowers/workout/sporttracksimport/">SportTracks</a></li>
|
<li id="sporttracks"><a href="/rowers/workout/sporttracksimport/">SportTracks</a></li>
|
||||||
<li id="mapmyfitness"><a href="/rowers/workout/underarmourimport/">MapMyFitness</a></li>
|
<li id="mapmyfitness"><a href="/rowers/workout/underarmourimport/">MapMyFitness</a></li>
|
||||||
<li id="polar"><a href="/rowers/workout/polarimport/">Polar</a></li>
|
<li id="polar"><a href="/rowers/workout/polarimport/">Polar</a></li>
|
||||||
|
<li id="rp3"><a href="/rowers/workout/rp3import/">RP3</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul> <!-- cd-accordion-menu -->
|
</ul> <!-- cd-accordion-menu -->
|
||||||
@@ -64,7 +65,7 @@
|
|||||||
{% if member == rower %}
|
{% if member == rower %}
|
||||||
•
|
•
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ member.user.first_name }} {{ member.user.last_name }}
|
{{ member.user.first_name }} {{ member.user.last_name }}
|
||||||
</a>
|
</a>
|
||||||
@@ -85,7 +86,7 @@
|
|||||||
{% for tteam in teams %}
|
{% for tteam in teams %}
|
||||||
<li>
|
<li>
|
||||||
<a href={{ request.path|teamurl:tteam }}>
|
<a href={{ request.path|teamurl:tteam }}>
|
||||||
<i class="fas fa-users fa-fw"></i>
|
<i class="fas fa-users fa-fw"></i>
|
||||||
{% if tteam == team %}
|
{% if tteam == team %}
|
||||||
•
|
•
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|||||||
43
rowers/templates/rp3_list_import.html
Normal file
43
rowers/templates/rp3_list_import.html
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{% extends "newbase.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% load rowerfilters %}
|
||||||
|
|
||||||
|
{% block title %}Workouts{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
<h1>Available on RP3</h1>
|
||||||
|
{% if workouts %}
|
||||||
|
<ul class="main-content">
|
||||||
|
<li class="grid_4">
|
||||||
|
<table width="70%" class="listtable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th> Import </th>
|
||||||
|
<th> Date</th>
|
||||||
|
<th> New</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for workout in workouts %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="/rowers/workout/rp3import/{{ workout|lookup:'id' }}/">Import</a></td>
|
||||||
|
<td>{{ workout|lookup:'starttime' }}</td>
|
||||||
|
<td>{{ workout|lookup:'new' }}</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{% else %}
|
||||||
|
<p>
|
||||||
|
No workouts found
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block sidebar %}
|
||||||
|
{% include 'menu_workouts.html' %}
|
||||||
|
{% endblock %}
|
||||||
@@ -537,6 +537,8 @@ urlpatterns = [
|
|||||||
re_path(r'^workout/c2list/(?P<page>\d+)/$',views.workout_c2import_view,name='workout_c2import_view'),
|
re_path(r'^workout/c2list/(?P<page>\d+)/$',views.workout_c2import_view,name='workout_c2import_view'),
|
||||||
re_path(r'^workout/c2list/user/(?P<userid>\d+)/$',views.workout_c2import_view,name='workout_c2import_view'),
|
re_path(r'^workout/c2list/user/(?P<userid>\d+)/$',views.workout_c2import_view,name='workout_c2import_view'),
|
||||||
re_path(r'^workout/c2list/(?P<page>\d+)/user/(?P<userid>\d+)/$',views.workout_c2import_view,name='workout_c2import_view'),
|
re_path(r'^workout/c2list/(?P<page>\d+)/user/(?P<userid>\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<userid>\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/$',views.workout_stravaimport_view,name='workout_stravaimport_view'),
|
||||||
re_path(r'^workout/stravaimport/user/(?P<userid>\d+)/$',views.workout_stravaimport_view,name='workout_stravaimport_view'),
|
re_path(r'^workout/stravaimport/user/(?P<userid>\d+)/$',views.workout_stravaimport_view,name='workout_stravaimport_view'),
|
||||||
re_path(r'^workout/c2import/all/$',views.workout_getc2workout_all,name='workout_getc2workout_all'),
|
re_path(r'^workout/c2import/all/$',views.workout_getc2workout_all,name='workout_getc2workout_all'),
|
||||||
|
|||||||
@@ -964,6 +964,78 @@ def rower_process_testcallback(request):
|
|||||||
|
|
||||||
return HttpResponse(text)
|
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
|
# The page where you select which Strava workout to import
|
||||||
|
|||||||
Reference in New Issue
Block a user