Private
Public Access
1
0

Merge branch 'develop' into feature/sensorfusion

This commit is contained in:
Sander Roosendaal
2017-03-04 22:24:35 +01:00
9 changed files with 460 additions and 374 deletions

View File

@@ -8,7 +8,7 @@ from rowers.tasks import handle_sendemail_unrecognized
from rowingdata import rower as rrower from rowingdata import rower as rrower
from rowingdata import main as rmain from rowingdata import main as rmain
from rowingdata import get_file_type from rowingdata import get_file_type,get_empower_rigging
from pandas import DataFrame,Series from pandas import DataFrame,Series
from pytz import timezone as tz,utc from pytz import timezone as tz,utc
@@ -31,7 +31,7 @@ import os
import pandas as pd import pandas as pd
import numpy as np import numpy as np
import itertools import itertools
import math
from tasks import handle_sendemail_unrecognized from tasks import handle_sendemail_unrecognized
from django.conf import settings from django.conf import settings
@@ -102,6 +102,11 @@ def clean_df_stats(datadf,workstrokesonly=True,ignorehr=True,
except KeyError: except KeyError:
pass pass
try:
datadf['hr'] = datadf['hr']+10
except KeyError:
pass
datadf=datadf.clip(lower=0) datadf=datadf.clip(lower=0)
datadf.replace(to_replace=0,value=np.nan,inplace=True) datadf.replace(to_replace=0,value=np.nan,inplace=True)
@@ -116,6 +121,11 @@ def clean_df_stats(datadf,workstrokesonly=True,ignorehr=True,
except KeyError: except KeyError:
pass pass
try:
datadf['hr'] = datadf['hr']-10
except KeyError:
pass
# clean data for useful ranges per column # clean data for useful ranges per column
if not ignorehr: if not ignorehr:
try: try:
@@ -326,7 +336,8 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
dosummary=True,title='Workout', dosummary=True,title='Workout',
notes='',totaldist=0,totaltime=0, notes='',totaldist=0,totaltime=0,
summary='', summary='',
makeprivate=False): makeprivate=False,
oarlength=2.89,inboard=0.88):
message = None message = None
powerperc = 100*np.array([r.pw_ut2, powerperc = 100*np.array([r.pw_ut2,
r.pw_ut1, r.pw_ut1,
@@ -432,7 +443,7 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
privacy = 'visible' privacy = 'visible'
# check for duplicate start times # check for duplicate start times
ws = Workout.objects.filter(starttime=workoutstarttime, ws = Workout.objects.filter(startdatetime=workoutstartdatetime,
user=r) user=r)
if (len(ws) != 0): if (len(ws) != 0):
message = "Warning: This workout probably already exists in the database" message = "Warning: This workout probably already exists in the database"
@@ -446,6 +457,7 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
csvfilename=f2,notes=notes,summary=summary, csvfilename=f2,notes=notes,summary=summary,
maxhr=maxhr,averagehr=averagehr, maxhr=maxhr,averagehr=averagehr,
startdatetime=workoutstartdatetime, startdatetime=workoutstartdatetime,
inboard=inboard,oarlength=oarlength,
privacy=privacy) privacy=privacy)
@@ -458,11 +470,13 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
# put stroke data in database # put stroke data in database
res = dataprep(row.df,id=w.id,bands=True, res = dataprep(row.df,id=w.id,bands=True,
barchart=True,otwpower=True,empower=True) barchart=True,otwpower=True,empower=True,inboard=inboard)
return (w.id,message) return (w.id,message)
def handle_nonpainsled(f2,fileformat,summary=''): def handle_nonpainsled(f2,fileformat,summary=''):
oarlength = 2.89
inboard = 0.88
# handle RowPro: # handle RowPro:
if (fileformat == 'rp'): if (fileformat == 'rp'):
row = RowProParser(f2) row = RowProParser(f2)
@@ -506,6 +520,7 @@ def handle_nonpainsled(f2,fileformat,summary=''):
if (fileformat == 'speedcoach2'): if (fileformat == 'speedcoach2'):
row = SpeedCoach2Parser(f2) row = SpeedCoach2Parser(f2)
try: try:
oarlength,inboard = get_empower_rigging(f2)
summary = row.allstats() summary = row.allstats()
except: except:
pass pass
@@ -534,7 +549,7 @@ def handle_nonpainsled(f2,fileformat,summary=''):
except: except:
os.remove(f_to_be_deleted+'.gz') os.remove(f_to_be_deleted+'.gz')
return (f2,summary) return (f2,summary,oarlength,inboard)
# Create new workout from file and store it in the database # Create new workout from file and store it in the database
# This routine should be used everywhere in views.py and mailprocessing.py # This routine should be used everywhere in views.py and mailprocessing.py
@@ -547,6 +562,8 @@ def new_workout_from_file(r,f2,
message = None message = None
fileformat = get_file_type(f2) fileformat = get_file_type(f2)
summary = '' summary = ''
oarlength = 2.89
inboard = 0.88
if len(fileformat)==3 and fileformat[0]=='zip': if len(fileformat)==3 and fileformat[0]=='zip':
f_to_be_deleted = f2 f_to_be_deleted = f2
with zipfile.ZipFile(f2) as z: with zipfile.ZipFile(f2) as z:
@@ -590,7 +607,7 @@ def new_workout_from_file(r,f2,
# handle non-Painsled by converting it to painsled compatible CSV # handle non-Painsled by converting it to painsled compatible CSV
if (fileformat != 'csv'): if (fileformat != 'csv'):
f2,summary = handle_nonpainsled(f2,fileformat,summary=summary) f2,summary,oarlength,inboard = handle_nonpainsled(f2,fileformat,summary=summary)
dosummary = (fileformat != 'fit') dosummary = (fileformat != 'fit')
@@ -599,6 +616,7 @@ def new_workout_from_file(r,f2,
makeprivate=makeprivate, makeprivate=makeprivate,
dosummary=dosummary, dosummary=dosummary,
summary=summary, summary=summary,
inboard=inboard,oarlength=oarlength,
title=title) title=title)
return (id,message,f2) return (id,message,f2)
@@ -905,7 +923,7 @@ def datafusion(id1,id2,columns,offset):
# saves it to the stroke_data table in the database # saves it to the stroke_data table in the database
# Takes a rowingdata object's DataFrame as input # Takes a rowingdata object's DataFrame as input
def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
empower=True): empower=True,inboard=0.88):
rowdatadf.set_index([range(len(rowdatadf))],inplace=True) rowdatadf.set_index([range(len(rowdatadf))],inplace=True)
t = rowdatadf.ix[:,'TimeStamp (sec)'] t = rowdatadf.ix[:,'TimeStamp (sec)']
t = pd.Series(t-rowdatadf.ix[0,'TimeStamp (sec)']) t = pd.Series(t-rowdatadf.ix[0,'TimeStamp (sec)'])
@@ -1018,8 +1036,15 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
finish = rowdatadf.ix[:,'finish'] finish = rowdatadf.ix[:,'finish']
peakforceangle = rowdatadf.ix[:,'peakforceangle'] peakforceangle = rowdatadf.ix[:,'peakforceangle']
driveenergy = rowdatadf.ix[:,'driveenergy'] driveenergy = rowdatadf.ix[:,'driveenergy']
arclength = (inboard-0.05)*(np.radians(finish)-np.radians(catch))
if arclength.mean()>0:
drivelength = arclength
else:
drivelength = driveenergy/(averageforce*4.44822) drivelength = driveenergy/(averageforce*4.44822)
slip = rowdatadf.ix[:,'slip'] slip = rowdatadf.ix[:,'slip']
totalangle = finish-catch
effectiveangle = finish-wash-catch-slip
if windowsize > 3 and windowsize<len(slip): if windowsize > 3 and windowsize<len(slip):
wash = savgol_filter(wash,windowsize,3) wash = savgol_filter(wash,windowsize,3)
slip = savgol_filter(slip,windowsize,3) slip = savgol_filter(slip,windowsize,3)
@@ -1028,6 +1053,8 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
peakforceangle = savgol_filter(peakforceangle,windowsize,3) peakforceangle = savgol_filter(peakforceangle,windowsize,3)
driveenergy = savgol_filter(driveenergy,windowsize,3) driveenergy = savgol_filter(driveenergy,windowsize,3)
drivelength = savgol_filter(drivelength,windowsize,3) drivelength = savgol_filter(drivelength,windowsize,3)
totalangle = savgol_filter(totalangle,windowsize,3)
effectiveangle = savgol_filter(effectiveangle,windowsize,3)
data['wash'] = wash data['wash'] = wash
data['catch'] = catch data['catch'] = catch
data['slip'] = slip data['slip'] = slip
@@ -1035,6 +1062,8 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
data['peakforceangle'] = peakforceangle data['peakforceangle'] = peakforceangle
data['driveenergy'] = driveenergy data['driveenergy'] = driveenergy
data['drivelength'] = drivelength data['drivelength'] = drivelength
data['totalangle'] = totalangle
data['effectiveangle'] = effectiveangle
except KeyError: except KeyError:
pass pass

View File

@@ -260,7 +260,7 @@ class WorkoutMultipleCompareForm(forms.Form):
from rowers.interactiveplots import axlabels from rowers.interactiveplots import axlabels
axlabels.pop('None') axlabels.pop('None')
axlabels = list(axlabels.items()) axlabels = list(sorted(axlabels.items(), key = lambda x:x[1]))
class ChartParamChoiceForm(forms.Form): class ChartParamChoiceForm(forms.Form):

View File

@@ -1,4 +1,3 @@
from rowers.models import Workout, User, Rower, WorkoutForm,RowerForm,GraphImage from rowers.models import Workout, User, Rower, WorkoutForm,RowerForm,GraphImage
from rowingdata import rower as rrower from rowingdata import rower as rrower
from rowingdata import main as rmain from rowingdata import main as rmain
@@ -51,66 +50,36 @@ import stravastuff
from rowers.dataprep import rdata from rowers.dataprep import rdata
import rowers.dataprep as dataprep import rowers.dataprep as dataprep
axlabels = { axes = (
'time': 'Time', ('time','Time',0,1e5,'basic'),
'distance': 'Distance (m)', ('distance', 'Distance (m)',0,1e5,'basic'),
'cumdist': 'Cumulative Distance (m)', ('cumdist', 'Cumulative Distance (m)',0,1e5,'basic'),
'hr': 'Heart Rate (bpm)', ('hr','Heart Rate (bpm)',100,200,'basic'),
'spm': 'Stroke Rate (spm)', ('spm', 'Stroke Rate (spm)',15,45,'basic'),
'pace': 'Pace (/500m)', ('pace', 'Pace (/500m)',1.0e3*210,1.0e3*75,'basic'),
'power': 'Power (Watt)', ('power', 'Power (Watt)',0,600,'basic'),
'averageforce': 'Average Drive Force (lbs)', ('averageforce', 'Average Drive Force (lbs)',0,200,'pro'),
'drivelength': 'Drive Length (m)', ('drivelength', 'Drive Length (m)',0.5,2.0,'pro'),
'peakforce': 'Peak Drive Force (lbs)', ('peakforce', 'Peak Drive Force (lbs)',0,400,'pro'),
'forceratio': 'Average/Peak Drive Force Ratio', ('forceratio', 'Average/Peak Force Ratio',0,1,'pro'),
'driveenergy': 'Work per Stroke (J)', ('driveenergy', 'Work per Stroke (J)',0,1000,'pro'),
'drivespeed': 'Drive Speed (m/s)', ('drivespeed', 'Drive Speed (m/s)',0,4,'pro'),
'slip': 'Slip (degrees)', ('slip', 'Slip (degrees)',0,20,'pro'),
'catch': 'Catch (degrees)', ('catch', 'Catch (degrees)',-40,-75,'pro'),
'finish': 'Finish (degrees)', ('finish', 'Finish (degrees)',20,55,'pro'),
'wash': 'Wash (degrees)', ('wash', 'Wash (degrees)',0,30,'pro'),
'peakforceangle': 'Peak Force Angle (degrees)', ('peakforceangle', 'Peak Force Angle (degrees)',-20,20,'pro'),
'rhythm': 'Stroke Rhythm (%)', ('totalangle', 'Drive Length (deg)',40,140,'pro'),
'None': '', ('effectiveangle', 'Effective Drive Length (deg)',40,140,'pro'),
} ('rhythm', 'Stroke Rhythm (%)',20,55,'pro'),
('None', 'None',0,1,'basic'),
)
yaxminima = { axlabels = {ax[0]:ax[1] for ax in axes}
'hr':100,
'spm':15,
'pace': 1.0e3*210,
'power': 0,
'averageforce': 0,
'peakforce': 0,
'forceratio':0,
'drivelength':0.5,
'driveenergy': 0,
'drivespeed': 0,
'slip': 0,
'catch': -40,
'finish': 20,
'wash': 0,
'peakforceangle': -20,
'rhythm':20,
}
yaxmaxima = { yaxminima = {ax[0]:ax[2] for ax in axes}
'hr':200,
'spm':45, yaxmaxima = {ax[0]:ax[3] for ax in axes}
'pace': 1.0e3*75,
'power': 600,
'averageforce':200,
'peakforce':400,
'forceratio':1,
'drivelength':2.0,
'driveenergy': 1000,
'drivespeed':4,
'slip': 15,
'catch': -75,
'finish': 55,
'wash': 30,
'peakforceangle': 20,
'rhythm':55,
}
def tailwind(bearing,vwind,winddir): def tailwind(bearing,vwind,winddir):
""" Calculates head-on head/tailwind in direction of rowing """ Calculates head-on head/tailwind in direction of rowing
@@ -139,9 +108,10 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
columns = ['catch','slip','wash','finish','averageforce', columns = ['catch','slip','wash','finish','averageforce',
'peakforceangle','peakforce','spm','distance', 'peakforceangle','peakforce','spm','distance',
'workoutstate'] 'workoutstate','driveenergy']
rowdata = dataprep.getsmallrowdata_db(columns,ids=ids) rowdata = dataprep.getsmallrowdata_db(columns,ids=ids)
rowdata.dropna(axis=1,how='all',inplace=True)
rowdata.dropna(axis=0,how='any',inplace=True) rowdata.dropna(axis=0,how='any',inplace=True)
workoutstateswork = [1,4,5,8,9,6,7] workoutstateswork = [1,4,5,8,9,6,7]
@@ -154,6 +124,8 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
except KeyError: except KeyError:
pass pass
if rowdata.empty:
return "","No Valid Data Available","",""
catchav = rowdata['catch'].mean() catchav = rowdata['catch'].mean()
finishav = rowdata['finish'].mean() finishav = rowdata['finish'].mean()
@@ -276,6 +248,7 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
var y = data['y'] var y = data['y']
var spm1 = data2['spm'] var spm1 = data2['spm']
var distance1 = data2['distance'] var distance1 = data2['distance']
var driveenergy1 = data2['driveenergy']
var thresholdforce = y[1] var thresholdforce = y[1]
@@ -291,6 +264,8 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
var maxspm = maxspm.value var maxspm = maxspm.value
var mindist = mindist.value var mindist = mindist.value
var maxdist = maxdist.value var maxdist = maxdist.value
var minwork = minwork.value
var maxwork = maxwork.value
var catchav = 0 var catchav = 0
var finishav = 0 var finishav = 0
@@ -305,6 +280,7 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
for (i=0; i<c.length; i++) { for (i=0; i<c.length; i++) {
if (spm1[i]>=minspm && spm1[i]<=maxspm) { if (spm1[i]>=minspm && spm1[i]<=maxspm) {
if (distance1[i]>=mindist && distance1[i]<=maxdist) { if (distance1[i]>=mindist && distance1[i]<=maxdist) {
if (driveenergy1[i]>=minwork && driveenergy1[i]<=maxwork) {
catchav += c[i] catchav += c[i]
finishav += finish[i] finishav += finish[i]
slipav += slip[i] slipav += slip[i]
@@ -316,6 +292,7 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
} }
} }
} }
}
catchav /= count catchav /= count
finishav /= count finishav /= count
@@ -349,6 +326,15 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
title="Max SPM",callback=callback) title="Max SPM",callback=callback)
callback.args["maxspm"] = slider_spm_max callback.args["maxspm"] = slider_spm_max
slider_work_min = Slider(start=0, end=1500,value=0, step=10,
title="Min Work per Stroke",callback=callback)
callback.args["minwork"] = slider_work_min
slider_work_max = Slider(start=0, end=1500,value=1500, step=10,
title="Max Work per Stroke",callback=callback)
callback.args["maxwork"] = slider_work_max
distmax = 100+100*int(rowdata['distance'].max()/100.) distmax = 100+100*int(rowdata['distance'].max()/100.)
slider_dist_min = Slider(start=0,end=distmax,value=0,step=1, slider_dist_min = Slider(start=0,end=distmax,value=0,step=1,
@@ -364,6 +350,8 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
slider_spm_max, slider_spm_max,
slider_dist_min, slider_dist_min,
slider_dist_max, slider_dist_max,
slider_work_min,
slider_work_max,
], ],
), ),
plot]) plot])
@@ -386,6 +374,9 @@ def interactive_histoall(theworkouts):
rowdata = dataprep.getsmallrowdata_db(['power'],ids=ids,doclean=True) rowdata = dataprep.getsmallrowdata_db(['power'],ids=ids,doclean=True)
rowdata.dropna(axis=0,how='any',inplace=True) rowdata.dropna(axis=0,how='any',inplace=True)
if rowdata.empty:
return "","No Valid Data Available","",""
histopwr = rowdata['power'].values histopwr = rowdata['power'].values
if len(histopwr) == 0: if len(histopwr) == 0:
return "","No valid data available","","" return "","No valid data available","",""
@@ -892,20 +883,21 @@ def interactive_cum_flex_chart2(theworkouts,promember=0,
# datadf = dataprep.smalldataprep(theworkouts,xparam,yparam1,yparam2) # datadf = dataprep.smalldataprep(theworkouts,xparam,yparam1,yparam2)
ids = [int(w.id) for w in theworkouts] ids = [int(w.id) for w in theworkouts]
datadf = dataprep.getsmallrowdata_db([xparam,yparam1,yparam2],ids=ids,doclean=False) columns = [xparam,yparam1,yparam2,'spm','driveenergy','distance']
datadf = dataprep.getsmallrowdata_db(columns,ids=ids,doclean=False)
yparamname1 = axlabels[yparam1] yparamname1 = axlabels[yparam1]
if yparam2 != 'None': if yparam2 != 'None':
yparamname2 = axlabels[yparam2] yparamname2 = axlabels[yparam2]
datadf = datadf[datadf[yparam1] > 0] #datadf = datadf[datadf[yparam1] > 0]
datadf = datadf[datadf[xparam] > 0] #datadf = datadf[datadf[xparam] > 0]
if yparam2 != 'None': #if yparam2 != 'None':
datadf = datadf[datadf[yparam2] > 0] # datadf = datadf[datadf[yparam2] > 0]
# check if dataframe not empty # check if dataframe not empty
if datadf.empty: if datadf.empty:
@@ -973,7 +965,7 @@ def interactive_cum_flex_chart2(theworkouts,promember=0,
line_dash=[6,6],line_width=2) line_dash=[6,6],line_width=2)
y2means = y1means y2means = y1means
xlabel = Label(x=370,y=130,x_units='screen',y_units='screen', xlabel = Label(x=100,y=130,x_units='screen',y_units='screen',
text=xparam+": {x1mean:6.2f}".format(x1mean=x1mean), text=xparam+": {x1mean:6.2f}".format(x1mean=x1mean),
background_fill_alpha=.7, background_fill_alpha=.7,
text_color='green', text_color='green',
@@ -985,7 +977,7 @@ def interactive_cum_flex_chart2(theworkouts,promember=0,
plot.add_layout(y1means) plot.add_layout(y1means)
y1label = Label(x=370,y=100,x_units='screen',y_units='screen', y1label = Label(x=100,y=100,x_units='screen',y_units='screen',
text=yparam1+": {y1mean:6.2f}".format(y1mean=y1mean), text=yparam1+": {y1mean:6.2f}".format(y1mean=y1mean),
background_fill_alpha=.7, background_fill_alpha=.7,
text_color='blue', text_color='blue',
@@ -1025,8 +1017,8 @@ def interactive_cum_flex_chart2(theworkouts,promember=0,
plot.add_layout(y2means) plot.add_layout(y2means)
y2label = Label(x=370,y=70,x_units='screen',y_units='screen', y2label = Label(x=100,y=70,x_units='screen',y_units='screen',
text=yparam2+": {y2mean:6.2f}".format(y2mean=y2mean), text=axlabels[yparam2]+": {y2mean:6.2f}".format(y2mean=y2mean),
background_fill_alpha=.7, background_fill_alpha=.7,
text_color='red', text_color='red',
) )
@@ -1048,6 +1040,7 @@ def interactive_cum_flex_chart2(theworkouts,promember=0,
var y2 = data['y2'] var y2 = data['y2']
var spm1 = data['spm'] var spm1 = data['spm']
var distance1 = data['distance'] var distance1 = data['distance']
var driveenergy1 = data['driveenergy']
var xname = data['xname'][0] var xname = data['xname'][0]
var yname1 = data['yname1'][0] var yname1 = data['yname1'][0]
var yname2 = data['yname2'][0] var yname2 = data['yname2'][0]
@@ -1056,6 +1049,8 @@ def interactive_cum_flex_chart2(theworkouts,promember=0,
var maxspm = maxspm.value var maxspm = maxspm.value
var mindist = mindist.value var mindist = mindist.value
var maxdist = maxdist.value var maxdist = maxdist.value
var minwork = minwork.value
var maxwork = maxwork.value
var xm = 0 var xm = 0
var ym1 = 0 var ym1 = 0
var ym2 = 0 var ym2 = 0
@@ -1075,6 +1070,7 @@ def interactive_cum_flex_chart2(theworkouts,promember=0,
for (i=0; i<x1.length; i++) { for (i=0; i<x1.length; i++) {
if (spm1[i]>=minspm && spm1[i]<=maxspm) { if (spm1[i]>=minspm && spm1[i]<=maxspm) {
if (distance1[i]>=mindist && distance1[i]<=maxdist) { if (distance1[i]>=mindist && distance1[i]<=maxdist) {
if (driveenergy1[i]>=minwork && driveenergy1[i]<=maxwork) {
data2['x1'].push(x1[i]) data2['x1'].push(x1[i])
data2['y1'].push(y1[i]) data2['y1'].push(y1[i])
data2['y2'].push(y2[i]) data2['y2'].push(y2[i])
@@ -1084,7 +1080,7 @@ def interactive_cum_flex_chart2(theworkouts,promember=0,
xm += x1[i] xm += x1[i]
ym1 += y1[i] ym1 += y1[i]
ym2 += y2[i] ym2 += y2[i]
}
} }
} }
} }
@@ -1099,9 +1095,9 @@ def interactive_cum_flex_chart2(theworkouts,promember=0,
x1means.location = xm x1means.location = xm
y1means.location = ym1 y1means.location = ym1
y2means.location = ym2 y2means.location = ym2
y1label.text = yname1+': '+ym1.toFixed(2) y1label.text = yname1+': '+(ym1).toFixed(2)
y2label.text = yname2+': '+ym2.toFixed(2) y2label.text = yname2+': '+(ym2).toFixed(2)
xlabel.text = xname+': '+xm.toFixed(2) xlabel.text = xname+': '+(xm).toFixed(2)
source2.trigger('change'); source2.trigger('change');
""") """)
@@ -1115,6 +1111,15 @@ def interactive_cum_flex_chart2(theworkouts,promember=0,
title="Max SPM",callback=callback) title="Max SPM",callback=callback)
callback.args["maxspm"] = slider_spm_max callback.args["maxspm"] = slider_spm_max
slider_work_min = Slider(start=0.0, end=1500,value=0.0, step=10,
title="Min Work per Stroke",callback=callback)
callback.args["minwork"] = slider_work_min
slider_work_max = Slider(start=0.0, end=1500,value=1500.0, step=10,
title="Max Work per Stroke",callback=callback)
callback.args["maxwork"] = slider_work_max
distmax = 100+100*int(datadf['distance'].max()/100.) distmax = 100+100*int(datadf['distance'].max()/100.)
slider_dist_min = Slider(start=0,end=distmax,value=0,step=1, slider_dist_min = Slider(start=0,end=distmax,value=0,step=1,
@@ -1130,6 +1135,8 @@ def interactive_cum_flex_chart2(theworkouts,promember=0,
slider_spm_max, slider_spm_max,
slider_dist_min, slider_dist_min,
slider_dist_max, slider_dist_max,
slider_work_min,
slider_work_max,
], ],
), ),
plot]) plot])
@@ -1157,11 +1164,13 @@ def interactive_flex_chart2(id=0,promember=0,
#rowdata,row = dataprep.getrowdata_db(id=id) #rowdata,row = dataprep.getrowdata_db(id=id)
columns = [xparam,yparam1,yparam2, columns = [xparam,yparam1,yparam2,
'ftime','distance','fpace', 'ftime','distance','fpace',
'power','hr','spm', 'power','hr','spm','driveenergy',
'time','pace','workoutstate'] 'time','pace','workoutstate','time']
rowdata = dataprep.getsmallrowdata_db(columns,ids=[id],doclean=True) rowdata = dataprep.getsmallrowdata_db(columns,ids=[id],doclean=True)
rowdata.dropna(axis=1,how='all',inplace=True) rowdata.dropna(axis=1,how='all',inplace=True)
rowdata.dropna(axis=0,how='any',inplace=True)
row = Workout.objects.get(id=id) row = Workout.objects.get(id=id)
if rowdata.empty: if rowdata.empty:
@@ -1182,7 +1191,7 @@ def interactive_flex_chart2(id=0,promember=0,
try: try:
rowdata['x1'] = rowdata.ix[:,xparam] rowdata['x1'] = rowdata.ix[:,xparam]
except KeyError: except KeyError:
rowdata['x1'] = 0*rowdata.ix[:'time'] rowdata['x1'] = 0*rowdata.ix[:,1]
try: try:
rowdata['y1'] = rowdata.ix[:,yparam1] rowdata['y1'] = rowdata.ix[:,yparam1]
@@ -1211,7 +1220,10 @@ def interactive_flex_chart2(id=0,promember=0,
# average values # average values
if xparam != 'time': if xparam != 'time':
try:
x1mean = rowdata['x1'].mean() x1mean = rowdata['x1'].mean()
except TypeError:
x1mean = 0
else: else:
x1mean = 0 x1mean = 0
@@ -1277,8 +1289,8 @@ def interactive_flex_chart2(id=0,promember=0,
line_dash=[6,6],line_width=2) line_dash=[6,6],line_width=2)
y2means = y1means y2means = y1means
xlabel = Label(x=370,y=130,x_units='screen',y_units='screen', xlabel = Label(x=100,y=130,x_units='screen',y_units='screen',
text=xparam+": {x1mean:6.2f}".format(x1mean=x1mean), text=axlabels[xparam]+": {x1mean:6.2f}".format(x1mean=x1mean),
background_fill_alpha=.7, background_fill_alpha=.7,
text_color='green', text_color='green',
) )
@@ -1290,8 +1302,8 @@ def interactive_flex_chart2(id=0,promember=0,
plot.add_layout(y1means) plot.add_layout(y1means)
y1label = Label(x=370,y=100,x_units='screen',y_units='screen', y1label = Label(x=100,y=100,x_units='screen',y_units='screen',
text=yparam1+": {y1mean:6.2f}".format(y1mean=y1mean), text=axlabels[yparam1]+": {y1mean:6.2f}".format(y1mean=y1mean),
background_fill_alpha=.7, background_fill_alpha=.7,
text_color='blue', text_color='blue',
) )
@@ -1365,8 +1377,8 @@ def interactive_flex_chart2(id=0,promember=0,
plot.add_layout(y2means) plot.add_layout(y2means)
y2label = Label(x=370,y=70,x_units='screen',y_units='screen', y2label = Label(x=100,y=70,x_units='screen',y_units='screen',
text=yparam2+": {y2mean:6.2f}".format(y2mean=y2mean), text=axlabels[yparam2]+": {y2mean:6.2f}".format(y2mean=y2mean),
background_fill_alpha=.7, background_fill_alpha=.7,
text_color='red', text_color='red',
) )
@@ -1400,6 +1412,7 @@ def interactive_flex_chart2(id=0,promember=0,
var y1 = data['y1'] var y1 = data['y1']
var y2 = data['y2'] var y2 = data['y2']
var spm1 = data['spm'] var spm1 = data['spm']
var driveenergy1 = data['driveenergy']
var time1 = data['time'] var time1 = data['time']
var pace1 = data['pace'] var pace1 = data['pace']
var hr1 = data['hr'] var hr1 = data['hr']
@@ -1414,6 +1427,8 @@ def interactive_flex_chart2(id=0,promember=0,
var maxspm = maxspm.value var maxspm = maxspm.value
var mindist = mindist.value var mindist = mindist.value
var maxdist = maxdist.value var maxdist = maxdist.value
var minwork = minwork.value
var maxwork = maxwork.value
var xm = 0 var xm = 0
var ym1 = 0 var ym1 = 0
var ym2 = 0 var ym2 = 0
@@ -1438,6 +1453,7 @@ def interactive_flex_chart2(id=0,promember=0,
for (i=0; i<x1.length; i++) { for (i=0; i<x1.length; i++) {
if (spm1[i]>=minspm && spm1[i]<=maxspm) { if (spm1[i]>=minspm && spm1[i]<=maxspm) {
if (distance1[i]>=mindist && distance1[i]<=maxdist) { if (distance1[i]>=mindist && distance1[i]<=maxdist) {
if (driveenergy1[i]>=minwork && driveenergy1[i]<=maxwork) {
data2['x1'].push(x1[i]) data2['x1'].push(x1[i])
data2['y1'].push(y1[i]) data2['y1'].push(y1[i])
data2['y2'].push(y2[i]) data2['y2'].push(y2[i])
@@ -1452,7 +1468,7 @@ def interactive_flex_chart2(id=0,promember=0,
xm += x1[i] xm += x1[i]
ym1 += y1[i] ym1 += y1[i]
ym2 += y2[i] ym2 += y2[i]
}
} }
} }
} }
@@ -1483,6 +1499,15 @@ def interactive_flex_chart2(id=0,promember=0,
title="Max SPM",callback=callback) title="Max SPM",callback=callback)
callback.args["maxspm"] = slider_spm_max callback.args["maxspm"] = slider_spm_max
slider_work_min = Slider(start=0.0, end=1500,value=0.0, step=10,
title="Min Work per Stroke",callback=callback)
callback.args["minwork"] = slider_work_min
slider_work_max = Slider(start=0.0, end=1500,value=1500.0, step=10,
title="Max Work per Stroke",callback=callback)
callback.args["maxwork"] = slider_work_max
distmax = 100+100*int(rowdata['distance'].max()/100.) distmax = 100+100*int(rowdata['distance'].max()/100.)
slider_dist_min = Slider(start=0,end=distmax,value=0,step=1, slider_dist_min = Slider(start=0,end=distmax,value=0,step=1,
@@ -1498,6 +1523,8 @@ def interactive_flex_chart2(id=0,promember=0,
slider_spm_max, slider_spm_max,
slider_dist_min, slider_dist_min,
slider_dist_max, slider_dist_max,
slider_work_min,
slider_work_max,
], ],
), ),
plot]) plot])
@@ -1512,7 +1539,9 @@ def interactive_flex_chart2(id=0,promember=0,
def interactive_bar_chart(id=0,promember=0): def interactive_bar_chart(id=0,promember=0):
# check if valid ID exists (workout exists) # check if valid ID exists (workout exists)
rowdata,row = dataprep.getrowdata_db(id=id) rowdata,row = dataprep.getrowdata_db(id=id)
rowdata.dropna(axis=1,how='all',inplace=True)
rowdata.dropna(axis=0,how='any',inplace=True) rowdata.dropna(axis=0,how='any',inplace=True)
if rowdata.empty: if rowdata.empty:
return "","No Valid Data Available" return "","No Valid Data Available"
@@ -1610,7 +1639,9 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line',
'workoutid'] 'workoutid']
datadf = dataprep.getsmallrowdata_db(columns,ids=ids) datadf = dataprep.getsmallrowdata_db(columns,ids=ids)
datadf.dropna(axis=1,how='all',inplace=True)
datadf.dropna(axis=0,how='any',inplace=True) datadf.dropna(axis=0,how='any',inplace=True)
tseconds = datadf.ix[:,'time'] tseconds = datadf.ix[:,'time']
yparamname = axlabels[yparam] yparamname = axlabels[yparam]
@@ -1672,12 +1703,15 @@ def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line',
group = datadf[datadf['workoutid']==int(id)].copy() group = datadf[datadf['workoutid']==int(id)].copy()
group.sort_values(by='time',ascending=True,inplace=True) group.sort_values(by='time',ascending=True,inplace=True)
group['x'] = group[xparam] group['x'] = group[xparam]
try:
group['y'] = group[yparam] group['y'] = group[yparam]
except KeyError:
group['y'] = 0.0*group['x']
ymean = group['y'].mean() ymean = group['y'].mean()
ylabel = Label(x=100,y=70+20*cntr, ylabel = Label(x=100,y=70+20*cntr,
x_units='screen',y_units='screen', x_units='screen',y_units='screen',
text=yparam+": {ymean:6.2f}".format(ymean=ymean), text=axlabels[yparam]+": {ymean:6.2f}".format(ymean=ymean),
background_fill_alpha=.7, background_fill_alpha=.7,
text_color=color, text_color=color,
) )
@@ -1772,8 +1806,11 @@ def interactive_comparison_chart(id1=0,id2=0,xparam='distance',yparam='spm',
rowdata1[n].fillna(value=0,inplace=True) rowdata1[n].fillna(value=0,inplace=True)
rowdata2[n].fillna(value=0,inplace=True) rowdata2[n].fillna(value=0,inplace=True)
rowdata1.dropna(axis=1,how='all',inplace=True)
rowdata1.dropna(axis=0,how='any',inplace=True) rowdata1.dropna(axis=0,how='any',inplace=True)
rowdata2.dropna(axis=1,how='all',inplace=True)
rowdata2.dropna(axis=0,how='any',inplace=True) rowdata2.dropna(axis=0,how='any',inplace=True)
row1 = Workout.objects.get(id=id1) row1 = Workout.objects.get(id=id1)
row2 = Workout.objects.get(id=id2) row2 = Workout.objects.get(id=id2)
@@ -1787,11 +1824,14 @@ def interactive_comparison_chart(id1=0,id2=0,xparam='distance',yparam='spm',
else: else:
rowdata2.sort_values(by='time',ascending=True,inplace=True) rowdata2.sort_values(by='time',ascending=True,inplace=True)
try:
x1 = rowdata1.ix[:,xparam] x1 = rowdata1.ix[:,xparam]
x2 = rowdata2.ix[:,xparam] x2 = rowdata2.ix[:,xparam]
y1 = rowdata1.ix[:,yparam] y1 = rowdata1.ix[:,yparam]
y2 = rowdata2.ix[:,yparam] y2 = rowdata2.ix[:,yparam]
except KeyError:
return "","No valid Data Available"
x_axis_type = 'linear' x_axis_type = 'linear'
y_axis_type = 'linear' y_axis_type = 'linear'
@@ -1914,7 +1954,7 @@ def interactive_comparison_chart(id1=0,id2=0,xparam='distance',yparam='spm',
plot.title.text = row1.name+' vs '+row2.name plot.title.text = row1.name+' vs '+row2.name
plot.title.text_font_size=value("1.2em") plot.title.text_font_size=value("1.2em")
plot.xaxis.axis_label = axlabels[xparam] plot.xaxis.axis_label = axlabels[xparam]
plot.yaxis.axis_label = axlabels[yparam]
if xparam == 'time': if xparam == 'time':
plot.xaxis[0].formatter = DatetimeTickFormatter( plot.xaxis[0].formatter = DatetimeTickFormatter(
@@ -1943,7 +1983,9 @@ def interactive_comparison_chart(id1=0,id2=0,xparam='distance',yparam='spm',
def interactive_otw_advanced_pace_chart(id=0,promember=0): def interactive_otw_advanced_pace_chart(id=0,promember=0):
# check if valid ID exists (workout exists) # check if valid ID exists (workout exists)
rowdata,row = dataprep.getrowdata_db(id=id) rowdata,row = dataprep.getrowdata_db(id=id)
rowdata.dropna(axis=1,how='all',inplace=True)
rowdata.dropna(axis=0,how='any',inplace=True) rowdata.dropna(axis=0,how='any',inplace=True)
if rowdata.empty: if rowdata.empty:
return "","No Valid Data Available" return "","No Valid Data Available"

View File

@@ -393,6 +393,12 @@ class Workout(models.Model):
maxhr = models.IntegerField(blank=True,null=True) maxhr = models.IntegerField(blank=True,null=True)
uploadedtostrava = models.IntegerField(default=0) uploadedtostrava = models.IntegerField(default=0)
uploadedtosporttracks = models.IntegerField(default=0) uploadedtosporttracks = models.IntegerField(default=0)
# empower stuff
inboard = models.FloatField(default=0.88)
oarlength = models.FloatField(default=2.89)
notes = models.CharField(blank=True,null=True,max_length=1000) notes = models.CharField(blank=True,null=True,max_length=1000)
summary = models.TextField(blank=True) summary = models.TextField(blank=True)
privacy = models.CharField(default='visible',max_length=30, privacy = models.CharField(default='visible',max_length=30,
@@ -492,6 +498,8 @@ class StrokeData(models.Model):
wash = models.FloatField(default=0,null=True,verbose_name='Wash') wash = models.FloatField(default=0,null=True,verbose_name='Wash')
peakforceangle = models.FloatField(default=0,null=True,verbose_name='Peak Force Angle') peakforceangle = models.FloatField(default=0,null=True,verbose_name='Peak Force Angle')
rhythm = models.FloatField(default=1.0,null=True,verbose_name='Rhythm') rhythm = models.FloatField(default=1.0,null=True,verbose_name='Rhythm')
totalangle = models.FloatField(default=0.0,null=True,verbose_name='Total Stroke Length (deg)')
effectiveangle = models.FloatField(default=0.0,null=True,verbose_name='Effective Stroke Length (deg)')
# A wrapper around the png files # A wrapper around the png files
class GraphImage(models.Model): class GraphImage(models.Model):

View File

@@ -6,14 +6,14 @@
{% block content %} {% block content %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
{{ interactiveplot |safe }} {{ interactiveplot |safe }}
<script> <script>
// Set things up to resize the plot on a window resize. You can play with // Set things up to resize the plot on a window resize. You can play with
// the arguments of resize_width_height() to change the plot's behavior. // the arguments of resize_width_height() to change the plot's behavior.
var plot_resize_setup = function () { var plot_resize_setup = function () {
@@ -27,23 +27,23 @@
plotresizer(); plotresizer();
}; };
window.addEventListener('load', plot_resize_setup); window.addEventListener('load', plot_resize_setup);
</script> </script>
<style> <style>
/* Need this to get the page in "desktop mode"; not having an infinite height.*/ /* Need this to get the page in "desktop mode"; not having an infinite height.*/
html, body {height: 100%; margin:5px;} html, body {height: 100%; margin:5px;}
</style> </style>
<div id="navigation" class="grid_12 alpha"> <div id="navigation" class="grid_12 alpha">
{% if user.is_authenticated and mayedit %} {% if user.is_authenticated and mayedit %}
<div class="grid_2 alpha"> <div class="grid_2 alpha">
<p> <p>
<a class="button gray small" href="/rowers/workout/{{ id }}/edit">Edit Workout</a> <a class="button gray small" href="/rowers/workout/{{ id }}/edit">Edit Workout</a>
</p> </p>
</div> </div>
<div class="grid_2 suffix_8 omega"> <div class="grid_2 suffix_8 omega">
<p> <p>
<a class="button gray small" href="/rowers/workout/compare/{{ id }}/advanced">Advanced Edit</a> <a class="button gray small" href="/rowers/workout/compare/{{ id }}/advanced">Advanced Edit</a>
</p> </p>
</div> </div>
{% endif %} {% endif %}
@@ -68,60 +68,50 @@
<p>&nbsp;</p> <p>&nbsp;</p>
<div id="plotbuttons" class="grid_12 alpha"> <div id="plotbuttons" class="grid_12 alpha">
<div id="x-axis" class="grid_6 alpha"> <div id="x-axis" class="grid_6 alpha">
<div class="grid_2 alpha dropdown"> <div class="grid_2 alpha dropdown">
<button class="grid_2 alpha button blue small dropbtn">X-axis</button> <button class="grid_2 alpha button blue small dropbtn">X-axis</button>
<div class="dropdown-content"> <div class="dropdown-content">
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/time/{{ yparam }}/{{ plottype }}">Time</a> {% for key, value in axchoicesbasic.items %}
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/distance/{{ yparam }}/{{ plottype }}">Distance</a> {% if key != 'None' %}
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1}}/{{ id2 }}/{{ key }}/{{ yparam }}/{{ plottype }}">{{ value }}</a>
{% endif %}
{% endfor %}
{% if promember %} {% if promember %}
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/power/{{ yparam }}/scatter">Power</a> {% for key, value in axchoicespro.items %}
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/hr/{{ yparam }}/scatter">HR</a> <a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ key }}/{{ yparam }}/{{ plottype }}">{{ value }}</a>
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/spm/{{ yparam }}/scatter">SPM</a> {% endfor %}
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/peakforce/{{ yparam }}/scatter">Peak Force</a>
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/averageforce/{{ yparam }}/scatter">Average Force</a>
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/forceratio/{{ yparam }}/scatter">Average/Peak Force Ratio</a>
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/drivelength/{{ yparam }}/scatter">Drive Length</a>
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/driveenergy/{{ yparam }}/scatter">Work per Stroke</a>
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/drivespeed/{{ yparam }}/scatter">Drive Speed</a>
{% else %} {% else %}
<a class="button rosy small" href="/rowers/promembership">Power (Pro)</a> {% for key, value in axchoicespro.items %}
<a class="button rosy small" href="/rowers/promembership">HR (Pro)</a> <a class="button rosy small" href="/rowers/promembership">{{ value }}</a>
<a class="button rosy small" href="/rowers/promembership">SPM (Pro)</a> {% endfor %}
<a class="button rosy small" href="/rowers/promembership">Peak Force (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Average/Peak Force Ratio (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Drive Length (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Work per Stroke (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Drive Speed (Pro)</a>
{% endif %} {% endif %}
</div> </div>
</div> </div>
<div class="grid_2 suffix_2 omega dropdown"> <div class="grid_2 suffix_2 omega dropdown">
<button class="grid_2 alpha button blue small dropbtn">Y-axis</button> <button class="grid_2 alpha button blue small dropbtn">Y-axis</button>
<div class="dropdown-content"> <div class="dropdown-content">
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/pace/{{ plottype }}">Pace</a> {% for key, value in axchoicesbasic.items %}
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/hr/{{ plottype }}">HR</a> {% if key not in noylist and key != 'None' %}
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/spm/{{ plottype }}">SPM</a> <a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/{{ key }}/{{ plottype }}">{{ value }}</a>
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/power/{{ plottype }}">Power</a>
{% if promember %}
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/peakforce/{{ plottype }}">Peak Force</a>
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/averageforce/{{ plottype }}">Average Force</a>
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/forceratio/{{ plottype }}">Average/Peak Force Ratio</a>
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/drivelength/{{ plottype }}">Drive Length</a>
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/driveenergy/{{ plottype }}">Work per Stroke</a>
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/drivespeed/{{ plottype }}">Drive Speed</a>
{% else %}
<a class="button rosy small" href="/rowers/promembership">Peak Force (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Average/Peak Force Ratio (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Drive Length (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Work per Stroke (Pro)</a>
{% endif %} {% endif %}
{% endfor %}
{% if promember %}
{% for key, value in axchoicespro.items %}
{% if key not in noylist %}
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/{{ key }}/{{ plottype }}">{{ value }}</a>
{% endif %}
{% endfor %}
{% else %}
{% for key, value in axchoicespro.items %}
{% if key not in noylist %}
<a class="button rosy small" href="/rowers/promembership">{{ value }} (Pro)</a>
{% endif %}
{% endfor %}
{% endif %}
</div> </div>
</div> </div>

View File

@@ -47,13 +47,13 @@
<ul> <ul>
<li>Advantages <li>Advantages
<ul> <ul>
<li>It may take up to five minutes for the workout to show up <li>It's a simple process, which can be automated.</li>
on the site.</li>
</ul> </ul>
</li> </li>
<li>Disadvantages <li>Disadvantages
<ul> <ul>
<li>It's a simple process, which can be automated.</li> <li>It may take up to five minutes for the workout to show up
on the site.</li>
</ul> </ul>
</li> </li>
</ul> </ul>
@@ -86,7 +86,7 @@
<li>The API is not stable and not fully tested yet.</li> <li>The API is not stable and not fully tested yet.</li>
<li>You need to register your app with us. We can revoke your <li>You need to register your app with us. We can revoke your
permissions if you misuse them.</li> permissions if you misuse them.</li>
<li>The first time user must grant permissions to your app.</li> <li>The user user must grant permissions to your app.</li>
<li>You need to manage authorization tokens.</li> <li>You need to manage authorization tokens.</li>
</ul> </ul>
</li> </li>

View File

@@ -51,121 +51,69 @@
<div class="grid_2 alpha dropdown"> <div class="grid_2 alpha dropdown">
<button class="grid_2 alpha button blue small dropbtn">X-axis</button> <button class="grid_2 alpha button blue small dropbtn">X-axis</button>
<div class="dropdown-content"> <div class="dropdown-content">
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/time/{{ yparam1 }}/{{ yparam2 }}/{{ plottype }}">Time</a> {% for key, value in axchoicesbasic.items %}
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/distance/{{ yparam1 }}/{{ yparam2 }}/{{ plottype }}">Distance</a> {% if key != 'None' %}
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/{{ key }}/{{ yparam1 }}/{{ yparam2 }}/{{ plottype }}">{{ value }}</a>
{% endif %}
{% endfor %}
{% if promember %} {% if promember %}
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/power/{{ yparam1 }}/{{ yparam2 }}/scatter">Power</a> {% for key, value in axchoicespro.items %}
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/hr/{{ yparam1 }}/{{ yparam2 }}/scatter">HR</a> <a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/{{ key }}/{{ yparam1 }}/{{ yparam2 }}/scatter">{{ value }}</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/spm/{{ yparam1 }}/{{ yparam2 }}/scatter">SPM</a> {% endfor %}
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/peakforce/{{ yparam1 }}/{{ yparam2 }}/scatter">Peak Force</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/averageforce/{{ yparam1 }}/{{ yparam2 }}/scatter">Average Force</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/forceratio/{{ yparam1 }}/{{ yparam2 }}/scatter">Average/Peak force ratio</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/drivelength/{{ yparam1 }}/{{ yparam2 }}/scatter">Drive Length</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/driveenergy/{{ yparam1 }}/{{ yparam2 }}/scatter">Work per Stroke</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/drivespeed/{{ yparam1 }}/{{ yparam2 }}/scatter">Drive Speed</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/catch/{{ yparam1 }}/{{ yparam2 }}/scatter">Catch Angle</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/finish/{{ yparam1 }}/{{ yparam2 }}/scatter">Finish Angle</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/slip/{{ yparam1 }}/{{ yparam2 }}/scatter">Slip</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/wash/{{ yparam1 }}/{{ yparam2 }}/scatter">Wash</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/peakforceangle/{{ yparam1 }}/{{ yparam2 }}/scatter">Peak Force Angle</a>
{% else %} {% else %}
<a class="button rosy small" href="/rowers/promembership">Power (Pro)</a> {% for key, value in axchoicespro.items %}
<a class="button rosy small" href="/rowers/promembership">HR (Pro)</a> <a class="button rosy small" href="/rowers/promembership">{{ value }}</a>
<a class="button rosy small" href="/rowers/promembership">SPM (Pro)</a> {% endfor %}
<a class="button rosy small" href="/rowers/promembership">Peak Force (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Average/Peak Force Ratio (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Drive Length (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Work per Stroke (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Drive Speed (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Catch Angle (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Finish Angle (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Slip (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Wash (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Peak Force Angle (Pro)</a>
{% endif %} {% endif %}
</div> </div>
</div> </div>
<div class="grid_2 dropdown"> <div id="left-y" class="grid_2 dropdown">
<button class="grid_2 alpha button blue small dropbtn">Left</button> <button class="grid_2 alpha button blue small dropbtn">Left</button>
<div class="dropdown-content"> <div class="dropdown-content">
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/pace/{{ yparam2 }}/{{ plottype }}">Pace</a> {% for key, value in axchoicesbasic.items %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/hr/{{ yparam2 }}/{{ plottype }}">HR</a> {% if key not in noylist and key != 'None' %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/spm/{{ yparam2 }}/{{ plottype }}">SPM</a> <a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ key }}/{{ yparam2 }}/{{ plottype }}">{{ value }}</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/power/{{ yparam2 }}/{{ plottype }}">Power</a> {% endif %}
{% endfor %}
{% if promember %} {% if promember %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/peakforce/{{ yparam2 }}/{{ plottype }}">Peak Force</a> {% for key, value in axchoicespro.items %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/averageforce/{{ yparam2 }}/{{ plottype }}">Average Force</a> {% if key not in noylist %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/forceratio/{{ yparam2 }}/{{ plottype }}">Average/Peak Force Ratio</a> <a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ key }}/{{ yparam2 }}/{{ plottype }}">{{ value }}</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/drivelength/{{ yparam2 }}/{{ plottype }}">Drive Length</a> {% endif %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/driveenergy/{{ yparam2 }}/{{ plottype }}">Work per Stroke</a> {% endfor %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/drivespeed/{{ yparam2 }}/{{ plottype }}">Drive Speed</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/rhythm/{{ yparam2 }}/{{ plottype }}">Drive Rhythm</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/catch/{{ yparam2 }}/{{ plottype }}">Catch Angle</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/finish/{{ yparam2 }}/{{ plottype }}">Finish Angle</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/slip/{{ yparam2 }}/{{ plottype }}">Slip</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/wash/{{ yparam2 }}/{{ plottype }}">Wash</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/peakforceangle/{{ yparam2 }}/{{ plottype }}">Peak Force Angle</a>
{% else %} {% else %}
<a class="button rosy small" href="/rowers/promembership">Peak Force (Pro)</a> {% for key, value in axchoicespro.items %}
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a> {% if key not in noylist %}
<a class="button rosy small" href="/rowers/promembership">Average/Peak Force Ratio (Pro)</a> <a class="button rosy small" href="/rowers/promembership">{{ value }} (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Drive Length (Pro)</a> {% endif %}
<a class="button rosy small" href="/rowers/promembership">Work per Stroke (Pro)</a> {% endfor %}
<a class="button rosy small" href="/rowers/promembership">Drive Speed (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Drive Rhythm (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Catch Angle (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Finish Angle(Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Wash (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Slip (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Peak Force Angle (Pro)</a>
{% endif %} {% endif %}
</div> </div>
</div> </div>
<div class="grid_2 dropdown omega"> <div id="right-y" class="grid_2 dropdown omega">
<button class="grid_2 alpha button blue small dropbtn">Right</button> <button class="grid_2 alpha button blue small dropbtn">Right</button>
<div class="dropdown-content"> <div class="dropdown-content">
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/hr/{{ plottype }}">HR</a> {% for key, value in axchoicesbasic.items %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/spm/{{ plottype }}">SPM</a> {% if key not in noylist %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/power/{{ plottype }}">Power</a> <a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/{{ key }}/{{ plottype }}">{{ value }}</a>
{% if promember %} {% endif %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/peakforce/{{ plottype }}">Peak Force</a> {% endfor %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/averageforce/{{ plottype }}">Average Force</a> {% if promember %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/forceratio/{{ plottype }}">Average/Peak Force Ratio</a> {% for key, value in axchoicespro.items %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/drivelength/{{ plottype }}">Drive Length</a> {% if key not in noylist %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/driveenergy/{{ plottype }}">Work per Stroke</a> <a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/{{ key }}/{{ plottype }}">{{ value }}</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/drivespeed/{{ plottype }}">Drive Speed</a> {% endif %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/rhythm/{{ plottype }}">Drive Rhythm</a> {% endfor %}
{% else %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/catch/{{ plottype }}">Catch Angle</a> {% for key, value in axchoicespro.items %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/finish/{{ plottype }}">Finish Angle</a> {% if key not in noylist %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/slip/{{ plottype }}">Slip</a> <a class="button rosy small" href="/rowers/promembership">{{ value }} (Pro)</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/wash/{{ plottype }}">Wash</a> {% endif %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/peakforceangle/{{ plottype }}">Peak Force Angle</a> {% endfor %}
{% else %}
<a class="button rosy small" href="/rowers/promembership">Peak Force (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Average/Peak Force Ratio (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Drive Length (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Work per Stroke (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Drive Speed (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Drive Rhythm (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Catch Angle (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Finish Angle (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Slip (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Wash (Pro)</a>
<a class="button rosy small" href="/rowers/promembership">Peak Force Angle (Pro)</a>
{% endif %} {% endif %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/None/{{ plottype }}">None</a>
</div> </div>
</div> </div>

View File

@@ -6,10 +6,21 @@
{% block content %} {% block content %}
<h1>{{ workout.name }}</h1> <h1>{{ workout.name }}</h1>
<div class="grid_2 suffix_10 alpha"> <div class="grid_2 alpha">
{% if user.is_authenticated and user == rower.user %}
<p>
<a class="button gray small" href="/rowers/workout/{{ graph.workout.id }}/edit">Edit Workout</a>
</p>
{% else %}
<p>
<a class="button gray small" href="/rowers/workout/{{ graph.workout.id }}">See Workout</a>
</p>
{% endif %}
</div>
<div class="grid_2 suffix_8 omega">
{% if user.is_authenticated and user == rower.user %} {% if user.is_authenticated and user == rower.user %}
<p> <p>
<a class="button red small" href="/rowers/graph/{{ graph.id }}/deleteconfirm">Delete</a> <a class="button red small" href="/rowers/graph/{{ graph.id }}/deleteconfirm">Delete Chart</a>
</p> </p>
{% else %} {% else %}
<p>&nbsp;</p> <p>&nbsp;</p>

View File

@@ -231,7 +231,6 @@ def rower_register_view(request):
theuser.email = email theuser.email = email
theuser.save() theuser.save()
therower = Rower(user=theuser) therower = Rower(user=theuser)
therower.save() therower.save()
@@ -3154,10 +3153,6 @@ def workout_stats_view(request,id=0,message="",successmessage=""):
fieldlist,fielddict = dataprep.getstatsfields() fieldlist,fielddict = dataprep.getstatsfields()
fielddict.pop('workoutstate') fielddict.pop('workoutstate')
print "aap"
print datadf['catch'].mean()
print "noot"
for field,verbosename in fielddict.iteritems(): for field,verbosename in fielddict.iteritems():
thedict = { thedict = {
@@ -3257,6 +3252,10 @@ def workout_comparison_view(request,id1=0,id2=0,xparam='distance',yparam='spm'):
promember=promember) promember=promember)
script = res[0] script = res[0]
div = res[1] div = res[1]
axchoicesbasic = {ax[0]:ax[1] for ax in axes if ax[4]=='basic'}
axchoicespro = {ax[0]:ax[1] for ax in axes if ax[4]=='pro'}
noylist = ["time","distance"]
axchoicesbasic.pop("cumdist") axchoicesbasic.pop("cumdist")
return render(request, return render(request,
@@ -3264,6 +3263,9 @@ def workout_comparison_view(request,id1=0,id2=0,xparam='distance',yparam='spm'):
{'interactiveplot':script, {'interactiveplot':script,
'the_div':div, 'the_div':div,
'id1':id1, 'id1':id1,
'id2':id2,
'axchoicesbasic':axchoicesbasic,
'axchoicespro':axchoicespro,
'noylist':noylist, 'noylist':noylist,
'xparam':xparam, 'xparam':xparam,
'yparam':yparam, 'yparam':yparam,
@@ -3287,12 +3289,33 @@ def workout_comparison_view2(request,id1=0,id2=0,xparam='distance',
promember=promember,plottype=plottype) promember=promember,plottype=plottype)
script = res[0] script = res[0]
div = res[1] div = res[1]
axchoicesbasic = {ax[0]:ax[1] for ax in axes if ax[4]=='basic'}
axchoicespro = {ax[0]:ax[1] for ax in axes if ax[4]=='pro'}
noylist = ["time","distance"]
axchoicesbasic.pop("cumdist")
row1 = Workout.objects.get(id=id1)
row2 = Workout.objects.get(id=id2)
if row1.workouttype != 'water' or row2.workouttype != 'water':
axchoicespro.pop('slip')
axchoicespro.pop('wash')
axchoicespro.pop('catch')
axchoicespro.pop('finish')
axchoicespro.pop('totalangle')
axchoicespro.pop('effectiveangle')
axchoicespro.pop('peakforceangle')
return render(request, return render(request,
'comparisonchart2.html', 'comparisonchart2.html',
{'interactiveplot':script, {'interactiveplot':script,
'the_div':div, 'the_div':div,
'id1':id1, 'id1':id1,
'id2':id2,
'axchoicesbasic':axchoicesbasic,
'axchoicespro':axchoicespro,
'noylist':noylist, 'noylist':noylist,
'xparam':xparam, 'xparam':xparam,
'yparam':yparam, 'yparam':yparam,
@@ -3433,6 +3456,12 @@ def workout_flexchart3_view(request,*args,**kwargs):
# div = res[1] # div = res[1]
# js_resources = res[2] # js_resources = res[2]
# css_resources = res[3] # css_resources = res[3]
axchoicesbasic = {ax[0]:ax[1] for ax in axes if ax[4]=='basic'}
axchoicespro = {ax[0]:ax[1] for ax in axes if ax[4]=='pro'}
noylist = ["time","distance"]
axchoicesbasic.pop("cumdist")
if row.workouttype == 'water': if row.workouttype == 'water':
return render(request, return render(request,
@@ -3447,13 +3476,25 @@ def workout_flexchart3_view(request,*args,**kwargs):
'yparam2':yparam2, 'yparam2':yparam2,
'plottype':plottype, 'plottype':plottype,
'mayedit':mayedit, 'mayedit':mayedit,
'promember':promember,
'axchoicesbasic':axchoicesbasic,
'axchoicespro':axchoicespro,
'noylist':noylist, 'noylist':noylist,
'workstrokesonly': not workstrokesonly, 'workstrokesonly': not workstrokesonly,
'favoritenr':favoritenr, 'favoritenr':favoritenr,
'maxfav':maxfav, 'maxfav':maxfav,
}) })
else:
axchoicespro.pop('slip')
axchoicespro.pop('wash')
axchoicespro.pop('catch')
axchoicespro.pop('finish')
axchoicespro.pop('totalangle')
axchoicespro.pop('effectiveangle')
axchoicespro.pop('peakforceangle')
return render(request, return render(request,
'flexchart3otw.html', 'flexchart3otw.html',
{'the_script':script, {'the_script':script,
'the_div':div, 'the_div':div,
@@ -3463,6 +3504,9 @@ def workout_flexchart3_view(request,*args,**kwargs):
'xparam':xparam, 'xparam':xparam,
'yparam1':yparam1, 'yparam1':yparam1,
'yparam2':yparam2, 'yparam2':yparam2,
'plottype':plottype,
'axchoicesbasic':axchoicesbasic,
'axchoicespro':axchoicespro,
'noylist':noylist, 'noylist':noylist,
'mayedit':mayedit, 'mayedit':mayedit,
'promember':promember, 'promember':promember,
@@ -3693,12 +3737,26 @@ def workout_comment_view(request,id=0):
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
form = WorkoutCommentForm() form = WorkoutCommentForm()
g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
if (len(g)<=3): if (len(g)<=3):
return render(request, return render(request,
'workout_comments.html', 'workout_comments.html',
{'workout':w,
'graphs1':g[0:3], 'graphs1':g[0:3],
'comments':comments, 'comments':comments,
'form':form, 'form':form,
})
else:
return render(request,
'workout_comments.html',
{'workout':w,
'graphs1':g[0:3],
'graphs1':g[3:6],
'comments':comments,
'form':form,
})
# The basic edit page # The basic edit page