Private
Public Access
1
0

Merge branch 'develop' into feature/revisedimports

This commit is contained in:
Sander Roosendaal
2018-06-28 16:46:08 +02:00
15 changed files with 18299 additions and 18202 deletions

View File

@@ -31,16 +31,21 @@ class RowerInline(admin.StackedInline):
('ftp','otwslack','pw_ut2','pw_ut1','pw_at','pw_tr','pw_an','max', ('ftp','otwslack','pw_ut2','pw_ut1','pw_at','pw_tr','pw_an','max',
'rest','ut2','ut1','at','tr','an','hrftp',)}), 'rest','ut2','ut1','at','tr','an','hrftp',)}),
('Import/Export Keys', ('Import/Export Keys',
{'fields':('c2token','tokenexpirydate','c2refreshtoken','c2_auto_export', {'fields':('c2token','tokenexpirydate',
'sporttrackstoken','sporttrackstokenexpirydate','sporttracksrefreshtoken', 'c2refreshtoken','c2_auto_export','c2_auto_import',
'sporttrackstoken','sporttrackstokenexpirydate',
'sporttracksrefreshtoken',
'sporttracks_auto_export', 'sporttracks_auto_export',
'underarmourtoken','underarmourtokenexpirydate','underarmourrefreshtoken', 'underarmourtoken','underarmourtokenexpirydate',
'underarmourrefreshtoken',
'mapmyfitness_auto_export', 'mapmyfitness_auto_export',
'tptoken','tptokenexpirydate','tprefreshtoken', 'tptoken','tptokenexpirydate','tprefreshtoken',
'trainingpeaks_auto_export', 'trainingpeaks_auto_export',
'polartoken','polartokenexpirydate','polarrefreshtoken','polaruserid', 'polartoken','polartokenexpirydate',
'polarrefreshtoken','polaruserid',
'polar_auto_import', 'polar_auto_import',
'stravatoken','stravaexportas','strava_auto_export', 'stravatoken','stravaexportas','strava_auto_export',
'strava_auto_import',
'runkeepertoken','runkeeper_auto_export',)}), 'runkeepertoken','runkeeper_auto_export',)}),
('Team', ('Team',
{'fields':('friends','privacy','team')}), {'fields':('friends','privacy','team')}),

View File

@@ -56,7 +56,6 @@ from django.conf import settings
from sqlalchemy import create_engine from sqlalchemy import create_engine
import sqlalchemy as sa import sqlalchemy as sa
import sys import sys
import utils import utils
import datautils import datautils
from utils import lbstoN,myqueue,is_ranking_piece,wavg from utils import lbstoN,myqueue,is_ranking_piece,wavg

View File

