runkeeper retired, updated sporttraclks
This commit is contained in:
@@ -46,7 +46,6 @@ class RowerInline(admin.StackedInline):
|
|||||||
'sporttrackstoken','sporttrackstokenexpirydate',
|
'sporttrackstoken','sporttrackstokenexpirydate',
|
||||||
'sporttracksrefreshtoken',
|
'sporttracksrefreshtoken',
|
||||||
'sporttracks_auto_export',
|
'sporttracks_auto_export',
|
||||||
'mapmyfitness_auto_export',
|
|
||||||
'tptoken','tptokenexpirydate','tprefreshtoken',
|
'tptoken','tptokenexpirydate','tprefreshtoken',
|
||||||
'trainingpeaks_auto_export',
|
'trainingpeaks_auto_export',
|
||||||
'polartoken','polartokenexpirydate',
|
'polartoken','polartokenexpirydate',
|
||||||
@@ -55,7 +54,6 @@ class RowerInline(admin.StackedInline):
|
|||||||
'stravatoken','stravatokenexpirydate','stravarefreshtoken',
|
'stravatoken','stravatokenexpirydate','stravarefreshtoken',
|
||||||
'stravaexportas','strava_auto_export',
|
'stravaexportas','strava_auto_export',
|
||||||
'strava_auto_import',
|
'strava_auto_import',
|
||||||
'runkeepertoken','runkeeper_auto_export',
|
|
||||||
'garmintoken','garminrefreshtoken')}),
|
'garmintoken','garminrefreshtoken')}),
|
||||||
('Team',
|
('Team',
|
||||||
{'fields':('friends','privacy','team')}),
|
{'fields':('friends','privacy','team')}),
|
||||||
|
|||||||
@@ -825,14 +825,9 @@ def make_authorization_url(request): # pragma: no cover
|
|||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
# Get workout from C2 ID
|
# Get workout from C2 ID
|
||||||
def get_workout(user,c2id,do_async=False):
|
def get_workout(user,c2id,do_async=True):
|
||||||
r = Rower.objects.get(user=user)
|
r = Rower.objects.get(user=user)
|
||||||
if (r.c2token == '') or (r.c2token is None): # pragma: no cover
|
thetoken = c2_open(user)
|
||||||
s = "Token doesn't exist. Need to authorize"
|
|
||||||
return custom_exception_handler(401,s) ,0
|
|
||||||
elif (timezone.now()>r.tokenexpirydate):
|
|
||||||
s = "Token expired. Needs to refresh."
|
|
||||||
return custom_exception_handler(401,s),0
|
|
||||||
|
|
||||||
job = myqueue(queuehigh,
|
job = myqueue(queuehigh,
|
||||||
handle_c2_getworkout,
|
handle_c2_getworkout,
|
||||||
|
|||||||
@@ -988,7 +988,6 @@ class Rower(models.Model):
|
|||||||
sporttracksrefreshtoken = models.CharField(default='',max_length=200,
|
sporttracksrefreshtoken = models.CharField(default='',max_length=200,
|
||||||
blank=True,null=True)
|
blank=True,null=True)
|
||||||
sporttracks_auto_export = models.BooleanField(default=False)
|
sporttracks_auto_export = models.BooleanField(default=False)
|
||||||
mapmyfitness_auto_export = models.BooleanField(default=False)
|
|
||||||
tptoken = models.CharField(default='',max_length=1000,blank=True,null=True)
|
tptoken = models.CharField(default='',max_length=1000,blank=True,null=True)
|
||||||
tptokenexpirydate = models.DateTimeField(blank=True,null=True)
|
tptokenexpirydate = models.DateTimeField(blank=True,null=True)
|
||||||
tprefreshtoken = models.CharField(default='',max_length=1000,
|
tprefreshtoken = models.CharField(default='',max_length=1000,
|
||||||
@@ -1037,9 +1036,6 @@ class Rower(models.Model):
|
|||||||
strava_auto_export = models.BooleanField(default=False)
|
strava_auto_export = models.BooleanField(default=False)
|
||||||
strava_auto_import = models.BooleanField(default=False)
|
strava_auto_import = models.BooleanField(default=False)
|
||||||
strava_auto_delete = models.BooleanField(default=False)
|
strava_auto_delete = models.BooleanField(default=False)
|
||||||
runkeepertoken = models.CharField(default='',max_length=200,
|
|
||||||
blank=True,null=True)
|
|
||||||
runkeeper_auto_export = models.BooleanField(default=False)
|
|
||||||
|
|
||||||
|
|
||||||
privacychoices = (
|
privacychoices = (
|
||||||
@@ -3198,7 +3194,6 @@ class Workout(models.Model):
|
|||||||
uploadedtostrava = models.BigIntegerField(default=0)
|
uploadedtostrava = models.BigIntegerField(default=0)
|
||||||
uploadedtosporttracks = models.BigIntegerField(default=0)
|
uploadedtosporttracks = models.BigIntegerField(default=0)
|
||||||
uploadedtotp = models.BigIntegerField(default=0)
|
uploadedtotp = models.BigIntegerField(default=0)
|
||||||
uploadedtorunkeeper = models.BigIntegerField(default=0)
|
|
||||||
uploadedtogarmin = models.BigIntegerField(default=0)
|
uploadedtogarmin = models.BigIntegerField(default=0)
|
||||||
uploadedtorp3 = models.BigIntegerField(default=0)
|
uploadedtorp3 = models.BigIntegerField(default=0)
|
||||||
uploadedtonk = models.BigIntegerField(default=0)
|
uploadedtonk = models.BigIntegerField(default=0)
|
||||||
@@ -3279,7 +3274,6 @@ class TombStone(models.Model):
|
|||||||
uploadedtostrava = models.BigIntegerField(default=0)
|
uploadedtostrava = models.BigIntegerField(default=0)
|
||||||
uploadedtosporttracks = models.BigIntegerField(default=0)
|
uploadedtosporttracks = models.BigIntegerField(default=0)
|
||||||
uploadedtotp = models.BigIntegerField(default=0)
|
uploadedtotp = models.BigIntegerField(default=0)
|
||||||
uploadedtorunkeeper = models.BigIntegerField(default=0)
|
|
||||||
uploadedtonk = models.BigIntegerField(default=0)
|
uploadedtonk = models.BigIntegerField(default=0)
|
||||||
|
|
||||||
@receiver(models.signals.pre_delete,sender=Workout)
|
@receiver(models.signals.pre_delete,sender=Workout)
|
||||||
@@ -3289,7 +3283,6 @@ def create_tombstone_on_delete(sender, instance, **kwargs):
|
|||||||
uploadedtoc2 = instance.uploadedtoc2,
|
uploadedtoc2 = instance.uploadedtoc2,
|
||||||
uploadedtostrava = instance.uploadedtostrava,
|
uploadedtostrava = instance.uploadedtostrava,
|
||||||
uploadedtotp = instance.uploadedtotp,
|
uploadedtotp = instance.uploadedtotp,
|
||||||
uploadedtorunkeeper = instance.uploadedtorunkeeper,
|
|
||||||
uploadedtonk = instance.uploadedtonk
|
uploadedtonk = instance.uploadedtonk
|
||||||
)
|
)
|
||||||
t.save()
|
t.save()
|
||||||
@@ -3864,8 +3857,6 @@ class RowerExportForm(ModelForm):
|
|||||||
'c2_auto_export',
|
'c2_auto_export',
|
||||||
'c2_auto_import',
|
'c2_auto_import',
|
||||||
'nk_auto_import',
|
'nk_auto_import',
|
||||||
'mapmyfitness_auto_export',
|
|
||||||
'runkeeper_auto_export',
|
|
||||||
'sporttracks_auto_export',
|
'sporttracks_auto_export',
|
||||||
'strava_auto_export',
|
'strava_auto_export',
|
||||||
'strava_auto_import',
|
'strava_auto_import',
|
||||||
@@ -4208,8 +4199,6 @@ class RowerImportExportForm(ModelForm):
|
|||||||
'polar_auto_import',
|
'polar_auto_import',
|
||||||
'c2_auto_export',
|
'c2_auto_export',
|
||||||
'c2_auto_import',
|
'c2_auto_import',
|
||||||
'mapmyfitness_auto_export',
|
|
||||||
'runkeeper_auto_export',
|
|
||||||
'sporttracks_auto_export',
|
'sporttracks_auto_export',
|
||||||
'strava_auto_export',
|
'strava_auto_export',
|
||||||
'strava_auto_import',
|
'strava_auto_import',
|
||||||
|
|||||||
@@ -1,530 +0,0 @@
|
|||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from __future__ import unicode_literals, absolute_import
|
|
||||||
# All the functionality needed to connect to Runkeeper
|
|
||||||
from rowers.imports import *
|
|
||||||
import re
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
from rowers.rower_rules import is_workout_user
|
|
||||||
|
|
||||||
from rowsandall_app.settings import (
|
|
||||||
C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET,
|
|
||||||
STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET,
|
|
||||||
RUNKEEPER_CLIENT_ID, RUNKEEPER_CLIENT_SECRET,RUNKEEPER_REDIRECT_URI,
|
|
||||||
)
|
|
||||||
|
|
||||||
from rowers.tasks import handle_runkeeper_sync
|
|
||||||
|
|
||||||
oauth_data = {
|
|
||||||
'client_id': RUNKEEPER_CLIENT_ID,
|
|
||||||
'client_secret': RUNKEEPER_CLIENT_SECRET,
|
|
||||||
'redirect_uri': RUNKEEPER_REDIRECT_URI,
|
|
||||||
'autorization_uri': "https://www.runkeeper.com/apps/authorize",
|
|
||||||
'content_type': 'application/x-www-form-urlencoded',
|
|
||||||
'tokenname': 'runkeepertoken',
|
|
||||||
'refreshtokenname': None,
|
|
||||||
'expirydatename': None,
|
|
||||||
'bearer_auth': True,
|
|
||||||
'base_url': "https://runkeeper.com/apps/token",
|
|
||||||
'headers': {'user-agent': 'sanderroosendaal'},
|
|
||||||
'scope':'write',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
import numpy
|
|
||||||
|
|
||||||
def splitrunkeeperlatlongdata(lijst,tname,latname,lonname):
|
|
||||||
t = []
|
|
||||||
lat = []
|
|
||||||
lon = []
|
|
||||||
for d in lijst:
|
|
||||||
t.append(d[tname])
|
|
||||||
lat.append(d[latname])
|
|
||||||
lon.append(d[lonname])
|
|
||||||
|
|
||||||
return [np.array(t),np.array(lat),np.array(lon)]
|
|
||||||
|
|
||||||
def splitrunkeeperdata(lijst,xname,yname):
|
|
||||||
x = []
|
|
||||||
y = []
|
|
||||||
for d in lijst:
|
|
||||||
x.append(d[xname])
|
|
||||||
y.append(d[yname])
|
|
||||||
|
|
||||||
return [np.array(x),np.array(y)]
|
|
||||||
|
|
||||||
|
|
||||||
# Checks if user has SportTracks token, renews them if they are expired
|
|
||||||
def runkeeper_open(user):
|
|
||||||
return imports_open(user,oauth_data)
|
|
||||||
|
|
||||||
# Exchange access code for long-lived access token
|
|
||||||
def get_token(code):
|
|
||||||
return imports_get_token(code,oauth_data)
|
|
||||||
|
|
||||||
# Make authorization URL including random string
|
|
||||||
def make_authorization_url(request): # pragma: no cover
|
|
||||||
return imports_make_authorization_url(oauth_data)
|
|
||||||
|
|
||||||
# Get list of workouts available on Runkeeper
|
|
||||||
def get_runkeeper_workout_list(user):
|
|
||||||
r = Rower.objects.get(user=user)
|
|
||||||
if (r.runkeepertoken == '') or (r.runkeepertoken is None):
|
|
||||||
s = "Token doesn't exist. Need to authorize"
|
|
||||||
return custom_exception_handler(401,s)
|
|
||||||
else:
|
|
||||||
# ready to fetch. Hurray
|
|
||||||
authorizationstring = str('Bearer ' + r.runkeepertoken)
|
|
||||||
headers = {'Authorization': authorizationstring,
|
|
||||||
'user-agent': 'sanderroosendaal',
|
|
||||||
'Content-Type': 'application/json'}
|
|
||||||
url = "https://api.runkeeper.com/fitnessActivities"
|
|
||||||
s = requests.get(url,headers=headers)
|
|
||||||
|
|
||||||
return s
|
|
||||||
|
|
||||||
# Get workout summary data by Runkeeper ID
|
|
||||||
def get_workout(user,runkeeperid,do_async=False):
|
|
||||||
r = Rower.objects.get(user=user)
|
|
||||||
if (r.runkeepertoken == '') or (r.runkeepertoken is None): # pragma: no cover
|
|
||||||
return custom_exception_handler(401,s)
|
|
||||||
s = "Token doesn't exist. Need to authorize"
|
|
||||||
else:
|
|
||||||
# ready to fetch. Hurray
|
|
||||||
authorizationstring = str('Bearer ' + r.runkeepertoken)
|
|
||||||
headers = {'Authorization': authorizationstring,
|
|
||||||
'user-agent': 'sanderroosendaal',
|
|
||||||
'Content-Type': 'application/json'}
|
|
||||||
url = "https://api.runkeeper.com/fitnessActivities/"+str(runkeeperid)
|
|
||||||
s = requests.get(url,headers=headers)
|
|
||||||
|
|
||||||
try:
|
|
||||||
data = s.json()
|
|
||||||
except ValueError: # pragma: no cover
|
|
||||||
data = {}
|
|
||||||
return data,"Something went wrong with the workout import"
|
|
||||||
|
|
||||||
strokedata = pd.DataFrame.from_dict({
|
|
||||||
key: pd.Series(value) for key, value in data.items()
|
|
||||||
})
|
|
||||||
|
|
||||||
return data,strokedata
|
|
||||||
|
|
||||||
# Create Workout Data for upload to SportTracks
|
|
||||||
def createrunkeeperworkoutdata(w):
|
|
||||||
filename = w.csvfilename
|
|
||||||
try:
|
|
||||||
row = rowingdata(csvfile=filename)
|
|
||||||
except: # pragma: no cover
|
|
||||||
return 0
|
|
||||||
|
|
||||||
try:
|
|
||||||
averagehr = int(row.df[' HRCur (bpm)'].mean())
|
|
||||||
maxhr = int(row.df[' HRCur (bpm)'].max())
|
|
||||||
except KeyError: # pragma: no cover
|
|
||||||
averagehr = 0
|
|
||||||
maxhr = 0
|
|
||||||
|
|
||||||
duration = w.duration.hour*3600
|
|
||||||
duration += w.duration.minute*60
|
|
||||||
duration += w.duration.second
|
|
||||||
duration += +1.0e-6*w.duration.microsecond
|
|
||||||
|
|
||||||
try:
|
|
||||||
t = row.df.loc[:,'TimeStamp (sec)'].values-row.df.loc[:,'TimeStamp (sec)'].iloc[0]
|
|
||||||
except KeyError: # pragma: no cover
|
|
||||||
return pd.DataFrame()
|
|
||||||
|
|
||||||
t[0] = t[1]
|
|
||||||
|
|
||||||
d = row.df.loc[:,'cum_dist'].values
|
|
||||||
d[0] = d[1]
|
|
||||||
t = t.astype(int)
|
|
||||||
d = d.astype(int)
|
|
||||||
spm = row.df[' Cadence (stokes/min)'].astype(int)
|
|
||||||
spm[0] = spm[1]
|
|
||||||
hr = row.df[' HRCur (bpm)'].astype(int)
|
|
||||||
|
|
||||||
haslatlon=1
|
|
||||||
|
|
||||||
try:
|
|
||||||
lat = row.df[' latitude'].values
|
|
||||||
lon = row.df[' longitude'].values
|
|
||||||
if not lat.std() and not lon.std(): # pragma: no cover
|
|
||||||
haslatlon = 0
|
|
||||||
except KeyError: # pragma: no cover
|
|
||||||
haslatlon = 0
|
|
||||||
|
|
||||||
t = t.tolist()
|
|
||||||
hr = hr.tolist()
|
|
||||||
d = d.tolist()
|
|
||||||
|
|
||||||
# path data
|
|
||||||
if haslatlon:
|
|
||||||
lat = lat.tolist()
|
|
||||||
lon = lon.tolist()
|
|
||||||
locdata = []
|
|
||||||
for e in zip(t,lat,lon):
|
|
||||||
point = {'timestamp':e[0],
|
|
||||||
'latitude':e[1],
|
|
||||||
'longitude':e[2],
|
|
||||||
'altitude':0,
|
|
||||||
"type":"gps"}
|
|
||||||
locdata.append(point)
|
|
||||||
|
|
||||||
hrdata = []
|
|
||||||
for e in zip(t,hr):
|
|
||||||
point = {'timestamp':e[0],
|
|
||||||
'heart_rate':e[1]
|
|
||||||
}
|
|
||||||
hrdata.append(point)
|
|
||||||
|
|
||||||
distancedata = []
|
|
||||||
for e in zip(t,d):
|
|
||||||
point = {'timestamp':e[0],
|
|
||||||
'distance':e[1]
|
|
||||||
}
|
|
||||||
distancedata.append(point)
|
|
||||||
|
|
||||||
st = w.startdatetime.astimezone(pytz.timezone(w.timezone))
|
|
||||||
start_time = st.strftime("%a, %d %b %Y %H:%M:%S")
|
|
||||||
|
|
||||||
try:
|
|
||||||
newnotes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com'
|
|
||||||
except TypeError:
|
|
||||||
newnotes = 'from '+w.workoutsource+' via rowsandall.com'
|
|
||||||
|
|
||||||
if haslatlon:
|
|
||||||
data = {
|
|
||||||
"type": "Rowing",
|
|
||||||
"start_time": start_time,
|
|
||||||
"total_distance": int(w.distance),
|
|
||||||
"duration": duration,
|
|
||||||
"notes": newnotes,
|
|
||||||
"average_heart_rate": averagehr,
|
|
||||||
"path": locdata,
|
|
||||||
"distance": distancedata,
|
|
||||||
"heart_rate": hrdata,
|
|
||||||
"post_to_twitter":"false",
|
|
||||||
"post_to_facebook":"false",
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
data = {
|
|
||||||
"type": "Rowing",
|
|
||||||
"start_time": start_time,
|
|
||||||
"total_distance": int(w.distance),
|
|
||||||
"duration": duration,
|
|
||||||
"notes": newnotes,
|
|
||||||
"avg_heartrate": averagehr,
|
|
||||||
"distance": distancedata,
|
|
||||||
"heart_rate": hrdata,
|
|
||||||
"post_to_twitter":"false",
|
|
||||||
"post_to_facebook":"false",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
# Obtain Runkeeper Workout ID from the response returned on successful
|
|
||||||
# upload
|
|
||||||
def getidfromresponse(response):
|
|
||||||
uri = response.headers["Location"]
|
|
||||||
|
|
||||||
tester = re.compile('^\/fitnessActivities\/(\d+)$')
|
|
||||||
id = int(tester.match(uri).group(1))
|
|
||||||
|
|
||||||
return int(id)
|
|
||||||
|
|
||||||
def geturifromid(access_token,id): # pragma: no cover
|
|
||||||
authorizationstring = str('Bearer ' + access_token)
|
|
||||||
headers = {'Authorization': authorizationstring,
|
|
||||||
'user-agent': 'sanderroosendaal',
|
|
||||||
'Content-Type': 'application/json'}
|
|
||||||
import urllib
|
|
||||||
url = "https://api.runkeeper.com/fitnessActivities/"+str(id)
|
|
||||||
response = requests.get(url,headers=headers)
|
|
||||||
try:
|
|
||||||
me_json = response.json()
|
|
||||||
except:
|
|
||||||
return ''
|
|
||||||
|
|
||||||
try:
|
|
||||||
res = me_json['uri']
|
|
||||||
except KeyError:
|
|
||||||
res = ''
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
# Get user id, having access token
|
|
||||||
# Handy for checking if the API access is working
|
|
||||||
def get_userid(access_token):
|
|
||||||
authorizationstring = str('Bearer ' + access_token)
|
|
||||||
headers = {'Authorization': authorizationstring,
|
|
||||||
'user-agent': 'sanderroosendaal',
|
|
||||||
'Content-Type': 'application/json'}
|
|
||||||
import urllib
|
|
||||||
url = "https://api.runkeeper.com/user"
|
|
||||||
response = requests.get(url,headers=headers)
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
me_json = response.json()
|
|
||||||
except: # pragma: no cover
|
|
||||||
return ''
|
|
||||||
|
|
||||||
try:
|
|
||||||
res = me_json['userID']
|
|
||||||
except KeyError: # pragma: no cover
|
|
||||||
res = ''
|
|
||||||
|
|
||||||
return str(res)
|
|
||||||
|
|
||||||
def default(o): # pragma: no cover
|
|
||||||
if isinstance(o, numpy.int64): return int(o)
|
|
||||||
raise TypeError
|
|
||||||
|
|
||||||
def workout_runkeeper_upload(user,w,asynchron=False): # pragma: no cover
|
|
||||||
message = "Uploading to Runkeeper"
|
|
||||||
rkid = 0
|
|
||||||
|
|
||||||
r = w.user
|
|
||||||
|
|
||||||
|
|
||||||
thetoken = runkeeper_open(r.user)
|
|
||||||
|
|
||||||
# ready to upload. Hurray
|
|
||||||
|
|
||||||
if (is_workout_user(user,w)):
|
|
||||||
data = createrunkeeperworkoutdata(w)
|
|
||||||
if not data:
|
|
||||||
message = "Data error in Runkeeper Upload"
|
|
||||||
rkid = 0
|
|
||||||
return message, rkid
|
|
||||||
|
|
||||||
authorizationstring = str('Bearer ' + thetoken)
|
|
||||||
headers = {'Authorization': authorizationstring,
|
|
||||||
'user-agent': 'sanderroosendaal',
|
|
||||||
'Content-Type': 'application/vnd.com.runkeeper.NewFitnessActivity+json',
|
|
||||||
'Content-Length':'nnn'}
|
|
||||||
|
|
||||||
url = "https://api.runkeeper.com/fitnessActivities"
|
|
||||||
if asynchron:
|
|
||||||
job = myqueue(queue,handle_runkeeper_sync,
|
|
||||||
w.id,url,headers,json.dumps(data,default=default))
|
|
||||||
return "Asynchronous sync",0
|
|
||||||
response = requests.post(url,headers=headers,data=json.dumps(data,default=default))
|
|
||||||
|
|
||||||
# check for duplicate error first
|
|
||||||
if (response.status_code == 409 ):
|
|
||||||
message = "Duplicate error"
|
|
||||||
w.uploadedtorunkeeper = -1
|
|
||||||
rkid = -1
|
|
||||||
w.save()
|
|
||||||
return message, rkid
|
|
||||||
elif (response.status_code == 201 or response.status_code==200):
|
|
||||||
rkid = getidfromresponse(response)
|
|
||||||
rkuri = geturifromid(thetoken,rkid)
|
|
||||||
w.uploadedtorunkeeper = rkid
|
|
||||||
w.save()
|
|
||||||
return 'Successfully synchronized to Runkeeper',rkid
|
|
||||||
else:
|
|
||||||
s = response
|
|
||||||
message = "Something went wrong in workout_runkeeper_upload_view: %s - %s" % (s.reason,s.text)
|
|
||||||
rkid = 0
|
|
||||||
return message, rkid
|
|
||||||
|
|
||||||
else:
|
|
||||||
message = "You are not authorized to upload this workout"
|
|
||||||
rkid = 0
|
|
||||||
return message, rkid
|
|
||||||
|
|
||||||
return message,rkid
|
|
||||||
|
|
||||||
# Create workout from RunKeeper Data
|
|
||||||
def add_workout_from_data(user,importid,data,strokedata,source='runkeeper',
|
|
||||||
workoutsource='runkeeper'):
|
|
||||||
# To Do - add utcoffset to time
|
|
||||||
workouttype = data['type']
|
|
||||||
if workouttype not in [x[0] for x in Workout.workouttypes]:
|
|
||||||
workouttype = 'other'
|
|
||||||
try:
|
|
||||||
comments = data['notes']
|
|
||||||
except: # pragma: no cover
|
|
||||||
comments = ''
|
|
||||||
|
|
||||||
try:
|
|
||||||
utcoffset = tz(data['utcoffset'])
|
|
||||||
except:
|
|
||||||
utcoffset = 0
|
|
||||||
|
|
||||||
r = Rower.objects.get(user=user)
|
|
||||||
|
|
||||||
try:
|
|
||||||
rowdatetime = iso8601.parse_date(data['start_time'])
|
|
||||||
except iso8601.ParseError:
|
|
||||||
try: # pragma: no cover
|
|
||||||
rowdatetime = datetime.strptime(data['start_time'],"%Y-%m-%d %H:%M:%S")
|
|
||||||
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
|
|
||||||
except ValueError:
|
|
||||||
try:
|
|
||||||
rowdatetime = parser.parse(data['start_time'])
|
|
||||||
#rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
|
|
||||||
except: # pragma: no cover
|
|
||||||
rowdatetime = datetime.strptime(data['date'],"%Y-%m-%d %H:%M:%S")
|
|
||||||
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
|
|
||||||
starttimeunix = arrow.get(rowdatetime).timestamp()
|
|
||||||
#starttimeunix = mktime(rowdatetime.utctimetuple())
|
|
||||||
starttimeunix += utcoffset*3600
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
title = data['name']
|
|
||||||
except:
|
|
||||||
title = "Imported data"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
res = splitrunkeeperdata(data['distance'],'timestamp','distance')
|
|
||||||
|
|
||||||
distance = res[1]
|
|
||||||
times_distance = res[0]
|
|
||||||
|
|
||||||
try:
|
|
||||||
l = data['path']
|
|
||||||
|
|
||||||
res = splitrunkeeperlatlongdata(l,'timestamp','latitude','longitude')
|
|
||||||
times_location = res[0]
|
|
||||||
latcoord = res[1]
|
|
||||||
loncoord = res[2]
|
|
||||||
|
|
||||||
except: # pragma: no cover
|
|
||||||
times_location = times_distance
|
|
||||||
latcoord = np.zeros(len(times_distance))
|
|
||||||
loncoord = np.zeros(len(times_distance))
|
|
||||||
if workouttype in types.otwtypes:
|
|
||||||
workouttype = 'rower'
|
|
||||||
|
|
||||||
try: # pragma: no cover
|
|
||||||
res = splitrunkeeperdata(data['cadence'],'timestamp','cadence')
|
|
||||||
times_spm = res[0]
|
|
||||||
spm = res[1]
|
|
||||||
except KeyError:
|
|
||||||
times_spm = times_distance
|
|
||||||
spm = 0*times_distance
|
|
||||||
|
|
||||||
try:
|
|
||||||
res = splitrunkeeperdata(data['heart_rate'],'timestamp','heart_rate')
|
|
||||||
hr = res[1]
|
|
||||||
times_hr = res[0]
|
|
||||||
except KeyError: # pragma: no cover
|
|
||||||
times_hr = times_distance
|
|
||||||
hr = 0*times_distance
|
|
||||||
|
|
||||||
|
|
||||||
# create data series and remove duplicates
|
|
||||||
distseries = pd.Series(distance,index=times_distance)
|
|
||||||
distseries = distseries.groupby(distseries.index).first()
|
|
||||||
latseries = pd.Series(latcoord,index=times_location)
|
|
||||||
try:
|
|
||||||
latseries = latseries.groupby(latseries.index).first()
|
|
||||||
except TypeError: # pragma: no cover
|
|
||||||
latseries = 0.0*distseries
|
|
||||||
|
|
||||||
lonseries = pd.Series(loncoord,index=times_location)
|
|
||||||
try:
|
|
||||||
lonseries = lonseries.groupby(lonseries.index).first()
|
|
||||||
except TypeError: # pragma: no cover
|
|
||||||
lonseries = 0.0*distseries
|
|
||||||
|
|
||||||
spmseries = pd.Series(spm,index=times_spm)
|
|
||||||
spmseries = spmseries.groupby(spmseries.index).first()
|
|
||||||
hrseries = pd.Series(hr,index=times_hr)
|
|
||||||
try:
|
|
||||||
hrseries = hrseries.groupby(hrseries.index).first()
|
|
||||||
except TypeError: # pragma: no cover
|
|
||||||
hrseries = 0*distseries
|
|
||||||
|
|
||||||
|
|
||||||
# Create dicts and big dataframe
|
|
||||||
d = {
|
|
||||||
' Horizontal (meters)': distseries,
|
|
||||||
' latitude': latseries,
|
|
||||||
' longitude': lonseries,
|
|
||||||
' Cadence (stokes/min)': spmseries,
|
|
||||||
' HRCur (bpm)' : hrseries,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
df = pd.DataFrame(d)
|
|
||||||
|
|
||||||
df = df.groupby(level=0).last()
|
|
||||||
|
|
||||||
cum_time = df.index.values
|
|
||||||
df[' ElapsedTime (sec)'] = cum_time
|
|
||||||
|
|
||||||
velo = df[' Horizontal (meters)'].diff()/df[' ElapsedTime (sec)'].diff()
|
|
||||||
|
|
||||||
df[' Power (watts)'] = 0.0*velo
|
|
||||||
|
|
||||||
nr_rows = len(velo.values)
|
|
||||||
|
|
||||||
df[' DriveLength (meters)'] = np.zeros(nr_rows)
|
|
||||||
df[' StrokeDistance (meters)'] = np.zeros(nr_rows)
|
|
||||||
df[' DriveTime (ms)'] = np.zeros(nr_rows)
|
|
||||||
df[' StrokeRecoveryTime (ms)'] = np.zeros(nr_rows)
|
|
||||||
df[' AverageDriveForce (lbs)'] = np.zeros(nr_rows)
|
|
||||||
df[' PeakDriveForce (lbs)'] = np.zeros(nr_rows)
|
|
||||||
df[' lapIdx'] = np.zeros(nr_rows)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
unixtime = cum_time+starttimeunix
|
|
||||||
try:
|
|
||||||
unixtime[0] = starttimeunix
|
|
||||||
except IndexError: # pragma: no cover
|
|
||||||
return (0,'No data to import')
|
|
||||||
|
|
||||||
df['TimeStamp (sec)'] = unixtime
|
|
||||||
|
|
||||||
|
|
||||||
dt = np.diff(cum_time).mean()
|
|
||||||
wsize = round(5./dt)
|
|
||||||
|
|
||||||
# velo2 = stravastuff.ewmovingaverage(velo,wsize)
|
|
||||||
|
|
||||||
# df[' Stroke500mPace (sec/500m)'] = 500./velo2
|
|
||||||
|
|
||||||
|
|
||||||
df = df.fillna(0)
|
|
||||||
|
|
||||||
df.sort_values(by='TimeStamp (sec)',ascending=True)
|
|
||||||
|
|
||||||
timestr = strftime("%Y%m%d-%H%M%S")
|
|
||||||
|
|
||||||
# csvfilename ='media/Import_'+str(importid)+'.csv'
|
|
||||||
csvfilename ='media/{code}_{importid}.csv'.format(
|
|
||||||
importid=importid,
|
|
||||||
code = uuid4().hex[:16]
|
|
||||||
)
|
|
||||||
|
|
||||||
res = df.to_csv(csvfilename+'.gz',index_label='index',
|
|
||||||
compression='gzip')
|
|
||||||
|
|
||||||
id,message = dataprep.save_workout_database(csvfilename,r,
|
|
||||||
workouttype=workouttype,
|
|
||||||
workoutsource='runkeeper',
|
|
||||||
title=title,
|
|
||||||
dosmooth=r.dosmooth,
|
|
||||||
notes=comments)
|
|
||||||
|
|
||||||
return (id,message)
|
|
||||||
@@ -120,7 +120,14 @@ def get_workout(user,sporttracksid,do_async=False):
|
|||||||
key: pd.Series(value) for key, value in data.items()
|
key: pd.Series(value) for key, value in data.items()
|
||||||
})
|
})
|
||||||
|
|
||||||
return data,strokedata
|
id,message = add_workout_from_data(
|
||||||
|
user,
|
||||||
|
sporttracksid,data,
|
||||||
|
strokedata,
|
||||||
|
source='sporttracks',
|
||||||
|
workoutsource='sporttracks')
|
||||||
|
|
||||||
|
return id
|
||||||
|
|
||||||
# Create Workout Data for upload to SportTracks
|
# Create Workout Data for upload to SportTracks
|
||||||
def createsporttracksworkoutdata(w):
|
def createsporttracksworkoutdata(w):
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ from rowingdata import rowingdata as rdata
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
|
|
||||||
from rowers.imports import splituadata
|
|
||||||
|
|
||||||
#from celery import app
|
#from celery import app
|
||||||
from rowers.celery import app
|
from rowers.celery import app
|
||||||
|
|||||||
@@ -192,21 +192,6 @@
|
|||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
<li id="export-rk">
|
|
||||||
{% if workout.uploadedtorunkeeper %}
|
|
||||||
<a href="https://runkeeper.com/user/{{ user|rkuserid }}/activity/{{ workout.uploadedtorunkeeper }}">
|
|
||||||
Runkeeper <i class="fas fa-check"></i>
|
|
||||||
</a>
|
|
||||||
{% elif user.rower.runkeepertoken == None or user.rower.runkeepertoken == '' %}
|
|
||||||
<a href="/rowers/me/runkeeperauthorize">
|
|
||||||
Connect to Runkeeper
|
|
||||||
</a>
|
|
||||||
{% else %}
|
|
||||||
<a href="/rowers/workout/{{ workout.id|encode }}/runkeeperuploadw/">
|
|
||||||
Runkeeper
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</li>
|
|
||||||
<li id="export-tp">
|
<li id="export-tp">
|
||||||
{% if workout.uploadedtotp %}
|
{% if workout.uploadedtotp %}
|
||||||
<a href="https://app.trainingpeaks.com">
|
<a href="https://app.trainingpeaks.com">
|
||||||
|
|||||||
@@ -40,7 +40,6 @@
|
|||||||
<li id="concept2"><a href="/rowers/workout/c2list/">Concept2</a></li>
|
<li id="concept2"><a href="/rowers/workout/c2list/">Concept2</a></li>
|
||||||
<li id="nklink"><a href="/rowers/workout/nkimport/">NK Logbook</a></li>
|
<li id="nklink"><a href="/rowers/workout/nkimport/">NK Logbook</a></li>
|
||||||
<li id="strava"><a href="/rowers/workout/stravaimport/">Strava</a></li>
|
<li id="strava"><a href="/rowers/workout/stravaimport/">Strava</a></li>
|
||||||
<li id="runkeeper"><a href="/rowers/workout/runkeeperimport/">RunKeeper</a></li>
|
|
||||||
<li id="sporttracks"><a href="/rowers/workout/sporttracksimport/">SportTracks</a></li>
|
<li id="sporttracks"><a href="/rowers/workout/sporttracksimport/">SportTracks</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>
|
<li id="rp3"><a href="/rowers/workout/rp3import/">RP3</a></li>
|
||||||
|
|||||||
@@ -29,9 +29,6 @@
|
|||||||
{% if rower.stravatoken is not None and rower.stravatoken != '' %}
|
{% if rower.stravatoken is not None and rower.stravatoken != '' %}
|
||||||
Strava,
|
Strava,
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if rower.runkeepertoken is not None and rower.runkeepertoken != '' %}
|
|
||||||
Runkeeper,
|
|
||||||
{% endif %}
|
|
||||||
{% if rower.rp3token is not None and rower.rp3token != '' %}
|
{% if rower.rp3token is not None and rower.rp3token != '' %}
|
||||||
RP3
|
RP3
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -76,7 +73,6 @@
|
|||||||
<p><a href="/rowers/me/c2authorize/"><img src="/static/img/blueC2logo.png" alt="connect with Concept2" width="120"></a></p>
|
<p><a href="/rowers/me/c2authorize/"><img src="/static/img/blueC2logo.png" alt="connect with Concept2" width="120"></a></p>
|
||||||
<p><a href="/rowers/me/nkauthorize/"><img src="/static/img/NKLiNKLogbook.png" alt="connect with NK Logbook" width="120"></a></p>
|
<p><a href="/rowers/me/nkauthorize/"><img src="/static/img/NKLiNKLogbook.png" alt="connect with NK Logbook" width="120"></a></p>
|
||||||
<p><a href="/rowers/me/sporttracksauthorize/"><img src="/static/img/sporttracks-button.png" alt="connect with SportTracks" width="120"></a></p>
|
<p><a href="/rowers/me/sporttracksauthorize/"><img src="/static/img/sporttracks-button.png" alt="connect with SportTracks" width="120"></a></p>
|
||||||
<p><a href="/rowers/me/runkeeperauthorize/"><img src="/static/img/rk-logo.png" alt="connect with RunKeeper" width="120"></a></p>
|
|
||||||
<p><a href="/rowers/me/polarauthorize/"><img src="/static/img/Polar_connectwith_btn_white.png"
|
<p><a href="/rowers/me/polarauthorize/"><img src="/static/img/Polar_connectwith_btn_white.png"
|
||||||
alt="connect with Polar" width="130"></a></p>
|
alt="connect with Polar" width="130"></a></p>
|
||||||
<p><a href="/rowers/me/tpauthorize/"><img src="/static/img/TP_logo_horz_2_color.png"
|
<p><a href="/rowers/me/tpauthorize/"><img src="/static/img/TP_logo_horz_2_color.png"
|
||||||
|
|||||||
@@ -19,9 +19,8 @@ from rowers.plannedsessions import (
|
|||||||
race_can_register, race_can_submit,race_rower_status
|
race_can_register, race_can_submit,race_rower_status
|
||||||
)
|
)
|
||||||
|
|
||||||
from rowers import c2stuff, runkeeperstuff
|
from rowers import c2stuff
|
||||||
from rowers.c2stuff import c2_open
|
from rowers.c2stuff import c2_open
|
||||||
from rowers.runkeeperstuff import runkeeper_open
|
|
||||||
from rowers.rower_rules import is_coach_user, is_workout_user, isplanmember,ispromember
|
from rowers.rower_rules import is_coach_user, is_workout_user, isplanmember,ispromember
|
||||||
from rowers.mytypes import (
|
from rowers.mytypes import (
|
||||||
otwtypes,adaptivetypes,sexcategories,weightcategories,workouttypes,
|
otwtypes,adaptivetypes,sexcategories,weightcategories,workouttypes,
|
||||||
@@ -453,16 +452,6 @@ def currency(word):
|
|||||||
|
|
||||||
return '{amount:.2f}'.format(amount=amount)
|
return '{amount:.2f}'.format(amount=amount)
|
||||||
|
|
||||||
@register.filter
|
|
||||||
def rkuserid(user):
|
|
||||||
try:
|
|
||||||
thetoken = runkeeper_open(user)
|
|
||||||
except NoTokenError: # pragma: no cover
|
|
||||||
return 0
|
|
||||||
|
|
||||||
rkuserid = runkeeperstuff.get_userid(thetoken)
|
|
||||||
|
|
||||||
return rkuserid
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def courselength(course): # pragma: no cover
|
def courselength(course): # pragma: no cover
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ from mock import Mock, patch
|
|||||||
import pandas as pd
|
import pandas as pd
|
||||||
import rowers.c2stuff as c2stuff
|
import rowers.c2stuff as c2stuff
|
||||||
import rowers.sporttracksstuff as sporttracksstuff
|
import rowers.sporttracksstuff as sporttracksstuff
|
||||||
import rowers.runkeeperstuff as runkeeperstuff
|
|
||||||
|
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
|
|
||||||
|
|||||||
@@ -313,8 +313,10 @@ class C2Objects(DjangoTestCase):
|
|||||||
|
|
||||||
response = self.c.get('/rowers/workout/c2import/12/',follow=True)
|
response = self.c.get('/rowers/workout/c2import/12/',follow=True)
|
||||||
|
|
||||||
|
expected_url = reverse('workout_c2import_view')
|
||||||
|
|
||||||
self.assertRedirects(response,
|
self.assertRedirects(response,
|
||||||
expected_url='/rowers/workout/'+encoded2+'/edit/',
|
expected_url=expected_url,
|
||||||
status_code=302,target_status_code=200)
|
status_code=302,target_status_code=200)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
@@ -485,13 +487,10 @@ class C2ObjectsTokenExpired(DjangoTestCase):
|
|||||||
@patch('rowers.dataprep.create_engine')
|
@patch('rowers.dataprep.create_engine')
|
||||||
def test_c2_import(self, mock_get, mocked_sqlalchemy):
|
def test_c2_import(self, mock_get, mocked_sqlalchemy):
|
||||||
|
|
||||||
response = self.c.get('/rowers/workout/c2import/12/',follow=True)
|
response = self.c.get('/rowers/workout/c2import/12/')
|
||||||
|
|
||||||
self.assertRedirects(response,
|
|
||||||
expected_url='/rowers/list-workouts/',
|
|
||||||
status_code=302,target_status_code=200)
|
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 302)
|
||||||
|
|
||||||
#@pytest.mark.django_db
|
#@pytest.mark.django_db
|
||||||
@override_settings(TESTING=True)
|
@override_settings(TESTING=True)
|
||||||
@@ -605,8 +604,10 @@ class NKObjects(DjangoTestCase):
|
|||||||
result = rowers.nkstuff.rower_nk_token_refresh(self.u)
|
result = rowers.nkstuff.rower_nk_token_refresh(self.u)
|
||||||
response = self.c.get('/rowers/workout/nkimport/469',follow=True)
|
response = self.c.get('/rowers/workout/nkimport/469',follow=True)
|
||||||
|
|
||||||
|
expected_url = reverse('workout_nkimport_view')
|
||||||
|
|
||||||
self.assertRedirects(response,
|
self.assertRedirects(response,
|
||||||
expected_url='/rowers/workout/'+encoded1+'/edit/',
|
expected_url=expected_url,
|
||||||
status_code=301,target_status_code=200)
|
status_code=301,target_status_code=200)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
@@ -626,8 +627,10 @@ class NKObjects(DjangoTestCase):
|
|||||||
result = rowers.nkstuff.rower_nk_token_refresh(self.u)
|
result = rowers.nkstuff.rower_nk_token_refresh(self.u)
|
||||||
response = self.c.get('/rowers/workout/nkimport/404',follow=True)
|
response = self.c.get('/rowers/workout/nkimport/404',follow=True)
|
||||||
|
|
||||||
|
expected_url = reverse('workout_nkimport_view')
|
||||||
|
|
||||||
self.assertRedirects(response,
|
self.assertRedirects(response,
|
||||||
expected_url='/rowers/workout/'+encoded1+'/edit/',
|
expected_url=expected_url,
|
||||||
status_code=301,target_status_code=200)
|
status_code=301,target_status_code=200)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
@@ -889,9 +892,10 @@ class StravaObjects(DjangoTestCase):
|
|||||||
mocked_getsmallrowdata_db):
|
mocked_getsmallrowdata_db):
|
||||||
|
|
||||||
response = self.c.get('/rowers/workout/stravaimport/12',follow=True)
|
response = self.c.get('/rowers/workout/stravaimport/12',follow=True)
|
||||||
|
expected_url = reverse('workout_stravaimport_view')
|
||||||
|
|
||||||
self.assertRedirects(response,
|
self.assertRedirects(response,
|
||||||
expected_url='/rowers/workout/'+encoded2+'/edit/',
|
expected_url=expected_url,
|
||||||
status_code=301,target_status_code=200)
|
status_code=301,target_status_code=200)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
@@ -1014,8 +1018,7 @@ class STObjects(DjangoTestCase):
|
|||||||
|
|
||||||
response = self.c.get('/rowers/workout/sporttracksimport/13/',follow=True)
|
response = self.c.get('/rowers/workout/sporttracksimport/13/',follow=True)
|
||||||
|
|
||||||
expected_url = reverse('workout_edit_view',
|
expected_url = '/rowers/workout/sporttracksimport/'
|
||||||
kwargs = {'id':encoder.encode_hex(2)})
|
|
||||||
|
|
||||||
self.assertRedirects(response,
|
self.assertRedirects(response,
|
||||||
expected_url=expected_url,
|
expected_url=expected_url,
|
||||||
@@ -1056,97 +1059,6 @@ class STObjects(DjangoTestCase):
|
|||||||
|
|
||||||
res = add_workout_from_data(self.u,1,data,data)
|
res = add_workout_from_data(self.u,1,data,data)
|
||||||
|
|
||||||
#@pytest.mark.django_db
|
|
||||||
@override_settings(TESTING=True)
|
|
||||||
class RunKeeperObjects(DjangoTestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.c = Client()
|
|
||||||
self.u = User.objects.create_user('john',
|
|
||||||
'sander@ds.ds',
|
|
||||||
'koeinsloot')
|
|
||||||
|
|
||||||
self.u.first_name = 'John'
|
|
||||||
self.u.last_name = 'Sander'
|
|
||||||
self.u.save()
|
|
||||||
self.r = Rower.objects.create(user=self.u,gdproptin=True,surveydone=True,
|
|
||||||
gdproptindate=timezone.now()
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
self.r.runkeepertoken = '12'
|
|
||||||
|
|
||||||
self.r.save()
|
|
||||||
|
|
||||||
self.c.login(username='john',password='koeinsloot')
|
|
||||||
|
|
||||||
self.nu = datetime.datetime.now()
|
|
||||||
|
|
||||||
filename = 'rowers/tests/testdata/testdata.csv'
|
|
||||||
|
|
||||||
rr = rrower(hrmax=self.r.max,hrut2=self.r.ut2,
|
|
||||||
hrut1=self.r.ut1,hrat=self.r.at,
|
|
||||||
hrtr=self.r.tr,hran=self.r.an,ftp=self.r.ftp)
|
|
||||||
row = rdata(csvfile=filename,rower=rr)
|
|
||||||
totaldist = row.df['cum_dist'].max()
|
|
||||||
totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min()
|
|
||||||
totaltime = totaltime+row.df.loc[:,' ElapsedTime (sec)'].iloc[0]
|
|
||||||
|
|
||||||
|
|
||||||
hours = int(totaltime/3600.)
|
|
||||||
minutes = int((totaltime - 3600.*hours)/60.)
|
|
||||||
seconds = int(totaltime - 3600.*hours - 60.*minutes)
|
|
||||||
tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds))
|
|
||||||
|
|
||||||
duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths)
|
|
||||||
|
|
||||||
|
|
||||||
workoutdate = row.rowdatetime.strftime('%Y-%m-%d')
|
|
||||||
workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
|
|
||||||
|
|
||||||
self.w = Workout.objects.create(
|
|
||||||
name='testworkout',workouttype='water',
|
|
||||||
user=self.r,date=self.nu.strftime('%Y-%m-%d'),
|
|
||||||
starttime=workoutstarttime,
|
|
||||||
startdatetime=row.rowdatetime,
|
|
||||||
duration=duration,distance=totaldist,
|
|
||||||
csvfilename=filename
|
|
||||||
)
|
|
||||||
|
|
||||||
@patch('rowers.runkeeperstuff.requests.post', side_effect=mocked_requests)
|
|
||||||
def test_runkeeper_callback(self, mock_post):
|
|
||||||
response = self.c.get('/runkeeper_callback?code=dsdoij232s',follow=True)
|
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
@patch('rowers.runkeeperstuff.requests.post', side_effect=mocked_requests)
|
|
||||||
@patch('rowers.runkeeperstuff.requests.get', side_effect=mocked_requests)
|
|
||||||
def test_runkeeper_upload(self, mock_get, mock_post):
|
|
||||||
response = self.c.get('/rowers/workout/'+encoded1+'/runkeeperuploadw/')
|
|
||||||
|
|
||||||
self.assertRedirects(response,
|
|
||||||
expected_url = '/rowers/workout/'+encoded1+'/edit/',
|
|
||||||
status_code=302,target_status_code=200)
|
|
||||||
|
|
||||||
self.assertEqual(response.url, '/rowers/workout/'+encoded1+'/edit/')
|
|
||||||
self.assertEqual(response.status_code, 302)
|
|
||||||
|
|
||||||
@patch('rowers.runkeeperstuff.requests.get', side_effect=mocked_requests)
|
|
||||||
def test_runkeeper_list(self, mock_get):
|
|
||||||
response = self.c.get('/rowers/workout/runkeeperimport',follow=True)
|
|
||||||
|
|
||||||
self.assertEqual(response.status_code,200)
|
|
||||||
|
|
||||||
@patch('rowers.imports.requests.get', side_effect=mocked_requests)
|
|
||||||
def test_runkeeper_import(self, mock_get):
|
|
||||||
|
|
||||||
response = self.c.get('/rowers/workout/runkeeperimport/12/',follow=True)
|
|
||||||
|
|
||||||
self.assertRedirects(response,
|
|
||||||
expected_url='/rowers/workout/'+encoded2+'/edit/',
|
|
||||||
status_code=302,target_status_code=200)
|
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#@pytest.mark.django_db
|
#@pytest.mark.django_db
|
||||||
|
|||||||
@@ -99,7 +99,6 @@ class TraverseLinksTest(TestCase):
|
|||||||
'.*authorize.*',
|
'.*authorize.*',
|
||||||
'.*youtu.*',
|
'.*youtu.*',
|
||||||
'.*earth.*',
|
'.*earth.*',
|
||||||
'.*runkeeper.*',
|
|
||||||
'.*c2list.*',
|
'.*c2list.*',
|
||||||
'.*stravaimport.*',
|
'.*stravaimport.*',
|
||||||
'.*performancephones.*',
|
'.*performancephones.*',
|
||||||
|
|||||||
@@ -124,7 +124,6 @@ def matchsync(line):
|
|||||||
tester3 = tester+'(.*)((tp)|(trainingpeaks))'
|
tester3 = tester+'(.*)((tp)|(trainingpeaks))'
|
||||||
tester4 = tester+'(.*)(strava)'
|
tester4 = tester+'(.*)(strava)'
|
||||||
tester5 = tester+'(.*)((st)|(sporttracks))'
|
tester5 = tester+'(.*)((st)|(sporttracks))'
|
||||||
tester6 = tester+'(.*)((rk)|(runkeeper))'
|
|
||||||
|
|
||||||
tester = re.compile(tester)
|
tester = re.compile(tester)
|
||||||
|
|
||||||
@@ -134,7 +133,6 @@ def matchsync(line):
|
|||||||
('upload_totp',re.compile(tester3)),
|
('upload_totp',re.compile(tester3)),
|
||||||
('upload_to_Strava',re.compile(tester4)),
|
('upload_to_Strava',re.compile(tester4)),
|
||||||
('upload_to_SportTracks',re.compile(tester5)),
|
('upload_to_SportTracks',re.compile(tester5)),
|
||||||
('upload_to_RunKeeper',re.compile(tester6)),
|
|
||||||
('upload_to_MapMyFitness',re.compile(tester7)),
|
('upload_to_MapMyFitness',re.compile(tester7)),
|
||||||
]
|
]
|
||||||
for t in testers:
|
for t in testers:
|
||||||
@@ -259,8 +257,6 @@ def getsyncoptions(uploadoptions,values): # pragma: no cover
|
|||||||
uploadoptions['upload_to_Strava'] = True
|
uploadoptions['upload_to_Strava'] = True
|
||||||
if v in ['st','sporttracks']:
|
if v in ['st','sporttracks']:
|
||||||
uploadoptions['upload_to_SportTracks'] = True
|
uploadoptions['upload_to_SportTracks'] = True
|
||||||
if v in ['rk','runkeeper']:
|
|
||||||
uploadoptions['upload_to_RunKeeper'] = True
|
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -487,7 +483,6 @@ def make_plot(r,w,f1,f2,plottype,title,imagename='',plotnr=0):
|
|||||||
import rowers.c2stuff as c2stuff
|
import rowers.c2stuff as c2stuff
|
||||||
import rowers.stravastuff as stravastuff
|
import rowers.stravastuff as stravastuff
|
||||||
import rowers.sporttracksstuff as sporttracksstuff
|
import rowers.sporttracksstuff as sporttracksstuff
|
||||||
import rowers.runkeeperstuff as runkeeperstuff
|
|
||||||
|
|
||||||
import rowers.tpstuff as tpstuff
|
import rowers.tpstuff as tpstuff
|
||||||
|
|
||||||
@@ -641,16 +636,6 @@ def do_sync(w,options, quick=False):
|
|||||||
id = 0
|
id = 0
|
||||||
|
|
||||||
|
|
||||||
if ('upload_to_RunKeeper' in options and options['upload_to_RunKeeper']) or (w.user.runkeeper_auto_export): # pragma: no cover
|
|
||||||
try:
|
|
||||||
message,id = runkeeperstuff.workout_runkeeper_upload(
|
|
||||||
w.user.user,w,asynchron=True,
|
|
||||||
)
|
|
||||||
except NoTokenError:
|
|
||||||
message = "Please connect to Runkeeper first"
|
|
||||||
id = 0
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if ('upload_to_TrainingPeaks' in options and options['upload_to_TrainingPeaks']) or (w.user.trainingpeaks_auto_export): # pragma: no cover
|
if ('upload_to_TrainingPeaks' in options and options['upload_to_TrainingPeaks']) or (w.user.trainingpeaks_auto_export): # pragma: no cover
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -508,13 +508,10 @@ urlpatterns = [
|
|||||||
re_path(r'^workout/sporttracksimport/all/$',views.workout_getsporttracksworkout_all,name='workout_getsporttracksworkout_all'),
|
re_path(r'^workout/sporttracksimport/all/$',views.workout_getsporttracksworkout_all,name='workout_getsporttracksworkout_all'),
|
||||||
re_path(r'^workout/polarimport/$',views.workout_polarimport_view,name='workout_polarimport_view'),
|
re_path(r'^workout/polarimport/$',views.workout_polarimport_view,name='workout_polarimport_view'),
|
||||||
re_path(r'^workout/polarimport/user/(?P<userid>\d+)/',views.workout_polarimport_view,name='workout_polarimport_view'),
|
re_path(r'^workout/polarimport/user/(?P<userid>\d+)/',views.workout_polarimport_view,name='workout_polarimport_view'),
|
||||||
re_path(r'^workout/runkeeperimport/$',views.workout_runkeeperimport_view,name='workout_runkeeperimport_view'),
|
|
||||||
re_path(r'^workout/runkeeperimport/user/(?P<userid>\d+)/$',views.workout_runkeeperimport_view,name='workout_runkeeperimport_view'),
|
|
||||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/c2uploadw/$',views.workout_c2_upload_view,name='workout_c2_upload_view'),
|
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/c2uploadw/$',views.workout_c2_upload_view,name='workout_c2_upload_view'),
|
||||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/stravauploadw/$',views.workout_strava_upload_view,name='workout_strava_upload_view'),
|
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/stravauploadw/$',views.workout_strava_upload_view,name='workout_strava_upload_view'),
|
||||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/recalcsummary/$',views.workout_recalcsummary_view,name='workout_recalcsummary_view'),
|
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/recalcsummary/$',views.workout_recalcsummary_view,name='workout_recalcsummary_view'),
|
||||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/sporttracksuploadw/$',views.workout_sporttracks_upload_view,name='workout_sporttracks_upload_view'),
|
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/sporttracksuploadw/$',views.workout_sporttracks_upload_view,name='workout_sporttracks_upload_view'),
|
||||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/runkeeperuploadw/$',views.workout_runkeeper_upload_view,name='workout_runkeeper_upload_view'),
|
|
||||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/tpuploadw/$',views.workout_tp_upload_view,name='workout_tp_upload_view'),
|
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/tpuploadw/$',views.workout_tp_upload_view,name='workout_tp_upload_view'),
|
||||||
re_path(r'^multi-compare/workout/(?P<id>\b[0-9A-Fa-f]+\b)/user/(?P<userid>\d+)/$',views.multi_compare_view,
|
re_path(r'^multi-compare/workout/(?P<id>\b[0-9A-Fa-f]+\b)/user/(?P<userid>\d+)/$',views.multi_compare_view,
|
||||||
name='multi_compare_view'),
|
name='multi_compare_view'),
|
||||||
@@ -599,7 +596,6 @@ urlpatterns = [
|
|||||||
re_path(r'^me/sporttracksauthorize/$',views.rower_sporttracks_authorize,name='rower_sporttracks_authorize'),
|
re_path(r'^me/sporttracksauthorize/$',views.rower_sporttracks_authorize,name='rower_sporttracks_authorize'),
|
||||||
re_path(r'^me/tpauthorize/$',views.rower_tp_authorize,name='rower_tp_authorize'),
|
re_path(r'^me/tpauthorize/$',views.rower_tp_authorize,name='rower_tp_authorize'),
|
||||||
re_path(r'^me/rp3authorize/$',views.rower_rp3_authorize,name='rower_rp3_authorize'),
|
re_path(r'^me/rp3authorize/$',views.rower_rp3_authorize,name='rower_rp3_authorize'),
|
||||||
re_path(r'^me/runkeeperauthorize/$',views.rower_runkeeper_authorize,name='rower_runkeeper_authorize'),
|
|
||||||
re_path(r'^me/sporttracksrefresh/$',views.rower_sporttracks_token_refresh,name='rower_sporttracks_token_refresh'),
|
re_path(r'^me/sporttracksrefresh/$',views.rower_sporttracks_token_refresh,name='rower_sporttracks_token_refresh'),
|
||||||
re_path(r'^me/tprefresh/$',views.rower_tp_token_refresh,name='rower_tp_token_refresh'),
|
re_path(r'^me/tprefresh/$',views.rower_tp_token_refresh,name='rower_tp_token_refresh'),
|
||||||
re_path(r'^me/c2refresh/$',views.rower_c2_token_refresh,name='rower_c2_token_refresh'),
|
re_path(r'^me/c2refresh/$',views.rower_c2_token_refresh,name='rower_c2_token_refresh'),
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import uuid
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import time
|
||||||
from fitparse import FitFile
|
from fitparse import FitFile
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
|||||||
@@ -225,67 +225,6 @@ def workout_c2_upload_view(request,id=0):
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
# Upload workout to RunKeeper
|
|
||||||
@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
|
|
||||||
def workout_runkeeper_upload_view(request,id=0):
|
|
||||||
message = ""
|
|
||||||
w = get_workout(id)
|
|
||||||
r = w.user
|
|
||||||
|
|
||||||
try:
|
|
||||||
thetoken = runkeeper_open(r.user)
|
|
||||||
except NoTokenError: # pragma: no cover
|
|
||||||
return HttpResponseRedirect("/rowers/me/runkeeperauthorize/")
|
|
||||||
|
|
||||||
# ready to upload. Hurray
|
|
||||||
|
|
||||||
|
|
||||||
data = runkeeperstuff.createrunkeeperworkoutdata(w)
|
|
||||||
if not data: # pragma: no cover
|
|
||||||
message = "Data error"
|
|
||||||
messages.error(request,message)
|
|
||||||
url = reverse(r.defaultlandingpage,
|
|
||||||
kwargs = {
|
|
||||||
'id':id,
|
|
||||||
})
|
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
|
|
||||||
authorizationstring = str('Bearer ' + thetoken)
|
|
||||||
headers = {'Authorization': authorizationstring,
|
|
||||||
'user-agent': 'sanderroosendaal',
|
|
||||||
'Content-Type': 'application/vnd.com.runkeeper.NewFitnessActivity+json',
|
|
||||||
'Content-Length':'nnn'}
|
|
||||||
|
|
||||||
url = "https://api.runkeeper.com/fitnessActivities"
|
|
||||||
response = requests.post(url,headers=headers,data=json.dumps(data,default=default))
|
|
||||||
|
|
||||||
# check for duplicate error first
|
|
||||||
if (response.status_code == 409 ): # pragma: no cover # pragma: no cover
|
|
||||||
message = "Duplicate error"
|
|
||||||
messages.error(request,message)
|
|
||||||
w.uploadedtorunkeeper = -1
|
|
||||||
w.save()
|
|
||||||
elif (response.status_code == 201 or response.status_code==200):
|
|
||||||
runkeeperid = runkeeperstuff.getidfromresponse(response)
|
|
||||||
w.uploadedtorunkeeper = runkeeperid
|
|
||||||
w.save()
|
|
||||||
url = reverse('workout_edit_view',
|
|
||||||
kwargs={'id':encoder.encode_hex(w.id)})
|
|
||||||
|
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
else: # pragma: no cover
|
|
||||||
s = response
|
|
||||||
message = "Something went wrong in workout_runkeeper_upload_view: %s - %s" % (s.reason,s.text)
|
|
||||||
messages.error(request,message)
|
|
||||||
|
|
||||||
|
|
||||||
url = reverse(r.defaultlandingpage,
|
|
||||||
kwargs = {
|
|
||||||
'id':encoder.encode_hex(w.id),
|
|
||||||
}) # pragma: no cover
|
|
||||||
|
|
||||||
return HttpResponseRedirect(url) # pragma: no cover
|
|
||||||
|
|
||||||
|
|
||||||
# Upload workout to SportTracks
|
# Upload workout to SportTracks
|
||||||
@permission_required('workout.change_workout',fn=get_workout_by_opaqueid)
|
@permission_required('workout.change_workout',fn=get_workout_by_opaqueid)
|
||||||
@@ -384,6 +323,7 @@ def rower_c2_authorize(request): # pragma: no cover
|
|||||||
"redirect_uri": C2_REDIRECT_URI}
|
"redirect_uri": C2_REDIRECT_URI}
|
||||||
url = "http://log.concept2.com/oauth/authorize?"+ urllib.parse.urlencode(params)
|
url = "http://log.concept2.com/oauth/authorize?"+ urllib.parse.urlencode(params)
|
||||||
url += "&scope="+scope
|
url += "&scope="+scope
|
||||||
|
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
# Garmin authorization
|
# Garmin authorization
|
||||||
@@ -429,23 +369,6 @@ def rower_polar_authorize(request): # pragma: no cover
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Runkeeper authorization
|
|
||||||
@login_required()
|
|
||||||
def rower_runkeeper_authorize(request): # pragma: no cover
|
|
||||||
# Generate a random string for the state parameter
|
|
||||||
# Save it for use later to prevent xsrf attacks
|
|
||||||
|
|
||||||
state = str(uuid4())
|
|
||||||
|
|
||||||
params = {"client_id": RUNKEEPER_CLIENT_ID,
|
|
||||||
"response_type": "code",
|
|
||||||
"state": state,
|
|
||||||
"redirect_uri": RUNKEEPER_REDIRECT_URI}
|
|
||||||
|
|
||||||
url = "https://runkeeper.com/apps/authorize?"+ urllib.parse.urlencode(params)
|
|
||||||
|
|
||||||
|
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
|
|
||||||
# SportTracks Authorization
|
# SportTracks Authorization
|
||||||
@login_required()
|
@login_required()
|
||||||
@@ -923,31 +846,6 @@ def rower_process_stravacallback(request):
|
|||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
|
||||||
# Process Runkeeper callback
|
|
||||||
@login_required()
|
|
||||||
def rower_process_runkeepercallback(request):
|
|
||||||
code = request.GET['code']
|
|
||||||
res = runkeeperstuff.get_token(code)
|
|
||||||
access_token = res[0]
|
|
||||||
|
|
||||||
if access_token == 0:# pragma: no cover
|
|
||||||
messages.error(request,"Something went wrong importing the token")
|
|
||||||
url = reverse('workouts_view')
|
|
||||||
|
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
r = getrower(request.user)
|
|
||||||
r.runkeepertoken = access_token
|
|
||||||
|
|
||||||
r.save()
|
|
||||||
|
|
||||||
successmessage = "Tokens stored. Good to go. Please check your import/export settings"
|
|
||||||
messages.info(request,successmessage)
|
|
||||||
url = reverse('rower_exportsettings_view')
|
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Process SportTracks callback
|
# Process SportTracks callback
|
||||||
@@ -1498,61 +1396,6 @@ def garmin_details_view(request):
|
|||||||
return HttpResponse(status=200)
|
return HttpResponse(status=200)
|
||||||
|
|
||||||
|
|
||||||
# The page where you select which RunKeeper workout to import
|
|
||||||
@login_required()
|
|
||||||
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
|
|
||||||
def workout_runkeeperimport_view(request,message="",userid=0):
|
|
||||||
res = runkeeperstuff.get_runkeeper_workout_list(request.user)
|
|
||||||
if (res.status_code != 200):
|
|
||||||
if (res.status_code == 401):
|
|
||||||
r = getrower(request.user)
|
|
||||||
if (r.runkeepertoken == '') or (r.runkeepertoken is None):
|
|
||||||
s = "Token doesn't exist. Need to authorize"
|
|
||||||
return HttpResponseRedirect("/rowers/me/runkeeperauthorize/")
|
|
||||||
message = "Something went wrong in workout_runkeeperimport_view" # pragma: no cover
|
|
||||||
messages.error(request,message) # pragma: no cover
|
|
||||||
|
|
||||||
if settings.DEBUG: # pragma: no cover
|
|
||||||
return HttpResponse(res)
|
|
||||||
else: # pragma: no cover
|
|
||||||
url = reverse('workouts_view')
|
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
|
|
||||||
workouts = []
|
|
||||||
for item in res.json()['items']:
|
|
||||||
d = int(float(item['total_distance']))
|
|
||||||
i = getidfromuri(item['uri'])
|
|
||||||
ttot = str(datetime.timedelta(seconds=int(float(item['duration']))))
|
|
||||||
s = item['start_time']
|
|
||||||
r = item['type']
|
|
||||||
keys = ['id','distance','duration','starttime','type']
|
|
||||||
values = [i,d,ttot,s,r]
|
|
||||||
|
|
||||||
res = dict(zip(keys,values))
|
|
||||||
workouts.append(res)
|
|
||||||
|
|
||||||
breadcrumbs = [
|
|
||||||
{
|
|
||||||
'url':'/rowers/list-workouts/',
|
|
||||||
'name':'Workouts'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'url':reverse('workout_runkeeperimport_view'),
|
|
||||||
'name':'Runkeeper'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
r = getrower(request.user)
|
|
||||||
|
|
||||||
return render(request,'runkeeper_list_import.html',
|
|
||||||
{'workouts':workouts,
|
|
||||||
'rower':r,
|
|
||||||
'active':'nav-workouts',
|
|
||||||
'breadcrumbs':breadcrumbs,
|
|
||||||
'teams':get_my_teams(request.user),
|
|
||||||
})
|
|
||||||
|
|
||||||
return HttpResponse(res) # pragma: no cover
|
|
||||||
|
|
||||||
|
|
||||||
# the page where you select which Polar workout to Import
|
# the page where you select which Polar workout to Import
|
||||||
@@ -1853,18 +1696,26 @@ importlistviews = {
|
|||||||
'strava':'workout_stravaimport_view',
|
'strava':'workout_stravaimport_view',
|
||||||
'polar':'workout_polarimport_view',
|
'polar':'workout_polarimport_view',
|
||||||
'ownapi':'workout_view',
|
'ownapi':'workout_view',
|
||||||
'runkeeper':'workout_runkeeperimport_view',
|
|
||||||
'sporttracks':'workout_sporttracksimport_view',
|
'sporttracks':'workout_sporttracksimport_view',
|
||||||
'trainingpeaks':'workout_view',
|
'trainingpeaks':'workout_view',
|
||||||
'nk':'workout_nkimport_view',
|
'nk':'workout_nkimport_view',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
importauthorizeviews = {
|
||||||
|
'c2':'rower_c2_authorize',
|
||||||
|
'strava':'rower_strava_authorize',
|
||||||
|
'polar':'rower_polar_authorize',
|
||||||
|
'ownapi':'workout_view',
|
||||||
|
'sporttracks':'rower_sporttracks_authorize',
|
||||||
|
'trainingpeaks':'rower_tp_authorize',
|
||||||
|
'nk':'rower_nk_authorize',
|
||||||
|
}
|
||||||
|
|
||||||
importsources = {
|
importsources = {
|
||||||
'c2':c2stuff,
|
'c2':c2stuff,
|
||||||
'strava':stravastuff,
|
'strava':stravastuff,
|
||||||
'polar':polarstuff,
|
'polar':polarstuff,
|
||||||
'ownapi':ownapistuff,
|
'ownapi':ownapistuff,
|
||||||
'runkeeper':runkeeperstuff,
|
|
||||||
'sporttracks':sporttracksstuff,
|
'sporttracks':sporttracksstuff,
|
||||||
'trainingpeaks':tpstuff,
|
'trainingpeaks':tpstuff,
|
||||||
'nk':nkstuff,
|
'nk':nkstuff,
|
||||||
@@ -1894,8 +1745,12 @@ def workout_getrp3importview(request,externalid):
|
|||||||
|
|
||||||
@login_required()
|
@login_required()
|
||||||
def workout_getimportview(request,externalid,source = 'c2',do_async=True):
|
def workout_getimportview(request,externalid,source = 'c2',do_async=True):
|
||||||
result = importsources[source].get_workout(request.user,externalid,
|
try:
|
||||||
do_async=do_async)
|
result = importsources[source].get_workout(request.user,externalid,
|
||||||
|
do_async=do_async)
|
||||||
|
except NoTokenError:
|
||||||
|
|
||||||
|
return HttpResponseRedirect(reverse(importauthorizeviews[source]))
|
||||||
|
|
||||||
if result: # pragma: no cover
|
if result: # pragma: no cover
|
||||||
messages.info(request,"Your workout will be imported in the background")
|
messages.info(request,"Your workout will be imported in the background")
|
||||||
@@ -1919,14 +1774,11 @@ def workout_getsporttracksworkout_all(request):
|
|||||||
])
|
])
|
||||||
newids = [stid for stid in stids if not stid in knownstids]
|
newids = [stid for stid in stids if not stid in knownstids]
|
||||||
for sporttracksid in newids:
|
for sporttracksid in newids:
|
||||||
data,strokedata = sporttracksstuff.get_workout(
|
id = sporttracksstuff.get_workout(
|
||||||
request.user,sporttracksid)
|
request.user,sporttracksid)
|
||||||
|
|
||||||
id,message = sporttracksstuff.add_workout_from_data(
|
|
||||||
request.user,sporttracksid,data,strokedata
|
|
||||||
)
|
|
||||||
if id==0: # pragma: no cover
|
if id==0: # pragma: no cover
|
||||||
messages.error(request,message)
|
messages.error(request,"Something went wrong with workout {id}".format(id=sporttracksid))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
w = Workout.objects.get(id=id)
|
w = Workout.objects.get(id=id)
|
||||||
|
|||||||
@@ -158,7 +158,6 @@ import rowers.nkstuff as nkstuff
|
|||||||
from rowers.c2stuff import c2_open
|
from rowers.c2stuff import c2_open
|
||||||
from rowers.nkstuff import nk_open
|
from rowers.nkstuff import nk_open
|
||||||
from rowers.rp3stuff import rp3_open
|
from rowers.rp3stuff import rp3_open
|
||||||
from rowers.runkeeperstuff import runkeeper_open
|
|
||||||
from rowers.sporttracksstuff import sporttracks_open
|
from rowers.sporttracksstuff import sporttracks_open
|
||||||
from rowers.tpstuff import tp_open
|
from rowers.tpstuff import tp_open
|
||||||
from iso8601 import ParseError
|
from iso8601 import ParseError
|
||||||
@@ -170,7 +169,6 @@ import rowers.sporttracksstuff as sporttracksstuff
|
|||||||
|
|
||||||
|
|
||||||
import rowers.tpstuff as tpstuff
|
import rowers.tpstuff as tpstuff
|
||||||
import rowers.runkeeperstuff as runkeeperstuff
|
|
||||||
import rowers.rp3stuff as rp3stuff
|
import rowers.rp3stuff as rp3stuff
|
||||||
import rowers.ownapistuff as ownapistuff
|
import rowers.ownapistuff as ownapistuff
|
||||||
from rowers.ownapistuff import TEST_CLIENT_ID, TEST_CLIENT_SECRET, TEST_REDIRECT_URI
|
from rowers.ownapistuff import TEST_CLIENT_ID, TEST_CLIENT_SECRET, TEST_REDIRECT_URI
|
||||||
@@ -180,7 +178,6 @@ from rowsandall_app.settings import (
|
|||||||
POLAR_CLIENT_ID, POLAR_REDIRECT_URI, POLAR_CLIENT_SECRET,
|
POLAR_CLIENT_ID, POLAR_REDIRECT_URI, POLAR_CLIENT_SECRET,
|
||||||
SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI,
|
SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI,
|
||||||
SPORTTRACKS_CLIENT_SECRET,
|
SPORTTRACKS_CLIENT_SECRET,
|
||||||
RUNKEEPER_CLIENT_ID,RUNKEEPER_REDIRECT_URI,RUNKEEPER_CLIENT_SECRET,
|
|
||||||
TP_CLIENT_ID,TP_REDIRECT_URI,TP_CLIENT_KEY,TP_CLIENT_SECRET,
|
TP_CLIENT_ID,TP_REDIRECT_URI,TP_CLIENT_KEY,TP_CLIENT_SECRET,
|
||||||
RP3_CLIENT_ID,RP3_REDIRECT_URI,RP3_CLIENT_KEY,RP3_CLIENT_SECRET,
|
RP3_CLIENT_ID,RP3_REDIRECT_URI,RP3_CLIENT_KEY,RP3_CLIENT_SECRET,
|
||||||
BRAINTREE_MERCHANT_ID,BRAINTREE_PUBLIC_KEY,BRAINTREE_PRIVATE_KEY,
|
BRAINTREE_MERCHANT_ID,BRAINTREE_PUBLIC_KEY,BRAINTREE_PRIVATE_KEY,
|
||||||
|
|||||||
@@ -4864,8 +4864,6 @@ def workout_upload_api(request):
|
|||||||
upload_to_c2 = optionsform.cleaned_data['upload_to_C2']
|
upload_to_c2 = optionsform.cleaned_data['upload_to_C2']
|
||||||
upload_to_strava = optionsform.cleaned_data['upload_to_Strava']
|
upload_to_strava = optionsform.cleaned_data['upload_to_Strava']
|
||||||
upload_to_st = optionsform.cleaned_data['upload_to_SportTracks']
|
upload_to_st = optionsform.cleaned_data['upload_to_SportTracks']
|
||||||
upload_to_rk = optionsform.cleaned_data['upload_to_RunKeeper']
|
|
||||||
upload_to_ua = optionsform.cleaned_data['upload_to_MapMyFitness']
|
|
||||||
upload_to_tp = optionsform.cleaned_data['upload_to_TrainingPeaks']
|
upload_to_tp = optionsform.cleaned_data['upload_to_TrainingPeaks']
|
||||||
makeprivate = optionsform.cleaned_data['makeprivate']
|
makeprivate = optionsform.cleaned_data['makeprivate']
|
||||||
else: # pragma: no cover
|
else: # pragma: no cover
|
||||||
@@ -5042,8 +5040,6 @@ def workout_upload_view(request,
|
|||||||
upload_to_c2 = uploadoptions.get('upload_to_C2',False)
|
upload_to_c2 = uploadoptions.get('upload_to_C2',False)
|
||||||
upload_to_strava = uploadoptions.get('upload_to_Strava',False)
|
upload_to_strava = uploadoptions.get('upload_to_Strava',False)
|
||||||
upload_to_st = uploadoptions.get('upload_to_SportTracks',False)
|
upload_to_st = uploadoptions.get('upload_to_SportTracks',False)
|
||||||
upload_to_rk = uploadoptions.get('upload_to_RunKeeper',False)
|
|
||||||
upload_to_ua = uploadoptions.get('upload_to_MapMyFitness',False)
|
|
||||||
upload_to_tp = uploadoptions.get('upload_to_TrainingPeaks',False)
|
upload_to_tp = uploadoptions.get('upload_to_TrainingPeaks',False)
|
||||||
|
|
||||||
response = {}
|
response = {}
|
||||||
@@ -5093,8 +5089,6 @@ def workout_upload_view(request,
|
|||||||
upload_to_c2 = optionsform.cleaned_data['upload_to_C2']
|
upload_to_c2 = optionsform.cleaned_data['upload_to_C2']
|
||||||
upload_to_strava = optionsform.cleaned_data['upload_to_Strava']
|
upload_to_strava = optionsform.cleaned_data['upload_to_Strava']
|
||||||
upload_to_st = optionsform.cleaned_data['upload_to_SportTracks']
|
upload_to_st = optionsform.cleaned_data['upload_to_SportTracks']
|
||||||
upload_to_rk = optionsform.cleaned_data['upload_to_RunKeeper']
|
|
||||||
upload_to_ua = optionsform.cleaned_data['upload_to_MapMyFitness']
|
|
||||||
upload_to_tp = optionsform.cleaned_data['upload_to_TrainingPeaks']
|
upload_to_tp = optionsform.cleaned_data['upload_to_TrainingPeaks']
|
||||||
makeprivate = optionsform.cleaned_data['makeprivate']
|
makeprivate = optionsform.cleaned_data['makeprivate']
|
||||||
landingpage = optionsform.cleaned_data['landingpage']
|
landingpage = optionsform.cleaned_data['landingpage']
|
||||||
@@ -5112,8 +5106,6 @@ def workout_upload_view(request,
|
|||||||
'upload_to_C2':upload_to_c2,
|
'upload_to_C2':upload_to_c2,
|
||||||
'upload_to_Strava':upload_to_strava,
|
'upload_to_Strava':upload_to_strava,
|
||||||
'upload_to_SportTracks':upload_to_st,
|
'upload_to_SportTracks':upload_to_st,
|
||||||
'upload_to_RunKeeper':upload_to_rk,
|
|
||||||
'upload_to_MapMyFitness':upload_to_ua,
|
|
||||||
'upload_to_TrainingPeaks':upload_to_tp,
|
'upload_to_TrainingPeaks':upload_to_tp,
|
||||||
'landingpage':landingpage,
|
'landingpage':landingpage,
|
||||||
'boattype': boattype,
|
'boattype': boattype,
|
||||||
@@ -5255,20 +5247,6 @@ def workout_upload_view(request,
|
|||||||
else:
|
else:
|
||||||
messages.error(request,message)
|
messages.error(request,message)
|
||||||
|
|
||||||
if (upload_to_rk): # pragma: no cover
|
|
||||||
try:
|
|
||||||
message,id = runkeeperstuff.workout_runkeeper_upload(
|
|
||||||
request.user,w
|
|
||||||
)
|
|
||||||
except NoTokenError:
|
|
||||||
message = "Please connect to Runkeeper first"
|
|
||||||
id = 0
|
|
||||||
|
|
||||||
if id>1:
|
|
||||||
messages.info(request,message)
|
|
||||||
else:
|
|
||||||
messages.error(request,message)
|
|
||||||
|
|
||||||
|
|
||||||
if (upload_to_tp): # pragma: no cover
|
if (upload_to_tp): # pragma: no cover
|
||||||
try:
|
try:
|
||||||
@@ -5447,14 +5425,9 @@ def workout_upload_view(request,
|
|||||||
if r.sporttracks_auto_export and ispromember(r.user): # pragma: no cover
|
if r.sporttracks_auto_export and ispromember(r.user): # pragma: no cover
|
||||||
uploadoptions['upload_to_SportTracks'] = True
|
uploadoptions['upload_to_SportTracks'] = True
|
||||||
|
|
||||||
if r.runkeeper_auto_export and ispromember(r.user): # pragma: no cover
|
|
||||||
uploadoptions['upload_to_RunKeeper'] = True
|
|
||||||
|
|
||||||
if r.trainingpeaks_auto_export and ispromember(r.user): # pragma: no cover
|
if r.trainingpeaks_auto_export and ispromember(r.user): # pragma: no cover
|
||||||
uploadoptions['upload_to_TrainingPeaks'] = True
|
uploadoptions['upload_to_TrainingPeaks'] = True
|
||||||
|
|
||||||
if r.mapmyfitness_auto_export and ispromember(r.user): # pragma: no cover
|
|
||||||
uploadoptions['upload_to_MapMyFitness'] = True
|
|
||||||
|
|
||||||
form = DocumentsForm(initial=docformoptions)
|
form = DocumentsForm(initial=docformoptions)
|
||||||
optionsform = UploadOptionsForm(initial=uploadoptions,
|
optionsform = UploadOptionsForm(initial=uploadoptions,
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ urlpatterns += [
|
|||||||
re_path(r'^garmin\_callback',rowersviews.rower_process_garmincallback),
|
re_path(r'^garmin\_callback',rowersviews.rower_process_garmincallback),
|
||||||
re_path(r'^sporttracks\_callback',rowersviews.rower_process_sporttrackscallback),
|
re_path(r'^sporttracks\_callback',rowersviews.rower_process_sporttrackscallback),
|
||||||
re_path(r'^polarflowcallback',rowersviews.rower_process_polarcallback),
|
re_path(r'^polarflowcallback',rowersviews.rower_process_polarcallback),
|
||||||
re_path(r'^runkeeper\_callback',rowersviews.rower_process_runkeepercallback),
|
|
||||||
re_path(r'^tp\_callback',rowersviews.rower_process_tpcallback),
|
re_path(r'^tp\_callback',rowersviews.rower_process_tpcallback),
|
||||||
re_path(r'^rp3\_callback',rowersviews.rower_process_rp3callback),
|
re_path(r'^rp3\_callback',rowersviews.rower_process_rp3callback),
|
||||||
re_path(r'^twitter\_callback',rowersviews.rower_process_twittercallback),
|
re_path(r'^twitter\_callback',rowersviews.rower_process_twittercallback),
|
||||||
|
|||||||
Reference in New Issue
Block a user