import working
This commit is contained in:
@@ -43,12 +43,22 @@
|
||||
<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>
|
||||
|
||||
<div class="grid_6 omega">
|
||||
<h3>Connect</h3>
|
||||
|
||||
<div class="grid_6">
|
||||
<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>
|
||||
@@ -67,10 +77,13 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="grid_6">
|
||||
<div class="grid_2 alpha suffix_4">
|
||||
<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 suffix_2 omega">
|
||||
<p><a href="/rowers/me/underarmourauthorize/"><img src="/static/img/UAbtn.png" alt="connect with Under Armour" width="120"></a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
37
rowers/templates/underarmour_list_import.html
Normal file
37
rowers/templates/underarmour_list_import.html
Normal file
@@ -0,0 +1,37 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Workouts{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<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 %}
|
||||
@@ -1,5 +1,6 @@
|
||||
from django import template
|
||||
from time import strftime
|
||||
import dateutil.parser
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@@ -27,6 +28,17 @@ def strfdeltah(tdelta):
|
||||
|
||||
return res
|
||||
|
||||
def secondstotimestring(tdelta):
|
||||
hours, rest = divmod(tdelta,3600)
|
||||
minutes,seconds = divmod(rest,60)
|
||||
res = "{hours:0>2}:{minutes:0>2}:{seconds:0>2}".format(
|
||||
hours=hours,
|
||||
minutes=minutes,
|
||||
seconds=seconds,
|
||||
)
|
||||
|
||||
return res
|
||||
|
||||
@register.filter
|
||||
def durationprint(d,dstring):
|
||||
if (d == None):
|
||||
@@ -57,6 +69,22 @@ def lookup(dict, key):
|
||||
s = s[:22]
|
||||
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
|
||||
|
||||
@register.filter(name='times')
|
||||
def times(number):
|
||||
return range(number)
|
||||
|
||||
@@ -171,9 +171,9 @@ def get_underarmour_workout_list(user):
|
||||
headers = {'Authorization': authorizationstring,
|
||||
'Api-Key': UNDERARMOUR_CLIENT_KEY,
|
||||
'user-agent': 'sanderroosendaal',
|
||||
'user':'v7.1/user/'+str(get_userid(r.underarmourtoken))+'/',
|
||||
'Content-Type': 'application/json'}
|
||||
url = "https://api.ua.com/v7.1/workout/"
|
||||
url = "https://api.ua.com/v7.1/workout/?user="+str(get_userid(r.underarmourtoken))
|
||||
|
||||
s = requests.get(url,headers=headers)
|
||||
|
||||
return s
|
||||
@@ -191,7 +191,7 @@ def get_underarmour_workout(user,underarmourid):
|
||||
'Api-Key': UNDERARMOUR_CLIENT_KEY,
|
||||
'user-agent': 'sanderroosendaal',
|
||||
'Content-Type': 'application/json'}
|
||||
url = "https://api.ua.com/v7.1/workout/"+str(underarmourid)+"/"
|
||||
url = "https://api.ua.com/v7.1/workout/"+str(underarmourid)+"/?field_set=time_series"
|
||||
s = requests.get(url,headers=headers)
|
||||
|
||||
return s
|
||||
@@ -292,13 +292,36 @@ def createunderarmourworkoutdata(w):
|
||||
|
||||
return data
|
||||
|
||||
# Obtain Underarmour Workout ID from the response returned on successful
|
||||
# upload
|
||||
def getidfromresponse(response):
|
||||
uri = response.headers["Location"]
|
||||
id = uri[len(uri)-9:]
|
||||
# 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 get_typefromid(typeid,user):
|
||||
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'}
|
||||
import urllib
|
||||
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
|
||||
|
||||
return int(id)
|
||||
|
||||
|
||||
# Get user id, having access token
|
||||
|
||||
@@ -229,6 +229,8 @@ urlpatterns = [
|
||||
url(r'^workout/sporttracksimport/(\d+)/$',views.workout_getsporttracksworkout_view),
|
||||
url(r'^workout/runkeeperimport/$',views.workout_runkeeperimport_view),
|
||||
url(r'^workout/runkeeperimport/(\d+)/$',views.workout_getrunkeeperworkout_view),
|
||||
url(r'^workout/underarmourimport/$',views.workout_underarmourimport_view),
|
||||
url(r'^workout/underarmourimport/(\d+)/$',views.workout_getunderarmourworkout_view),
|
||||
url(r'^workout/(\d+)/deleteconfirm$',views.workout_delete_confirm_view),
|
||||
url(r'^workout/(\d+)/c2uploadw/$',views.workout_c2_upload_view),
|
||||
url(r'^workout/(\d+)/stravauploadw/$',views.workout_strava_upload_view),
|
||||
|
||||
245
rowers/views.py
245
rowers/views.py
@@ -214,6 +214,15 @@ def get_time(second):
|
||||
def getidfromsturi(uri,length=8):
|
||||
return uri[len(uri)-length:]
|
||||
|
||||
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 splitrunkeeperlatlongdata(lijst,tname,latname,lonname):
|
||||
t = []
|
||||
lat = []
|
||||
@@ -816,6 +825,173 @@ def add_workout_from_stdata(user,importid,data):
|
||||
|
||||
|
||||
|
||||
unixtime = cum_time+starttimeunix
|
||||
unixtime[0] = starttimeunix
|
||||
|
||||
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'
|
||||
|
||||
res = df.to_csv(csvfilename+'.gz',index_label='index',
|
||||
compression='gzip')
|
||||
|
||||
id,message = dataprep.save_workout_database(csvfilename,r,
|
||||
workouttype=workouttype,
|
||||
title=title,
|
||||
notes=comments)
|
||||
|
||||
return (id,message)
|
||||
|
||||
# Create workout from SportTracks Data, which are slightly different
|
||||
# than Strava or Concept2 data
|
||||
def add_workout_from_underarmourdata(user,importid,data):
|
||||
workouttype = 'water'
|
||||
|
||||
try:
|
||||
comments = data['notes']
|
||||
except:
|
||||
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:
|
||||
try:
|
||||
rowdatetime = datetime.datetime.strptime(data['start_datetime'],"%Y-%m-%d %H:%M:%S")
|
||||
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
|
||||
except:
|
||||
try:
|
||||
rowdatetime = dateutil.parser.parse(data['start_datetime'])
|
||||
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
|
||||
except:
|
||||
rowdatetime = datetime.datetime.strptime(data['date'],"%Y-%m-%d %H:%M:%S")
|
||||
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
|
||||
starttimeunix = mktime(rowdatetime.utctimetuple())
|
||||
|
||||
|
||||
try:
|
||||
title = data['name']
|
||||
except:
|
||||
title = "Imported data"
|
||||
|
||||
timeseries = data['time_series']
|
||||
|
||||
# position, distance, speed, cadence, power,
|
||||
|
||||
res = splituadata(timeseries['distance'])
|
||||
|
||||
distance = res[1]
|
||||
|
||||
times_distance = res[0]
|
||||
|
||||
print distance[0:5]
|
||||
print times_distance[0:5]
|
||||
|
||||
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:
|
||||
times_location = times_distance
|
||||
latcoord = np.zeros(len(times_distance))
|
||||
loncoord = np.zeros(len(times_distance))
|
||||
if workouttype == 'water':
|
||||
workouttype = 'rower'
|
||||
|
||||
try:
|
||||
res = splituadata(timeseries['cadence'])
|
||||
times_spm = res[0]
|
||||
spm = res[1]
|
||||
except KeyError:
|
||||
times_spm = times_distance
|
||||
spm = 0*times_distance
|
||||
|
||||
try:
|
||||
res = splituadata(timeseries['heartrate'])
|
||||
hr = res[1]
|
||||
times_hr = res[0]
|
||||
except KeyError:
|
||||
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
|
||||
|
||||
@@ -1401,7 +1577,7 @@ def rower_underarmour_authorize(request):
|
||||
redirect_uri = UNDERARMOUR_REDIRECT_URI
|
||||
redirect_uri = 'http://localhost:8000/underarmour_callback'
|
||||
|
||||
url = 'https://api.mapmyfitness.com/v7.1/oauth2/authorize/?' \
|
||||
url = 'https://www.mapmyfitness.com/v7.1/oauth2/authorize/?' \
|
||||
'client_id={0}&response_type=code&redirect_uri={1}'.format(
|
||||
UNDERARMOUR_CLIENT_KEY, redirect_uri
|
||||
)
|
||||
@@ -4896,6 +5072,50 @@ def workout_runkeeperimport_view(request,message=""):
|
||||
|
||||
return HttpResponse(res)
|
||||
|
||||
# The page where you select which RunKeeper workout to import
|
||||
@login_required()
|
||||
def workout_underarmourimport_view(request,message=""):
|
||||
res = underarmourstuff.get_underarmour_workout_list(request.user)
|
||||
if (res.status_code != 200):
|
||||
if (res.status_code == 401):
|
||||
r = Rower.objects.get(user=request.user)
|
||||
if (r.underarmourtoken == '') or (r.underarmourtoken is None):
|
||||
s = "Token doesn't exist. Need to authorize"
|
||||
return HttpResponseRedirect("/rowers/me/underarmourauthorize/")
|
||||
message = "Something went wrong in workout_underarmourimport_view"
|
||||
if settings.DEBUG:
|
||||
return HttpResponse(res)
|
||||
else:
|
||||
url = reverse(workouts_view,
|
||||
kwargs = {
|
||||
'message': str(message)
|
||||
})
|
||||
return HttpResponseRedirect(url)
|
||||
else:
|
||||
workouts = []
|
||||
items = res.json()['_embedded']['workouts']
|
||||
for item in items:
|
||||
if 'has_time_series' in item:
|
||||
if item['has_time_series']:
|
||||
s = item['start_datetime']
|
||||
i,r = underarmourstuff.get_idfromuri(request.user,item['_links'])
|
||||
n = item['name']
|
||||
d = item['aggregates']['distance_total']
|
||||
ttot = item['aggregates']['active_time_total']
|
||||
keys = ['id','distance','duration','starttime','type']
|
||||
values = [i,d,ttot,s,r]
|
||||
thedict = dict(zip(keys,values))
|
||||
|
||||
workouts.append(thedict)
|
||||
|
||||
return render(request,'underarmour_list_import.html',
|
||||
{'workouts':workouts,
|
||||
'teams':get_my_teams(request.user),
|
||||
'message':message,
|
||||
})
|
||||
|
||||
return HttpResponse(res)
|
||||
|
||||
# The page where you select which SportTracks workout to import
|
||||
@login_required()
|
||||
def workout_sporttracksimport_view(request,message=""):
|
||||
@@ -5078,6 +5298,29 @@ def workout_getrunkeeperworkout_view(request,runkeeperid):
|
||||
})
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
# Imports a workout from Underarmour
|
||||
@login_required()
|
||||
def workout_getunderarmourworkout_view(request,underarmourid):
|
||||
res = underarmourstuff.get_underarmour_workout(request.user,underarmourid)
|
||||
data = res.json()
|
||||
|
||||
id,message = add_workout_from_underarmourdata(request.user,underarmourid,data)
|
||||
w = Workout.objects.get(id=id)
|
||||
w.uploadedtounderarmour=underarmourid
|
||||
w.save()
|
||||
if message:
|
||||
url = reverse(workout_edit_view,
|
||||
kwargs = {
|
||||
'id':id,
|
||||
'message':message,
|
||||
})
|
||||
else:
|
||||
url = reverse(workout_edit_view,
|
||||
kwargs = {
|
||||
'id':id,
|
||||
})
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
|
||||
|
||||
# Imports a workout from SportTracks
|
||||
|
||||
BIN
static/img/UAbtn.png
Normal file
BIN
static/img/UAbtn.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.8 KiB |
Reference in New Issue
Block a user