@@ -282,7 +282,11 @@ def add_c2_stroke_data_db(strokedata,workoutid,starttimeunix,csvfilename,
res = df.to_csv(csvfilename,index_label='index', res = df.to_csv(csvfilename,index_label='index',
compression='gzip') compression='gzip')
data = dataprep(df,id=workoutid,bands=False,debug=debug)
try:
data = dataprep(df,id=workoutid,bands=False,debug=debug)
except:
return 0
return data return data
@@ -627,11 +631,11 @@ def delete_strokedata(id,debug=False):
conn.close() conn.close()
engine.dispose() engine.dispose()
def update_strokedata(id,df,debug=False): def update_strokedata(id,df,debug=False,bands=True,barchart=True,otwpower=True):
delete_strokedata(id,debug=debug) delete_strokedata(id,debug=debug)
if debug: if debug:
print "updating ",id print "updating ",id
rowdata = dataprep(df,id=id,bands=True,barchart=True,otwpower=True, rowdata = dataprep(df,id=id,bands=False,barchart=True,otwpower=True,
debug=debug) debug=debug)
return rowdata return rowdata
@@ -1032,27 +1036,28 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
data = DataFrame( data = DataFrame(
dict( dict(
time = t*1e3, time=t * 1e3,
hr = hr, hr=hr,
pace = p*1e3, pace=p * 1e3,
spm = spm, spm=spm,
cumdist = cumdist,
ftime = niceformat(t2),
fpace = nicepaceformat(p2),
velo=velo, velo=velo,
driveenergy=driveenergy, cumdist=cumdist,
distanceperstroke=distanceperstroke, ftime=niceformat(t2),
power=power, fpace=nicepaceformat(p2),
driveenergy=driveenergy,
power=power,
workoutstate=workoutstate, workoutstate=workoutstate,
averageforce=averageforce, averageforce=averageforce,
drivelength=drivelength, drivelength=drivelength,
peakforce=peakforce, peakforce=peakforce,
forceratio=forceratio, forceratio=forceratio,
distance=distance, distance=distance,
drivespeed=drivespeed, drivespeed=drivespeed,
) rhythm=rhythm,
) distanceperstroke=distanceperstroke,
)
)
if bands: if bands:
# HR bands # HR bands
@@ -1220,12 +1225,19 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
data = data.replace([-np.inf,np.inf],np.nan) data = data.replace([-np.inf,np.inf],np.nan)
data = data.fillna(method='ffill') data = data.fillna(method='ffill')
data.dropna(axis=0,inplace=True,how='all')
data.dropna(axis=1,inplace=True,how='any')
# write data if id given # write data if id given
if id != 0: if id != 0:
data['workoutid'] = id
if debug: if debug:
engine = create_engine(database_url_debug, echo=False) engine = create_engine(database_url_debug, echo=False)
else: else:
engine = create_engine(database_url, echo=False) engine = create_engine(database_url, echo=False)
with engine.connect() as conn, conn.begin(): with engine.connect() as conn, conn.begin():
data.to_sql('strokedata',engine,if_exists='append',index=False) data.to_sql('strokedata',engine,if_exists='append',index=False)
conn.close() conn.close()

View File

@@ -159,10 +159,6 @@ class Command(BaseCommand):
for r in rowers: for r in rowers:
c2stuff.get_c2_workouts(r) c2stuff.get_c2_workouts(r)
# Strava
rowers = Rower.objects.filter(strava_auto_import=True)
for r in rowers:
stravastuff.get_strava_workouts(r)
messages = Message.objects.filter(mailbox_id = workoutmailbox.id) messages = Message.objects.filter(mailbox_id = workoutmailbox.id)
message_ids = [m.id for m in messages] message_ids = [m.id for m in messages]
@@ -245,5 +241,10 @@ class Command(BaseCommand):
if message.attachments.exists() is False: if message.attachments.exists() is False:
message.delete() message.delete()
# Strava
rowers = Rower.objects.filter(strava_auto_import=True)
for r in rowers:
stravastuff.get_strava_workouts(r)
self.stdout.write(self.style.SUCCESS( self.stdout.write(self.style.SUCCESS(
'Successfully processed email attachments')) 'Successfully processed email attachments'))

View File

@@ -74,6 +74,7 @@ rowingmetrics = (
'verbose_name': 'Boat Speed (m/s)', 'verbose_name': 'Boat Speed (m/s)',
'ax_min': 0, 'ax_min': 0,
'ax_max': 8, 'ax_max': 8,
'default': 0,
'mode':'both', 'mode':'both',
'type':'pro'}), 'type':'pro'}),

View File

@@ -94,19 +94,6 @@ def get_strava_workout_list(user):
return s return s
def add_stroke_data(user,stravaid,workoutid,startdatetime,csvfilename):
r = Rower.objects.get(user=user)
starttimeunix = arrow.get(startdatetime).timestamp
job = myqueue(queue,
handle_strava_import_stroke_data,
r.stravatoken,
stravaid,
workoutid,
starttimeunix,
csvfilename)
# gets all new Strava workouts for a rower # gets all new Strava workouts for a rower
def get_strava_workouts(rower): def get_strava_workouts(rower):
@@ -131,11 +118,11 @@ def get_strava_workouts(rower):
newids = [stravaid for stravaid in stravaids if not stravaid in knownstravaids] newids = [stravaid for stravaid in stravaids if not stravaid in knownstravaids]
for stravaid in newids: for stravaid in newids:
workoutid = create_async_workout(alldata,rower.user,stravaid) result = create_async_workout(alldata,rower.user,stravaid)
return 1 return 1
def create_async_workout(alldata,user,stravaid): def create_async_workout(alldata,user,stravaid,debug=False):
data = alldata[stravaid] data = alldata[stravaid]
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
distance = data['distance'] distance = data['distance']
@@ -200,29 +187,38 @@ def create_async_workout(alldata,user,stravaid):
code = uuid4().hex[:16] code = uuid4().hex[:16]
) )
w = Workout( # w = Workout(
user=r, # user=r,
workouttype = workouttype, # workouttype = workouttype,
name = title, # name = title,
date = workoutdate, # date = workoutdate,
starttime = starttime, # starttime = starttime,
startdatetime = rowdatetime, # startdatetime = rowdatetime,
timezone = thetimezone, # timezone = thetimezone,
duration = duration, # duration = duration,
distance=distance, # distance=distance,
weightcategory = weightcategory, # weightcategory = weightcategory,
uploadedtostrava = stravaid, # uploadedtostrava = stravaid,
csvfilename = csvfilename, # csvfilename = csvfilename,
notes = '' # notes = ''
) # )
w.save()
# Check if workout has stroke data, and get the stroke data # Check if workout has stroke data, and get the stroke data
result = add_stroke_data(user,stravaid,w.id,rowdatetime,csvfilename) starttimeunix = arrow.get(rowdatetime).timestamp
return w.id job = myqueue(queue,
handle_strava_import_stroke_data,
title,
user.email,
r.stravatoken,
stravaid,
starttimeunix,
csvfilename,
)
return 1
from utils import get_strava_stream from utils import get_strava_stream

