Private
Public Access
1
0

import working

This commit is contained in:
Sander Roosendaal
2017-04-16 14:14:20 +02:00
parent cb7d43487e
commit d70df62b63
7 changed files with 359 additions and 13 deletions

View File

@@ -43,12 +43,22 @@
<p>Import workouts from RunKeeper</p> <p>Import workouts from RunKeeper</p>
</div> </div>
</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>
<div class="grid_6 omega"> <div class="grid_6 omega">
<h3>Connect</h3> <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. <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 You only need to do this once. After that, the site will have access until you
revoke the authorization for the "rowingdata" app.</p> revoke the authorization for the "rowingdata" app.</p>
@@ -67,10 +77,13 @@
</div> </div>
</div> </div>
<div class="grid_6"> <div class="grid_6 alpha">
<div class="grid_2 alpha suffix_4"> <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> <p><a href="/rowers/me/runkeeperauthorize/"><img src="/static/img/rk-logo.png" alt="connect with RunKeeper" width="120"></a></p>
</div> </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>
</div> </div>

View 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 %}

View File

@@ -1,5 +1,6 @@
from django import template from django import template
from time import strftime from time import strftime
import dateutil.parser
register = template.Library() register = template.Library()
@@ -27,6 +28,17 @@ def strfdeltah(tdelta):
return res 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 @register.filter
def durationprint(d,dstring): def durationprint(d,dstring):
if (d == None): if (d == None):
@@ -57,6 +69,22 @@ def lookup(dict, key):
s = s[:22] s = s[:22]
return s 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') @register.filter(name='times')
def times(number): def times(number):
return range(number) return range(number)

View File

@@ -171,9 +171,9 @@ def get_underarmour_workout_list(user):
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'Api-Key': UNDERARMOUR_CLIENT_KEY, 'Api-Key': UNDERARMOUR_CLIENT_KEY,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'user':'v7.1/user/'+str(get_userid(r.underarmourtoken))+'/',
'Content-Type': 'application/json'} '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) s = requests.get(url,headers=headers)
return s return s
@@ -191,7 +191,7 @@ def get_underarmour_workout(user,underarmourid):
'Api-Key': UNDERARMOUR_CLIENT_KEY, 'Api-Key': UNDERARMOUR_CLIENT_KEY,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'} '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) s = requests.get(url,headers=headers)
return s return s
@@ -292,13 +292,36 @@ def createunderarmourworkoutdata(w):
return data return data
# Obtain Underarmour Workout ID from the response returned on successful # Obtain Underarmour Workout ID and activity type
# upload def get_idfromuri(user,links):
def getidfromresponse(response): id = links['self'][0]['id']
uri = response.headers["Location"] typeid = links['activity_type'][0]['id']
id = uri[len(uri)-9:]
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 # Get user id, having access token

View File

@@ -229,6 +229,8 @@ urlpatterns = [
url(r'^workout/sporttracksimport/(\d+)/$',views.workout_getsporttracksworkout_view), url(r'^workout/sporttracksimport/(\d+)/$',views.workout_getsporttracksworkout_view),
url(r'^workout/runkeeperimport/$',views.workout_runkeeperimport_view), url(r'^workout/runkeeperimport/$',views.workout_runkeeperimport_view),
url(r'^workout/runkeeperimport/(\d+)/$',views.workout_getrunkeeperworkout_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+)/deleteconfirm$',views.workout_delete_confirm_view),
url(r'^workout/(\d+)/c2uploadw/$',views.workout_c2_upload_view), url(r'^workout/(\d+)/c2uploadw/$',views.workout_c2_upload_view),
url(r'^workout/(\d+)/stravauploadw/$',views.workout_strava_upload_view), url(r'^workout/(\d+)/stravauploadw/$',views.workout_strava_upload_view),

View File

@@ -214,6 +214,15 @@ def get_time(second):
def getidfromsturi(uri,length=8): def getidfromsturi(uri,length=8):
return uri[len(uri)-length:] 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): def splitrunkeeperlatlongdata(lijst,tname,latname,lonname):
t = [] t = []
lat = [] 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 = cum_time+starttimeunix
unixtime[0] = starttimeunix unixtime[0] = starttimeunix
@@ -1401,7 +1577,7 @@ def rower_underarmour_authorize(request):
redirect_uri = UNDERARMOUR_REDIRECT_URI redirect_uri = UNDERARMOUR_REDIRECT_URI
redirect_uri = 'http://localhost:8000/underarmour_callback' 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( 'client_id={0}&response_type=code&redirect_uri={1}'.format(
UNDERARMOUR_CLIENT_KEY, redirect_uri UNDERARMOUR_CLIENT_KEY, redirect_uri
) )
@@ -4896,6 +5072,50 @@ def workout_runkeeperimport_view(request,message=""):
return HttpResponse(res) 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 # The page where you select which SportTracks workout to import
@login_required() @login_required()
def workout_sporttracksimport_view(request,message=""): def workout_sporttracksimport_view(request,message=""):
@@ -5078,6 +5298,29 @@ def workout_getrunkeeperworkout_view(request,runkeeperid):
}) })
return HttpResponseRedirect(url) 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 # Imports a workout from SportTracks

BIN
static/img/UAbtn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB