Private
Public Access
1
0

Merge branch 'feature/newc2' into develop

This commit is contained in:
Sander Roosendaal
2021-05-24 20:54:37 +02:00
38 changed files with 3062 additions and 3477 deletions
+34
View File
File diff suppressed because one or more lines are too long
-4
View File
@@ -46,9 +46,6 @@ class RowerInline(admin.StackedInline):
'sporttrackstoken','sporttrackstokenexpirydate',
'sporttracksrefreshtoken',
'sporttracks_auto_export',
'underarmourtoken','underarmourtokenexpirydate',
'underarmourrefreshtoken',
'mapmyfitness_auto_export',
'tptoken','tptokenexpirydate','tprefreshtoken',
'trainingpeaks_auto_export',
'polartoken','polartokenexpirydate',
@@ -57,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')}),
+18 -281
View File
@@ -22,15 +22,7 @@ from scipy import optimize
from json.decoder import JSONDecodeError
from pytz.exceptions import UnknownTimeZoneError
def dologging(s):
tstamp = time.localtime()
timestamp = time.strftime('%b-%d-%Y %H:%M:%S', tstamp)
with open('debuglog.log','a') as f:
f.write('\n')
f.write(timestamp)
f.write(' ')
f.write(s)
from rowers.utils import dologging
from rowsandall_app.settings import (
C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET,
@@ -39,6 +31,7 @@ from rowsandall_app.settings import (
from rowers.tasks import (
handle_c2_import_stroke_data, handle_c2_sync, handle_c2_async_workout,
handle_c2_getworkout
)
import django_rq
queue = django_rq.get_queue('default')
@@ -832,92 +825,19 @@ 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
else:
# ready to fetch. Hurray
authorizationstring = str('Bearer ' + r.c2token)
headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'}
url = "https://log.concept2.com/api/users/me/results/"+str(c2id)
s = requests.get(url,headers=headers)
thetoken = c2_open(user)
if s.status_code != 200: # pragma: no cover
if s.status_code == 404:
raise PermissionDenied("You have no access to this resource")
else:
s = "Something went wrong with the import"
return custom_exception_handler(401,s), 0
job = myqueue(queuehigh,
handle_c2_getworkout,
user.id,
r.c2token,
c2id,
r.defaulttimezone)
data = s.json()['data']
alldata = {c2id:data}
splitdata = None
#with open('c2temp.json','w') as f:
# f.write(json.dumps(s.json()))
# print(s.json())
return 1
if do_async:
print('aap',alldata)
job = myqueue(queuehigh,
handle_c2_async_workout,
alldata,
r.user.id,
r.c2token,
c2id,
0,
r.defaulttimezone)
return data, pd.DataFrame()
if 'workout' in data:
if 'splits' in data['workout']: # pragma: no cover
splitdata = data['workout']['splits']
elif 'intervals' in data['workout']: # pragma: no cover
splitdata = data['workout']['intervals']
else: # pragma: no cover
splitdata = None
# Check if workout has stroke data, and get the stroke data
if data['stroke_data']:
res2 = get_c2_workout_strokes(user,c2id)
if res2.status_code == 200:
strokedata = pd.DataFrame.from_dict(res2.json()['data'])
else: # pragma: no cover
strokedata = pd.DataFrame()
else: # pragma: no cover
strokedata = pd.DataFrame()
return data,strokedata
# Get stroke data belonging to C2 ID
def get_c2_workout_strokes(user,c2id):
r = Rower.objects.get(user=user)
if (r.c2token == '') or (r.c2token is None): # pragma: no cover
return custom_exception_handler(401,s)
s = "Token doesn't exist. Need to authorize"
elif (timezone.now()>r.tokenexpirydate): # pragma: no cover
s = "Token expired. Needs to refresh."
return custom_exception_handler(401,s)
else:
# ready to fetch. Hurray
authorizationstring = str('Bearer ' + r.c2token)
headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'}
url = "https://log.concept2.com/api/users/me/results/"+str(c2id)+"/strokes"
s = requests.get(url,headers=headers)
return s
# Get list of C2 workouts. We load only the first page,
# assuming that users don't want to import their old workouts
@@ -1011,6 +931,7 @@ def default(o): # pragma: no cover
# Uploading workout
def workout_c2_upload(user,w,asynchron=False):
message = 'trying C2 upload'
try:
if mytypes.c2mapping[w.workouttype] is None: # pragma: no cover
return "This workout type cannot be uploaded to Concept2",0
@@ -1022,14 +943,16 @@ def workout_c2_upload(user,w,asynchron=False):
r = Rower.objects.get(user=user)
# ready to upload. Hurray
if (is_workout_user(user,w)):
c2userid = get_userid(r.c2token)
if not c2userid: # pragma: no cover
raise NoTokenError("User has no token")
dologging('Upload to C2 user {userid}'.format(userid=user.id))
dologging('debuglog.log','Upload to C2 user {userid}'.format(userid=user.id))
data = createc2workoutdata(w)
dologging(json.dumps(data))
dologging('debuglog.log',json.dumps(data))
if data == 0: # pragma: no cover
return "Error: No data file. Contact info@rowsandall.com if the problem persists",0
@@ -1040,7 +963,7 @@ def workout_c2_upload(user,w,asynchron=False):
'Content-Type': 'application/json'}
import urllib
url = "https://log.concept2.com/api/users/%s/results" % (c2userid)
if not asynchron:
if not asynchron: # pragma: no cover
response = requests.post(url,headers=headers,data=json.dumps(data,default=default))
@@ -1060,6 +983,7 @@ def workout_c2_upload(user,w,asynchron=False):
message = "Something went wrong in workout_c2_upload_view. Response code 200/201 but C2 sync failed: "+response.text
c2id = 0
else: # pragma: no cover
job = myqueue(queue,
handle_c2_sync,
w.id,
@@ -1089,190 +1013,3 @@ def rower_c2_token_refresh(user):
return r.c2token
else: # pragma: no cover
return None
# Create workout data from Strava or Concept2
# data and create the associated Workout object and save it
def add_workout_from_data(user,importid,data,strokedata,
source='c2',splitdata=None,
workoutsource='concept2'):
try:
workouttype = mytypes.c2mappinginv[data['type']]
except KeyError: # pragma: no cover
workouttype = 'rower'
if workouttype not in [x[0] for x in Workout.workouttypes]: # pragma: no cover
workouttype = 'other'
try:
comments = data['comments']
except: # pragma: no cover
comments = ' '
try:
thetimezone = pytz.timezone(data['timezone'])
except UnknownTimeZoneError:
thetimezone = 'UTC'
r = Rower.objects.get(user=user)
try:
rowdatetime = iso8601.parse_date(data['date_utc'])
thetimezone = 'UTC'
except KeyError: # pragma: no cover
rowdatetime = iso8601.parse_date(data['start_date'])
rowdatetime = rowdatetime.make_aware(thetimezone)
except ParseError: # pragma: no cover
rowdatetime = iso8601.parse_date(data['date'])
rowdatetime = rowdatetime.make_aware(thetimezone)
try:
c2intervaltype = data['workout_type']
except KeyError: # pragma: no cover
c2intervaltype = ''
try:
title = data['name']
except KeyError:
title = ""
try:
t = data['comments'].split('\n', 1)[0]
title += t[:40]
except: # pragma: no cover
title = ''
try:
comments = data['comments']
except KeyError: # pragma: no cover
comments = ''
starttimeunix = arrow.get(rowdatetime).timestamp()
res = make_cumvalues(0.1*strokedata['t'])
cum_time = res[0]
lapidx = res[1]
totaltime = data['time']/10.
starttimeunix = starttimeunix - totaltime
unixtime = cum_time+starttimeunix
# unixtime[0] = starttimeunix
seconds = 0.1*strokedata.loc[:,'t']
nr_rows = len(unixtime)
try: # pragma: no cover
latcoord = strokedata.loc[:,'lat']
loncoord = strokedata.loc[:,'lon']
except:
latcoord = np.zeros(nr_rows)
loncoord = np.zeros(nr_rows)
try:
strokelength = strokedata.loc[:,'strokelength']
except:
strokelength = np.zeros(nr_rows)
dist2 = 0.1*strokedata.loc[:,'d']
try:
spm = strokedata.loc[:,'spm']
except KeyError: # pragma: no cover
spm = 0*dist2
try:
hr = strokedata.loc[:,'hr']
except KeyError: # pragma: no cover
hr = 0*spm
pace = strokedata.loc[:,'p']/10.
pace = np.clip(pace,0,1e4)
pace = pace.replace(0,300)
velo = 500./pace
power = 2.8*velo**3
if workouttype in ['bike','bikeerg']: # pragma: no cover
velo = 1000./pace
pace = 500./velo
# save csv
# Create data frame with all necessary data to write to csv
df = pd.DataFrame({'TimeStamp (sec)':unixtime,
' Horizontal (meters)': dist2,
' Cadence (stokes/min)':spm,
' HRCur (bpm)':hr,
' longitude':loncoord,
' latitude':latcoord,
' Stroke500mPace (sec/500m)':pace,
' Power (watts)':power,
' DragFactor':np.zeros(nr_rows),
' DriveLength (meters)':np.zeros(nr_rows),
' StrokeDistance (meters)':strokelength,
' DriveTime (ms)':np.zeros(nr_rows),
' StrokeRecoveryTime (ms)':np.zeros(nr_rows),
' AverageDriveForce (lbs)':np.zeros(nr_rows),
' PeakDriveForce (lbs)':np.zeros(nr_rows),
' lapIdx':lapidx,
' WorkoutState': 4,
' ElapsedTime (sec)':seconds
})
df.sort_values(by='TimeStamp (sec)',ascending=True)
timestr = strftime("%Y%m%d-%H%M%S")
# Create CSV file name and save data to CSV file
csvfilename ='media/{code}_{importid}.csv'.format(
importid=importid,
code = uuid4().hex[:16]
)
res = df.to_csv(csvfilename+'.gz',index_label='index',
compression='gzip')
# with Concept2
if source=='c2':
try:
totaldist = data['distance']
totaltime = data['time']/10.
except KeyError: # pragma: no cover
totaldist = 0
totaltime = 0
else: # pragma: no cover
totaldist = 0
totaltime = 0
id,message = dataprep.save_workout_database(
csvfilename,r,
workouttype=workouttype,
title=title,notes=comments,
workoutsource=workoutsource,
dosummary=True,dosmooth=False,
)
w = Workout.objects.get(id=id)
try:
local_tz = pytz.timezone(data['timezone'])
except UnknownTimeZoneError:
local_tz = pytz.utc
# local_tz = pytz.timezone(thetimezone)
w.startdatetime = w.startdatetime.astimezone(local_tz)
w.starttime = w.startdatetime.strftime('%H:%M:%S')
w.timezone = local_tz
w.duration = dataprep.totaltime_sec_to_string(totaltime)
w.distance = totaldist
w.save()
return id,message
+1 -1
View File
@@ -800,7 +800,7 @@ def clean_df_stats(datadf, workstrokesonly=True, ignorehr=True,
def getpartofday(row,r):
workoutstartdatetime = row.rowdatetime
try:
try: # pragma: no cover
latavg = row.df[' latitude'].mean()
lonavg = row.df[' longitude'].mean()
+3 -11
View File
@@ -71,26 +71,18 @@ def splitstdata(lijst):
return [np.array(t),np.array(latlong)]
def splituadata(lijst):
t = []
y = []
for d in lijst:
t.append(d[0])
y.append(d[1])
return np.array(t),np.array(y)
def imports_open(user,oauth_data):
r = Rower.objects.get(user=user)
token = getattr(r,oauth_data['tokenname'])
try:
refreshtoken = getattr(r,oauth_data['refreshtokenname'])
except (TypeError,AttributeError,KeyError):
except (TypeError,AttributeError,KeyError): # pragma: no cover
refreshtoken = None
try:
tokenexpirydate = getattr(r,oauth_data['expirydatename'])
except (TypeError,AttributeError,KeyError):
except (TypeError,AttributeError,KeyError): # pragma: no cover
tokenexpirydate = None
@@ -243,7 +235,7 @@ def imports_get_token(
base_uri,
data=json.dumps(post_data),
headers=headers)
else:
else: # pragma: no cover
response = requests.post(
base_uri,
data=post_data,
-18
View File
@@ -988,11 +988,6 @@ class Rower(models.Model):
sporttracksrefreshtoken = models.CharField(default='',max_length=200,
blank=True,null=True)
sporttracks_auto_export = models.BooleanField(default=False)
underarmourtoken = models.CharField(default='',max_length=200,blank=True,null=True)
underarmourtokenexpirydate = models.DateTimeField(blank=True,null=True)
underarmourrefreshtoken = models.CharField(default='',max_length=200,
blank=True,null=True)
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,
@@ -1041,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 = (
@@ -3201,9 +3193,7 @@ class Workout(models.Model):
maxhr = models.BigIntegerField(blank=True,null=True)
uploadedtostrava = models.BigIntegerField(default=0)
uploadedtosporttracks = models.BigIntegerField(default=0)
uploadedtounderarmour = 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)
@@ -3283,9 +3273,7 @@ class TombStone(models.Model):
uploadedtoc2 = models.IntegerField(default=0)
uploadedtostrava = models.BigIntegerField(default=0)
uploadedtosporttracks = models.BigIntegerField(default=0)
uploadedtounderarmour = 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)
@@ -3294,9 +3282,7 @@ def create_tombstone_on_delete(sender, instance, **kwargs):
user=instance.user,
uploadedtoc2 = instance.uploadedtoc2,
uploadedtostrava = instance.uploadedtostrava,
uploadedtounderarmour = instance.uploadedtounderarmour,
uploadedtotp = instance.uploadedtotp,
uploadedtorunkeeper = instance.uploadedtorunkeeper,
uploadedtonk = instance.uploadedtonk
)
t.save()
@@ -3871,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',
@@ -4215,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',
-34
View File
@@ -183,38 +183,6 @@ stcollection = (
stmapping = {key:value for key,value in Reverse(stcollection)}
rkcollection = (
('water','Rowing'),
('rower','Rowing'),
('skierg','Cross-Country Skiing'),
('bike','Cycling'),
('bikeerg','Cycling'),
('dynamic','Rowing'),
('slides','Rowing'),
('paddle','Other:Paddling'),
('snow','Cross-Country Skiing'),
('coastal','Rowing'),
('c-boat','Rowing'),
('churchboat','Rowing'),
('Ride','Cycling'),
('Run','Running'),
('NordicSki','Cross-Country Skiing'),
('Swim','Swimming'),
('Hike','Hiking'),
('Walk','Walking'),
('Canoeing','Other'),
('Crossfit','CrossFit'),
('StandUpPaddling','Other'),
('IceSkate','Skating'),
('WeightTraining','Other'),
('InlineSkate','Skating'),
('Kayaking','Other'),
('Workout','Other'),
('other','Other'),
('Yoga','Other'),
)
rkmapping = {key:value for key,value in Reverse(rkcollection)}
polarcollection = (
('water','Rowing'),
@@ -320,7 +288,6 @@ stravamappinginv = {value:key for key,value in Reverse(stravacollection) if valu
stmappinginv = {value:key for key,value in Reverse(stcollection) if value is not None}
rkmappinginv = {value:key for key,value in Reverse(rkcollection) if value is not None}
polarmappinginv = {value:key for key,value in Reverse(polarcollection) if value is not None}
@@ -365,7 +332,6 @@ workoutsources = (
('strava','strava'),
('concept2','concept2'),
('sporttracks','sporttracks'),
('runkeeper','runkeeper'),
('mapmyfitness','mapmyfitness'),
('csv','painsled'),
('tcx','tcx'),
+2 -2
View File
@@ -35,7 +35,7 @@ def add_workout_from_data(userid,nkid,data,strokedata,source='nk',splitdata=None
try:
userid=int(userid)
except TypeError:
except TypeError: # pragma: no cover
userid = userid.id
strokedata.to_csv(csvfilename, index_label='index', compression='gzip')
@@ -108,7 +108,7 @@ def add_workout_from_data(userid,nkid,data,strokedata,source='nk',splitdata=None
try:
workoutid = response.json()['id']
except KeyError:
except KeyError: # pragma: no cover
workoutid = 1
+18 -75
View File
@@ -247,7 +247,7 @@ def get_nk_workout_list(user,fake=False,after=0,before=0):
#
def get_workout(user,nkid,do_async=False):
def get_workout(user,nkid,do_async=True):
r = Rower.objects.get(user=user)
if (r.nktoken == '') or (r.nktoken is None): # pragma: no cover
s = "Token doesn't exist. Need to authorize"
@@ -260,79 +260,22 @@ def get_workout(user,nkid,do_async=False):
'sessionIds': nkid,
}
if do_async: # pragma: no cover
res = get_nk_workout_list(r.user)
if res.status_code != 200:
return 0
alldata = {}
for item in res.json():
alldata[item['id']] = item
res = get_nk_workout_list(r.user)
if res.status_code != 200: # pragma: no cover
return 0
alldata = {}
for item in res.json():
alldata[item['id']] = item
res = myqueue(
queuehigh,
handle_nk_async_workout,
alldata,
r.user.id,
r.nktoken,
nkid,
0,
r.defaulttimezone,
)
res = myqueue(
queuehigh,
handle_nk_async_workout,
alldata,
r.user.id,
r.nktoken,
nkid,
0,
r.defaulttimezone,
)
return {},pd.DataFrame()
authorizationstring = str('Bearer ' + r.nktoken)
headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json',
}
# get strokes
url = NK_API_LOCATION+"api/v1/sessions/strokes"
response = requests.get(url,headers=headers,params=params)
if response.status_code != 200: # pragma: no cover
# error handling and logging
return {},pd.DataFrame()
jsonData = response.json()
strokeData = jsonData[str(nkid)]
df = strokeDataToDf(strokeData)
# get workout data
after = df['timestamp'].min()
before = df['timestamp'].max()
after = arrow.get(after/1000.)
before = arrow.get(before/1000.)
after = after-timedelta(days=1)
before = before+timedelta(days=1)
before = str(int(before.timestamp())*1000)
after = str(int(after.timestamp())*1000)
url = NK_API_LOCATION+"api/v1/sessions/"
params = {
'after':after,
'before': before,
}
response = requests.get(url, headers=headers,params=params)
if response.status_code != 200: # pragma: no cover
# error handling and logging
return {},df
jsondata = response.json()
workoutdata = {}
for w in jsondata:
if str(w['id']) == str(nkid):
workoutdata = w
return workoutdata, df
return res
-530
View File
@@ -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)
+9 -2
View File
@@ -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):
@@ -252,7 +259,7 @@ def createsporttracksworkoutdata(w):
# Obtain SportTracks Workout ID from the response returned on successful
# upload
def getidfromresponse(response):
def getidfromresponse(response): # pragma: no cover
t = response.json()
uri = t['uris'][0]
regex = '.*?sporttracks\.mobi\/api\/v2\/fitnessActivities/(\d+)\.json$'
+2 -267
View File
@@ -382,118 +382,9 @@ def async_get_workout(user,stravaid):
return job
# Get a Strava workout summary data and stroke data by ID
def get_workout(user,stravaid,do_async=False):
if do_async: # pragma: no cover
res = async_get_workout(user,stravaid)
return {},pd.DataFrame()
try:
thetoken = strava_open(user)
except NoTokenError: # pragma: no cover
s = "Token error"
return custom_exception_handler(401,s)
def get_workout(user,stravaid,do_async=True):
return async_get_workout(user,stravaid)
r = Rower.objects.get(user=user)
if (r.stravatoken == '') or (r.stravatoken is None): # pragma: no cover
s = "Token doesn't exist. Need to authorize"
return custom_exception_handler(401,s)
elif (r.stravatokenexpirydate is not None and timezone.now()>r.stravatokenexpirydate): # pragma: no cover
s = "Token expired. Needs to refresh."
return custom_exception_handler(401,s)
else:
# ready to fetch. Hurray
fetchresolution = 'high'
series_type = 'time'
authorizationstring = str('Bearer ' + r.stravatoken)
headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json',
'resolution': 'medium',}
url = "https://www.strava.com/api/v3/activities/"+str(stravaid)
workoutsummary = requests.get(url,headers=headers).json()
workoutsummary['timezone'] = "Etc/UTC"
try:
startdatetime = workoutsummary['start_date']
except KeyError: # pragma: no cover
startdatetime = timezone.now()
spm = get_strava_stream(r,'cadence',stravaid)
hr = get_strava_stream(r,'heartrate',stravaid)
t = get_strava_stream(r,'time',stravaid)
velo = get_strava_stream(r,'velocity_smooth',stravaid)
d = get_strava_stream(r,'distance',stravaid)
coords = get_strava_stream(r,'latlng',stravaid)
power = get_strava_stream(r,'watts',stravaid)
if t is not None:
nr_rows = len(t)
else: # pragma: no cover
duration = int(workoutsummary['elapsed_time'])
t = pd.Series(range(duration+1))
nr_rows = len(t)
if nr_rows == 0: # pragma: no cover
return (0,"Error: Time data had zero length")
if d is None: # pragma: no cover
d = 0*t
if spm is None: # pragma: no cover
spm = np.zeros(nr_rows)
if power is None: # pragma: no cover
power = np.zeros(nr_rows)
if hr is None: # pragma: no cover
hr = np.zeros(nr_rows)
if velo is None: # pragma: no cover
velo = np.zeros(nr_rows)
dt = np.diff(t).mean()
wsize = round(5./dt)
velo2 = ewmovingaverage(velo,wsize)
if coords is not None:
try:
lat = coords[:,0]
lon = coords[:,1]
except IndexError: # pragma: no cover
lat = np.zeros(len(t))
lon = np.zeros(len(t))
else: # pragma: no cover
lat = np.zeros(len(t))
lon = np.zeros(len(t))
strokelength = velo*60./(spm)
strokelength[np.isinf(strokelength)] = 0.0
pace = 500./(1.0*velo2)
pace[np.isinf(pace)] = 0.0
df = pd.DataFrame({'t':10*t,
'd':10*d,
'p':10*pace,
'spm':spm,
'hr':hr,
'lat':lat,
'lon':lon,
'power':power,
'strokelength':strokelength,
})
# startdatetime = datetime.datetime.strptime(startdatetime,"%Y-%m-%d-%H:%M:%S")
return [workoutsummary,df]
# Generate Workout data for Strava (a TCX file)
def createstravaworkoutdata(w,dozip=True):
@@ -577,162 +468,6 @@ def handle_stravaexport(f2,workoutname,stravatoken,description='',
return (res.id,message)
# Create workout data from Strava or Concept2
# data and create the associated Workout object and save it
def add_workout_from_data(user,importid,data,strokedata,
source='strava',splitdata=None,
workoutsource='strava'):
try:
workouttype = mytypes.stravamappinginv[data['type']]
except KeyError: # pragma: no cover
workouttype = 'other'
if workouttype.lower() == 'rowing': # pragma: no cover
workouttype = 'rower'
if 'summary_polyline' in data['map'] and workouttype=='rower': # pragma: no cover
workouttype = 'water'
if workouttype not in [x[0] for x in Workout.workouttypes]: # pragma: no cover
workouttype = 'other'
try:
comments = data['comments']
except:
comments = ' '
try:
thetimezone = tz(data['timezone'])
except:
thetimezone = 'UTC'
r = Rower.objects.get(user=user)
try:
rowdatetime = iso8601.parse_date(data['date_utc'])
except KeyError:
rowdatetime = iso8601.parse_date(data['start_date'])
except ParseError: # pragma: no cover
rowdatetime = iso8601.parse_date(data['date'])
try:
intervaltype = data['workout_type']
except KeyError:
intervaltype = ''
try:
title = data['name']
except KeyError: # pragma: no cover
title = ""
try:
t = data['comments'].split('\n', 1)[0]
title += t[:20]
except:
title = ''
starttimeunix = arrow.get(rowdatetime).timestamp()
res = make_cumvalues(0.1*strokedata['t'])
cum_time = res[0]
lapidx = res[1]
unixtime = cum_time+starttimeunix
seconds = 0.1*strokedata.loc[:,'t']
nr_rows = len(unixtime)
try:
latcoord = strokedata.loc[:,'lat']
loncoord = strokedata.loc[:,'lon']
if latcoord.std() == 0 and loncoord.std() == 0 and workouttype == 'water': # pragma: no cover
workouttype = 'rower'
except: # pragma: no cover
latcoord = np.zeros(nr_rows)
loncoord = np.zeros(nr_rows)
if workouttype == 'water':
workouttype = 'rower'
try:
strokelength = strokedata.loc[:,'strokelength']
except: # pragma: no cover
strokelength = np.zeros(nr_rows)
dist2 = 0.1*strokedata.loc[:,'d']
try:
spm = strokedata.loc[:,'spm']
except KeyError: # pragma: no cover
spm = 0*dist2
try:
hr = strokedata.loc[:,'hr']
except KeyError: # pragma: no cover
hr = 0*spm
pace = strokedata.loc[:,'p']/10.
pace = np.clip(pace,0,1e4)
pace = pace.replace(0,300)
velo = 500./pace
try:
power = strokedata.loc[:,'power']
except KeyError: # pragma: no cover
power = 2.8*velo**3
#if power.std() == 0 and power.mean() == 0:
# power = 2.8*velo**3
# save csv
# Create data frame with all necessary data to write to csv
df = pd.DataFrame({'TimeStamp (sec)':unixtime,
' Horizontal (meters)': dist2,
' Cadence (stokes/min)':spm,
' HRCur (bpm)':hr,
' longitude':loncoord,
' latitude':latcoord,
' Stroke500mPace (sec/500m)':pace,
' Power (watts)':power,
' DragFactor':np.zeros(nr_rows),
' DriveLength (meters)':np.zeros(nr_rows),
' StrokeDistance (meters)':strokelength,
' DriveTime (ms)':np.zeros(nr_rows),
' StrokeRecoveryTime (ms)':np.zeros(nr_rows),
' AverageDriveForce (lbs)':np.zeros(nr_rows),
' PeakDriveForce (lbs)':np.zeros(nr_rows),
' lapIdx':lapidx,
' ElapsedTime (sec)':seconds
})
df.sort_values(by='TimeStamp (sec)',ascending=True)
timestr = strftime("%Y%m%d-%H%M%S")
# Create CSV file name and save data to CSV file
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,
dosmooth=r.dosmooth,
workouttype=workouttype,
title=title,notes=comments,
workoutsource=workoutsource,
dosummary=True
)
return id,message
def workout_strava_upload(user,w, quick=False,asynchron=True):
try:
+26 -52
View File
@@ -25,7 +25,6 @@ from rowingdata import rowingdata as rdata
from datetime import timedelta
from sqlalchemy import create_engine
from simplejson.errors import JSONDecodeError
#from celery import app
from rowers.celery import app
@@ -66,7 +65,8 @@ from django_rq import job
from django.utils import timezone
from django.utils.html import strip_tags
from rowers.utils import deserialize_list,ewmovingaverage,wavg
from rowers.utils import deserialize_list,ewmovingaverage,wavg,dologging
import rowers.utils as utils
from rowers.emails import htmlstrip
from rowers import mytypes
@@ -127,15 +127,6 @@ from rowers.courseutils import (
InvalidTrajectoryError
)
def dologging(s):
tstamp = time.localtime()
timestamp = time.strftime('%b-%d-%Y %H:%M:%S', tstamp)
with open('debuglog.log','a') as f:
f.write('\n')
f.write(timestamp)
f.write(' ')
f.write(s)
# Concept2 logbook sends over split data for each interval
# We use it here to generate a custom summary
# Some users complained about small differences
@@ -327,20 +318,6 @@ def handle_sporttracks_sync(workoutid,url,headers,data,debug=False,**kwargs):
return 1
@app.task
def handle_runkeeper_sync(workoutid,url,headers,data,debug=False,**kwargs):
response = requests.post(url,headers=headers,data=data)
if response.status_code not in [200,201]: # pragma: no cover
return 0
uri = response.headers["Location"]
tester = re.compile('^\/fitnessActivities\/(\d+)$')
id = int(tester.match(uri).group(1))
res = update_workout_field_sql(workoutid,'uploadedtorunkeeper',id,debug=debug)
return 1
@app.task
def handle_strava_sync(stravatoken,workoutid,filename,name,activity_type,description,debug=False,**kwargs):
@@ -2850,7 +2827,7 @@ def handle_rp3_async_workout(userid,rp3token,rp3id,startdatetime,max_attempts,de
try:
workout_download_details = pd.json_normalize(response.json()['data']['download'])
except JSONDecodeError:
except JSONDecodeError: # pragma: no cover
return 0
if workout_download_details.iat[0,1] == 'ready':
@@ -2990,7 +2967,23 @@ def handle_nk_async_workout(alldata,userid,nktoken,nkid,delaysec,defaulttimezone
# return
return workoutid
@app.task
def handle_c2_getworkout(userid,c2token,c2id,defaulttimezone,debug=False,**kwargs):
authorizationstring = str('Bearer ' + c2token)
headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'}
url = "https://log.concept2.com/api/users/me/results/"+str(c2id)
s = requests.get(url,headers=headers)
if s.status_code != 200: # pragma: no cover
return 0
data = s.json()['data']
alldata = {c2id:data}
splitdata = None
return handle_c2_async_workout(alldata,userid,c2token,c2id,0,defaulttimezone)
@app.task
def handle_c2_async_workout(alldata,userid,c2token,c2id,delaysec,defaulttimezone,debug=False,**kwargs):
@@ -3005,15 +2998,12 @@ def handle_c2_async_workout(alldata,userid,c2token,c2id,delaysec,defaulttimezone
c2id = data['id']
workouttype = data['type']
verified = data['verified']
try:
startdatetime = iso8601.parse_date(data['date_utc'])
except: # pragma: no cover
startdatetime = iso8601.parse_date(data['date'])
weightclass = data['weight_class']
s = 'User {userid}, C2 ID {c2id}'.format(userid=userid,c2id=c2id)
dologging(s)
dologging(json.dumps(data))
dologging('debuglog.log',s)
dologging('debuglog.log',json.dumps(data))
try:
title = data['name']
@@ -3032,30 +3022,13 @@ def handle_c2_async_workout(alldata,userid,c2token,c2id,delaysec,defaulttimezone
# Create CSV file name and save data to CSV file
csvfilename ='media/Import_'+str(c2id)+'.csv.gz'
totaltime = data['time']/10.
duration = totaltime_sec_to_string(totaltime)
starttimeunix = arrow.get(startdatetime).timestamp()-totaltime
startdatetime = arrow.get(starttimeunix)
startdatetime,starttime,workoutdate,duration,starttimeunix,timezone = utils.get_startdatetime_from_c2data(data)
try:
timezone = pytz.timezone(data['timezone'])
except UnknownTimeZoneError:
timezone = pytz.utc
startdatetime = startdatetime.astimezone(timezone)
s = 'Time zone {timezone}, stardatetime {startdatetime}, duration {duration}'.format(
timezone=timezone,startdatetime=startdatetime,
duration=duration)
dologging(s)
workoutdate = startdatetime.astimezone(
timezone
).strftime('%Y-%m-%d')
starttime = startdatetime.astimezone(
timezone
).strftime('%H:%M:%S')
dologging('debuglog.log',s)
try:
notes = data['comments']
@@ -3240,7 +3213,7 @@ def handle_c2_async_workout(alldata,userid,c2token,c2id,delaysec,defaulttimezone
units = getlist(sa,sel='unit')
types = getlist(sa,sel='type')
rowdata = rdata(csvfilename)
rowdata = rdata(csvfile=csvfilename)
if rowdata:
rowdata.updateintervaldata(values,
units,types,results)
@@ -3251,6 +3224,7 @@ def handle_c2_async_workout(alldata,userid,c2token,c2id,delaysec,defaulttimezone
return workoutid
@app.task
def fetch_strava_workout(stravatoken,oauth_data,stravaid,csvfilename,userid,debug=False,**kwargs):
fetchresolution = 'high'
-209
View File
@@ -1,209 +0,0 @@
{% extends "base.html" %}
{% load static %}
{% load rowerfilters %}
{% block title %}Export {% endblock %}
{% block content %}
<div id="exportbuttons" class="grid_6 alpha">
<h3>Export Workout</h3>
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id|encode }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id|encode }}/workflow">Workflow View</a>
</p>
</div>
<div class="grid_2 omega">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id|encode }}/advanced">Advanced Edit</a>
</p>
</div>
<div class="grid_6 alpha">
<p>
Click on the icon to upload this workout to your site of choice. A checkmark indicates that the workout has already been uploaded. If the button is grayed out, click it to authorize the connection to that site. Use TCX or CSV export to email a TCX or CSV file of your workout to yourself.
</p>
</div>
{% if workout.uploadedtoc2 == 0 %}
{% if user.rower.c2token == None or user.rower.c2token == '' %}
<div class="grid_1 alpha">
<a href="/rowers/me/c2authorize">
<img src="/static/img/c2square_gray.png" alt="C2 icon" width="60" height="60"></a>
</div>
{% else %}
<div class="grid_1 alpha">
<a href="/rowers/workout/{{ workout.id|encode }}/c2uploadw"><img src="/static/img/c2square.jpg" alt="Concept2 icon" width="60" height="60"></a>
</div>
{% endif %}
{% else %}
<div class="grid_1 alpha">
<a href="http://log.concept2.com/profile/{{ c2userid }}/log/{{ workout.uploadedtoc2 }}">
<img src="/static/img/c2square_checked.png" alt="Concept2 icon" width="60" height="60"></a>
</div>
{% endif %}
{% if workout.uploadedtostrava == 0 %}
{% if user.rower.stravatoken == None or user.rower.stravatoken == '' %}
<div class="grid_1">
<a href="/rowers/me/stravaauthorize">
<img src="/static/img/stravasquare_gray.png" alt="Strava icon" width="60" height="60"></a>
</div>
{% else %}
<div class="grid_1">
<a href="/rowers/workout/{{ workout.id|encode }}/stravauploadw"><img src="/static/img/stravasquare.png" alt="Strava icon" width="60" height="60"></a>
</div>
{% endif %}
{% else %}
<div class="grid_1">
<a href="https://www.strava.com/activities/{{ workout.uploadedtostrava }}">
<img src="/static/img/stravasquare_checked.png" alt="Concept2 icon" width="60" height="60"></a>
</div>
{% endif %}
{% if workout.uploadedtosporttracks == 0 %}
{% if user.rower.sporttrackstoken == None or user.rower.sporttrackstoken == '' %}
<div class="grid_1">
<a href="/rowers/me/sporttracksauthorize">
<img src="/static/img/sporttrackssquare_gray.png" alt="SportTracks icon" width="60" height="60"></a>
</div>
{% else %}
<div class="grid_1">
<a href="/rowers/workout/{{ workout.id|encode }}/sporttracksuploadw">
<img src="/static/img/sporttrackssquare.png" alt="SportTracks icon" width="60" height="60"></a>
</div>
{% endif %}
{% else %}
<div class="grid_1">
<a href="https://sporttracks.mobi/activity/{{ workout.uploadedtosporttracks }}">
<img src="/static/img/sporttrackssquare_checked.png" alt="Concept2 icon" width="60" height="60"></a>
</div>
{% endif %}
<div class="grid_1">
<a href="/rowers/workout/{{ workout.id|encode }}/emailtcx">
<img src="/static/img/export.png" alt="TCX Export" width="60" height="60"></a>
</div>
<div class="grid_1">
<a href="/rowers/workout/{{ workout.id|encode }}/emailcsv">
<img src="/static/img/CSVsquare.png" alt="CSV Export" width="60" height="60"></a>
</div>
<div class="grid_6 alpha">
{% if workout.uploadedtorunkeeper == 0 %}
{% if user.rower.runkeepertoken == None or user.rower.runkeepertoken == '' %}
<div class="grid_1 alpha">
<a href="/rowers/me/runkeeperauthorize">
<img src="/static/img/rkgray.png" alt="Runkeeper icon" width="60" height="60"></a>
</div>
{% else %}
<div class="grid_1 alpha">
<a href="/rowers/workout/{{ workout.id|encode }}/runkeeperuploadw"><img src="/static/img/rksquare.png" alt="Runkeeper icon" width="60" height="60"></a>
</div>
{% endif %}
{% else %}
<div class="grid_1 alpha">
<a href="https://runkeeper.com/user/{{ rkuserid }}/activity/{{ workout.uploadedtorunkeeper }}">
<img src="/static/img/rkchecked.png" alt="Runkeeper icon" width="60" height="60"></a>
</div>
{% endif %}
{% if workout.uploadedtounderarmour == 0 %}
{% if user.rower.underarmourtoken == None or user.rower.underarmourtoken == '' %}
<div class="grid_1">
<a href="/rowers/me/underarmourauthorize">
<img src="/static/img/uagray.png" alt="Underarmour icon" width="60" height="60"></a>
</div>
{% else %}
<div class="grid_1">
<a href="/rowers/workout/{{ workout.id|encode }}/underarmouruploadw"><img src="/static/img/uasquare.png" alt="Underarmour icon" width="60" height="60"></a>
</div>
{% endif %}
{% else %}
<div class="grid_1">
<a href="https://www.mapmyfitness.com/workout/{{ workout.uploadedtounderarmour }}">
<img src="/static/img/uachecked.png" alt="Underarmour icon" width="60" height="60"></a>
</div>
{% endif %}
{% if workout.uploadedtotp == 0 %}
{% if user.rower.tptoken == None or user.rower.tptoken == '' %}
<div class="grid_1">
<a href="/rowers/me/tpauthorize">
<img src="/static/img/tpgray.png" alt="TrainingPeaks icon" width="60" height="60"></a>
</div>
{% else %}
<div class="grid_1">
<a href="/rowers/workout/{{ workout.id|encode }}/tpuploadw"><img src="/static/img/tpicon.png" alt="Tp icon" width="60" height="60"></a>
</div>
{% endif %}
{% else %}
<div class="grid_1">
<a href="https://app.trainingpeaks.com">
<img src="/static/img/tpchecked.png" alt="TrainingPeaks icon" width="60" height="60"></a>
</div>
{% endif %}
<div class="grid_1 omega">
<a href="/rowers/workout/{{ workout.id|encode }}/emailgpx">
<img src="/static/img/gpx.jpg" alt="GPX Export" width="60" height="60"></a>
</div>
</div>
</div>
<div class="grid_6 omega">
<h3>Connect</h3>
<div class="grid_6">
<p>Click one of the below logos to connect to the service of your choice.
You only need to do this once. After that, the site will have access until you
revoke the authorization for the "rowingdata" app.</p>
<div class="grid_2 alpha">
<p><a href="/rowers/me/stravaauthorize/"><img src="/static/img/ConnectWithStrava.png" alt="connect with strava" width="120"></a></p>
</div>
<div class="grid_2">
<p><a href="/rowers/me/c2authorize/"><img src="/static/img/blueC2logo.png" alt="connect with Concept2" width="120"></a></p>
</div>
<div class="grid_2 omega">
<p><a href="/rowers/me/sporttracksauthorize/"><img src="/static/img/sporttracks-button.png" alt="connect with SportTracks" width="120"></a></p>
</div>
</div>
<div class="grid_6">
<div class="grid_2 alpha">
<p><a href="/rowers/me/runkeeperauthorize/"><img src="/static/img/rk-logo.png" alt="connect with Runkeeper" width="120"></a></p>
</div>
<div class="grid_2">
<p><a href="/rowers/me/underarmourauthorize/"><img src="/static/img/UAbtn.png" alt="connect with Under Armour" width="120"></a></p>
</div>
<div class="grid_2 omega">
<p><a href="/rowers/me/tpauthorize/"><img src="/static/img/TP_logo_horz_2_color.png" alt="connect with TrainingPeaks" width="150"></a></p>
</div>
</div>
<div class="grid_6 alpha">
<p>
<h2>Export Settings</h2>
<div class="grid_2 suffix_4 alpha">
<a class="button gray small" href="/rowers/me/exportsettings">Manage Export Settings</a>
</div>
</p>
</div>
</div>
{% endblock %}
-137
View File
@@ -1,137 +0,0 @@
{% extends "base.html" %}
{% block title %}Import Workouts{% endblock title %}
{% block content %}
<div class="grid_6 alpha">
<h2>Import Workouts</h2>
<div class="grid_6">
<div class="grid_3 alpha">
<p>
<a href="/rowers/workout/stravaimport"><img src="/static/img/stravalogo.png" alt="strava logo" width="140"></a>
</p>
</div>
<div class="grid_3 omega">
<p>Import workouts from Strava</p>
</div>
</div>
<div class="grid_6">
<div class="grid_3 alpha">
<p>
<a href="/rowers/workout/c2list"><img src="/static/img/blueC2logo.png" alt="Concept2 logo" width="140"></a>
</p>
</div>
<div class="grid_3 omega">
<p>Import workouts from the Concept2 logbook</p>
</div>
<div class="grid_3 alpha">
<p>
<a href="/rowers/workout/sporttracksimport"><img src="/static/img/sporttracks-button.png" alt="SportTracks logo" width="140"></a>
</p>
</div>
<div class="grid_3 omega">
<p>Import workouts from SportTracks</p>
</div>
</div>
<div class="grid_6">
<div class="grid_3 alpha">
<p>
<a href="/rowers/workout/runkeeperimport"><img src="/static/img/rk-logo.png" alt="Runkeeper logo" width="140"></a>
</p>
</div>
<div class="grid_3 omega">
<p>Import workouts from RunKeeper</p>
</div>
</div>
<div class="grid_6">
<div class="grid_3 alpha">
<p>
<a href="/rowers/workout/underarmourimport"><img src="/static/img/UAbtn.png" alt="Under Armour logo" width="140"></a>
</p>
</div>
<div class="grid_3 omega">
<p>Import workouts from MapMyFitness/UnderArmour</p>
</div>
</div>
<div class="grid_6">
<div class="grid_3 alpha">
<p>
<a href="/rowers/workout/polarimport"><img src="/static/img/Polar_connectwith_btn_white.png" alt="Polar logo" width="140"></a>
</p>
</div>
<div class="grid_3 omega">
<p>Import workouts from Polar Flow. </p><p><b>Note: No workout selection possible. Automatically imports all new workouts</b></p>
</div>
</div>
</div>
<div class="grid_6 omega">
<h2>Connect</h2>
<div class="grid_6 alpha">
<p>Click one of the below logos to connect to the service of your choice.
You only need to do this once. After that, the site will have
access until you
revoke the authorization for the "rowingdata" app.</p>
<div class="grid_2 alpha">
<p><a href="/rowers/me/stravaauthorize/"><img src="/static/img/ConnectWithStrava.png" alt="connect with strava" width="120"></a></p>
</div>
<div class="grid_2">
<p><a href="/rowers/me/c2authorize/"><img src="/static/img/blueC2logo.png" alt="connect with Concept2" width="120"></a></p>
</div>
<div class="grid_2 omega">
<p><a href="/rowers/me/sporttracksauthorize/"><img src="/static/img/sporttracks-button.png" alt="connect with SportTracks" width="120"></a></p>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p><a href="/rowers/me/runkeeperauthorize/"><img src="/static/img/rk-logo.png" alt="connect with RunKeeper" width="120"></a></p>
</div>
<div class="grid_2">
<p><a href="/rowers/me/underarmourauthorize/"><img src="/static/img/UAbtn.png" alt="connect with Under Armour" width="120"></a></p>
</div>
<div class="grid_2 omega">
<p><a href="/rowers/me/polarauthorize/"><img src="/static/img/Polar_connectwith_btn_white.png"
alt="connect with Polar" width="130"></a></p>
</div>
</div>
<h2>Auto Import/Export Settings</h2>
<p>Use the form below to set your auto import/export settings.
As we implement auto import/export for various partner sites, this form will be expanded.</p>
<p>Auto Import = rowsandall.com will regularly poll the partner site for new
workouts and automatically import new workouts
</p>
<p>Auto Export = New workouts uploaded to rowsandall.com will be automatically synchronized
to the partner site</p>
<p>These settings only have effect if you are a user on one
of the <a href="/rowers/paidplans">paid plans</a></p>
<div class="grid_6 alpha">
<form id="importexportform" method="post">
<table width=70%>
{{ form.as_table }}
</table>
{% csrf_token %}
<div id="formbutton">
<input type="submit" value="Save">
</div>
</form>
</div>
</div>
{% endblock content %}
-1
View File
@@ -39,7 +39,6 @@
<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="mapmyfitness"><a href="/rowers/workout/underarmourimport/">MapMyFitness</a></li>
<li id="polar"><a href="/rowers/workout/polarimport/">Polar</a></li>
</ul>
</li>
-30
View File
@@ -192,36 +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-mmf">
{% if workout.uploadedtounderarmour %}
<a href="https://www.mapmyfitness.com/workout/{{ workout.uploadedtounderarmour }}">
MapMyFitness <i class="fas fa-check"></i>
</a>
{% elif user.rower.underarmourtoken == None or user.rower.underarmourtoken == '' %}
<a href="/rowers/me/underarmourauthorize">
Connect to MapMyFitness
</a>
{% else %}
<a href="/rowers/workout/{{ workout.id|encode }}/underarmouruploadw/">
MapMyFitness
</a>
{% endif %}
</li>
<li id="export-tp">
{% if workout.uploadedtotp %}
<a href="https://app.trainingpeaks.com">
-2
View File
@@ -40,9 +40,7 @@
<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="mapmyfitness"><a href="/rowers/workout/underarmourimport/">MapMyFitness</a></li>
<li id="polar"><a href="/rowers/workout/polarimport/">Polar</a></li>
<li id="rp3"><a href="/rowers/workout/rp3import/">RP3</a></li>
</ul>
@@ -17,9 +17,6 @@
{% if rower.sporttrackstoken is not None and rower.sporttrackstoken != '' %}
SportTracks,
{% endif %}
{% if rower.underarmourtoken is not None and rower.underarmourtoken != '' %}
Under Armour (MapMyFitness),
{% endif %}
{% if rower.tptoken is not None and rower.tptoken != '' %}
TrainingPeaks,
{% endif %}
@@ -32,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 %}
@@ -79,8 +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/underarmourauthorize/"><img src="/static/img/UAbtn.png" alt="connect with Under Armour" 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"
@@ -1,41 +0,0 @@
{% extends "newbase.html" %}
{% load static %}
{% load rowerfilters %}
{% block title %}Workouts{% endblock %}
{% block main %}
<h1>Available on MapMyFitness (UnderArmour)</h1>
{% if workouts %}
<table width="70%" class="listtable">
<thead>
<tr>
<th> Import </th>
<th> Date/Time </th>
<th> Duration </th>
<th> Total Distance</th>
<th> Type</th>
</tr>
</thead>
<tbody>
{% for workout in workouts %}
<tr>
<td>
<a href="/rowers/workout/underarmourimport/{{ workout|ualookup:'id' }}/">Import</a></td>
<td>{{ workout|ualookup:'starttime' }}</td>
<td>{{ workout|ualookup:'duration' }} </td>
<td>{{ workout|ualookup:'distance' }} m</td>
<td>{{ workout|ualookup:'type' }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p> No workouts found. We only list workouts with time data series. </p>
{% endif %}
{% endblock %}
{% block sidebar %}
{% include 'menu_workouts.html' %}
{% endblock %}
+2 -28
View File
@@ -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,
@@ -434,7 +433,7 @@ def deltatimeprint(d): # pragma: no cover
return strfdeltah(d)
@register.filter
def c2userid(user):
def c2userid(user): # pragma: no cover
try:
thetoken = c2_open(user)
except NoTokenError: # pragma: no cover
@@ -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
@@ -495,21 +484,6 @@ def lookuplong(dict, key):
return s
@register.filter
def ualookup(dict, key):
s = dict.get(key)
if key=='distance':
s = int(float(s))
if key=='duration':
s = secondstotimestring(int(s))
if key=='starttime':
s = dateutil.parser.parse(s)
return s
from rowers.models import PlannedSession
@register.filter
+11 -1
View File
@@ -334,9 +334,19 @@ def mocked_getsmallrowdata_db(*args, **kwargs):
return df
def mocked_getsmallrowdata_db_updatecp(*args, **kwargs):
df = pd.read_csv('rowers/tests/testdata/colsfromdb.csv')
return df
def mocked_getsmallrowdata_db_setcp(*args, **kwargs):
df = pd.read_csv('rowers/tests/testdata/colsfromdb2.csv')
return df
def mocked_getsmallrowdata_db_water(*args, **kwargs):
df = pd.read_csv('rowers/tests/testdata/colsfromdb.csv')
df = pd.read_csv('rowers/tests/testdata/colsfromdb3.csv')
return df
-1
View File
@@ -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
+2 -18
View File
@@ -462,22 +462,6 @@ class AsyncTaskTests(TestCase):
res = tasks.handle_sporttracks_sync(1,url,headers,json.dumps(data,default=sporttracksstuff.default))
self.assertEqual(res,1)
@patch('rowers.runkeeperstuff.requests.post', side_effect=mocked_requests)
@patch('rowers.runkeeperstuff.requests.get', side_effect=mocked_requests)
def test_runkeeper_sync(self, mock_get, mock_post):
authorizationstring = str('Bearer aap')
headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/vnd.com.runkeeper.NewFitnessActivity+json',
'Content-Length':'nnn'}
url = "https://api.runkeeper.com/fitnessActivities"
data = runkeeperstuff.createrunkeeperworkoutdata(self.wwater)
res = tasks.handle_runkeeper_sync(1,url,headers,json.dumps(data,default=sporttracksstuff.default))
self.assertEqual(res,1)
@patch('rowers.c2stuff.requests.post',side_effect=mocked_requests)
@patch('rowers.c2stuff.requests.get',side_effect=mocked_requests)
@@ -519,14 +503,14 @@ class AsyncTaskTests(TestCase):
@patch('rowers.dataprepnodjango.getsmallrowdata_db')
def test_handle_updatecp(self,mocked_getsmallrowdata_db):
def test_handle_updatecp(self,mocked_getsmallrowdata_db_updatecp):
rower_id = 1
workoutids = [1]
res = tasks.handle_updatecp(rower_id,workoutids)
self.assertEqual(res,1)
@patch('rowers.dataprepnodjango.getsmallrowdata_db')
def test_handle_setcp(self,mocked_getsmallrowdata_db):
def test_handle_setcp(self,mocked_getsmallrowdata_db_db_setcp):
strokesdf = pd.read_csv('rowers/tests/testdata/uhfull.csv')
filename = 'rowers/tests/testdata/temp/pq.gz'
workoutids = 1
+41 -249
View File
@@ -19,6 +19,8 @@ from rowers import stravastuff
import urllib
import json
import rowers.utils as utils
from django.db import transaction
import rowers.garmin_stuff as gs
@@ -313,8 +315,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)
@@ -324,15 +328,18 @@ class C2Objects(DjangoTestCase):
def test_c2_import_tz(self, mock_get, mocked_sqlalchemy):
response = self.c.get('/rowers/workout/c2import/22/',follow=True)
expected_url = '/rowers/workout/c2list/'
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)
w = Workout.objects.get(id=2)
self.assertEqual(w.timezone,'Europe/Prague')
with open('rowers/tests/testdata/c2_timezone.json','r') as infile:
data = json.load(infile)
timezone = str(utils.get_timezone_from_c2data(data['data']))
self.assertEqual(timezone,'Europe/Prague')
@patch('rowers.c2stuff.requests.get', side_effect=mocked_requests)
@@ -340,68 +347,46 @@ class C2Objects(DjangoTestCase):
def test_c2_import_tz3(self, mock_get, mocked_sqlalchemy):
response = self.c.get('/rowers/workout/c2import/32/',follow=True)
expected_url = '/rowers/workout/c2list/'
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)
w = Workout.objects.get(id=2)
self.assertEqual(w.timezone,'UTC')
with open('rowers/tests/testdata/c2_timezonebad.json','r') as infile:
data = json.load(infile)
@patch('rowers.c2stuff.requests.get', side_effect=mocked_requests)
timezone = str(utils.get_timezone_from_c2data(data['data']))
self.assertEqual(timezone,'UTC')
@patch('rowers.tasks.requests.get', side_effect=mocked_requests)
@patch('rowers.dataprep.create_engine')
def test_c2_import_tz2(self, mock_get, mocked_sqlalchemy):
response = self.c.get('/rowers/workout/c2import/31/',follow=True)
expected_url = '/rowers/workout/c2list/'
result = tasks.handle_c2_getworkout(self.r.user.id,self.r.c2token,31,self.r.defaulttimezone)
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)
w = Workout.objects.get(id=2)
self.assertEqual(w.timezone,'Europe/Amsterdam')
with open('rowers/tests/testdata/c2_timezone2.json','r') as infile:
data = json.load(infile)
self.assertEqual(w.starttime.strftime("%H:%M:%S"),"20:04:56")
timezone = str(utils.get_timezone_from_c2data(data['data']))
self.assertEqual(timezone,'Europe/Amsterdam')
startdatetime,starttime,workoutdate,duration,starttimeunix,timezone = utils.get_startdatetime_from_c2data(data['data'])
self.assertEqual(starttime,"20:04:55")
@patch('rowers.dataprep.create_engine')
def test_strokedata(self, mocked_sqlalchemy):
with open('rowers/tests/testdata/c2stroketestdata.txt','r') as infile:
res = json.load(infile)
strokedata = pd.DataFrame.from_dict(res['data'])
with open('rowers/tests/testdata/c2testdata.txt','r') as infile:
res = json.load(infile)
data = res['data']
from rowers.views import add_workout_from_strokedata
res = add_workout_from_strokedata(self.u,1,data,strokedata,source='c2')
@patch('rowers.dataprep.create_engine')
def test_strokedatanohr(self, mocked_sqlalchemy):
with open('rowers/tests/testdata/c2strokedatanohr.txt','r') as infile:
res = json.load(infile)
strokedata = pd.DataFrame.from_dict(res['data'])
with open('rowers/tests/testdata/c2testdata.txt','r') as infile:
res = json.load(infile)
data = res['data']
from rowers.views import add_workout_from_strokedata
res = add_workout_from_strokedata(self.u,1,data,strokedata,source='c2')
@patch('rowers.tasks.requests.get',side_effect=mocked_requests)
@patch('rowers.tasks.requests.post',side_effect=mocked_requests)
@patch('rowers.tasks.requests.session', side_effect=mocked_requests)
@@ -485,13 +470,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 +587,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 +610,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 +875,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 +1001,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,200 +1042,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
@override_settings(TESTING=True)
class UAObjects(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.underarmourtoken = '12'
self.r.underarmourrefreshtoken = '12'
self.r.underarmourtokenexpirydate = arrow.get(datetime.datetime.now()+datetime.timedelta(days=1)).datetime
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.imports.requests.post', side_effect=mocked_requests)
def test_underarmour_callback(self, mock_post):
response = self.c.get('/underarmour_callback?code=dsdoij232s',follow=True)
self.assertEqual(response.status_code, 200)
@patch('rowers.underarmourstuff.requests.post', side_effect=mocked_requests)
def test_underarmour_token_refresh(self, mock_post):
response = self.c.get('/rowers/me/underarmourrefresh/',follow=True)
self.assertEqual(response.status_code, 200)
@patch('rowers.underarmourstuff.requests.post', side_effect=mocked_requests)
@patch('rowers.underarmourstuff.requests.get', side_effect=mocked_requests)
def test_underarmour_upload(self, mock_get, mock_post):
response = self.c.get('/rowers/workout/'+encoded1+'/underarmouruploadw/')
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.underarmourstuff.requests.get', side_effect=mocked_requests)
def test_underarmour_list(self, mock_get):
response = self.c.get('/rowers/workout/underarmourimport',follow=True)
self.assertEqual(response.status_code,200)
@patch('rowers.imports.requests.get', side_effect=mocked_requests)
@patch('rowers.dataprep.create_engine')
def test_underarmour_import(self, mock_get, mocked_sqlalchemy):
response = self.c.get('/rowers/workout/underarmourimport/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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-9
View File
@@ -94,14 +94,10 @@
104,127,workout_sporttracksimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,TRUE,TRUE,
105,129,workout_getsporttracksworkout_all,gets all C2 workouts (now redirects due to NoTokenError,TRUE,302,basic,302,302,FALSE,302,302,FALSE,302,302,FALSE,FALSE,FALSE,TRUE,TRUE,
106,130,workout_polarimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,TRUE,TRUE,
107,132,workout_runkeeperimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,TRUE,TRUE,
108,134,workout_underarmourimport_view,list workouts to be imported (test stops at notokenerror),TRUE,302,basic,302,302,basic,403,403,coach,302,403,FALSE,TRUE,FALSE,TRUE,TRUE,
109,135,workout_c2_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE,
110,136,workout_strava_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE,
111,137,workout_recalcsummary_view,recalculates workout summary,TRUE,403,basic,302,403,basic,403,403,coach,302,403,FALSE,FALSE,TRUE,TRUE,TRUE,
112,138,workout_sporttracks_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE,
113,139,workout_runkeeper_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE,
114,140,workout_underarmour_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE,
115,141,workout_tp_upload_view,uploads workout to C2,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,TRUE,FALSE,FALSE,
116,142,multi_compare_view,compare workouts ,TRUE,302,basic,302,403,basic,302,403,coach,302,302,FALSE,FALSE,TRUE,TRUE,TRUE,
117,145,alerts_view,view alerts,TRUE,302,basic,200,403,basic,403,403,coach,200,302,FALSE,TRUE,FALSE,TRUE,TRUE,
@@ -151,11 +147,8 @@
165,210,rower_revokeapp_view,revoke app,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
166,211,rower_strava_authorize,authorize on strava,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
167,212,rower_sporttracks_authorize,authorize on sporttracks,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
168,213,rower_underarmour_authorize,authorize on UA,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
169,214,rower_tp_authorize,authorize on TP,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
170,215,rower_runkeeper_authorize,authorize on RK,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
171,216,rower_sporttracks_token_refresh,refresh token,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
172,217,rower_underarmour_token_refresh,refresh token,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
173,218,rower_tp_token_refresh,refresh token,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
174,219,rower_c2_token_refresh,refresh token,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
175,220,rower_favoritecharts_view,See favorite charts,TRUE,302,pro,200,302,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE,
@@ -232,9 +225,7 @@
247,343,rower_process_callback,Tested in specific test suite,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
248,344,rower_process_stravacallback,Tested in specific test suite,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
249,345,rower_process_sporttrackscallback,Tested in specific test suite,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
250,346,rower_process_underarmourcallback,Tested in specific test suite,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
251,347,rower_process_polarcallback,Tested in specific test suite,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
252,348,rower_process_runkeepercallback,Tested in specific test suite,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
253,349,rower_process_tpcallback,Tested in specific test suite,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
254,350,rower_process_twittercallback,Tested in specific test suite,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
255,351,set_language,Set language,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE,
1 id view function anonymous anonymous_response own own_response own_nonperm member member_response member_nonperm coachee coachee_response coachee_nonperm is_staff userid workoutid dotest realtest kwargs
94 104 127 workout_sporttracksimport_view list workouts to be imported (test stops at notokenerror) TRUE 302 basic 302 302 basic 403 403 coach 302 403 FALSE TRUE FALSE TRUE TRUE
95 105 129 workout_getsporttracksworkout_all gets all C2 workouts (now redirects due to NoTokenError TRUE 302 basic 302 302 FALSE 302 302 FALSE 302 302 FALSE FALSE FALSE TRUE TRUE
96 106 130 workout_polarimport_view list workouts to be imported (test stops at notokenerror) TRUE 302 basic 302 302 basic 403 403 coach 302 403 FALSE TRUE FALSE TRUE TRUE
107 132 workout_runkeeperimport_view list workouts to be imported (test stops at notokenerror) TRUE 302 basic 302 302 basic 403 403 coach 302 403 FALSE TRUE FALSE TRUE TRUE
108 134 workout_underarmourimport_view list workouts to be imported (test stops at notokenerror) TRUE 302 basic 302 302 basic 403 403 coach 302 403 FALSE TRUE FALSE TRUE TRUE
97 109 135 workout_c2_upload_view uploads workout to C2 TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE TRUE FALSE FALSE
98 110 136 workout_strava_upload_view uploads workout to C2 TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE TRUE FALSE FALSE
99 111 137 workout_recalcsummary_view recalculates workout summary TRUE 403 basic 302 403 basic 403 403 coach 302 403 FALSE FALSE TRUE TRUE TRUE
100 112 138 workout_sporttracks_upload_view uploads workout to C2 TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE TRUE FALSE FALSE
113 139 workout_runkeeper_upload_view uploads workout to C2 TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE TRUE FALSE FALSE
114 140 workout_underarmour_upload_view uploads workout to C2 TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE TRUE FALSE FALSE
101 115 141 workout_tp_upload_view uploads workout to C2 TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE TRUE FALSE FALSE
102 116 142 multi_compare_view compare workouts TRUE 302 basic 302 403 basic 302 403 coach 302 302 FALSE FALSE TRUE TRUE TRUE
103 117 145 alerts_view view alerts TRUE 302 basic 200 403 basic 403 403 coach 200 302 FALSE TRUE FALSE TRUE TRUE
147 165 210 rower_revokeapp_view revoke app TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
148 166 211 rower_strava_authorize authorize on strava TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
149 167 212 rower_sporttracks_authorize authorize on sporttracks TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
168 213 rower_underarmour_authorize authorize on UA TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
150 169 214 rower_tp_authorize authorize on TP TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
170 215 rower_runkeeper_authorize authorize on RK TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
151 171 216 rower_sporttracks_token_refresh refresh token TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
172 217 rower_underarmour_token_refresh refresh token TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
152 173 218 rower_tp_token_refresh refresh token TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
153 174 219 rower_c2_token_refresh refresh token TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
154 175 220 rower_favoritecharts_view See favorite charts TRUE 302 pro 200 302 pro 403 403 coach 200 403 FALSE TRUE FALSE TRUE TRUE
225 247 343 rower_process_callback Tested in specific test suite TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
226 248 344 rower_process_stravacallback Tested in specific test suite TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
227 249 345 rower_process_sporttrackscallback Tested in specific test suite TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
250 346 rower_process_underarmourcallback Tested in specific test suite TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
228 251 347 rower_process_polarcallback Tested in specific test suite TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
252 348 rower_process_runkeepercallback Tested in specific test suite TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
229 253 349 rower_process_tpcallback Tested in specific test suite TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
230 254 350 rower_process_twittercallback Tested in specific test suite TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
231 255 351 set_language Set language TRUE 200 basic 200 302 basic 200 302 coach 200 302 FALSE FALSE FALSE FALSE FALSE
-2
View File
@@ -99,8 +99,6 @@ class TraverseLinksTest(TestCase):
'.*authorize.*',
'.*youtu.*',
'.*earth.*',
'.*underarmour.*',
'.*runkeeper.*',
'.*c2list.*',
'.*stravaimport.*',
'.*performancephones.*',
-544
View File
@@ -1,544 +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
from rowers.imports import *
import numpy
import rowers.mytypes as mytypes
from rowers.mytypes import otwtypes
from rowers.rower_rules import is_workout_user
from rowsandall_app.settings import (
UNDERARMOUR_CLIENT_KEY,
UNDERARMOUR_CLIENT_SECRET,
UNDERARMOUR_REDIRECT_URI,
)
oauth_data = {
'client_id': UNDERARMOUR_CLIENT_KEY,
'client_secret': UNDERARMOUR_CLIENT_SECRET,
'redirect_uri': UNDERARMOUR_REDIRECT_URI,
'autorization_uri': "https://www.mapmyfitness.com/v7.1/oauth2/uacf/authorize/",
'content_type': 'application/x-www-form-urlencoded',
'tokenname': 'underarmourtoken',
'refreshtokenname': 'underarmourrefreshtoken',
'expirydatename': 'underarmourtokenexpirydate',
'bearer_auth': True,
'base_url': "https://api.ua.com/v7.1/oauth2/access_token/",
'scope':'write',
}
# Checks if user has UnderArmour token, renews them if they are expired
def underarmour_open(user):
return imports_open(user,oauth_data)
# Refresh ST token using refresh token
def do_refresh_token(refreshtoken,access_token):
return imports_do_refresh_token(
refreshtoken,oauth_data,access_token=access_token
)
# 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):
return imports_make_authorization_url(oauth_data) # pragma: no cover
# Get list of workouts available on Underarmour
def get_underarmour_workout_list(user):
r = Rower.objects.get(user=user)
if (r.underarmourtoken == '') or (r.underarmourtoken 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.underarmourtoken)
headers = {'Authorization': authorizationstring,
'Api-Key': UNDERARMOUR_CLIENT_KEY,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'}
url = "https://api.ua.com/v7.1/workout/?user="+str(get_userid(r.underarmourtoken))+"&order_by=-start_datetime"
s = requests.get(url,headers=headers)
return s
# Get workout summary data by Underarmour ID
def get_workout(user,underarmourid,do_async=False):
r = Rower.objects.get(user=user)
if (r.underarmourtoken == '') or (r.underarmourtoken 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.underarmourtoken)
headers = {'Authorization': authorizationstring,
'Api-Key': UNDERARMOUR_CLIENT_KEY,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'}
url = "https://api.ua.com/v7.1/workout/"+str(underarmourid)+"/?field_set=time_series"
s = requests.get(url,headers=headers)
data = s.json()
strokedata = pd.DataFrame.from_dict({
key: pd.Series(value) for key, value in data.items()
})
return data,strokedata
# Create Workout Data for upload to Underarmour
def createunderarmourworkoutdata(w):
filename = w.csvfilename
try:
row = rowingdata(csvfile=filename)
except: # pragma: no cover
return 0
st = w.startdatetime.astimezone(pytz.timezone(w.timezone))
start_time = st.isoformat()
averagehr = int(row.df[' HRCur (bpm)'].mean())
minhr = int(row.df[' HRCur (bpm)'].min())
maxhr = int(row.df[' HRCur (bpm)'].max())
averagespm = int(row.df[' Cadence (stokes/min)'].mean()/2.)
minspm = int(row.df[' Cadence (stokes/min)'].min()/2.)
maxspm = int(row.df[' Cadence (stokes/min)'].max()/2.)
maxhr = int(row.df[' HRCur (bpm)'].max())
duration = w.duration.hour*3600
duration += w.duration.minute*60
duration += w.duration.second
duration += +1.0e-6*w.duration.microsecond
name = w.name
try:
notes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com'
except TypeError:
notes = 'from '+w.workoutsource+' via rowsandall.com'
# adding diff, trying to see if this is valid
#t = row.df.loc[:,'TimeStamp (sec)'].values-10*row.df.ix[0,'TimeStamp (sec)']
t = row.df.loc[:,'TimeStamp (sec)'].values #-row.df.ix[0,'TimeStamp (sec)']
# t += arrow.get(st).timestamp()
# t[0] = t[1]
d = row.df.loc[:,'cum_dist'].values
d[0] = d[1]
t = t.astype(float).tolist()
d = d.astype(int).tolist()
spm = row.df[' Cadence (stokes/min)'].astype(int).tolist()
spm[0] = spm[1]
hr = row.df[' HRCur (bpm)'].astype(int).tolist()
speed = row.df[' AverageBoatSpeed (m/s)']
speedmin = float(row.df[' AverageBoatSpeed (m/s)'].min())
speedmax = float(row.df[' AverageBoatSpeed (m/s)'].max())
speedmean = float(row.df[' AverageBoatSpeed (m/s)'].mean())
speed = speed.replace(np.inf,0).tolist()
haslatlon=1
try: # pragma: no cover
lat = row.df[' latitude']
lon = row.df[' longitude']
if not lat.std() and not lon.std():
haslatlon = 0
except KeyError:
haslatlon = 0
# path data
if haslatlon: # pragma: no cover
locdata = []
for e in zip(t,lat.values,lon.values):
point = {
'lat':e[1],
'lng':e[2],
'elevation':0,
}
locdata.append([e[0],point])
hrdata = []
for e in zip(t,hr):
point = [e[0],
e[1]
]
hrdata.append(point)
distancedata = []
for e in zip(t,d):
point = [e[0],
e[1]
]
distancedata.append(point)
spmdata = []
for e in zip(t,spm):
spmdata.append([e[0],e[1]])
timeseries = {
"distance": distancedata,
"heartrate": hrdata,
"cadence": spmdata,
}
aggregates = {
"elapsed_time_total": int(duration),
"active_time_total": int(duration),
"distance_total": int(max(d)),
"heartrate_avg": averagehr,
"heart_rate_min": minhr,
"heart_rate_max": maxhr,
"speed_min": speedmin,
"speed_max": speedmax,
"speed_avg": speedmean,
"cadence_min": minspm,
"cadence_max": maxspm,
"cadence_avg": averagespm,
}
if haslatlon: # pragma: no cover
timeseries["position"] = locdata
data = {
"start_datetime": start_time,
"name": name,
"has_time_series": True,
"time_series": timeseries,
"aggregates": aggregates,
"start_locale_timezone": "Etc/UTC",
"activity_type": "/v7.1/activity_type/128/",
"notes": notes,
}
return data
# Obtain Underarmour Workout ID and activity type
def get_idfromuri(user,links):
id = links['self'][0]['id']
typeid = links['activity_type'][0]['id']
typename = get_typefromid(typeid,user)
return id,typename
def getidfromresponse(response):
t = response.json()
links = t["_links"]
id = links["self"][0]["id"]
return int(id)
def refresh_ua_actlist(user): # pragma: no cover
r = Rower.objects.get(user=user)
authorizationstring = str('Bearer ' + r.underarmourtoken)
headers = {'Authorization': authorizationstring,
'Api-Key': UNDERARMOUR_CLIENT_KEY,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'}
url = "https://api.ua.com/v7.1/activity_type/"
response = requests.get(url,headers=headers)
me_json = response.json()
types = me_json["_embedded"]["activity_types"]
w = {int(t["_links"]["self"][0]["id"]):t["name"] for t in types}
wdf = pd.Series(w,name='Name')
wdf.to_csv('static/rigging/ua2.csv',index_label='id',header=True)
return w
try:
activities = pd.read_csv('static/rigging/ua2.csv',index_col='id')
actdict = activities.to_dict()['Name']
except: # pragma: no cover
actdict = {}
def get_typefromid(typeid,user):
r = Rower.objects.get(user=user)
try:
res = actdict[int(typeid)]
except KeyError: # pragma: no cover
authorizationstring = str('Bearer ' + r.underarmourtoken)
headers = {'Authorization': authorizationstring,
'Api-Key': UNDERARMOUR_CLIENT_KEY,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'}
url = "https://api.ua.com/v7.1/activity_type/"+str(typeid)
response = requests.get(url,headers=headers)
me_json = response.json()
try:
res = me_json['name']
except KeyError:
res = 0
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,
'Api-Key': UNDERARMOUR_CLIENT_KEY,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'}
url = "https://api.ua.com/v7.1/user/self/"
response = requests.get(url,headers=headers)
me_json = response.json()
try:
res = me_json['id']
except KeyError: # pragma: no cover
res = 0
return res
def default(o): # pragma: no cover
if isinstance(o, numpy.int64): return int(o)
raise TypeError
def workout_ua_upload(user,w): # pragma: no cover
message = "Uploading to MapMyFitness"
uaid = 0
r = w.user
thetoken = underarmour_open(r.user)
# ready to upload. Hurray
if (is_workout_user(user,w)):
data = createunderarmourworkoutdata(w)
# return HttpResponse(json.dumps(data))
if not data:
message = "Data error"
uaid = 0
return message, uaid
authorizationstring = str('Bearer ' + thetoken)
headers = {'Authorization': authorizationstring,
'Api-Key': UNDERARMOUR_CLIENT_KEY,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json',
}
url = "https://api.ua.com/v7.1/workout/"
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.uploadedtounderarmour = -1
uaid = -1
w.save()
elif (response.status_code == 201 or response.status_code==200):
uaid = getidfromresponse(response)
w.uploadedtounderarmour = uaid
w.save()
return 'Successfully synchronized with MapMyFitness',uaid
else:
s = response
message = "Something went wrong in workout_underarmour_upload_view: %s - %s" % (s.reason,s.text)
uaid = 0
return message, uaid
else:
message = "You are not authorized to upload this workout"
uaid = 0
return message, uaid
return message, uaid
# Create workout from SportTracks Data, which are slightly different
# than Strava or Concept2 data
def add_workout_from_data(user,importid,data,strokedata,
source='mapmyfitness',
workoutsource='mapmyfitness'):
workouttype = 'water'
try:
comments = data['notes']
except: # pragma: no cover
comments = ''
try:
thetimezone = tz(data['start_locale_timezone'])
except:
thetimezone = 'UTC'
r = Rower.objects.get(user=user)
try:
rowdatetime = iso8601.parse_date(data['start_datetime'])
except iso8601.ParseError: # pragma: no cover
try:
rowdatetime = datetime.strptime(data['start_datetime'],"%Y-%m-%d %H:%M:%S")
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
except:
try:
rowdatetime = parser.parse(data['start_datetime'])
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
except:
rowdatetime = datetime.strptime(data['date'],"%Y-%m-%d %H:%M:%S")
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
starttimeunix = arrow.get(rowdatetime).timestamp()
try:
title = data['name']
except: # pragma: no cover
title = "Imported data"
timeseries = data['time_series']
# position, distance, speed, cadence, power,
try:
res = splituadata(timeseries['distance'])
distance = res[1]
times_distance = res[0]
except KeyError: # pragma: no cover
message = "Error. No distance data"
return (0,message)
try:
l = timeseries['position']
res = splituadata(l)
times_location = res[0]
latlong = res[1]
latcoord = []
loncoord = []
for coord in latlong:
lat = coord['lat']
lon = coord['lng']
latcoord.append(lat)
loncoord.append(lon)
except: # pragma: no cover
times_location = times_distance
latcoord = np.zeros(len(times_distance))
loncoord = np.zeros(len(times_distance))
if workouttype in otwtypes:
workouttype = 'rower'
try:
res = splituadata(timeseries['cadence'])
times_spm = res[0]
spm = res[1]
except KeyError: # pragma: no cover
times_spm = times_distance
spm = 0*times_distance
try:
res = splituadata(timeseries['heartrate'])
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)
latseries = latseries.groupby(latseries.index).first()
lonseries = pd.Series(loncoord,index=times_location)
lonseries = lonseries.groupby(lonseries.index).first()
spmseries = pd.Series(spm,index=times_spm)
spmseries = spmseries.groupby(spmseries.index).first()
hrseries = pd.Series(hr,index=times_hr)
hrseries = hrseries.groupby(hrseries.index).first()
# 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
unixtime[0] = starttimeunix
df['TimeStamp (sec)'] = unixtime
dt = np.diff(cum_time).mean()
wsize = round(5./dt)
df = df.fillna(0)
df.sort_values(by='TimeStamp (sec)',ascending=True)
timestr = strftime("%Y%m%d-%H%M%S")
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='mapmyfitness',
dosmooth=r.dosmooth,
title=title,
notes=comments)
return (id,message)
+1 -27
View File
@@ -124,8 +124,6 @@ def matchsync(line):
tester3 = tester+'(.*)((tp)|(trainingpeaks))'
tester4 = tester+'(.*)(strava)'
tester5 = tester+'(.*)((st)|(sporttracks))'
tester6 = tester+'(.*)((rk)|(runkeeper))'
tester7 = tester+'(.*)((mapmyfitness)|(underarmour)|(ua))'
tester = re.compile(tester)
@@ -135,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:
@@ -260,10 +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
if v in ['ua','underarmour','mapmyfitness']:
uploadoptions['upload_to_MapMyFitness'] = True
except AttributeError:
pass
@@ -490,8 +483,7 @@ 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.underarmourstuff as underarmourstuff
import rowers.tpstuff as tpstuff
from rowers.rower_rules import is_promember
@@ -644,24 +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_MapMyFitness' in options and options['upload_to_MapMyFitness']) or (w.user.mapmyfitness_auto_export): # pragma: no cover
try:
message,id = underarmourstuff.workout_ua_upload(
w.user.user,w
)
except NoTokenError:
message = "Please connect to MapMyFitness first"
id = 0
if ('upload_to_TrainingPeaks' in options and options['upload_to_TrainingPeaks']) or (w.user.trainingpeaks_auto_export): # pragma: no cover
try:
-9
View File
@@ -508,16 +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/underarmourimport/user/(?P<userid>\d+)/$',views.workout_underarmourimport_view,name='workout_underarmourimport_view'),
re_path(r'^workout/underarmourimport/$',views.workout_underarmourimport_view,name='workout_underarmourimport_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)/underarmouruploadw/$',views.workout_underarmour_upload_view,name='workout_underarmour_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'),
@@ -600,12 +594,9 @@ urlpatterns = [
re_path(r'^me/stravaauthorize/$',views.rower_strava_authorize,name='rower_strava_authorize'),
re_path(r'^me/garminauthorize/$',views.rower_garmin_authorize,name='rower_garmin_authorize'),
re_path(r'^me/sporttracksauthorize/$',views.rower_sporttracks_authorize,name='rower_sporttracks_authorize'),
re_path(r'^me/underarmourauthorize/$',views.rower_underarmour_authorize,name='rower_underarmour_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/underarmourrefresh/$',views.rower_underarmour_token_refresh,name='rower_underarmoud_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/favoritecharts/$',views.rower_favoritecharts_view,name='rower_favoritecharts_view'),
+49 -1
View File
@@ -15,7 +15,7 @@ import uuid
import datetime
import json
import time
from fitparse import FitFile
from django.conf import settings
@@ -26,6 +26,11 @@ import requests
from django.http import HttpResponse
import humanize
from pytz.exceptions import UnknownTimeZoneError
import pytz
import iso8601
from iso8601 import ParseError
import arrow
lbstoN = 4.44822
@@ -105,6 +110,15 @@ info_calls = [
import random
def dologging(filename,s):
tstamp = time.localtime()
timestamp = time.strftime('%b-%d-%Y %H:%M:%S', tstamp)
with open(filename,'a') as f:
f.write('\n')
f.write(timestamp)
f.write(' ')
f.write(s)
def to_pace(pace):
minutes, seconds = divmod(pace,60)
seconds, rest = divmod(seconds, 1)
@@ -1223,3 +1237,37 @@ def intervals_to_string(vals, units, typ):
s = s[1:]
return s
def get_timezone_from_c2data(data):
try:
timezone = pytz.timezone(data['timezone'])
except UnknownTimeZoneError:
timezone = pytz.utc
except KeyError: # pragma: no cover
timezone = pytz.utc
return timezone
def get_startdatetime_from_c2data(data):
timezone = get_timezone_from_c2data(data)
try:
startdatetime = iso8601.parse_date(data['date_utc'])
except: # pragma: no cover
startdatetime = iso8601.parse_date(data['date'])
totaltime = data['time']/10.
duration = totaltime_sec_to_string(totaltime)
starttimeunix = arrow.get(startdatetime).timestamp()-totaltime
startdatetime = arrow.get(starttimeunix)
startdatetime = startdatetime.astimezone(timezone)
workoutdate = startdatetime.astimezone(
timezone
).strftime('%Y-%m-%d')
starttime = startdatetime.astimezone(
timezone
).strftime('%H:%M:%S')
return startdatetime,starttime,workoutdate,duration,starttimeunix,timezone
+42 -670
View File
@@ -81,113 +81,17 @@ def workout_tp_upload_view(request,id=0):
def workout_strava_upload_view(request,id=0):
message = ""
r = getrower(request.user)
res = -1
w = get_workout_by_opaqueid(request,id)
result = -1
comment, result = stravastuff.workout_strava_upload(r.user,w,asynchron=True)
messages.info(request,'Your workout will be synchronized to Strava in the background')
try:
thetoken = strava_open(request.user)
except NoTokenError: # pragma: no cover
return HttpResponseRedirect("/rowers/me/stravaauthorize/")
if (r.stravatoken == '') or (r.stravatoken is None): # pragma: no cover
s = "Token doesn't exist. Need to authorize"
return HttpResponseRedirect("/rowers/me/stravaauthorize/")
else:
# ready to upload. Hurray
w = get_workout_by_opaqueid(request,id)
r = w.user
try:
tcxfile,tcxmessg = stravastuff.createstravaworkoutdata(w)
if tcxfile:
with open(tcxfile,'rb') as f:
try:
newnotes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com'
except TypeError:
newnotes = 'from '+w.workoutsource+' via rowsandall.com'
activity_type = 'Ride'
if w.workouttype in mytypes.rowtypes:
activity_type = r.stravaexportas
if activity_type == 'match':
try:
activity_type = mytypes.stravamapping[w.workouttype]
except KeyError: # pragma: no cover
activity_type = 'Ride'
else: # pragma: no cover
try:
activity_type = mytypes.stravamapping[w.workouttype]
except KeyError:
activity_type = 'Ride'
res,mes = stravastuff.handle_stravaexport(
f,w.name,
r.stravatoken,
description=newnotes,
activity_type=activity_type,quick=False)
if res==0: # pragma: no cover
messages.error(request,mes)
w.uploadedtostrava = -1
w.save()
try:
os.remove(tcxfile)
except WindowsError:
pass
url = reverse(r.defaultlandingpage,
kwargs = {
'id':encoder.encode_hex(w.id),
})
response = HttpResponseRedirect(url)
return response
try:
w.uploadedtostrava = res
w.save()
try:
os.remove(tcxfile)
except WindowsError: # pragma: no cover
pass
url = reverse('workout_edit_view',kwargs={'id':w.id})
messages.info(request,mes)
except: # pragma: no cover
with open("media/stravaerrors.log","a") as errorlog:
errorstring = str(sys.exc_info()[0])
timestr = strftime("%Y%m%d-%H%M%S")
errorlog.write(timestr+errorstring+"\r\n")
errorlog.write("views.py line 826\r\n")
message = 'Error: '+errorstring
messages.error(request,message)
else: # pragma: no cover # No tcxfile
message = "Strava Data error "+tcxmessg
messages.error(request,message)
w.uploadedtostrava = -1
w.save()
url = reverse(r.defaultlandingpage,
kwargs = {
'id':encoder.encode_hex(w.id),
})
response = HttpResponseRedirect(url)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':encoder.encode_hex(w.id),
}
)
response = HttpResponseRedirect(url)
except ActivityUploadFailed as e: # pragma: no cover
message = "Strava Upload error: %s" % e
messages.error(request,message)
w.uploadedtostrava = -1
w.save()
os.remove(tcxfile)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':encoder.encode_hex(w.id),
})
response = HttpResponseRedirect(url)
return response
url = reverse(r.defaultlandingpage,
kwargs = {
'id':encoder.encode_hex(w.id),
}
)
return HttpResponseRedirect(url)
# Upload workout to Concept2 logbook
@login_required()
@@ -205,15 +109,12 @@ def workout_c2_upload_view(request,id=0):
dologging(s)
try:
message,c2id = c2stuff.workout_c2_upload(request.user,w)
message,c2id = c2stuff.workout_c2_upload(request.user,w,asynchron=True)
except NoTokenError: # pragma: no cover
print('jet')
return HttpResponseRedirect("/rowers/me/c2authorize/")
if message and c2id <=0: # pragma: no cover
messages.error(request,message)
elif message:
messages.info(request,message)
messages.info(request,'Your workout will be synchronized to the Concept2 Logbook in the background')
url = reverse(r.defaultlandingpage,
kwargs = {
@@ -221,131 +122,9 @@ def workout_c2_upload_view(request,id=0):
})
response = HttpResponseRedirect(url)
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
return HttpResponseRedirect(url)
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 Underarmour
@permission_required('workout.change_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def workout_underarmour_upload_view(request,id=0):
message = ""
w = get_workout(id)
r = w.user
try:
thetoken = underarmour_open(r.user)
except NoTokenError: # pragma: no cover
return HttpResponseRedirect("/rowers/me/underarmourauthorize/")
# ready to upload. Hurray
data = underarmourstuff.createunderarmourworkoutdata(w)
if not data: # pragma: no cover
message = "Data error"
messages.error(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':encoder.encode_hex(w.id),
})
return HttpResponseRedirect(url)
authorizationstring = str('Bearer ' + thetoken)
headers = {'Authorization': authorizationstring,
'Api-Key': UNDERARMOUR_CLIENT_KEY,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json',
}
url = "https://api.ua.com/v7.1/workout/"
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.uploadedtounderarmour = -1
w.save()
elif (response.status_code == 201 or response.status_code==200):
underarmourid = underarmourstuff.getidfromresponse(response)
w.uploadedtounderarmour = underarmourid
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_underarmour_upload_view: %s " % s.reason
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)
@@ -355,53 +134,9 @@ def workout_sporttracks_upload_view(request,id=0):
w = get_workout(id)
r = w.user
try:
thetoken = sporttracks_open(r.user)
except NoTokenError: # pragma: no cover
return HttpResponseRedirect("/rowers/me/sporttracksauthorize/")
message, res = sporttracksstuff.workout_sporttracks_upload(r.user,w,asynchron=True)
data = sporttracksstuff.createsporttracksworkoutdata(w)
if not data: # pragma: no cover
message = "Data error"
messages.error(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':encoder.encode_hex(w.id),
})
return HttpResponseRedirect(url)
authorizationstring = str('Bearer ' + thetoken)
headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'}
url = "https://api.sporttracks.mobi/api/v2/fitnessActivities.json"
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
message = "Duplicate error"
messages.error(request,message)
w.uploadedtosporttracks = -1
w.save()
elif (response.status_code == 201 or response.status_code==200):
s= response.json()
sporttracksid = sporttracksstuff.getidfromresponse(response)
w.uploadedtosporttracks = sporttracksid
w.save()
message = "Upload to SportTracks was successful"
messages.info(request,message)
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_sporttracks_upload_view: %s" % s.reason
messages.error(request,message)
messages.info(request,'Your workout will be synchronized with SportTracks in the background')
url = reverse(r.defaultlandingpage,
kwargs = {
@@ -444,6 +179,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
@@ -489,23 +225,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()
@@ -525,24 +244,8 @@ def rower_sporttracks_authorize(request): # pragma: no cover
return HttpResponseRedirect(url)
# Underarmour Authorization
@login_required()
def rower_underarmour_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())
redirect_uri = UNDERARMOUR_REDIRECT_URI
url = 'https://www.mapmyfitness.com/v7.1/oauth2/authorize/?' \
'client_id={0}&response_type=code&redirect_uri={1}'.format(
UNDERARMOUR_CLIENT_KEY, redirect_uri
)
return HttpResponseRedirect(url)
# Underarmour Authorization
# RP3 Authorization
@login_required()
def rower_rp3_authorize(request): # pragma: no cover
# Generate a random string for the state parameter
@@ -557,7 +260,7 @@ def rower_rp3_authorize(request): # pragma: no cover
return HttpResponseRedirect(url)
# Underarmour Authorization
# TrainingPeaks Authorization
@login_required()
def rower_tp_authorize(request): # pragma: no cover
# Generate a random string for the state parameter
@@ -603,33 +306,6 @@ def rower_c2_token_refresh(request):
return HttpResponseRedirect(url)
# Underarmour token refresh. URL for manual refresh. Not visible to users
@login_required()
def rower_underarmour_token_refresh(request):
r = getrower(request.user)
res = underarmourstuff.do_refresh_token(
r.underarmourrefreshtoken,
r.underarmourtoken
)
access_token = res[0]
expires_in = res[1]
refresh_token = res[2]
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
r = getrower(request.user)
r.underarmourtoken = access_token
r.underarmourtokenexpirydate = expirydatetime
r.underarmourrefreshtoken = refresh_token
r.save()
successmessage = "Tokens refreshed. Good to go"
messages.info(request,successmessage)
url = reverse('workouts_view')
return HttpResponseRedirect(url)
# TrainingPeaks token refresh. URL for manual refresh. Not visible to users
@@ -1026,31 +702,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
@@ -1086,29 +737,6 @@ def rower_process_sporttrackscallback(request):
# Process Underarmour callback
@login_required()
def rower_process_underarmourcallback(request):
code = request.GET['code']
res = underarmourstuff.get_token(code)
access_token = res[0]
expires_in = res[1]
refresh_token = res[2]
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
r = getrower(request.user)
r.underarmourtoken = access_token
r.underarmourtokenexpirydate = expirydatetime
r.underarmourrefreshtoken = refresh_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 RP3 callback
@login_required()
@@ -1624,112 +1252,7 @@ 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 RunKeeper workout to import
@login_required()
@permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True)
def workout_underarmourimport_view(request,message="",userid=0):
res = underarmourstuff.get_underarmour_workout_list(request.user)
if (res.status_code != 200):
return HttpResponseRedirect("/rowers/me/underarmourauthorize/")
workouts = []
items = res.json()['_embedded']['workouts']
for item in items:
s = item['start_datetime']
i,r = underarmourstuff.get_idfromuri(request.user,item['_links'])
n = item['name']
try:
d = item['aggregates']['distance_total']
except KeyError: # pragma: no cover
d = 0
try:
ttot = item['aggregates']['active_time_total']
except KeyError:
ttot = 0
keys = ['id','distance','duration','starttime','type']
values = [i,d,ttot,s,r]
thedict = dict(zip(keys,values))
workouts.append(thedict)
rower = getrower(request.user)
breadcrumbs = [
{
'url':'/rowers/list-workouts/',
'name':'Workouts'
},
{
'url':reverse('workout_c2import_view'),
'name':'Concept2'
},
]
return render(request,'underarmour_list_import.html',
{'workouts':workouts,
'breadcrumbs':breadcrumbs,
'rower':rower,
'active':'nav-workouts',
'teams':get_my_teams(request.user),
})
return HttpResponse(res) # pragma: no cover
# the page where you select which Polar workout to Import
@login_required()
@@ -2029,22 +1552,28 @@ importlistviews = {
'strava':'workout_stravaimport_view',
'polar':'workout_polarimport_view',
'ownapi':'workout_view',
'runkeeper':'workout_runkeeperimport_view',
'sporttracks':'workout_sporttracksimport_view',
'trainingpeaks':'workout_view',
'underarmour':'workout_underarmourimport_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,
'underarmour':underarmourstuff,
'nk':nkstuff,
}
@@ -2071,178 +1600,24 @@ def workout_getrp3importview(request,externalid):
return HttpResponseRedirect(url)
@login_required()
def workout_getimportview(request,externalid,source = 'c2',do_async=False):
data,strokedata = importsources[source].get_workout(request.user,externalid,
do_async=do_async)
def workout_getimportview(request,externalid,source = 'c2',do_async=True):
try:
result = importsources[source].get_workout(request.user,externalid,
do_async=do_async)
except NoTokenError:
if do_async: # pragma: no cover
return HttpResponseRedirect(reverse(importauthorizeviews[source]))
if result: # pragma: no cover
messages.info(request,"Your workout will be imported in the background")
# this should return to the respective import list page
url = reverse(importlistviews[source])
return HttpResponseRedirect(url)
if not data: # pragma: no cover
messages.error(request,"No strokedata received")
url = reverse('workouts_view')
return HttpResponseRedirect(url)
try:
workouttype = mytypes.c2mappinginv[data['type']]
except KeyError:
workouttype = 'rower'
# Now works only for C2
try:
if strokedata == 0:
messages.error(request,'An error occurred importing the workout from Concept2')
url = reverse('workouts_view')
return HttpResponseRedirect(url)
except ValueError:
pass
if strokedata.empty: # pragma: no cover
distance = data['distance']
c2id = data['id']
workouttype = mytypes.c2mappinginv[data['type']]
verified = data['verified']
startdatetime = iso8601.parse_date(data['date'])
weightclass = data['weight_class']
weightcategory = 'hwt'
if weightclass == "L":
weightcategory = 'lwt'
totaltime = data['time']/10.
duration = dataprep.totaltime_sec_to_string(totaltime)
duration = datetime.datetime.strptime(duration,'%H:%M:%S.%f').time()
try:
timezone_str = data['timezone']
except:
timezone_str = 'UTC'
if timezone_str is None:
timezone_str = 'UTC'
workoutdate = startdatetime.astimezone(
pytz.timezone(timezone_str)
).strftime('%Y-%m-%d')
starttime = startdatetime.astimezone(
pytz.timezone(timezone_str)
).strftime('%H:%M:%S')
try:
notes = data['comments']
name = notes[:40]
except (KeyError,TypeError):
comments = 'C2 Import Workout from {startdatetime}'.format(startdatetime=startdatetime)
name = notes
r = getrower(request.user)
id, message = dataprep.create_row_df(r,
distance,
duration,
startdatetime,
workouttype=workouttype)
w = Workout.objects.get(id=id)
w.uploadedtoc2 = c2id
w.name = name
w.notes = notes
w.workouttype = workouttype
w.save()
message = "This workout does not have any stroke data associated with it. We created synthetic stroke data."
messages.info(request,message)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':encoder.encode_hex(w.id),
})
return HttpResponseRedirect(url)
# strokedata not empty - continue
id,message = importsources[source].add_workout_from_data(
request.user,
externalid,data,
strokedata,
source=source,
workoutsource=source)
w = get_workout(encoder.encode_hex(id))
if 'workout' in data: # pragma: no cover
if 'splits' in data['workout']:
splitdata = data['workout']['splits']
elif 'intervals' in data['workout']:
splitdata = data['workout']['intervals']
else:
splitdata = False
else:
splitdata = False
# splitdata (only for C2)
if splitdata: # pragma: no cover
w.summary,sa,results = c2stuff.summaryfromsplitdata(splitdata,data,w.csvfilename,workouttype=workouttype)
w.save()
from rowingdata.trainingparser import getlist
# set stroke data in CSV file
if sa:
values = getlist(sa)
units = getlist(sa,sel='unit')
types = getlist(sa,sel='type')
rowdata = rdata(csvfile=w.csvfilename)
if rowdata:
rowdata.updateintervaldata(values,
units,types,results)
rowdata.write_csv(w.csvfilename,gzip=True)
dataprep.update_strokedata(w.id,rowdata.df)
if source == 'strava':
w.uploadedtostrava = externalid
elif source == 'c2':
w.uploadedtoc2 = externalid
elif source == 'polar': # pragma: no cover
w.uploadedtopolar = externalid
elif source == 'runkeeper':
w.uploadedtorunkeeper = externalid
elif source == 'sporttracks':
w.uploadedtosporttracks = externalid
elif source == 'trainingpeaks': # pragma: no cover
w.uploadedtotp = externalid
elif source == 'underarmour':
w.uploadedtounderarmour = externalid
w.save()
if message: # pragma: no cover
messages.error(request,message)
r = getrower(request.user)
url = reverse(r.defaultlandingpage,
kwargs = {
'id':encoder.encode_hex(w.id)
})
else: # pragma: no cover
messages.error(request,'Error getting the workout')
url = reverse(importlistviews[source])
return HttpResponseRedirect(url)
# Imports all new workouts from SportTracks
@login_required()
def workout_getsporttracksworkout_all(request):
@@ -2255,14 +1630,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)
+2 -167
View File
@@ -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
@@ -167,10 +166,9 @@ import rowers.garmin_stuff as garmin_stuff
from rowers.stravastuff import strava_open
import rowers.polarstuff as polarstuff
import rowers.sporttracksstuff as sporttracksstuff
import rowers.underarmourstuff as underarmourstuff
from rowers.underarmourstuff import underarmour_open
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,9 +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,
UNDERARMOUR_CLIENT_ID, UNDERARMOUR_REDIRECT_URI,
UNDERARMOUR_CLIENT_SECRET,UNDERARMOUR_CLIENT_KEY,
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,
@@ -1278,166 +1273,6 @@ def sendmail(request):
return HttpResponseRedirect('/rowers/email/')
# Create workout data from Strava or Concept2
# data and create the associated Workout object and save it
def add_workout_from_strokedata(user,importid,data,strokedata,
source='c2',splitdata=None,
workoutsource='concept2'):
try:
workouttype = data['type']
except KeyError: # pragma: no cover
workouttype = 'rower'
if workouttype not in [x[0] for x in Workout.workouttypes]: # pragma: no cover
workouttype = 'other'
try:
comments = data['comments']
except: # pragma: no cover
comments = ' '
# comments = "Imported data \n %s" % comments
# comments = "Imported data \n"+comments # str(comments)
try:
thetimezone = tz(data['timezone'])
except: # pragma: no cover
thetimezone = 'UTC'
r = getrower(user)
try:
rowdatetime = iso8601.parse_date(data['date_utc'])
except KeyError: # pragma: no cover
rowdatetime = iso8601.parse_date(data['start_date'])
except ParseError: # pragma: no cover
rowdatetime = iso8601.parse_date(data['date'])
try:
c2intervaltype = data['workout_type']
except KeyError: # pragma: no cover
c2intervaltype = ''
try:
title = data['name']
except KeyError: # pragma: no cover
title = ""
try:
t = data['comments'].split('\n', 1)[0]
title += t[:20]
except:
title = ''
starttimeunix = arrow.get(rowdatetime).timestamp()
res = make_cumvalues(0.1*strokedata['t'])
cum_time = res[0]
lapidx = res[1]
unixtime = cum_time+starttimeunix
# unixtime[0] = starttimeunix
seconds = 0.1*strokedata.loc[:,'t']
nr_rows = len(unixtime)
try: # pragma: no cover
latcoord = strokedata.loc[:,'lat']
loncoord = strokedata.loc[:,'lon']
except:
latcoord = np.zeros(nr_rows)
loncoord = np.zeros(nr_rows)
try:
strokelength = strokedata.loc[:,'strokelength']
except:
strokelength = np.zeros(nr_rows)
dist2 = 0.1*strokedata.loc[:,'d']
try:
spm = strokedata.loc[:,'spm']
except KeyError: # pragma: no cover
spm = 0*dist2
try:
hr = strokedata.loc[:,'hr']
except KeyError:
hr = 0*spm
pace = strokedata.loc[:,'p']/10.
pace = np.clip(pace,0,1e4)
pace = pace.replace(0,300)
velo = 500./pace
power = 2.8*velo**3
# save csv
# Create data frame with all necessary data to write to csv
df = pd.DataFrame({'TimeStamp (sec)':unixtime,
' Horizontal (meters)': dist2,
' Cadence (stokes/min)':spm,
' HRCur (bpm)':hr,
' longitude':loncoord,
' latitude':latcoord,
' Stroke500mPace (sec/500m)':pace,
' Power (watts)':power,
' DragFactor':np.zeros(nr_rows),
' DriveLength (meters)':np.zeros(nr_rows),
' StrokeDistance (meters)':strokelength,
' DriveTime (ms)':np.zeros(nr_rows),
' StrokeRecoveryTime (ms)':np.zeros(nr_rows),
' AverageDriveForce (lbs)':np.zeros(nr_rows),
' PeakDriveForce (lbs)':np.zeros(nr_rows),
' lapIdx':lapidx,
' ElapsedTime (sec)':seconds
})
df.sort_values(by='TimeStamp (sec)',ascending=True)
timestr = strftime("%Y%m%d-%H%M%S")
# Create CSV file name and save data to CSV file
csvfilename ='media/{code}_{importid}.csv'.format(
importid=importid,
code = uuid4().hex[:16]
)
res = df.to_csv(csvfilename+'.gz',index_label='index',
compression='gzip')
# with Concept2
if source=='c2':
try:
totaldist = data['distance']
totaltime = data['time']/10.
except KeyError: # pragma: no cover
totaldist = 0
totaltime = 0
else: # pragma: no cover
totaldist = 0
totaltime = 0
id,message = dataprep.save_workout_database(
csvfilename,r,
workouttype=workouttype,
dosmooth=r.dosmooth,
title=title,notes=comments,
# totaldist=totaldist,
# totaltime=totaltime,
workoutsource=workoutsource,
dosummary=True
)
return id,message
def keyvalue_get_default(key,options,def_options): # pragma: no cover
try:
-1
View File
@@ -329,7 +329,6 @@ def rower_exportsettings_view(request,userid=0):
'polar_auto_import':'polartoken',
'c2_auto_export':'c2token',
'c2_auto_import':'c2token',
'mapmyfitness_auto_export':'underarmourtoken',
'runkeeper_auto_export':'runkeepertoken',
'sporttracks_auto_export':'sporttrackstoken',
'strava_auto_export':'stravatoken',
-42
View File
@@ -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,35 +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_ua): # pragma: no cover
try:
message,id = underarmourstuff.workout_ua_upload(
request.user,w
)
except NoTokenError:
message = "Please connect to MapMyFitness first"
id = 0
if id>1:
messages.info(request,message)
else:
messages.error(request,message)
if (upload_to_tp): # pragma: no cover
try:
@@ -5462,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,
-2
View File
@@ -74,9 +74,7 @@ urlpatterns += [
re_path(r'^stravacall\_back',rowersviews.rower_process_stravacallback),
re_path(r'^garmin\_callback',rowersviews.rower_process_garmincallback),
re_path(r'^sporttracks\_callback',rowersviews.rower_process_sporttrackscallback),
re_path(r'^underarmour\_callback',rowersviews.rower_process_underarmourcallback),
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'^rp3\_callback',rowersviews.rower_process_rp3callback),
re_path(r'^twitter\_callback',rowersviews.rower_process_twittercallback),