runkeeper retired, updated sporttraclks
This commit is contained in:
@@ -46,7 +46,6 @@ class RowerInline(admin.StackedInline):
|
||||
'sporttrackstoken','sporttrackstokenexpirydate',
|
||||
'sporttracksrefreshtoken',
|
||||
'sporttracks_auto_export',
|
||||
'mapmyfitness_auto_export',
|
||||
'tptoken','tptokenexpirydate','tprefreshtoken',
|
||||
'trainingpeaks_auto_export',
|
||||
'polartoken','polartokenexpirydate',
|
||||
@@ -55,7 +54,6 @@ class RowerInline(admin.StackedInline):
|
||||
'stravatoken','stravatokenexpirydate','stravarefreshtoken',
|
||||
'stravaexportas','strava_auto_export',
|
||||
'strava_auto_import',
|
||||
'runkeepertoken','runkeeper_auto_export',
|
||||
'garmintoken','garminrefreshtoken')}),
|
||||
('Team',
|
||||
{'fields':('friends','privacy','team')}),
|
||||
|
||||
@@ -825,14 +825,9 @@ def make_authorization_url(request): # pragma: no cover
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
# 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)
|
||||
if (r.c2token == '') or (r.c2token is None): # pragma: no cover
|
||||
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
|
||||
thetoken = c2_open(user)
|
||||
|
||||
job = myqueue(queuehigh,
|
||||
handle_c2_getworkout,
|
||||
|
||||
@@ -988,7 +988,6 @@ class Rower(models.Model):
|
||||
sporttracksrefreshtoken = models.CharField(default='',max_length=200,
|
||||
blank=True,null=True)
|
||||
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)
|
||||
tptokenexpirydate = models.DateTimeField(blank=True,null=True)
|
||||
tprefreshtoken = models.CharField(default='',max_length=1000,
|
||||
@@ -1037,9 +1036,6 @@ class Rower(models.Model):
|
||||
strava_auto_export = models.BooleanField(default=False)
|
||||
strava_auto_import = 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 = (
|
||||
@@ -3198,7 +3194,6 @@ class Workout(models.Model):
|
||||
uploadedtostrava = models.BigIntegerField(default=0)
|
||||
uploadedtosporttracks = models.BigIntegerField(default=0)
|
||||
uploadedtotp = models.BigIntegerField(default=0)
|
||||
uploadedtorunkeeper = models.BigIntegerField(default=0)
|
||||
uploadedtogarmin = models.BigIntegerField(default=0)
|
||||
uploadedtorp3 = models.BigIntegerField(default=0)
|
||||
uploadedtonk = models.BigIntegerField(default=0)
|
||||
@@ -3279,7 +3274,6 @@ class TombStone(models.Model):
|
||||
uploadedtostrava = models.BigIntegerField(default=0)
|
||||
uploadedtosporttracks = models.BigIntegerField(default=0)
|
||||
uploadedtotp = models.BigIntegerField(default=0)
|
||||
uploadedtorunkeeper = models.BigIntegerField(default=0)
|
||||
uploadedtonk = models.BigIntegerField(default=0)
|
||||
|
||||
@receiver(models.signals.pre_delete,sender=Workout)
|
||||
@@ -3289,7 +3283,6 @@ def create_tombstone_on_delete(sender, instance, **kwargs):
|
||||
uploadedtoc2 = instance.uploadedtoc2,
|
||||
uploadedtostrava = instance.uploadedtostrava,
|
||||
uploadedtotp = instance.uploadedtotp,
|
||||
uploadedtorunkeeper = instance.uploadedtorunkeeper,
|
||||
uploadedtonk = instance.uploadedtonk
|
||||
)
|
||||
t.save()
|
||||
@@ -3864,8 +3857,6 @@ class RowerExportForm(ModelForm):
|
||||
'c2_auto_export',
|
||||
'c2_auto_import',
|
||||
'nk_auto_import',
|
||||
'mapmyfitness_auto_export',
|
||||
'runkeeper_auto_export',
|
||||
'sporttracks_auto_export',
|
||||
'strava_auto_export',
|
||||
'strava_auto_import',
|
||||
@@ -4208,8 +4199,6 @@ class RowerImportExportForm(ModelForm):
|
||||
'polar_auto_import',
|
||||
'c2_auto_export',
|
||||
'c2_auto_import',
|
||||
'mapmyfitness_auto_export',
|
||||
'runkeeper_auto_export',
|
||||
'sporttracks_auto_export',
|
||||
'strava_auto_export',
|
||||
'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()
|
||||
})
|
||||
|
||||
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
|
||||
def createsporttracksworkoutdata(w):
|
||||
|
||||
@@ -25,7 +25,6 @@ from rowingdata import rowingdata as rdata
|
||||
from datetime import timedelta
|
||||
from sqlalchemy import create_engine
|
||||
|
||||
from rowers.imports import splituadata
|
||||
|
||||
#from celery import app
|
||||
from rowers.celery import app
|
||||
|
||||
@@ -192,21 +192,6 @@
|
||||
</a>
|
||||
{% endif %}
|
||||
</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">
|
||||
{% if workout.uploadedtotp %}
|
||||
<a href="https://app.trainingpeaks.com">
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
<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="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="polar"><a href="/rowers/workout/polarimport/">Polar</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 != '' %}
|
||||
Strava,
|
||||
{% endif %}
|
||||
{% if rower.runkeepertoken is not None and rower.runkeepertoken != '' %}
|
||||
Runkeeper,
|
||||
{% endif %}
|
||||
{% if rower.rp3token is not None and rower.rp3token != '' %}
|
||||
RP3
|
||||
{% 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/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/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"
|
||||
alt="connect with Polar" width="130"></a></p>
|
||||
<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
|
||||
)
|
||||
|
||||
from rowers import c2stuff, runkeeperstuff
|
||||
from rowers import c2stuff
|
||||
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.mytypes import (
|
||||
otwtypes,adaptivetypes,sexcategories,weightcategories,workouttypes,
|
||||
@@ -453,16 +452,6 @@ def currency(word):
|
||||
|
||||
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
|
||||
def courselength(course): # pragma: no cover
|
||||
|
||||
@@ -62,7 +62,6 @@ from mock import Mock, patch
|
||||
import pandas as pd
|
||||
import rowers.c2stuff as c2stuff
|
||||
import rowers.sporttracksstuff as sporttracksstuff
|
||||
import rowers.runkeeperstuff as runkeeperstuff
|
||||
|
||||
from django.urls import reverse, reverse_lazy
|
||||
|
||||
|
||||
@@ -313,8 +313,10 @@ class C2Objects(DjangoTestCase):
|
||||
|
||||
response = self.c.get('/rowers/workout/c2import/12/',follow=True)
|
||||
|
||||
expected_url = reverse('workout_c2import_view')
|
||||
|
||||
self.assertRedirects(response,
|
||||
expected_url='/rowers/workout/'+encoded2+'/edit/',
|
||||
expected_url=expected_url,
|
||||
status_code=302,target_status_code=200)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
@@ -485,13 +487,10 @@ class C2ObjectsTokenExpired(DjangoTestCase):
|
||||
@patch('rowers.dataprep.create_engine')
|
||||
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
|
||||
@override_settings(TESTING=True)
|
||||
@@ -605,8 +604,10 @@ class NKObjects(DjangoTestCase):
|
||||
result = rowers.nkstuff.rower_nk_token_refresh(self.u)
|
||||
response = self.c.get('/rowers/workout/nkimport/469',follow=True)
|
||||
|
||||
expected_url = reverse('workout_nkimport_view')
|
||||
|
||||
self.assertRedirects(response,
|
||||
expected_url='/rowers/workout/'+encoded1+'/edit/',
|
||||
expected_url=expected_url,
|
||||
status_code=301,target_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)
|
||||
response = self.c.get('/rowers/workout/nkimport/404',follow=True)
|
||||
|
||||
expected_url = reverse('workout_nkimport_view')
|
||||
|
||||
self.assertRedirects(response,
|
||||
expected_url='/rowers/workout/'+encoded1+'/edit/',
|
||||
expected_url=expected_url,
|
||||
status_code=301,target_status_code=200)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
@@ -889,9 +892,10 @@ class StravaObjects(DjangoTestCase):
|
||||
mocked_getsmallrowdata_db):
|
||||
|
||||
response = self.c.get('/rowers/workout/stravaimport/12',follow=True)
|
||||
expected_url = reverse('workout_stravaimport_view')
|
||||
|
||||
self.assertRedirects(response,
|
||||
expected_url='/rowers/workout/'+encoded2+'/edit/',
|
||||
expected_url=expected_url,
|
||||
status_code=301,target_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)
|
||||
|
||||
expected_url = reverse('workout_edit_view',
|
||||
kwargs = {'id':encoder.encode_hex(2)})
|
||||
expected_url = '/rowers/workout/sporttracksimport/'
|
||||
|
||||
self.assertRedirects(response,
|
||||
expected_url=expected_url,
|
||||
@@ -1056,97 +1059,6 @@ class STObjects(DjangoTestCase):
|
||||
|
||||
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
|
||||
|
||||
@@ -99,7 +99,6 @@ class TraverseLinksTest(TestCase):
|
||||
'.*authorize.*',
|
||||
'.*youtu.*',
|
||||
'.*earth.*',
|
||||
'.*runkeeper.*',
|
||||
'.*c2list.*',
|
||||
'.*stravaimport.*',
|
||||
'.*performancephones.*',
|
||||
|
||||
@@ -124,7 +124,6 @@ def matchsync(line):
|
||||
tester3 = tester+'(.*)((tp)|(trainingpeaks))'
|
||||
tester4 = tester+'(.*)(strava)'
|
||||
tester5 = tester+'(.*)((st)|(sporttracks))'
|
||||
tester6 = tester+'(.*)((rk)|(runkeeper))'
|
||||
|
||||
tester = re.compile(tester)
|
||||
|
||||
@@ -134,7 +133,6 @@ def matchsync(line):
|
||||
('upload_totp',re.compile(tester3)),
|
||||
('upload_to_Strava',re.compile(tester4)),
|
||||
('upload_to_SportTracks',re.compile(tester5)),
|
||||
('upload_to_RunKeeper',re.compile(tester6)),
|
||||
('upload_to_MapMyFitness',re.compile(tester7)),
|
||||
]
|
||||
for t in testers:
|
||||
@@ -259,8 +257,6 @@ def getsyncoptions(uploadoptions,values): # pragma: no cover
|
||||
uploadoptions['upload_to_Strava'] = True
|
||||
if v in ['st','sporttracks']:
|
||||
uploadoptions['upload_to_SportTracks'] = True
|
||||
if v in ['rk','runkeeper']:
|
||||
uploadoptions['upload_to_RunKeeper'] = True
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
@@ -487,7 +483,6 @@ def make_plot(r,w,f1,f2,plottype,title,imagename='',plotnr=0):
|
||||
import rowers.c2stuff as c2stuff
|
||||
import rowers.stravastuff as stravastuff
|
||||
import rowers.sporttracksstuff as sporttracksstuff
|
||||
import rowers.runkeeperstuff as runkeeperstuff
|
||||
|
||||
import rowers.tpstuff as tpstuff
|
||||
|
||||
@@ -641,16 +636,6 @@ def do_sync(w,options, quick=False):
|
||||
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
|
||||
try:
|
||||
|
||||
@@ -508,13 +508,10 @@ urlpatterns = [
|
||||
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/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)/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)/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'^multi-compare/workout/(?P<id>\b[0-9A-Fa-f]+\b)/user/(?P<userid>\d+)/$',views.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/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/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/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'),
|
||||
|
||||
@@ -15,7 +15,7 @@ import uuid
|
||||
import datetime
|
||||
|
||||
import json
|
||||
|
||||
import time
|
||||
from fitparse import FitFile
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
@@ -225,67 +225,6 @@ def workout_c2_upload_view(request,id=0):
|
||||
|
||||
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
|
||||
@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}
|
||||
url = "http://log.concept2.com/oauth/authorize?"+ urllib.parse.urlencode(params)
|
||||
url += "&scope="+scope
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
# 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
|
||||
@login_required()
|
||||
@@ -923,31 +846,6 @@ def rower_process_stravacallback(request):
|
||||
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
|
||||
@@ -1498,61 +1396,6 @@ def garmin_details_view(request):
|
||||
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
|
||||
@@ -1853,18 +1696,26 @@ importlistviews = {
|
||||
'strava':'workout_stravaimport_view',
|
||||
'polar':'workout_polarimport_view',
|
||||
'ownapi':'workout_view',
|
||||
'runkeeper':'workout_runkeeperimport_view',
|
||||
'sporttracks':'workout_sporttracksimport_view',
|
||||
'trainingpeaks':'workout_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 = {
|
||||
'c2':c2stuff,
|
||||
'strava':stravastuff,
|
||||
'polar':polarstuff,
|
||||
'ownapi':ownapistuff,
|
||||
'runkeeper':runkeeperstuff,
|
||||
'sporttracks':sporttracksstuff,
|
||||
'trainingpeaks':tpstuff,
|
||||
'nk':nkstuff,
|
||||
@@ -1894,8 +1745,12 @@ def workout_getrp3importview(request,externalid):
|
||||
|
||||
@login_required()
|
||||
def workout_getimportview(request,externalid,source = 'c2',do_async=True):
|
||||
result = importsources[source].get_workout(request.user,externalid,
|
||||
do_async=do_async)
|
||||
try:
|
||||
result = importsources[source].get_workout(request.user,externalid,
|
||||
do_async=do_async)
|
||||
except NoTokenError:
|
||||
|
||||
return HttpResponseRedirect(reverse(importauthorizeviews[source]))
|
||||
|
||||
if result: # pragma: no cover
|
||||
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]
|
||||
for sporttracksid in newids:
|
||||
data,strokedata = sporttracksstuff.get_workout(
|
||||
id = sporttracksstuff.get_workout(
|
||||
request.user,sporttracksid)
|
||||
|
||||
id,message = sporttracksstuff.add_workout_from_data(
|
||||
request.user,sporttracksid,data,strokedata
|
||||
)
|
||||
if id==0: # pragma: no cover
|
||||
messages.error(request,message)
|
||||
messages.error(request,"Something went wrong with workout {id}".format(id=sporttracksid))
|
||||
|
||||
else:
|
||||
w = Workout.objects.get(id=id)
|
||||
|
||||
@@ -158,7 +158,6 @@ import rowers.nkstuff as nkstuff
|
||||
from rowers.c2stuff import c2_open
|
||||
from rowers.nkstuff import nk_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
|
||||
from iso8601 import ParseError
|
||||
@@ -170,7 +169,6 @@ import rowers.sporttracksstuff as sporttracksstuff
|
||||
|
||||
|
||||
import rowers.tpstuff as tpstuff
|
||||
import rowers.runkeeperstuff as runkeeperstuff
|
||||
import rowers.rp3stuff as rp3stuff
|
||||
import rowers.ownapistuff as ownapistuff
|
||||
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,
|
||||
SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI,
|
||||
SPORTTRACKS_CLIENT_SECRET,
|
||||
RUNKEEPER_CLIENT_ID,RUNKEEPER_REDIRECT_URI,RUNKEEPER_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,
|
||||
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_strava = optionsform.cleaned_data['upload_to_Strava']
|
||||
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']
|
||||
makeprivate = optionsform.cleaned_data['makeprivate']
|
||||
else: # pragma: no cover
|
||||
@@ -5042,8 +5040,6 @@ def workout_upload_view(request,
|
||||
upload_to_c2 = uploadoptions.get('upload_to_C2',False)
|
||||
upload_to_strava = uploadoptions.get('upload_to_Strava',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)
|
||||
|
||||
response = {}
|
||||
@@ -5093,8 +5089,6 @@ def workout_upload_view(request,
|
||||
upload_to_c2 = optionsform.cleaned_data['upload_to_C2']
|
||||
upload_to_strava = optionsform.cleaned_data['upload_to_Strava']
|
||||
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']
|
||||
makeprivate = optionsform.cleaned_data['makeprivate']
|
||||
landingpage = optionsform.cleaned_data['landingpage']
|
||||
@@ -5112,8 +5106,6 @@ def workout_upload_view(request,
|
||||
'upload_to_C2':upload_to_c2,
|
||||
'upload_to_Strava':upload_to_strava,
|
||||
'upload_to_SportTracks':upload_to_st,
|
||||
'upload_to_RunKeeper':upload_to_rk,
|
||||
'upload_to_MapMyFitness':upload_to_ua,
|
||||
'upload_to_TrainingPeaks':upload_to_tp,
|
||||
'landingpage':landingpage,
|
||||
'boattype': boattype,
|
||||
@@ -5255,20 +5247,6 @@ def workout_upload_view(request,
|
||||
else:
|
||||
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
|
||||
try:
|
||||
@@ -5447,14 +5425,9 @@ def workout_upload_view(request,
|
||||
if r.sporttracks_auto_export and ispromember(r.user): # pragma: no cover
|
||||
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
|
||||
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)
|
||||
optionsform = UploadOptionsForm(initial=uploadoptions,
|
||||
|
||||
Reference in New Issue
Block a user