View File

@@ -31,7 +31,7 @@ from rowsandall_app.settings import SITE_URL
from rowsandall_app.settings_dev import SITE_URL as SITE_URL_DEV from rowsandall_app.settings_dev import SITE_URL as SITE_URL_DEV
from rowsandall_app.settings import PROGRESS_CACHE_SECRET from rowsandall_app.settings import PROGRESS_CACHE_SECRET
from rowsandall_app.settings import SETTINGS_NAME from rowsandall_app.settings import SETTINGS_NAME
from rowsandall_app.settings import workoutemailbox
import pandas as pd import pandas as pd
@@ -47,7 +47,8 @@ from rowers.dataprepnodjango import (
update_agegroup_db,fitnessmetric_to_sql, update_agegroup_db,fitnessmetric_to_sql,
add_c2_stroke_data_db,totaltime_sec_to_string, add_c2_stroke_data_db,totaltime_sec_to_string,
create_c2_stroke_data_db,update_empower, create_c2_stroke_data_db,update_empower,
database_url_debug,database_url,dataprep database_url_debug,database_url,dataprep,
# create_strava_stroke_data_db
) )
@@ -82,8 +83,10 @@ def add(x, y):
@app.task @app.task
def handle_strava_import_stroke_data(stravatoken, def handle_strava_import_stroke_data(title,
stravaid,workoutid, useremail,
stravatoken,
stravaid,
starttimeunix, starttimeunix,
csvfilename,debug=True,**kwargs): csvfilename,debug=True,**kwargs):
# ready to fetch. Hurray # ready to fetch. Hurray
@@ -100,6 +103,8 @@ def handle_strava_import_stroke_data(stravatoken,
workoutsummary['timezone'] = "Etc/UTC" workoutsummary['timezone'] = "Etc/UTC"
startdatetime = workoutsummary['start_date'] startdatetime = workoutsummary['start_date']
r = type('Rower', (object,), {"stravatoken": stravatoken})
spmjson = get_strava_stream(r,'cadence',stravaid) spmjson = get_strava_stream(r,'cadence',stravaid)
hrjson = get_strava_stream(r,'heartrate',stravaid) hrjson = get_strava_stream(r,'heartrate',stravaid)
timejson = get_strava_stream(r,'time',stravaid) timejson = get_strava_stream(r,'time',stravaid)
@@ -202,12 +207,23 @@ def handle_strava_import_stroke_data(stravatoken,
df.sort_values(by='TimeStamp (sec)',ascending=True) df.sort_values(by='TimeStamp (sec)',ascending=True)
res = df.to_csv(csvfilename+'.gz',index_label='index',compression='gzip') res = df.to_csv(csvfilename,index_label='index')
data = dataprep(df,id=workoutid,bands=False,debug=debug) d = {
# startdatetime = datetime.datetime.strptime(startdatetime,"%Y-%m-%d-%H:%M:%S") 'stravaid':stravaid
}
return 1 res = send_template_email(useremail,[workoutemailbox],
title,'workoutemail.html',
d,
attach_file=csvfilename)
time.sleep(1)
os.remove(csvfilename)
return res
@app.task @app.task

View File

@@ -1,6 +1,4 @@
{% extends "emailbase.html" %} {% extends "emailbase.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block body %} {% block body %}
<p>Dear <strong>{{ first_name }}</strong>,</p> <p>Dear <strong>{{ first_name }}</strong>,</p>

View File

@@ -0,0 +1,7 @@
{% extends "emailbase.html" %}
{% block body %}
stravaid {{ stravaid }}
{% endblock %}

View File

@@ -98,6 +98,17 @@ def matchsync(line):
return results return results
def getstravaid(uploadoptions,body):
stravaid = 0
tester = re.compile('^(stravaid)(.*?)(\d+)')
for line in body.splitlines():
if tester.match(line.lower()):
stravaid = tester.match(line.lower()).group(3)
uploadoptions['stravaid'] = int(stravaid)
return uploadoptions
def gettypeoptions_body2(uploadoptions,body): def gettypeoptions_body2(uploadoptions,body):
tester = re.compile('^(workout)') tester = re.compile('^(workout)')
testerb = re.compile('^(boat)') testerb = re.compile('^(boat)')
@@ -272,6 +283,7 @@ def upload_options(body):
uploadoptions = getsyncoptions_body2(uploadoptions,body) uploadoptions = getsyncoptions_body2(uploadoptions,body)
uploadoptions = getprivateoptions_body2(uploadoptions,body) uploadoptions = getprivateoptions_body2(uploadoptions,body)
typeoptions = gettypeoptions_body2(uploadoptions,body) typeoptions = gettypeoptions_body2(uploadoptions,body)
uploadoptions = getstravaid(uploadoptions,body)
except IOError: except IOError:
pm = exc.problem_mark pm = exc.problem_mark
strpm = str(pm) strpm = str(pm)
@@ -376,6 +388,13 @@ def make_private(w,options):
from rowers.utils import isprorower from rowers.utils import isprorower
def do_sync(w,options): def do_sync(w,options):
try:
if options['stravaid'] != 0:
w.uploadedtostrava = options['stravaid']
w.save()
except KeyError:
pass
if ('upload_to_C2' in options and options['upload_to_C2']) or (w.user.c2_auto_export and isprorower(w.user)): if ('upload_to_C2' in options and options['upload_to_C2']) or (w.user.c2_auto_export and isprorower(w.user)):
try: try:
message,id = c2stuff.workout_c2_upload(w.user.user,w) message,id = c2stuff.workout_c2_upload(w.user.user,w)

View File

@@ -327,6 +327,7 @@ urlpatterns = [
# url(r'^workout/stravaimport/(?P<stravaid>\d+)/$',views.workout_getstravaworkout_view), # url(r'^workout/stravaimport/(?P<stravaid>\d+)/$',views.workout_getstravaworkout_view),
url(r'^workout/(?P<source>\w+.*)import/(?P<externalid>\d+)/$',views.workout_getimportview), url(r'^workout/(?P<source>\w+.*)import/(?P<externalid>\d+)/$',views.workout_getimportview),
url(r'^workout/stravaimport/all/$',views.workout_getstravaworkout_all), url(r'^workout/stravaimport/all/$',views.workout_getstravaworkout_all),
url(r'^workout/stravaimport/next/$',views.workout_getstravaworkout_next),
url(r'^workout/sporttracksimport/$',views.workout_sporttracksimport_view), url(r'^workout/sporttracksimport/$',views.workout_sporttracksimport_view),
url(r'^workout/sporttracksimport/all/$',views.workout_getsporttracksworkout_all), url(r'^workout/sporttracksimport/all/$',views.workout_getsporttracksworkout_all),
url(r'^workout/polarimport/$',views.workout_polarimport_view), url(r'^workout/polarimport/$',views.workout_polarimport_view),

View File

@@ -286,6 +286,7 @@ def myqueue(queue,function,*args,**kwargs):
job_id = str(uuid.uuid4()) job_id = str(uuid.uuid4())
kwargs['job_id'] = job_id kwargs['job_id'] = job_id
kwargs['jobkey'] = job_id kwargs['jobkey'] = job_id
kwargs['timeout'] = 3600
job = queue.enqueue(function,*args,**kwargs) job = queue.enqueue(function,*args,**kwargs)

View File

@@ -2134,6 +2134,7 @@ def imports_view(request):
setattr(r,attr,value) setattr(r,attr,value)
r.save() r.save()
messages.info(request,'Settings Saved')
else: else:
form = RowerImportExportForm(instance=r) form = RowerImportExportForm(instance=r)
@@ -9727,6 +9728,40 @@ def workout_getstravaworkout_all(request):
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
# Imports all new workouts from SportTracks
@login_required()
def workout_getstravaworkout_next(request):
r = Rower.objects.get(user=request.user)
res = stravastuff.get_strava_workout_list(r.user)
if (res.status_code != 200):
return 0
else:
stravaids = [int(item['id']) for item in res.json()]
alldata = {}
for item in res.json():
alldata[item['id']] = item
knownstravaids = uniqify([
w.uploadedtostrava for w in Workout.objects.filter(user=r)
])
newids = [stravaid for stravaid in stravaids if not stravaid in knownstravaids]
theid = newids[0]
workoutid = stravastuff.create_async_workout(alldata,r.user,stravaid,debug=True)
url = reverse(workouts_view)
return HttpResponseRedirect(url)
@login_required @login_required

View File

@@ -409,3 +409,9 @@ except KeyError:
WARNING_MESSAGE = '' WARNING_MESSAGE = ''
SETTINGS_NAME = 'rowsandall_app.settings' SETTINGS_NAME = 'rowsandall_app.settings'
# Workout email box
try:
workoutemailbox = CFG['workoutemailbox']
except KeyError:
workoutemailbox = 'workouts@rowsandall.com'