2466 lines
62 KiB
Python
2466 lines
62 KiB
Python
from rowers.models import Workout, User, Rower, WorkoutForm,RowerForm,GraphImage
|
|
from rowingdata import rower as rrower
|
|
from rowingdata import main as rmain
|
|
from rowingdata import cumcpdata,histodata
|
|
|
|
from rowingdata import rowingdata as rrdata
|
|
from rowingdata import TCXParser,RowProParser,ErgDataParser
|
|
from rowingdata import painsledDesktopParser,speedcoachParser,ErgStickParser
|
|
|
|
from bokeh.plotting import figure, ColumnDataSource, Figure,curdoc
|
|
from bokeh.models import CustomJS,Slider
|
|
from bokeh.charts import Histogram
|
|
from bokeh.resources import CDN,INLINE
|
|
from bokeh.embed import components
|
|
from bokeh.layouts import layout,widgetbox
|
|
from bokeh.layouts import row as layoutrow
|
|
from bokeh.layouts import column as layoutcolumn
|
|
from bokeh.models import LinearAxis,LogAxis,Range1d,DatetimeTickFormatter,HoverTool
|
|
from bokeh.io import output_file, show
|
|
from bokeh.models import (
|
|
GMapPlot, GMapOptions, ColumnDataSource, Circle,
|
|
DataRange1d, PanTool, WheelZoomTool, BoxSelectTool,
|
|
SaveTool, ResizeTool, ResetTool, TapTool,CrosshairTool,BoxZoomTool,
|
|
Span,
|
|
)
|
|
#from bokeh.models.widgets import Slider, Select, TextInput
|
|
from bokeh.core.properties import value
|
|
|
|
from collections import OrderedDict
|
|
from django.conf import settings
|
|
|
|
import datetime
|
|
import math
|
|
import numpy as np
|
|
import pandas as pd
|
|
from pytz import timezone as tz,utc
|
|
from django.utils.timezone import get_current_timezone
|
|
from django.utils.timezone import activate
|
|
activate(settings.TIME_ZONE)
|
|
thetimezone = get_current_timezone()
|
|
|
|
from scipy.stats import linregress
|
|
from scipy import optimize
|
|
from scipy.signal import savgol_filter
|
|
|
|
import stravastuff
|
|
|
|
def rdata(file,rower=rrower()):
|
|
try:
|
|
res = rrdata(file,rower=rower)
|
|
except IOError:
|
|
res = 0
|
|
|
|
return res
|
|
|
|
|
|
def tailwind(bearing,vwind,winddir):
|
|
""" Calculates head-on head/tailwind in direction of rowing
|
|
|
|
positive numbers are tail wind
|
|
|
|
"""
|
|
|
|
b = np.radians(bearing)
|
|
w = np.radians(winddir)
|
|
|
|
vtail = -vwind*np.cos(w-b)
|
|
|
|
return vtail
|
|
|
|
def niceformat(values):
|
|
out = []
|
|
for v in values:
|
|
formattedv = v.strftime("%H:%M:%S")
|
|
out.append(formattedv)
|
|
|
|
return out
|
|
|
|
def nicepaceformat(values):
|
|
out = []
|
|
for v in values:
|
|
formattedv = v.strftime("%M:%S.%f")
|
|
out.append(formattedv[:-5])
|
|
|
|
|
|
return out
|
|
|
|
def get_datetimes(seconds,tzinfo=0):
|
|
out = []
|
|
for second in seconds:
|
|
if (second<=0) or (second>1e9):
|
|
days = 0
|
|
hours = 0
|
|
minutes=0
|
|
sec=0
|
|
microsecond = 0
|
|
elif math.isnan(second):
|
|
days = 0
|
|
hours = 0
|
|
minutes=0
|
|
sec=0
|
|
microsecond = 0
|
|
else:
|
|
days = int(second/(24.*3600.)) % (24*3600)
|
|
hours = int((second-24.*3600.*days)/3600.) % 24
|
|
|
|
minutes = int((second-3600.*(hours+24.*days))/60.) % 60
|
|
sec = int(second-3600.*(hours+24.*days)-60.*minutes) % 60
|
|
microsecond = int(1.0e6*(second-3600.*(hours+24.*days)-60.*minutes-sec))
|
|
dt = datetime.datetime(2016,5,1,hours,minutes,sec,microsecond)
|
|
|
|
if tzinfo:
|
|
dt = thetimezone.localize(dt, is_dst=True)
|
|
# this is ugly
|
|
# dt = dt+datetime.timedelta(hours=1)
|
|
out.append(dt)
|
|
|
|
|
|
|
|
return out
|
|
|
|
def interactive_histoall(theworkouts):
|
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
|
|
|
|
therows = []
|
|
for workout in theworkouts:
|
|
f1 = workout.csvfilename
|
|
rowdata = rdata(f1)
|
|
if rowdata != 0:
|
|
therows.append(rowdata)
|
|
|
|
histopwr = histodata(therows)
|
|
# throw out nans
|
|
histopwr = histopwr[~np.isinf(histopwr)]
|
|
histopwr = histopwr[histopwr > 25]
|
|
|
|
plot = Figure(tools=TOOLS,plot_width=900,
|
|
toolbar_sticky=False,
|
|
toolbar_location="above"
|
|
)
|
|
|
|
hist,edges = np.histogram(histopwr,bins=150)
|
|
|
|
histsum = np.cumsum(hist)
|
|
histsum = 100.*histsum/max(histsum)
|
|
|
|
hist_norm = 100.*hist/float(hist.sum())
|
|
|
|
source = ColumnDataSource(
|
|
data = dict(
|
|
left = edges[:-1],
|
|
right = edges[1:],
|
|
histsum = histsum,
|
|
hist_norm = hist_norm,
|
|
)
|
|
)
|
|
|
|
|
|
# plot.quad(top='hist_norm',bottom=0,left=edges[:-1],right=edges[1:])
|
|
plot.quad(top='hist_norm',bottom=0,left='left',right='right',source=source)
|
|
|
|
plot.xaxis.axis_label = "Power (W)"
|
|
plot.yaxis.axis_label = "% of strokes"
|
|
plot.y_range = Range1d(0,1.05*max(hist_norm))
|
|
|
|
hover = plot.select(dict(type=HoverTool))
|
|
|
|
hover.tooltips = OrderedDict([
|
|
('Power(W)','@left{int}'),
|
|
('% of strokes','@hist_norm'),
|
|
('Cumulative %','@histsum{int}'),
|
|
])
|
|
|
|
hover.mode = 'mouse'
|
|
|
|
plot.extra_y_ranges = {"fraction": Range1d(start=0,end=105)}
|
|
plot.line('right','histsum',source=source,color="red",
|
|
y_range_name="fraction")
|
|
plot.add_layout(LinearAxis(y_range_name="fraction",
|
|
axis_label="Cumulative % of strokes"),'right')
|
|
|
|
script, div = components(plot)
|
|
return [script,div]
|
|
|
|
def googlemap_chart(lat,lon,name=""):
|
|
# plot tools
|
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,resize,crosshair'
|
|
|
|
map_options = GMapOptions(lat = lat.mean(),lng=lon.mean(),
|
|
map_type="roadmap",zoom=11)
|
|
|
|
plot = GMapPlot(
|
|
x_range=DataRange1d(), y_range=DataRange1d(),
|
|
map_options=map_options,
|
|
api_key = "AIzaSyAgu1w9QSthaGPMLp8y9JedPoMc9sfEgJ8",
|
|
plot_width=400,plot_height=400,
|
|
toolbar_sticky=False,
|
|
|
|
)
|
|
|
|
|
|
source = ColumnDataSource(
|
|
data = dict(
|
|
lat=lat,
|
|
lon=lon,
|
|
)
|
|
)
|
|
|
|
circle = Circle(x="lon",y="lat",size=5,fill_color="blue",
|
|
fill_alpha=0.2,line_color=None)
|
|
plot.add_glyph(source,circle)
|
|
plot.add_tools(PanTool(), WheelZoomTool(),
|
|
SaveTool(), ResizeTool(), ResetTool(),
|
|
TapTool(),CrosshairTool(),
|
|
)
|
|
plot.title.text = name
|
|
plot.title.text_font="1.0em"
|
|
script, div = components(plot)
|
|
|
|
return [script,div]
|
|
|
|
|
|
def interactive_cpchart(thedistances,thesecs,theavpower,
|
|
theworkouts,promember=0):
|
|
|
|
message = 0
|
|
# plot tools
|
|
if (promember==1):
|
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
|
|
else:
|
|
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
|
|
|
|
|
|
x_axis_type = 'log'
|
|
y_axis_type = 'linear'
|
|
|
|
velo = thedistances/thesecs
|
|
p = 500./velo
|
|
|
|
p2 = get_datetimes(p)
|
|
|
|
source = ColumnDataSource(
|
|
data = dict(
|
|
dist = thedistances,
|
|
duration = thesecs,
|
|
spm = 0*theavpower,
|
|
tim = niceformat(get_datetimes(thesecs,tzinfo=1)),
|
|
power = theavpower,
|
|
pace = nicepaceformat(p2),
|
|
)
|
|
)
|
|
|
|
# fitting the data to Paul
|
|
if len(thedistances)>=2:
|
|
paulslope, paulintercept,r,p,stderr = linregress(np.log10(thedistances),p)
|
|
else:
|
|
paulslope = 5.0/np.log10(2.0)
|
|
paulintercept = p[0]-paulslope*np.log10(thedistances[0])
|
|
|
|
fitx = np.arange(100)*2*max(np.log10(thedistances))/100.
|
|
|
|
fitp = paulslope*fitx+paulintercept
|
|
|
|
fitvelo = 500./fitp
|
|
fitpower = 2.8*(fitvelo**3)
|
|
fitt = 10**fitx/fitvelo
|
|
fitp2 = get_datetimes(fitp)
|
|
|
|
|
|
sourcepaul = ColumnDataSource(
|
|
data = dict(
|
|
dist = 10**fitx,
|
|
duration = fitt,
|
|
power = fitpower,
|
|
spm = 0*fitpower,
|
|
tim = niceformat(get_datetimes(fitt,tzinfo=1)),
|
|
pace = nicepaceformat(fitp2),
|
|
)
|
|
)
|
|
|
|
# fitting the data to simple CP model
|
|
# simpleslope, simpleintercept,r,p,stderr = linregress(1./thesecs,theavpower)
|
|
|
|
# fitx = 1.e-4+np.arange(10000)/10000.
|
|
# fitpower = simpleslope*fitx+simpleintercept
|
|
# fitvelo = (fitpower/2.8)**(1./3.)
|
|
# fitt = 1./fitx
|
|
# fitdist = fitt*fitvelo
|
|
# fitp = 500./fitvelo
|
|
# fitp2 = get_datetimes(fitp)
|
|
|
|
# sourcesimple = ColumnDataSource(
|
|
# data = dict(
|
|
# dist = fitdist,
|
|
# duration = fitt,
|
|
# power = fitpower,
|
|
# tim = niceformat(get_datetimes(fitt)),
|
|
# pace = nicepaceformat(fitp2),
|
|
# )
|
|
# )
|
|
|
|
# fitting the data to three parameter CP model
|
|
fitfunc = lambda pars,x: pars[0]/(1+(x/pars[2])) + pars[1]/(1+(x/pars[3]))
|
|
errfunc = lambda pars,x,y: fitfunc(pars,x)-y
|
|
|
|
p0 = [500,350,10,8000]
|
|
|
|
p1 = p0
|
|
if len(thesecs)>=4:
|
|
p1, success = optimize.leastsq(errfunc, p0[:], args = (thesecs,theavpower))
|
|
else:
|
|
factor = fitfunc(p0,thesecs.mean())/theavpower.mean()
|
|
p1 = [p0[0]/factor,p0[1]/factor,p0[2],p0[3]]
|
|
|
|
|
|
fitt = 10**(4*np.arange(100)/100.)
|
|
|
|
fitpower = fitfunc(p1,fitt)
|
|
|
|
message = ""
|
|
if len(fitpower[fitpower<0]) > 0:
|
|
message = "CP model fit didn't give correct results"
|
|
|
|
fitvelo = (fitpower/2.8)**(1./3.)
|
|
fitdist = fitt*fitvelo
|
|
fitp = 500./fitvelo
|
|
fitp2 = get_datetimes(fitp)
|
|
|
|
sourcecomplex = ColumnDataSource(
|
|
data = dict(
|
|
dist = fitdist,
|
|
duration = fitt,
|
|
tim = niceformat(get_datetimes(fitt,tzinfo=1)),
|
|
spm = 0*fitpower,
|
|
power = fitpower,
|
|
pace = nicepaceformat(fitp2),
|
|
)
|
|
)
|
|
|
|
|
|
|
|
# making the plot
|
|
plot = Figure(tools=TOOLS,x_axis_type=x_axis_type,
|
|
plot_width=900,
|
|
toolbar_location="above",
|
|
toolbar_sticky=False)
|
|
plot.circle('duration','power',source=source,fill_color='red',size=15,
|
|
legend='Power')
|
|
plot.xaxis.axis_label = "Duration (seconds)"
|
|
plot.yaxis.axis_label = "Power (W)"
|
|
|
|
therows = []
|
|
for workout in theworkouts:
|
|
f1 = workout.csvfilename
|
|
rowdata = rdata(f1)
|
|
if rowdata != 0:
|
|
therows.append(rowdata)
|
|
|
|
cpdata = cumcpdata(therows)
|
|
|
|
velo = cpdata['Distance']/cpdata['Delta']
|
|
|
|
p = 500./velo
|
|
|
|
p2 = get_datetimes(p)
|
|
|
|
source2 = ColumnDataSource(
|
|
data = dict(
|
|
duration = cpdata['Delta'],
|
|
power = cpdata['CP'],
|
|
tim = niceformat(get_datetimes(cpdata['Delta'],tzinfo=1)),
|
|
dist = cpdata['Distance'],
|
|
pace = nicepaceformat(p2),
|
|
)
|
|
)
|
|
|
|
plot.circle('duration','power',source=source2,
|
|
fill_color='blue',size=3,
|
|
legend = 'Power from segments')
|
|
|
|
# for workout in theworkouts:
|
|
# f1 = workout.csvfilename
|
|
# rowdata = rdata(f1)
|
|
# cpdata = rowdata.getcp()
|
|
|
|
# source2 = ColumnDataSource(
|
|
# data = dict(
|
|
# duration = cpdata['Delta'],
|
|
# power = cpdata['CP'],
|
|
# dist = cpdata['Distance'],
|
|
# )
|
|
# )
|
|
|
|
# plot.circle('duration','power',source=source2,fill_color='blue',size=3)
|
|
|
|
|
|
hover = plot.select(dict(type=HoverTool))
|
|
|
|
hover.tooltips = OrderedDict([
|
|
('Duration ','@tim'),
|
|
('Power (W)','@power{int}'),
|
|
('Distance (m)','@dist{int}'),
|
|
('Pace (/500m)','@pace'),
|
|
])
|
|
|
|
hover.mode = 'mouse'
|
|
|
|
plot.y_range = Range1d(0,1.5*max(theavpower))
|
|
plot.x_range = Range1d(1,2*max(thesecs))
|
|
plot.legend.orientation = "vertical"
|
|
|
|
# plot.extra_y_ranges = {"distance": Range1d(start=0,
|
|
# end=1.1*max(thedistances))}
|
|
# plot.circle('duration','dist',source=source,fill_color='white',size=15,
|
|
# legend='Distance',
|
|
# y_range_name="distance")
|
|
# plot.line('duration','dist',source=sourcepaul,y_range_name="distance")
|
|
# plot.line('duration','dist',source=sourcesimple,legend="Simple CP model",
|
|
# color='red',y_range_name="distance")
|
|
# plot.line('duration','dist',source=sourcecomplex,legend="Simple CP model",
|
|
# color='green',y_range_name="distance")
|
|
# plot.add_layout(LinearAxis(y_range_name="distance",
|
|
# axis_label="Distance (m)"),'right')
|
|
|
|
plot.line('duration','power',source=sourcepaul,legend="Paul's Law")
|
|
# plot.line('duration','power',source=sourcesimple,legend="Simple CP model",
|
|
# color='red')
|
|
plot.line('duration','power',source=sourcecomplex,legend="CP Model",
|
|
color='green')
|
|
|
|
|
|
script, div = components(plot)
|
|
|
|
return [script,div,paulslope,paulintercept,p1,message]
|
|
|
|
def interactive_windchart(id=0,promember=0):
|
|
# check if valid ID exists (workout exists)
|
|
row = Workout.objects.get(id=id)
|
|
# g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
|
|
|
|
f1 = row.csvfilename
|
|
|
|
# create interactive plot
|
|
plot = Figure(plot_width=400,plot_height=300)
|
|
# get user
|
|
# u = User.objects.get(id=row.user.id)
|
|
r = row.user
|
|
u = r.user
|
|
|
|
|
|
|
|
rr = rrower(hrmax=r.max,hrut2=r.ut2,
|
|
hrut1=r.ut1,hrat=r.at,
|
|
hrtr=r.tr,hran=r.an)
|
|
|
|
rowdata = rdata(f1,rower=rr)
|
|
if rowdata == 0:
|
|
return 0
|
|
|
|
dist = rowdata.df.ix[:,'cum_dist']
|
|
|
|
try:
|
|
vwind = rowdata.df.ix[:,'vwind']
|
|
winddirection = rowdata.df.ix[:,'winddirection']
|
|
bearing = rowdata.df.ix[:,'bearing']
|
|
except KeyError:
|
|
rowdata.add_wind(0,0)
|
|
rowdata.add_bearing()
|
|
vwind = rowdata.df.ix[:,'vwind']
|
|
winddirection = rowdata.df.ix[:,'winddirection']
|
|
bearing = rowdata.df.ix[:,'winddirection']
|
|
rowdata.write_csv(f1)
|
|
|
|
winddirection = winddirection % 360
|
|
winddirection = (winddirection + 360) % 360
|
|
|
|
tw = tailwind(bearing,vwind,1.0*winddirection)
|
|
|
|
|
|
source = ColumnDataSource(
|
|
data = dict(
|
|
dist=dist,
|
|
vwind=vwind,
|
|
tw=tw,
|
|
winddirection=winddirection,
|
|
)
|
|
)
|
|
|
|
# plot tools
|
|
if (promember==1):
|
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,resize,crosshair'
|
|
else:
|
|
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,crosshair'
|
|
|
|
|
|
|
|
# making the plot
|
|
plot = Figure(tools=TOOLS,plot_width=400,height=500,
|
|
# toolbar_location="below",
|
|
toolbar_sticky=False,
|
|
)
|
|
plot.line('dist','vwind',source=source,legend="Wind Speed (m/s)")
|
|
plot.line('dist','tw',source=source,legend="Tail (+)/Head (-) Wind (m/s)",color='black')
|
|
plot.title.text = row.name
|
|
# plot.title.text_font_size=value("1.0em")
|
|
plot.title.text_font="1.0em"
|
|
plot.xaxis.axis_label = "Distance (m)"
|
|
plot.yaxis.axis_label = "Wind Speed (m/s)"
|
|
plot.y_range = Range1d(-7,7)
|
|
|
|
|
|
plot.extra_y_ranges = {"winddirection": Range1d(start=0,end=360)}
|
|
plot.line('dist','winddirection',source=source,
|
|
legend='Wind Direction',color="red",
|
|
y_range_name="winddirection")
|
|
plot.add_layout(LinearAxis(y_range_name="winddirection",axis_label="Wind Direction (degree)"),'right')
|
|
|
|
|
|
script, div = components(plot)
|
|
|
|
return [script,div]
|
|
|
|
def interactive_streamchart(id=0,promember=0):
|
|
# check if valid ID exists (workout exists)
|
|
row = Workout.objects.get(id=id)
|
|
# g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
|
|
|
|
f1 = row.csvfilename
|
|
|
|
# create interactive plot
|
|
plot = Figure(plot_width=400,
|
|
)
|
|
# get user
|
|
# u = User.objects.get(id=row.user.id)
|
|
r = row.user
|
|
u = r.user
|
|
|
|
|
|
|
|
rr = rrower(hrmax=r.max,hrut2=r.ut2,
|
|
hrut1=r.ut1,hrat=r.at,
|
|
hrtr=r.tr,hran=r.an)
|
|
|
|
rowdata = rdata(f1,rower=rr)
|
|
if rowdata == 0:
|
|
return "","CSV Data File Not Found"
|
|
|
|
dist = rowdata.df.ix[:,'cum_dist']
|
|
|
|
try:
|
|
vstream = rowdata.df.ix[:,'vstream']
|
|
except KeyError:
|
|
rowdata.add_stream(0)
|
|
vstream = rowdata.df.ix[:,'vstream']
|
|
rowdata.write_csv(f1)
|
|
|
|
# plot tools
|
|
if (promember==1):
|
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,resize,crosshair'
|
|
else:
|
|
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,crosshair'
|
|
|
|
|
|
|
|
# making the plot
|
|
plot = Figure(tools=TOOLS,plot_width=400,height=500,
|
|
# toolbar_location="below",
|
|
toolbar_sticky=False,
|
|
)
|
|
plot.line(dist,vstream,legend="River Stream Velocity (m/s)")
|
|
plot.title.text = row.name
|
|
plot.title.text_font_size=value("1.0em")
|
|
plot.xaxis.axis_label = "Distance (m)"
|
|
plot.yaxis.axis_label = "River Current (m/s)"
|
|
plot.y_range = Range1d(-2,2)
|
|
|
|
|
|
script, div = components(plot)
|
|
|
|
return [script,div]
|
|
|
|
def interactive_chart(id=0,promember=0):
|
|
# Add hover to this comma-separated string and see what changes
|
|
if (promember==1):
|
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
|
|
else:
|
|
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
|
|
|
|
# check if valid ID exists (workout exists)
|
|
row = Workout.objects.get(id=id)
|
|
# g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
|
|
|
|
f1 = row.csvfilename
|
|
|
|
# create interactive plot
|
|
# get user
|
|
# u = User.objects.get(id=row.user.id)
|
|
r = row.user
|
|
u = r.user
|
|
|
|
|
|
|
|
rr = rrower(hrmax=r.max,hrut2=r.ut2,
|
|
hrut1=r.ut1,hrat=r.at,
|
|
hrtr=r.tr,hran=r.an)
|
|
|
|
rowdata = rdata(f1,rower=rr)
|
|
if rowdata == 0:
|
|
return "","CSV Data File Not Found"
|
|
|
|
t = rowdata.df.ix[:,'TimeStamp (sec)']
|
|
t = t-rowdata.df.ix[0,'TimeStamp (sec)']
|
|
|
|
row_index = rowdata.df.ix[:,' Stroke500mPace (sec/500m)'] > 3000
|
|
rowdata.df.loc[row_index,' Stroke500mPace (sec/500m)'] = 3000.
|
|
|
|
p = rowdata.df.ix[:,' Stroke500mPace (sec/500m)']
|
|
hr = rowdata.df.ix[:,' HRCur (bpm)']
|
|
spm = rowdata.df.ix[:,' Cadence (stokes/min)']
|
|
cumdist = rowdata.df.ix[:,'cum_dist']
|
|
|
|
f = rowdata.df['TimeStamp (sec)'].diff().mean()
|
|
windowsize = 2*(int(10./(f)))+1
|
|
if windowsize <= 3:
|
|
windowsize = 5
|
|
|
|
if windowsize > 3:
|
|
spm = savgol_filter(spm,windowsize,3)
|
|
|
|
|
|
|
|
|
|
t2 = get_datetimes(t,tzinfo=1)
|
|
|
|
# p[p>3000] = 3000.
|
|
|
|
p2 = get_datetimes(p)
|
|
|
|
source = ColumnDataSource(
|
|
data = dict(
|
|
x=t2,
|
|
y=hr,
|
|
y2=p2,
|
|
tf = niceformat(t2),
|
|
pace = nicepaceformat(p2),
|
|
heartrate = hr,
|
|
spm=spm,
|
|
spmc=np.rint(10*spm)/10.,
|
|
cumdist=cumdist,
|
|
)
|
|
)
|
|
|
|
|
|
|
|
plot = Figure(x_axis_type="datetime",y_axis_type="datetime",
|
|
plot_width=400,
|
|
plot_height=400,
|
|
toolbar_sticky=False,
|
|
# toolbar_location="below",
|
|
tools=TOOLS)
|
|
|
|
# plot.line(t2,p2,legend="Pace")
|
|
plot.line('x','y2',source=source,legend="Pace")
|
|
plot.title.text = row.name
|
|
plot.title.text_font_size=value("1.0em")
|
|
plot.xaxis.axis_label = "Time"
|
|
plot.yaxis.axis_label = "Pace (/500m)"
|
|
plot.xaxis[0].formatter = DatetimeTickFormatter(
|
|
hours = ["%H"],
|
|
minutes = ["%M"],
|
|
seconds = ["%S"],
|
|
days = ["0"],
|
|
months = [""],
|
|
years = [""]
|
|
)
|
|
plot.yaxis[0].formatter = DatetimeTickFormatter(
|
|
seconds = ["%S"],
|
|
minutes = ["%M"]
|
|
)
|
|
ymax = datetime.datetime(2016,5,1,0,1,30)
|
|
ymin = datetime.datetime(2016,5,1,0,2,30)
|
|
|
|
if row.workouttype == 'water':
|
|
ymax = datetime.datetime(2016,5,1,0,1,30)
|
|
ymin = datetime.datetime(2016,5,1,0,3,30)
|
|
|
|
plot.y_range = Range1d(ymin,ymax)
|
|
|
|
|
|
hover = plot.select(dict(type=HoverTool))
|
|
|
|
|
|
hover.tooltips = OrderedDict([
|
|
('Time','@tf'),
|
|
('Pace','@pace'),
|
|
('HR','@heartrate'),
|
|
('SPM','@spmc{1.1}'),
|
|
('Distance','@cumdist{1.1}'),
|
|
])
|
|
|
|
hover.mode = 'mouse'
|
|
|
|
plot.extra_y_ranges = {"hr": Range1d(start=100,end=200)}
|
|
# plot.line(t2,hr,color="red",y_range_name="hr",legend="Heart Rate")
|
|
plot.line('x','y',source=source,color="red",
|
|
y_range_name="hr", legend="Heart Rate")
|
|
plot.add_layout(LinearAxis(y_range_name="hr",axis_label="HR"),'right')
|
|
|
|
plot.legend.location = "bottom_right"
|
|
|
|
script, div = components(plot)
|
|
|
|
return [script,div]
|
|
|
|
def interactive_cum_flex_chart(theworkouts,promember=0,
|
|
xparam='spm',
|
|
yparam1='power',
|
|
yparam2='hr',
|
|
):
|
|
|
|
|
|
therows = []
|
|
for workout in theworkouts:
|
|
f1 = workout.csvfilename
|
|
rowdata = rdata(f1)
|
|
if rowdata != 0:
|
|
therows.append(rowdata.df)
|
|
|
|
thedata = pd.concat(therows)
|
|
|
|
csvcolumns = {
|
|
'time': 'TimeStamp (sec)',
|
|
'distance': 'cum_dist',
|
|
'hr': ' HRCur (bpm)',
|
|
'spm': ' Cadence (stokes/min)',
|
|
'pace': ' Stroke500mPace (sec/500m)',
|
|
'power': ' Power (watts)',
|
|
'averageforce': ' AverageDriveForce (lbs)',
|
|
'drivelength': ' DriveLength (meters)',
|
|
'peakforce': ' PeakDriveForce (lbs)',
|
|
'driveenergy': 'driveenergy',
|
|
'drivespeed': 'drivespeed',
|
|
}
|
|
|
|
axlabels = {
|
|
'time': 'Time',
|
|
'distance': 'Distance (m)',
|
|
'hr': 'Heart Rate (bpm)',
|
|
'spm': 'Stroke Rate (spm)',
|
|
'pace': 'Pace (/500m)',
|
|
'power': 'Power (Watt)',
|
|
'averageforce': 'Average Drive Force (lbs)',
|
|
'drivelength': 'Drive Length (m)',
|
|
'peakforce': 'Peak Drive Force (lbs)',
|
|
'driveenergy': 'Work per Stroke (J)',
|
|
'drivespeed': 'Drive Speed (m/s)',
|
|
'None': '',
|
|
}
|
|
|
|
yparamname1 = axlabels[yparam1]
|
|
yparamname2 = axlabels[yparam2]
|
|
|
|
yaxminima = {
|
|
'hr':100,
|
|
'spm':15,
|
|
'pace': datetime.datetime(2016,5,1,0,3,30),
|
|
'power': 0,
|
|
'averageforce': 0,
|
|
'peakforce': 0,
|
|
'drivelength':0.5,
|
|
'driveenergy': 0,
|
|
'drivespeed': 0,
|
|
}
|
|
|
|
yaxmaxima = {
|
|
'hr':200,
|
|
'spm':45,
|
|
'pace':datetime.datetime(2016,5,1,0,1,30),
|
|
'power': 600,
|
|
'averageforce':200,
|
|
'peakforce':400,
|
|
'drivelength':2.0,
|
|
'driveenergy': 1000,
|
|
'drivespeed':4,
|
|
}
|
|
|
|
|
|
thedata['driveenergy'] = thedata[' DriveLength (meters)']*thedata[' AverageDriveForce (lbs)']*4.44822
|
|
|
|
# throw out zeros from dataframe
|
|
thedata = thedata[thedata[csvcolumns[yparam1]] > 0]
|
|
thedata = thedata[thedata[csvcolumns[xparam]] > 0]
|
|
if yparam2 != 'None':
|
|
thedata = thedata[thedata[csvcolumns[yparam2]] > 0]
|
|
|
|
# check if dataframe not empty
|
|
if thedata.empty:
|
|
return ['','<p>No non-zero data in selection</p>','','']
|
|
|
|
spm = thedata.ix[:,csvcolumns['spm']]
|
|
|
|
f = thedata['TimeStamp (sec)'].diff().mean()
|
|
if not np.isnan(f):
|
|
windowsize = 2*(int(10./(f)))+1
|
|
else:
|
|
windowsize = 5
|
|
|
|
if windowsize <= 3:
|
|
windowsize = 5
|
|
|
|
if windowsize > 3:
|
|
spm = savgol_filter(spm,windowsize,3)
|
|
|
|
thedata[' Cadence (stokes/min)'] = spm
|
|
|
|
drivelength = thedata[' DriveLength (meters)']
|
|
if windowsize > 3:
|
|
drivelength = savgol_filter(drivelength,windowsize,3)
|
|
thedata[' DriveLength (meters)'] = drivelength
|
|
|
|
thedata['drivespeed'] = drivelength/thedata[' DriveTime (ms)']*1.0e3
|
|
|
|
|
|
x1 = thedata.ix[:,csvcolumns[xparam]]
|
|
|
|
y1 = thedata.ix[:,csvcolumns[yparam1]]
|
|
if yparam2 != 'None':
|
|
y2 = thedata.ix[:,csvcolumns[yparam2]]
|
|
else:
|
|
y2 = y1
|
|
|
|
|
|
if xparam=='time':
|
|
xaxmax = x1.max()
|
|
xaxmin = x1.min()
|
|
xaxmax = get_datetimes([xaxmax],tzinfo=1)[0]
|
|
xaxmin = get_datetimes([xaxmin],tzinfo=1)[0]
|
|
x1 = get_datetimes(x1,tzinfo=1)
|
|
elif xparam=='distance':
|
|
xaxmax = x1.max()
|
|
xaxmin = x1.min()
|
|
else:
|
|
xaxmax = yaxmaxima[xparam]
|
|
xaxmin = yaxminima[xparam]
|
|
|
|
# average values
|
|
if xparam != 'time':
|
|
x1mean = x1.mean()
|
|
else:
|
|
x1mean = 0
|
|
|
|
y1mean = y1.mean()
|
|
y2mean = y2.mean()
|
|
|
|
if xparam != 'time':
|
|
xvals = xaxmin+np.arange(100)*(xaxmax-xaxmin)/100.
|
|
else:
|
|
xvals = np.arange(100)
|
|
|
|
x_axis_type = 'linear'
|
|
y_axis_type = 'linear'
|
|
if xparam == 'time':
|
|
x_axis_type = 'datetime'
|
|
|
|
if yparam1 == 'pace':
|
|
y_axis_type = 'datetime'
|
|
y1 = get_datetimes(y1)
|
|
ymax = datetime.datetime(2016,5,1,0,1,30)
|
|
ymin = datetime.datetime(2016,5,1,0,2,30)
|
|
|
|
|
|
time = thedata.ix[:,csvcolumns['time']]
|
|
|
|
hr = thedata.ix[:,csvcolumns['hr']]
|
|
if windowsize > 3:
|
|
hr = savgol_filter(hr,windowsize,3)
|
|
|
|
|
|
pace = thedata.ix[:,csvcolumns['pace']]
|
|
|
|
distance = thedata.ix[:,csvcolumns['distance']]
|
|
|
|
power = thedata.ix[:,csvcolumns['power']]
|
|
|
|
|
|
# Add hover to this comma-separated string and see what changes
|
|
if (promember==1):
|
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
|
|
else:
|
|
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
|
|
|
|
plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type,
|
|
tools=TOOLS,
|
|
toolbar_location="above",
|
|
toolbar_sticky=False)
|
|
|
|
x1means = Span(location=x1mean,dimension='height',line_color='green',
|
|
line_dash=[6,6], line_width=2)
|
|
|
|
y1means = Span(location=y1mean,dimension='width',line_color='blue',
|
|
line_dash=[6,6],line_width=2)
|
|
y2means = y1means
|
|
|
|
if (xparam != 'time') and (xparam != 'distance'):
|
|
plot.add_layout(x1means)
|
|
|
|
plot.add_layout(y1means)
|
|
|
|
source = ColumnDataSource(
|
|
data = dict(
|
|
x1=x1,
|
|
y1=y1,
|
|
y2=y2,
|
|
time=niceformat(get_datetimes(time,tzinfo=1)),
|
|
pace=nicepaceformat(get_datetimes(pace)),
|
|
hr = hr,
|
|
spm = spm,
|
|
spmc=np.rint(10*spm)/10.,
|
|
distance=distance,
|
|
power=power,
|
|
)
|
|
)
|
|
|
|
source2 = ColumnDataSource(
|
|
data = dict(
|
|
x1=x1,
|
|
y1=y1,
|
|
y2=y2,
|
|
time=niceformat(get_datetimes(time,tzinfo=1)),
|
|
pace=nicepaceformat(get_datetimes(pace)),
|
|
hr = hr,
|
|
spm = spm,
|
|
spmc=np.rint(10*spm)/10.,
|
|
distance=distance,
|
|
power=power,
|
|
)
|
|
)
|
|
|
|
|
|
# plot.circle('x1','y1',source=source,legend=yparam1,size=3)
|
|
plot.circle('x1','y1',source=source2,fill_alpha=0.3,line_color=None,
|
|
legend=yparamname1,
|
|
)
|
|
|
|
plot.xaxis.axis_label = axlabels[xparam]
|
|
plot.yaxis.axis_label = axlabels[yparam1]
|
|
|
|
|
|
yrange1 = Range1d(start=yaxminima[yparam1],end=yaxmaxima[yparam1])
|
|
plot.y_range = yrange1
|
|
|
|
if (xparam != 'time') and (xparam != 'distance'):
|
|
xrange1 = Range1d(start=yaxminima[xparam],end=yaxmaxima[xparam])
|
|
plot.x_range = xrange1
|
|
|
|
if xparam == 'time':
|
|
xrange1 = Range1d(start=xaxmin,end=xaxmax)
|
|
plot.x_range = xrange1
|
|
plot.xaxis[0].formatter = DatetimeTickFormatter(
|
|
hours = ["%H"],
|
|
minutes = ["%M"],
|
|
seconds = ["%S"],
|
|
days = ["0"],
|
|
months = [""],
|
|
years = [""]
|
|
)
|
|
|
|
|
|
|
|
if yparam1 == 'pace':
|
|
plot.y_range = Range1d(ymin,ymax)
|
|
plot.yaxis[0].formatter = DatetimeTickFormatter(
|
|
seconds = ["%S"],
|
|
minutes = ["%M"]
|
|
)
|
|
|
|
if yparam2 != 'None':
|
|
yrange2 = Range1d(start=yaxminima[yparam2],end=yaxmaxima[yparam2])
|
|
plot.extra_y_ranges = {"yax2": yrange2}
|
|
|
|
plot.circle('x1','y2',color="red",y_range_name="yax2",
|
|
legend=yparamname2,
|
|
source=source2,fill_alpha=0.3,line_color=None)
|
|
|
|
plot.add_layout(LinearAxis(y_range_name="yax2",
|
|
axis_label=axlabels[yparam2]),'right')
|
|
|
|
y2means = Span(location=y2mean,dimension='width',line_color='red',
|
|
line_dash=[6,6],line_width=2,y_range_name="yax2")
|
|
|
|
|
|
plot.add_layout(y2means)
|
|
|
|
hover = plot.select(dict(type=HoverTool))
|
|
|
|
|
|
hover.tooltips = OrderedDict([
|
|
('Time','@time'),
|
|
('Pace','@pace'),
|
|
('HR','@hr'),
|
|
('SPM','@spmc{1.1}'),
|
|
('Power','@power{int}'),
|
|
])
|
|
|
|
hover.mode = 'mouse'
|
|
|
|
callback = CustomJS(args = dict(source=source,source2=source2,
|
|
x1means=x1means,
|
|
y1means=y1means,
|
|
y2means=y2means), code="""
|
|
var data = source.data
|
|
var data2 = source2.data
|
|
var x1 = data['x1']
|
|
var y1 = data['y1']
|
|
var y2 = data['y2']
|
|
var spm1 = data['spm']
|
|
var time1 = data['time']
|
|
var pace1 = data['pace']
|
|
var hr1 = data['hr']
|
|
var spmc1 = data['spmc']
|
|
var distance1 = data['distance']
|
|
var power1 = data['power']
|
|
|
|
var minspm = minspm.value
|
|
var maxspm = maxspm.value
|
|
var mindist = mindist.value
|
|
var maxdist = maxdist.value
|
|
var xm = 0
|
|
var ym1 = 0
|
|
var ym2 = 0
|
|
|
|
data2['x1'] = []
|
|
data2['y1'] = []
|
|
data2['y2'] = []
|
|
data2['spm'] = []
|
|
data2['time'] = []
|
|
data2['pace'] = []
|
|
data2['hr'] = []
|
|
data2['spmc'] = []
|
|
data2['distance'] = []
|
|
data2['power'] = []
|
|
data2['x1mean'] = []
|
|
data2['y1mean'] = []
|
|
data2['y2mean'] = []
|
|
data2['xvals'] = []
|
|
data2['y1vals'] = []
|
|
data2['y2vals'] = []
|
|
|
|
for (i=0; i<x1.length; i++) {
|
|
if (spm1[i]>=minspm && spm1[i]<=maxspm) {
|
|
if (distance1[i]>=mindist && distance1[i]<=maxdist) {
|
|
data2['x1'].push(x1[i])
|
|
data2['y1'].push(y1[i])
|
|
data2['y2'].push(y2[i])
|
|
data2['spm'].push(spm1[i])
|
|
data2['time'].push(time1[i])
|
|
data2['pace'].push(pace1[i])
|
|
data2['hr'].push(hr1[i])
|
|
data2['spmc'].push(spmc1[i])
|
|
data2['distance'].push(distance1[i])
|
|
data2['power'].push(power1[i])
|
|
|
|
xm += x1[i]
|
|
ym1 += y1[i]
|
|
ym2 += y2[i]
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
xm /= data2['x1'].length
|
|
ym1 /= data2['x1'].length
|
|
ym2 /= data2['x1'].length
|
|
|
|
data2['x1mean'] = [xm,xm]
|
|
data2['y1mean'] = [ym1,ym1]
|
|
data2['y2mean'] = [ym2,ym2]
|
|
x1means.location = xm
|
|
y1means.location = ym1
|
|
y2means.location = ym2
|
|
|
|
source2.trigger('change');
|
|
""")
|
|
|
|
slider_spm_min = Slider(start=15.0, end=55,value=15.0, step=.1,
|
|
title="Min SPM",callback=callback)
|
|
callback.args["minspm"] = slider_spm_min
|
|
|
|
|
|
slider_spm_max = Slider(start=15.0, end=55,value=55.0, step=.1,
|
|
title="Max SPM",callback=callback)
|
|
callback.args["maxspm"] = slider_spm_max
|
|
|
|
distmax = 100+100*int(distance.max()/100.)
|
|
|
|
slider_dist_min = Slider(start=0,end=distmax,value=0,step=1,
|
|
title="Min Distance",callback=callback)
|
|
callback.args["mindist"] = slider_dist_min
|
|
|
|
slider_dist_max = Slider(start=0,end=distmax,value=distmax,
|
|
step=1,
|
|
title="Max Distance",callback=callback)
|
|
callback.args["maxdist"] = slider_dist_max
|
|
|
|
layout = layoutrow([layoutcolumn([slider_spm_min,
|
|
slider_spm_max,
|
|
slider_dist_min,
|
|
slider_dist_max,
|
|
],
|
|
),
|
|
plot])
|
|
|
|
script, div = components(layout)
|
|
js_resources = INLINE.render_js()
|
|
css_resources = INLINE.render_css()
|
|
|
|
|
|
|
|
return [script,div,js_resources,css_resources]
|
|
|
|
|
|
|
|
def interactive_flex_chart(id=0,promember=0,
|
|
xparam='time',
|
|
yparam1='pace',
|
|
yparam2='hr',
|
|
plottype='line',
|
|
workstrokesonly=False):
|
|
|
|
csvcolumns = {
|
|
'time': 'TimeStamp (sec)',
|
|
'distance': 'cum_dist',
|
|
'hr': ' HRCur (bpm)',
|
|
'spm': ' Cadence (stokes/min)',
|
|
'pace': ' Stroke500mPace (sec/500m)',
|
|
'power': ' Power (watts)',
|
|
'averageforce': ' AverageDriveForce (lbs)',
|
|
'drivelength': ' DriveLength (meters)',
|
|
'peakforce': ' PeakDriveForce (lbs)',
|
|
'driveenergy': 'driveenergy',
|
|
'drivespeed': 'drivespeed',
|
|
}
|
|
|
|
axlabels = {
|
|
'time': 'Time',
|
|
'distance': 'Distance (m)',
|
|
'hr': 'Heart Rate (bpm)',
|
|
'spm': 'Stroke Rate (spm)',
|
|
'pace': 'Pace (/500m)',
|
|
'power': 'Power (Watt)',
|
|
'averageforce': 'Average Drive Force (lbs)',
|
|
'drivelength': 'Drive Length (m)',
|
|
'peakforce': 'Peak Drive Force (lbs)',
|
|
'driveenergy': 'Work per Stroke (J)',
|
|
'drivespeed': 'Drive Speed (m/s)',
|
|
'None': '',
|
|
}
|
|
|
|
yaxminima = {
|
|
'hr':100,
|
|
'spm':15,
|
|
'pace': datetime.datetime(2016,5,1,0,3,30),
|
|
'power': 0,
|
|
'averageforce': 0,
|
|
'peakforce': 0,
|
|
'drivelength':0.5,
|
|
'driveenergy': 0,
|
|
'drivespeed': 0,
|
|
}
|
|
|
|
yaxmaxima = {
|
|
'hr':200,
|
|
'spm':45,
|
|
'pace':datetime.datetime(2016,5,1,0,1,30),
|
|
'power': 600,
|
|
'averageforce':200,
|
|
'peakforce':400,
|
|
'drivelength':2.0,
|
|
'driveenergy': 1000,
|
|
'drivespeed':4,
|
|
}
|
|
|
|
# check if valid ID exists (workout exists)
|
|
row = Workout.objects.get(id=id)
|
|
# g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
|
|
|
|
f1 = row.csvfilename
|
|
|
|
r = row.user
|
|
u = r.user
|
|
|
|
rr = rrower(hrmax=r.max,hrut2=r.ut2,
|
|
hrut1=r.ut1,hrat=r.at,
|
|
hrtr=r.tr,hran=r.an)
|
|
|
|
rowdata = rdata(f1,rower=rr)
|
|
if rowdata == 0:
|
|
return "","CSV Data File Not Found"
|
|
|
|
workoutstateswork = [1,4,5,8,9,6,7]
|
|
workoutstatesrest = [3]
|
|
workoutstatetransition = [0,2,10,11,12,13]
|
|
|
|
if workstrokesonly:
|
|
try:
|
|
rowdata.df = rowdata.df[~rowdata.df[' WorkoutState'].isin(workoutstatesrest)]
|
|
except KeyError:
|
|
pass
|
|
|
|
|
|
rowdata.df['driveenergy'] = rowdata.df[' DriveLength (meters)']*rowdata.df[' AverageDriveForce (lbs)']*4.44822
|
|
|
|
f = rowdata.df['TimeStamp (sec)'].diff().mean()
|
|
windowsize = 2*(int(10./(f)))+1
|
|
if windowsize <= 3:
|
|
windowsize = 5
|
|
|
|
spm = rowdata.df.ix[:,csvcolumns['spm']]
|
|
hr = rowdata.df.ix[:,csvcolumns['hr']]
|
|
|
|
|
|
if windowsize > 3:
|
|
spm = savgol_filter(spm,windowsize,3)
|
|
hr = savgol_filter(hr,windowsize,3)
|
|
|
|
rowdata.df[' Cadence (stokes/min)'] = spm
|
|
rowdata.df[' HRCur (bpm)'] = hr
|
|
|
|
drivelength = rowdata.df[' DriveLength (meters)']
|
|
if windowsize > 3:
|
|
drivelength = savgol_filter(drivelength,windowsize,3)
|
|
|
|
rowdata.df[' DriveLength (meters)'] = drivelength
|
|
|
|
rowdata.df['drivespeed'] = drivelength/rowdata.df[' DriveTime (ms)']*1.0e3
|
|
|
|
# get user
|
|
# u = User.objects.get(id=row.user.id)
|
|
|
|
x1 = rowdata.df.ix[:,csvcolumns[xparam]]
|
|
|
|
y1 = rowdata.df.ix[:,csvcolumns[yparam1]]
|
|
|
|
|
|
if yparam2 != 'None':
|
|
y2 = rowdata.df.ix[:,csvcolumns[yparam2]]
|
|
else:
|
|
y2 = y1
|
|
|
|
if xparam=='time':
|
|
|
|
xaxmax = x1.max()
|
|
xaxmin = x1.min()
|
|
xaxmax = get_datetimes([xaxmax],tzinfo=1)[0]
|
|
xaxmin = get_datetimes([xaxmin],tzinfo=1)[0]
|
|
x1 = get_datetimes(x1,tzinfo=1)
|
|
|
|
if xparam=='distance':
|
|
xaxmax = x1.max()
|
|
xaxmin = x1.min()
|
|
|
|
# average values
|
|
y1mean = y1.median()+0.0*np.arange(100)
|
|
y2mean = y2.median()+0.0*np.arange(100)
|
|
if xparam != 'time':
|
|
x1mean = x1.median()+0.0*np.arange(100)
|
|
else:
|
|
x1mean = 0+0.0*np.arange(100)
|
|
|
|
|
|
|
|
if xparam != 'time' and xparam != 'distance' and yparam1 != 'pace':
|
|
xvals = yaxminima[xparam]+np.arange(100)*(yaxmaxima[xparam]-yaxminima[xparam])/100.
|
|
y1vals = yaxminima[yparam1]+np.arange(100)*(yaxmaxima[yparam1]-yaxminima[yparam1])/100.
|
|
else:
|
|
xvals = np.arange(100)
|
|
y1vals = np.arange(100)
|
|
|
|
|
|
|
|
# constant power plot
|
|
if yparam1 == 'driveenergy':
|
|
if xparam == 'spm':
|
|
yconstantpower = y1.median()*x1.median()/xvals
|
|
|
|
|
|
|
|
|
|
x_axis_type = 'linear'
|
|
y_axis_type = 'linear'
|
|
if xparam == 'time':
|
|
x_axis_type = 'datetime'
|
|
|
|
if yparam1 == 'pace':
|
|
y_axis_type = 'datetime'
|
|
y1 = get_datetimes(y1)
|
|
ymax = datetime.datetime(2016,5,1,0,1,30)
|
|
ymin = datetime.datetime(2016,5,1,0,2,30)
|
|
|
|
if row.workouttype == 'water':
|
|
ymax = datetime.datetime(2016,5,1,0,1,30)
|
|
ymin = datetime.datetime(2016,5,1,0,3,30)
|
|
|
|
time = rowdata.df.ix[:,csvcolumns['time']]
|
|
time = time-time[time.index[0]]
|
|
|
|
hr = rowdata.df.ix[:,csvcolumns['hr']]
|
|
|
|
|
|
pace = rowdata.df.ix[:,csvcolumns['pace']]
|
|
|
|
distance = rowdata.df.ix[:,csvcolumns['distance']]
|
|
|
|
power = rowdata.df.ix[:,csvcolumns['power']]
|
|
|
|
# Add hover to this comma-separated string and see what changes
|
|
if (promember==1):
|
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
|
|
else:
|
|
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
|
|
|
|
plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type,
|
|
tools=TOOLS,
|
|
toolbar_sticky=False,
|
|
plot_width=900,
|
|
)
|
|
|
|
|
|
source = ColumnDataSource(
|
|
data = dict(
|
|
x1=x1,
|
|
y1=y1,
|
|
y2=y2,
|
|
time=niceformat(get_datetimes(time,tzinfo=1)),
|
|
pace=nicepaceformat(get_datetimes(pace)),
|
|
hr = hr,
|
|
spm = spm,
|
|
spmc=np.rint(10*spm)/10.,
|
|
distance=distance,
|
|
power=power,
|
|
xvals=xvals,
|
|
y1mean=y1mean,
|
|
x1mean=x1mean,
|
|
y1vals=y1vals,
|
|
)
|
|
)
|
|
|
|
|
|
# average values
|
|
plot.line('xvals','y1mean',color="black",source=source)
|
|
plot.line('x1mean','y1vals',color="black",source=source)
|
|
if yparam1 == 'driveenergy':
|
|
if xparam == 'spm':
|
|
plot.line(xvals,yconstantpower,color="green",legend="Constant Power")
|
|
|
|
if plottype=='line':
|
|
plot.line('x1','y1',source=source,legend=axlabels[yparam1])
|
|
elif plottype=='scatter':
|
|
# plot.circle('x1','y1',source=source,legend=yparam1,size=3)
|
|
plot.scatter('x1','y1',source=source,legend=axlabels[yparam1],fill_alpha=0.4,
|
|
line_color=None)
|
|
|
|
plot.title.text = row.name
|
|
plot.title.text_font_size=value("1.0em")
|
|
plot.xaxis.axis_label = axlabels[xparam]
|
|
plot.yaxis.axis_label = axlabels[yparam1]
|
|
|
|
|
|
yrange1 = Range1d(start=yaxminima[yparam1],end=yaxmaxima[yparam1])
|
|
plot.y_range = yrange1
|
|
|
|
if (xparam != 'time') and (xparam != 'distance'):
|
|
xrange1 = Range1d(start=yaxminima[xparam],end=yaxmaxima[xparam])
|
|
plot.x_range = xrange1
|
|
|
|
if xparam == 'time':
|
|
xrange1 = Range1d(start=xaxmin,end=xaxmax)
|
|
plot.x_range = xrange1
|
|
plot.xaxis[0].formatter = DatetimeTickFormatter(
|
|
hours = ["%H"],
|
|
minutes = ["%M"],
|
|
seconds = ["%S"],
|
|
days = ["0"],
|
|
months = [""],
|
|
years = [""]
|
|
)
|
|
|
|
|
|
if yparam1 == 'pace':
|
|
plot.y_range = Range1d(ymin,ymax)
|
|
plot.yaxis[0].formatter = DatetimeTickFormatter(
|
|
seconds = ["%S"],
|
|
minutes = ["%M"]
|
|
)
|
|
|
|
if yparam2 != 'None':
|
|
yrange2 = Range1d(start=yaxminima[yparam2],end=yaxmaxima[yparam2])
|
|
plot.extra_y_ranges = {"yax2": yrange2}
|
|
plot.line(xvals,y2mean,color="black",y_range_name="yax2")
|
|
|
|
if plottype=='line':
|
|
plot.line('x1','y2',color="red",y_range_name="yax2",
|
|
legend=axlabels[yparam2],
|
|
source=source)
|
|
|
|
elif plottype=='scatter':
|
|
# plot.circle(x1,y2,color="red",y_range_name="yax2",legend=yparam2,
|
|
# source=source,size=3)
|
|
plot.scatter('x1','y2',source=source,legend=axlabels[yparam2]
|
|
,fill_alpha=0.4,
|
|
line_color=None,color="red",y_range_name="yax2")
|
|
|
|
plot.add_layout(LinearAxis(y_range_name="yax2",
|
|
axis_label=axlabels[yparam2]),'right')
|
|
|
|
|
|
hover = plot.select(dict(type=HoverTool))
|
|
|
|
|
|
hover.tooltips = OrderedDict([
|
|
('Time','@time'),
|
|
('Distance','@distance{int}'),
|
|
('Pace','@pace'),
|
|
('HR','@hr'),
|
|
('SPM','@spmc{1.1}'),
|
|
('Power','@power{int}'),
|
|
])
|
|
|
|
hover.mode = 'mouse'
|
|
|
|
|
|
|
|
script, div = components(plot)
|
|
|
|
return [script,div]
|
|
|
|
|
|
def interactive_flex_chart2(id=0,promember=0,
|
|
xparam='time',
|
|
yparam1='pace',
|
|
yparam2='hr',
|
|
plottype='line',
|
|
workstrokesonly=False):
|
|
|
|
csvcolumns = {
|
|
'time': 'TimeStamp (sec)',
|
|
'distance': 'cum_dist',
|
|
'hr': ' HRCur (bpm)',
|
|
'spm': ' Cadence (stokes/min)',
|
|
'pace': ' Stroke500mPace (sec/500m)',
|
|
'power': ' Power (watts)',
|
|
'averageforce': ' AverageDriveForce (lbs)',
|
|
'drivelength': ' DriveLength (meters)',
|
|
'peakforce': ' PeakDriveForce (lbs)',
|
|
'driveenergy': 'driveenergy',
|
|
'drivespeed': 'drivespeed',
|
|
}
|
|
|
|
axlabels = {
|
|
'time': 'Time',
|
|
'distance': 'Distance (m)',
|
|
'hr': 'Heart Rate (bpm)',
|
|
'spm': 'Stroke Rate (spm)',
|
|
'pace': 'Pace (/500m)',
|
|
'power': 'Power (Watt)',
|
|
'averageforce': 'Average Drive Force (lbs)',
|
|
'drivelength': 'Drive Length (m)',
|
|
'peakforce': 'Peak Drive Force (lbs)',
|
|
'driveenergy': 'Work per Stroke (J)',
|
|
'drivespeed': 'Drive Speed (m/s)',
|
|
'None': '',
|
|
}
|
|
|
|
yaxminima = {
|
|
'hr':100,
|
|
'spm':15,
|
|
'pace': datetime.datetime(2016,5,1,0,3,30),
|
|
'power': 0,
|
|
'averageforce': 0,
|
|
'peakforce': 0,
|
|
'drivelength':0.5,
|
|
'driveenergy': 0,
|
|
'drivespeed': 0,
|
|
}
|
|
|
|
yaxmaxima = {
|
|
'hr':200,
|
|
'spm':45,
|
|
'pace':datetime.datetime(2016,5,1,0,1,30),
|
|
'power': 600,
|
|
'averageforce':200,
|
|
'peakforce':400,
|
|
'drivelength':2.0,
|
|
'driveenergy': 1000,
|
|
'drivespeed':4,
|
|
}
|
|
|
|
# check if valid ID exists (workout exists)
|
|
row = Workout.objects.get(id=id)
|
|
# g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime")
|
|
|
|
f1 = row.csvfilename
|
|
|
|
r = row.user
|
|
u = r.user
|
|
|
|
rr = rrower(hrmax=r.max,hrut2=r.ut2,
|
|
hrut1=r.ut1,hrat=r.at,
|
|
hrtr=r.tr,hran=r.an)
|
|
|
|
rowdata = rdata(f1,rower=rr)
|
|
if rowdata == 0:
|
|
return "","CSV Data File Not Found"
|
|
|
|
workoutstateswork = [1,4,5,8,9,6,7]
|
|
workoutstatesrest = [3]
|
|
workoutstatetransition = [0,2,10,11,12,13]
|
|
|
|
if workstrokesonly:
|
|
try:
|
|
rowdata.df = rowdata.df[~rowdata.df[' WorkoutState'].isin(workoutstatesrest)]
|
|
except KeyError:
|
|
pass
|
|
|
|
|
|
rowdata.df['driveenergy'] = rowdata.df[' DriveLength (meters)']*rowdata.df[' AverageDriveForce (lbs)']*4.44822
|
|
|
|
spm = rowdata.df.ix[:,csvcolumns['spm']]
|
|
hr = rowdata.df.ix[:,csvcolumns['hr']]
|
|
|
|
f = rowdata.df['TimeStamp (sec)'].diff().mean()
|
|
windowsize = 2*(int(10./(f)))+1
|
|
if windowsize <= 3:
|
|
windowsize = 5
|
|
|
|
if windowsize > 3:
|
|
spm = savgol_filter(spm,windowsize,3)
|
|
hr = savgol_filter(hr,windowsize,3)
|
|
|
|
rowdata.df[' Cadence (stokes/min)'] = spm
|
|
rowdata.df[' HRCur (bpm)'] = hr
|
|
|
|
drivelength = rowdata.df[' DriveLength (meters)']
|
|
if windowsize > 3:
|
|
drivelength = savgol_filter(drivelength,windowsize,3)
|
|
|
|
rowdata.df[' DriveLength (meters)'] = drivelength
|
|
|
|
rowdata.df['drivespeed'] = drivelength/rowdata.df[' DriveTime (ms)']*1.0e3
|
|
|
|
# get user
|
|
# u = User.objects.get(id=row.user.id)
|
|
|
|
x1 = rowdata.df.ix[:,csvcolumns[xparam]]
|
|
|
|
y1 = rowdata.df.ix[:,csvcolumns[yparam1]]
|
|
|
|
|
|
if yparam2 != 'None':
|
|
y2 = rowdata.df.ix[:,csvcolumns[yparam2]]
|
|
else:
|
|
y2 = y1
|
|
|
|
if xparam=='time':
|
|
xaxmax = x1.max()
|
|
xaxmin = x1.min()
|
|
xaxmax = get_datetimes([xaxmax],tzinfo=1)[0]
|
|
xaxmin = get_datetimes([xaxmin],tzinfo=1)[0]
|
|
x1 = get_datetimes(x1,tzinfo=1)
|
|
elif xparam=='distance':
|
|
xaxmax = x1.max()
|
|
xaxmin = x1.min()
|
|
else:
|
|
xaxmax = yaxmaxima[xparam]
|
|
xaxmin = yaxminima[xparam]
|
|
|
|
# average values
|
|
if xparam != 'time':
|
|
x1mean = x1.mean()
|
|
else:
|
|
x1mean = 0
|
|
|
|
y1mean = y1.mean()
|
|
y2mean = y2.mean()
|
|
|
|
if xparam != 'time':
|
|
xvals = xaxmin+np.arange(100)*(xaxmax-xaxmin)/100.
|
|
else:
|
|
xvals = np.arange(100)
|
|
|
|
# constant power plot
|
|
if yparam1 == 'driveenergy':
|
|
if xparam == 'spm':
|
|
yconstantpower = y1.median()*x1.median()/xvals
|
|
|
|
|
|
|
|
|
|
x_axis_type = 'linear'
|
|
y_axis_type = 'linear'
|
|
if xparam == 'time':
|
|
x_axis_type = 'datetime'
|
|
|
|
if yparam1 == 'pace':
|
|
y_axis_type = 'datetime'
|
|
y1 = get_datetimes(y1)
|
|
ymax = datetime.datetime(2016,5,1,0,1,30)
|
|
ymin = datetime.datetime(2016,5,1,0,2,30)
|
|
|
|
if row.workouttype == 'water':
|
|
ymax = datetime.datetime(2016,5,1,0,1,30)
|
|
ymin = datetime.datetime(2016,5,1,0,3,30)
|
|
|
|
time = rowdata.df.ix[:,csvcolumns['time']]
|
|
time = time-time[time.index[0]]
|
|
|
|
hr = rowdata.df.ix[:,csvcolumns['hr']]
|
|
|
|
|
|
pace = rowdata.df.ix[:,csvcolumns['pace']]
|
|
|
|
distance = rowdata.df.ix[:,csvcolumns['distance']]
|
|
|
|
power = rowdata.df.ix[:,csvcolumns['power']]
|
|
|
|
# prepare data
|
|
source = ColumnDataSource(
|
|
data = dict(
|
|
x1=x1,
|
|
y1=y1,
|
|
y2=y2,
|
|
time=niceformat(get_datetimes(time,tzinfo=1)),
|
|
pace=nicepaceformat(get_datetimes(pace)),
|
|
hr = hr,
|
|
spm = spm,
|
|
spmc=np.rint(10*spm)/10.,
|
|
distance=distance,
|
|
power=power,
|
|
# xvals=xvals,
|
|
y1mean=[y1mean,y1mean],
|
|
y2mean=[y2mean,y2mean],
|
|
x1mean=[x1mean,x1mean],
|
|
)
|
|
)
|
|
|
|
# second source for filtering
|
|
source2 = ColumnDataSource(
|
|
data = dict(
|
|
x1=x1,
|
|
y1=y1,
|
|
y2=y2,
|
|
time=niceformat(get_datetimes(time,tzinfo=1)),
|
|
pace=nicepaceformat(get_datetimes(pace)),
|
|
hr = hr,
|
|
spm = spm,
|
|
spmc=np.rint(10*spm)/10.,
|
|
distance=distance,
|
|
power=power,
|
|
# xvals=xvals,
|
|
y1mean=[y1mean,y1mean],
|
|
y2mean=[y2mean,y2mean],
|
|
x1mean=[x1mean,x1mean],
|
|
)
|
|
)
|
|
|
|
# Add hover to this comma-separated string and see what changes
|
|
if (promember==1):
|
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
|
|
else:
|
|
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
|
|
|
|
|
|
|
|
sizing_mode = 'fixed' # 'scale_width' also looks nice with this example
|
|
|
|
plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type,
|
|
tools=TOOLS,
|
|
toolbar_sticky=False,
|
|
# plot_width=900,
|
|
)
|
|
x1means = Span(location=x1mean,dimension='height',line_color='green',
|
|
line_dash=[6,6], line_width=2)
|
|
|
|
y1means = Span(location=y1mean,dimension='width',line_color='blue',
|
|
line_dash=[6,6],line_width=2)
|
|
y2means = y1means
|
|
|
|
if (xparam != 'time') and (xparam != 'distance'):
|
|
plot.add_layout(x1means)
|
|
|
|
plot.add_layout(y1means)
|
|
|
|
|
|
|
|
# average values
|
|
if yparam1 == 'driveenergy':
|
|
if xparam == 'spm':
|
|
plot.line(xvals,yconstantpower,color="green",legend="Constant Power")
|
|
|
|
if plottype=='line':
|
|
plot.line('x1','y1',source=source2,legend=axlabels[yparam1])
|
|
elif plottype=='scatter':
|
|
# plot.circle('x1','y1',source=source2,legend=yparam1,size=3)
|
|
plot.scatter('x1','y1',source=source2,legend=axlabels[yparam1],fill_alpha=0.4,
|
|
line_color=None)
|
|
|
|
plot.title.text = row.name
|
|
plot.title.text_font_size=value("1.0em")
|
|
plot.xaxis.axis_label = axlabels[xparam]
|
|
plot.yaxis.axis_label = axlabels[yparam1]
|
|
|
|
|
|
|
|
yrange1 = Range1d(start=yaxminima[yparam1],end=yaxmaxima[yparam1])
|
|
plot.y_range = yrange1
|
|
|
|
if (xparam != 'time') and (xparam != 'distance'):
|
|
xrange1 = Range1d(start=yaxminima[xparam],end=yaxmaxima[xparam])
|
|
plot.x_range = xrange1
|
|
|
|
if xparam == 'time':
|
|
xrange1 = Range1d(start=xaxmin,end=xaxmax)
|
|
plot.x_range = xrange1
|
|
plot.xaxis[0].formatter = DatetimeTickFormatter(
|
|
hours = ["%H"],
|
|
minutes = ["%M"],
|
|
seconds = ["%S"],
|
|
days = ["0"],
|
|
months = [""],
|
|
years = [""]
|
|
)
|
|
|
|
|
|
if yparam1 == 'pace':
|
|
plot.y_range = Range1d(ymin,ymax)
|
|
plot.yaxis[0].formatter = DatetimeTickFormatter(
|
|
seconds = ["%S"],
|
|
minutes = ["%M"]
|
|
)
|
|
|
|
if yparam2 != 'None':
|
|
yrange2 = Range1d(start=yaxminima[yparam2],end=yaxmaxima[yparam2])
|
|
plot.extra_y_ranges = {"yax2": yrange2}
|
|
|
|
if plottype=='line':
|
|
plot.line('x1','y2',color="red",y_range_name="yax2",
|
|
legend=axlabels[yparam2],
|
|
source=source2)
|
|
<<<<<<< HEAD
|
|
|
|
elif plottype=='scatter':
|
|
# plot.circle(x1,y2,color="red",y_range_name="yax2",legend=yparam2,
|
|
# source=source,size=3)
|
|
plot.scatter('x1','y2',source=source2,legend=axlabels[yparam2]
|
|
,fill_alpha=0.4,
|
|
line_color=None,color="red",y_range_name="yax2")
|
|
|
|
plot.add_layout(LinearAxis(y_range_name="yax2",
|
|
axis_label=axlabels[yparam2]),'right')
|
|
|
|
y2means = Span(location=y2mean,dimension='width',line_color='red',
|
|
line_dash=[6,6],line_width=2,y_range_name="yax2")
|
|
|
|
|
|
plot.add_layout(y2means)
|
|
|
|
hover = plot.select(dict(type=HoverTool))
|
|
|
|
|
|
hover.tooltips = OrderedDict([
|
|
('Time','@time'),
|
|
('Distance','@distance'),
|
|
('Pace','@pace'),
|
|
('HR','@hr'),
|
|
('SPM','@spmc{1.1}'),
|
|
('Power','@power{int}'),
|
|
])
|
|
|
|
hover.mode = 'mouse'
|
|
|
|
=======
|
|
|
|
elif plottype=='scatter':
|
|
# plot.circle(x1,y2,color="red",y_range_name="yax2",legend=yparam2,
|
|
# source=source,size=3)
|
|
plot.scatter('x1','y2',source=source2,legend=axlabels[yparam2]
|
|
,fill_alpha=0.4,
|
|
line_color=None,color="red",y_range_name="yax2")
|
|
|
|
plot.add_layout(LinearAxis(y_range_name="yax2",
|
|
axis_label=axlabels[yparam2]),'right')
|
|
|
|
y2means = Span(location=y2mean,dimension='width',line_color='red',
|
|
line_dash=[6,6],line_width=2,y_range_name="yax2")
|
|
|
|
|
|
plot.add_layout(y2means)
|
|
|
|
hover = plot.select(dict(type=HoverTool))
|
|
|
|
|
|
hover.tooltips = OrderedDict([
|
|
('Time','@time'),
|
|
('Distance','@distance'),
|
|
('Pace','@pace'),
|
|
('HR','@hr'),
|
|
('SPM','@spmc{1.1}'),
|
|
('Power','@power{int}'),
|
|
])
|
|
|
|
hover.mode = 'mouse'
|
|
|
|
>>>>>>> feature/sliders
|
|
callback = CustomJS(args = dict(source=source,source2=source2,
|
|
x1means=x1means,
|
|
y1means=y1means,
|
|
y2means=y2means), code="""
|
|
var data = source.data
|
|
var data2 = source2.data
|
|
var x1 = data['x1']
|
|
var y1 = data['y1']
|
|
var y2 = data['y2']
|
|
var spm1 = data['spm']
|
|
var time1 = data['time']
|
|
var pace1 = data['pace']
|
|
var hr1 = data['hr']
|
|
var spmc1 = data['spmc']
|
|
var distance1 = data['distance']
|
|
var power1 = data['power']
|
|
|
|
var minspm = minspm.value
|
|
var maxspm = maxspm.value
|
|
var mindist = mindist.value
|
|
var maxdist = maxdist.value
|
|
var xm = 0
|
|
var ym1 = 0
|
|
var ym2 = 0
|
|
|
|
data2['x1'] = []
|
|
data2['y1'] = []
|
|
data2['y2'] = []
|
|
data2['spm'] = []
|
|
data2['time'] = []
|
|
data2['pace'] = []
|
|
data2['hr'] = []
|
|
data2['spmc'] = []
|
|
data2['distance'] = []
|
|
data2['power'] = []
|
|
data2['x1mean'] = []
|
|
data2['y1mean'] = []
|
|
data2['y2mean'] = []
|
|
data2['xvals'] = []
|
|
data2['y1vals'] = []
|
|
data2['y2vals'] = []
|
|
|
|
for (i=0; i<x1.length; i++) {
|
|
if (spm1[i]>=minspm && spm1[i]<=maxspm) {
|
|
if (distance1[i]>=mindist && distance1[i]<=maxdist) {
|
|
data2['x1'].push(x1[i])
|
|
data2['y1'].push(y1[i])
|
|
data2['y2'].push(y2[i])
|
|
data2['spm'].push(spm1[i])
|
|
data2['time'].push(time1[i])
|
|
data2['pace'].push(pace1[i])
|
|
data2['hr'].push(hr1[i])
|
|
data2['spmc'].push(spmc1[i])
|
|
data2['distance'].push(distance1[i])
|
|
data2['power'].push(power1[i])
|
|
|
|
xm += x1[i]
|
|
ym1 += y1[i]
|
|
ym2 += y2[i]
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
xm /= data2['x1'].length
|
|
ym1 /= data2['x1'].length
|
|
ym2 /= data2['x1'].length
|
|
|
|
data2['x1mean'] = [xm,xm]
|
|
data2['y1mean'] = [ym1,ym1]
|
|
data2['y2mean'] = [ym2,ym2]
|
|
x1means.location = xm
|
|
y1means.location = ym1
|
|
y2means.location = ym2
|
|
|
|
source2.trigger('change');
|
|
""")
|
|
|
|
slider_spm_min = Slider(start=15.0, end=55,value=15.0, step=.1,
|
|
title="Min SPM",callback=callback)
|
|
callback.args["minspm"] = slider_spm_min
|
|
|
|
|
|
slider_spm_max = Slider(start=15.0, end=55,value=55.0, step=.1,
|
|
title="Max SPM",callback=callback)
|
|
callback.args["maxspm"] = slider_spm_max
|
|
|
|
distmax = 100+100*int(distance.max()/100.)
|
|
|
|
slider_dist_min = Slider(start=0,end=distmax,value=0,step=1,
|
|
title="Min Distance",callback=callback)
|
|
callback.args["mindist"] = slider_dist_min
|
|
|
|
slider_dist_max = Slider(start=0,end=distmax,value=distmax,
|
|
step=1,
|
|
title="Max Distance",callback=callback)
|
|
callback.args["maxdist"] = slider_dist_max
|
|
|
|
layout = layoutrow([layoutcolumn([slider_spm_min,
|
|
slider_spm_max,
|
|
slider_dist_min,
|
|
slider_dist_max,
|
|
],
|
|
),
|
|
plot])
|
|
|
|
script, div = components(layout)
|
|
js_resources = INLINE.render_js()
|
|
css_resources = INLINE.render_css()
|
|
|
|
|
|
|
|
return [script,div,js_resources,css_resources]
|
|
|
|
|
|
def interactive_bar_chart(id=0,promember=0):
|
|
# check if valid ID exists (workout exists)
|
|
row = Workout.objects.get(id=id)
|
|
|
|
f1 = row.csvfilename
|
|
|
|
# create interactive plot
|
|
plot = Figure(plot_width=400,plot_height=300)
|
|
# get user
|
|
# u = User.objects.get(id=row.user.id)
|
|
r = row.user
|
|
u = r.user
|
|
|
|
rr = rrower(hrmax=r.max,hrut2=r.ut2,
|
|
hrut1=r.ut1,hrat=r.at,
|
|
hrtr=r.tr,hran=r.an)
|
|
|
|
rowdata = rdata(f1,rower=rr)
|
|
if rowdata == 0:
|
|
return "","CSV Data File Not Found"
|
|
|
|
t = rowdata.df.ix[:,'TimeStamp (sec)']
|
|
t = t-rowdata.df.ix[0,'TimeStamp (sec)']
|
|
|
|
row_index = rowdata.df.ix[:,' Stroke500mPace (sec/500m)'] > 3000
|
|
rowdata.df.loc[row_index,' Stroke500mPace (sec/500m)'] = 3000.
|
|
|
|
|
|
p = rowdata.df.ix[:,' Stroke500mPace (sec/500m)']
|
|
hr = rowdata.df.ix[:,' HRCur (bpm)']
|
|
spm = rowdata.df.ix[:,' Cadence (stokes/min)']
|
|
f = rowdata.df['TimeStamp (sec)'].diff().mean()
|
|
windowsize = 2*(int(10./(f)))+1
|
|
if windowsize <= 3:
|
|
windowsize = 5
|
|
|
|
if windowsize > 3:
|
|
spm = savgol_filter(spm,windowsize,3)
|
|
rowdata.df[' Cadence (stokes/min)'] = spm
|
|
drivelength = rowdata.df[' DriveLength (meters)']
|
|
if windowsize > 3:
|
|
drivelength = savgol_filter(drivelength,windowsize,3)
|
|
rowdata.df[' DriveLength (meters)'] = drivelength
|
|
|
|
hr_ut2 = rowdata.df.ix[:,'hr_ut2']
|
|
hr_ut1 = rowdata.df.ix[:,'hr_ut1']
|
|
hr_at = rowdata.df.ix[:,'hr_at']
|
|
hr_tr = rowdata.df.ix[:,'hr_tr']
|
|
hr_an = rowdata.df.ix[:,'hr_an']
|
|
hr_max = rowdata.df.ix[:,'hr_max']
|
|
|
|
# time increments for bar chart
|
|
time_increments = rowdata.df.ix[:,' ElapsedTime (sec)'].diff()
|
|
time_increments[0] = time_increments[1]
|
|
time_increments = 0.5*time_increments+0.5*np.abs(time_increments)
|
|
|
|
|
|
# Add hover to this comma-separated string and see what changes
|
|
if (promember==1):
|
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
|
|
else:
|
|
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
|
|
|
|
t2 = get_datetimes(t,tzinfo=1)
|
|
p2 = get_datetimes(p)
|
|
|
|
source = ColumnDataSource(
|
|
data = dict(
|
|
x=t2,
|
|
x_right = get_datetimes(t+time_increments,tzinfo=1),
|
|
hr=hr,
|
|
hr_ut2=hr_ut2,
|
|
hr_ut1=hr_ut1,
|
|
hr_at=hr_at,
|
|
hr_tr=hr_tr,
|
|
hr_an=hr_an,
|
|
hr_max=hr_max,
|
|
hr_bottom = 0.0*hr,
|
|
y2=p2,
|
|
tf = niceformat(t2),
|
|
pace = nicepaceformat(p2),
|
|
heartrate = hr,
|
|
spmc=np.rint(10*spm)/10.,
|
|
spm=spm,
|
|
)
|
|
)
|
|
|
|
|
|
plot = Figure(x_axis_type="datetime",y_axis_type="datetime",
|
|
toolbar_sticky=False,
|
|
plot_width=920,
|
|
# toolbar_location="above",
|
|
tools=TOOLS)
|
|
plot.title.text = row.name
|
|
plot.title.text_font_size=value("1.0em")
|
|
plot.xaxis.axis_label = "Time"
|
|
plot.yaxis.axis_label = "Pace (/500m)"
|
|
plot.xaxis[0].formatter = DatetimeTickFormatter(
|
|
hours ="",
|
|
minutes = ["%M"],
|
|
seconds = ["%S"],
|
|
days = ["0"],
|
|
months = [""],
|
|
years = [""]
|
|
)
|
|
|
|
plot.yaxis[0].formatter = DatetimeTickFormatter(
|
|
seconds = ["%S"],
|
|
minutes = ["%M"],
|
|
)
|
|
ymax = datetime.datetime(2016,5,1,0,1,30)
|
|
ymin = datetime.datetime(2016,5,1,0,2,30)
|
|
|
|
if row.workouttype == 'water':
|
|
ymax = datetime.datetime(2016,5,1,0,1,30)
|
|
ymin = datetime.datetime(2016,5,1,0,3,30)
|
|
|
|
plot.y_range = Range1d(ymin,ymax)
|
|
|
|
|
|
hover = plot.select(dict(type=HoverTool))
|
|
|
|
|
|
hover.tooltips = OrderedDict([
|
|
('Time','@tf'),
|
|
('Pace','@pace'),
|
|
('HR','@heartrate'),
|
|
('SPM','@spmc{1.1}'),
|
|
])
|
|
|
|
hover.mode = 'mouse'
|
|
|
|
plot.extra_y_ranges = {"hr": Range1d(start=100,end=200)}
|
|
plot.quad(left='x',top='hr_ut2',bottom='hr_bottom',
|
|
right='x_right',source=source,color="gray",
|
|
y_range_name="hr", legend="<UT2")
|
|
plot.quad(left='x',top='hr_ut1',bottom='hr_bottom',
|
|
right='x_right',source=source,color="tan",
|
|
y_range_name="hr", legend="UT2")
|
|
plot.quad(left='x',top='hr_at',bottom='hr_bottom',
|
|
right='x_right',source=source,color="green",
|
|
y_range_name="hr", legend="UT1")
|
|
plot.quad(left='x',top='hr_tr',bottom='hr_bottom',
|
|
right='x_right',source=source,color="blue",
|
|
y_range_name="hr", legend="AT")
|
|
plot.quad(left='x',top='hr_an',bottom='hr_bottom',
|
|
right='x_right',source=source,color="violet",
|
|
y_range_name="hr", legend="TR")
|
|
plot.quad(left='x',top='hr_max',bottom='hr_bottom',
|
|
right='x_right',source=source,color="red",
|
|
y_range_name="hr", legend="AN")
|
|
|
|
|
|
plot.add_layout(LinearAxis(y_range_name="hr",axis_label="HR"),'right')
|
|
|
|
plot.line('x','y2',source=source,legend="Pace",color="black")
|
|
|
|
|
|
script, div = components(plot)
|
|
|
|
return [script,div]
|
|
|
|
def interactive_comparison_chart(id1=0,id2=0,xparam='distance',yparam='spm',
|
|
promember=0,plottype='line'):
|
|
|
|
|
|
csvcolumns = {
|
|
'time': 'TimeStamp (sec)',
|
|
'distance': 'cum_dist',
|
|
'hr': ' HRCur (bpm)',
|
|
'spm': ' Cadence (stokes/min)',
|
|
'pace': ' Stroke500mPace (sec/500m)',
|
|
'power': ' Power (watts)',
|
|
'averageforce': ' AverageDriveForce (lbs)',
|
|
'drivelength': ' DriveLength (meters)',
|
|
'peakforce': ' PeakDriveForce (lbs)',
|
|
'driveenergy': 'driveenergy',
|
|
'drivespeed': 'drivespeed',
|
|
}
|
|
|
|
axlabels = {
|
|
'time': 'Time',
|
|
'distance': 'Distance (m)',
|
|
'hr': 'Heart Rate (bpm)',
|
|
'spm': 'Stroke Rate (spm)',
|
|
'pace': 'Pace (/500m)',
|
|
'power': 'Power (Watt)',
|
|
'averageforce': 'Average Drive Force (lbs)',
|
|
'drivelength': 'Drive Length (m)',
|
|
'peakforce': 'Peak Drive Force (lbs)',
|
|
'driveenergy': 'Work per Stroke (J)',
|
|
'drivespeed': 'Drive Speed (m/s)',
|
|
}
|
|
|
|
|
|
# check if valid ID exists (workout exists)
|
|
row1 = Workout.objects.get(id=id1)
|
|
row2 = Workout.objects.get(id=id2)
|
|
|
|
f1 = row1.csvfilename
|
|
f2 = row2.csvfilename
|
|
|
|
|
|
rowdata1 = rdata(f1)
|
|
if rowdata1 == 0:
|
|
return "","CSV Data File Not Found"
|
|
|
|
rowdata2 = rdata(f2)
|
|
if rowdata2 == 0:
|
|
return "","CSV Data File Not Found"
|
|
|
|
rowdata1.df['driveenergy'] = rowdata1.df[' DriveLength (meters)']*rowdata1.df[' AverageDriveForce (lbs)']*4.44822
|
|
rowdata2.df['driveenergy'] = rowdata2.df[' DriveLength (meters)']*rowdata2.df[' AverageDriveForce (lbs)']*4.44822
|
|
|
|
spm1 = rowdata1.df.ix[:,csvcolumns['spm']]
|
|
spm2 = rowdata2.df.ix[:,csvcolumns['spm']]
|
|
|
|
f = rowdata1.df['TimeStamp (sec)'].diff().mean()
|
|
windowsize = 2*(int(10./(f)))+1
|
|
if windowsize <= 3:
|
|
windowsize = 5
|
|
|
|
if windowsize > 3:
|
|
spm1 = savgol_filter(spm1,windowsize,3)
|
|
rowdata1.df[' Cadence (stokes/min)'] = spm1
|
|
drivelength1 = rowdata1.df[' DriveLength (meters)']
|
|
if windowsize > 3:
|
|
drivelength1 = savgol_filter(drivelength1,windowsize,3)
|
|
rowdata1.df[' DriveLength (meters)'] = drivelength1
|
|
|
|
drivelength2 = rowdata2.df[' DriveLength (meters)']
|
|
if windowsize > 3:
|
|
drivelength2 = savgol_filter(drivelength2,windowsize,3)
|
|
rowdata2.df[' DriveLength (meters)'] = drivelength2
|
|
|
|
|
|
f = rowdata2.df['TimeStamp (sec)'].diff().mean()
|
|
windowsize = 2*(int(10./(f)))+1
|
|
if windowsize <= 3:
|
|
windowsize = 5
|
|
|
|
if windowsize > 3:
|
|
spm2 = savgol_filter(spm2,windowsize,3)
|
|
rowdata2.df[' Cadence (stokes/min)'] = spm2
|
|
drivelength2 = rowdata2.df[' DriveLength (meters)']
|
|
if windowsize > 3:
|
|
drivelength2 = savgol_filter(drivelength2,windowsize,3)
|
|
rowdata2.df[' DriveLength (meters)'] = drivelength2
|
|
|
|
rowdata1.df['drivespeed'] = drivelength1/rowdata1.df[' DriveTime (ms)']*1.0e3
|
|
rowdata2.df['drivespeed'] = drivelength2/rowdata2.df[' DriveTime (ms)']*1.0e3
|
|
|
|
|
|
x1 = rowdata1.df.ix[:,csvcolumns[xparam]]
|
|
x2 = rowdata2.df.ix[:,csvcolumns[xparam]]
|
|
|
|
y1 = rowdata1.df.ix[:,csvcolumns[yparam]]
|
|
y2 = rowdata2.df.ix[:,csvcolumns[yparam]]
|
|
|
|
if xparam=='time':
|
|
x1 = x1-x1[0]
|
|
x2 = x2-x2[0]
|
|
x1 = get_datetimes(x1,tzinfo=1)
|
|
x2 = get_datetimes(x2,tzinfo=1)
|
|
|
|
x_axis_type = 'linear'
|
|
y_axis_type = 'linear'
|
|
if xparam == 'time':
|
|
x_axis_type = 'datetime'
|
|
|
|
if yparam == 'pace':
|
|
y_axis_type = 'datetime'
|
|
y1 = get_datetimes(y1)
|
|
y2 = get_datetimes(y2)
|
|
ymax = datetime.datetime(2016,5,1,0,1,30)
|
|
ymin = datetime.datetime(2016,5,1,0,2,30)
|
|
|
|
if row1.workouttype == 'water':
|
|
ymax = datetime.datetime(2016,5,1,0,1,30)
|
|
ymin = datetime.datetime(2016,5,1,0,3,30)
|
|
|
|
time1 = rowdata1.df.ix[:,csvcolumns['time']]
|
|
time2 = rowdata2.df.ix[:,csvcolumns['time']]
|
|
|
|
time1 = time1-time1[0]
|
|
time2 = time2-time2[0]
|
|
|
|
hr1 = rowdata1.df.ix[:,csvcolumns['hr']]
|
|
hr2 = rowdata2.df.ix[:,csvcolumns['hr']]
|
|
|
|
|
|
pace1 = rowdata1.df.ix[:,csvcolumns['pace']]
|
|
pace2 = rowdata2.df.ix[:,csvcolumns['pace']]
|
|
|
|
distance1 = rowdata1.df.ix[:,csvcolumns['distance']]
|
|
distance2 = rowdata2.df.ix[:,csvcolumns['distance']]
|
|
|
|
|
|
if (promember==1):
|
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
|
|
else:
|
|
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
|
|
|
|
|
|
|
|
source = ColumnDataSource(
|
|
data = dict(
|
|
x1=x1,
|
|
x2=x2,
|
|
y1=y1,
|
|
y2=y2,
|
|
time1=niceformat(get_datetimes(time1,tzinfo=1)),
|
|
time2=niceformat(get_datetimes(time2,tzinfo=1)),
|
|
pace1=nicepaceformat(get_datetimes(pace1)),
|
|
pace2=nicepaceformat(get_datetimes(pace2)),
|
|
hr1 = hr1,
|
|
hr2 = hr2,
|
|
spm1 = spm1,
|
|
spm2 = spm2,
|
|
spm1c=np.rint(10*spm1)/10.,
|
|
spm2c=np.rint(10*spm2)/10.,
|
|
distance1=distance1,
|
|
distance2=distance2,
|
|
)
|
|
)
|
|
|
|
|
|
|
|
# create interactive plot
|
|
plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type,
|
|
tools=TOOLS,
|
|
plot_width=920,
|
|
# toolbar_location="above",
|
|
toolbar_sticky=False)
|
|
|
|
|
|
# plot.multi_line([x1,x2],[y1,y2],color=["blue","red"])
|
|
if plottype=='line':
|
|
plot.line('x1','y1',source=source,color="blue",legend=row1.name)
|
|
plot.line('x2','y2',source=source,color="red",legend=row2.name)
|
|
elif plottype=='scatter':
|
|
# plot.circle('x1','y1',source=source,color="blue",legend=row1.name,size=3)
|
|
# plot.circle('x2','y2',source=source,color="red",legend=row2.name,size=3)
|
|
plot.scatter('x1','y1',source=source,legend=row1.name,fill_alpha=0.4,
|
|
line_color=None)
|
|
plot.scatter('x2','y2',source=source,legend=row2.name,fill_alpha=0.4,
|
|
line_color=None,color="red")
|
|
|
|
|
|
|
|
|
|
|
|
# plot.scatter('x1','y1',source=source,color="blue")
|
|
# plot.scatter('x2','y2',source=source,color="red")
|
|
|
|
plot.legend.location = "bottom_right"
|
|
|
|
plot.title.text = row1.name+' vs '+row2.name
|
|
plot.title.text_font_size=value("1.2em")
|
|
plot.xaxis.axis_label = axlabels[xparam]
|
|
plot.yaxis.axis_label = axlabels[yparam]
|
|
|
|
if xparam == 'time':
|
|
plot.xaxis[0].formatter = DatetimeTickFormatter(
|
|
hours = ["%H"],
|
|
minutes = ["%M"],
|
|
seconds = ["%S"],
|
|
days = ["0"],
|
|
months = [""],
|
|
years = [""]
|
|
)
|
|
|
|
|
|
if yparam == 'pace':
|
|
plot.y_range = Range1d(ymin,ymax)
|
|
plot.yaxis[0].formatter = DatetimeTickFormatter(
|
|
seconds = ["%S"],
|
|
minutes = ["%M"]
|
|
)
|
|
|
|
|
|
#hover = [t for t in plot.tools if isinstance(t, HoverTool)][0]
|
|
hover = plot.select(dict(type=HoverTool))
|
|
|
|
|
|
hover.tooltips = OrderedDict([
|
|
('time1','@time1'),
|
|
('time2','@time2'),
|
|
('pace1','@pace1'),
|
|
('pace2','@pace2'),
|
|
('hr1','@hr1'),
|
|
('hr2','@hr2'),
|
|
('spm1','@spm1c{1.1}'),
|
|
('spm2','@spm2c{1.1}'),
|
|
('distance1','@distance1{5}'),
|
|
('distance2','@distance2{5}'),
|
|
])
|
|
|
|
hover.mode = 'mouse'
|
|
|
|
script, div = components(plot)
|
|
|
|
return [script,div]
|
|
|
|
def interactive_otw_advanced_pace_chart(id=0,promember=0):
|
|
# check if valid ID exists (workout exists)
|
|
row = Workout.objects.get(id=id)
|
|
|
|
f1 = row.csvfilename
|
|
|
|
|
|
# get user
|
|
# u = User.objects.get(id=row.user.id)
|
|
r = row.user
|
|
u = r.user
|
|
|
|
rowdata = rdata(f1)
|
|
if rowdata == 0:
|
|
return "","CSV Data File Not Found"
|
|
|
|
t = rowdata.df.ix[:,'TimeStamp (sec)']
|
|
t = t-rowdata.df.ix[0,'TimeStamp (sec)']
|
|
|
|
|
|
row_index = rowdata.df.ix[:,' Stroke500mPace (sec/500m)'] > 3000
|
|
rowdata.df.loc[row_index,' Stroke500mPace (sec/500m)'] = 3000.
|
|
|
|
|
|
p = rowdata.df.ix[:,' Stroke500mPace (sec/500m)']
|
|
try:
|
|
nowindpace = rowdata.df.ix[:,'nowindpace']
|
|
except KeyError:
|
|
nowindpace = p
|
|
try:
|
|
equivergpower = rowdata.df.ix[:,'equivergpower']
|
|
except KeyError:
|
|
equivergpower = 0*p+50.
|
|
|
|
ergvelo = (equivergpower/2.8)**(1./3.)
|
|
|
|
# ergvelo = stravastuff.ewmovingaverage(ergvelo,25)
|
|
|
|
ergpace = 500./ergvelo
|
|
ergpace[ergpace == np.inf] = 240.
|
|
|
|
hr = rowdata.df.ix[:,' HRCur (bpm)']
|
|
spm = rowdata.df.ix[:,' Cadence (stokes/min)']
|
|
|
|
f = rowdata.df['TimeStamp (sec)'].diff().mean()
|
|
windowsize = 2*(int(10./(f)))+1
|
|
if windowsize <= 3:
|
|
windowsize = 5
|
|
|
|
if windowsize > 3:
|
|
spm = savgol_filter(spm,windowsize,3)
|
|
rowdata.df[' Cadence (stokes/min)'] = spm
|
|
drivelength = rowdata.df[' DriveLength (meters)']
|
|
if windowsize > 3:
|
|
drivelength = savgol_filter(drivelength,windowsize,3)
|
|
rowdata.df[' DriveLength (meters)'] = drivelength
|
|
|
|
t2 = get_datetimes(t,tzinfo=1)
|
|
p2 = get_datetimes(p)
|
|
nowindp2 = get_datetimes(nowindpace)
|
|
ergpace2 = get_datetimes(ergpace)
|
|
|
|
# Add hover to this comma-separated string and see what changes
|
|
if (promember==1):
|
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
|
|
else:
|
|
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
|
|
|
|
source = ColumnDataSource(
|
|
data = dict(
|
|
x=t2,
|
|
p=p2,
|
|
nowindp2 = nowindp2,
|
|
ergpace2 = ergpace2,
|
|
tf = niceformat(t2),
|
|
pace = nicepaceformat(p2),
|
|
ergpace = nicepaceformat(ergpace2),
|
|
nowindpace = nicepaceformat(nowindp2),
|
|
heartrate = hr,
|
|
spm=spm,
|
|
spmc=np.rint(10*spm)/10.,
|
|
)
|
|
)
|
|
|
|
plot = Figure(x_axis_type="datetime",y_axis_type="datetime",
|
|
tools=TOOLS,
|
|
plot_width=920,
|
|
toolbar_sticky=False)
|
|
|
|
plot.title.text = row.name
|
|
plot.title.text_font_size=value("1.2em")
|
|
plot.xaxis.axis_label = "Time"
|
|
plot.yaxis.axis_label = "Pace (/500m)"
|
|
plot.xaxis[0].formatter = DatetimeTickFormatter(
|
|
hours = ["%H"],
|
|
minutes = ["%M"],
|
|
seconds = ["%S"],
|
|
days = ["0"],
|
|
months = [""],
|
|
years = [""]
|
|
)
|
|
plot.yaxis[0].formatter = DatetimeTickFormatter(
|
|
seconds = ["%S"],
|
|
minutes = ["%M"]
|
|
)
|
|
|
|
ymax = datetime.datetime(2016,5,1,0,1,30)
|
|
ymin = datetime.datetime(2016,5,1,0,3,30)
|
|
|
|
plot.y_range = Range1d(ymin,ymax)
|
|
|
|
|
|
hover = plot.select(dict(type=HoverTool))
|
|
|
|
plot.line('x','p',source=source,legend="Pace",color="black")
|
|
plot.line('x','nowindp2',source=source,legend="Corrected Pace",color="red")
|
|
plot.line('x','ergpace2',source=source,legend="Equivalent Erg Pace",color="blue")
|
|
|
|
hover.tooltips = OrderedDict([
|
|
('Time','@tf'),
|
|
('Pace','@pace'),
|
|
('Corrected Pace','@nowindpace'),
|
|
('Equiv. Erg Pace','@ergpace'),
|
|
('HR','@heartrate'),
|
|
('SPM','@spmc{1.1}'),
|
|
])
|
|
|
|
hover.mode = 'mouse'
|
|
|
|
script, div = components(plot)
|
|
|
|
return [script,div]
|
|
|