3071 lines
85 KiB
Python
3071 lines
85 KiB
Python
import colorsys
|
|
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 math import pi
|
|
from django.utils import timezone
|
|
|
|
from bokeh.palettes import Dark2_8 as palette
|
|
import itertools
|
|
from bokeh.plotting import figure, ColumnDataSource, Figure,curdoc
|
|
from bokeh.models import CustomJS,Slider, TextInput,BoxAnnotation
|
|
from bokeh.charts import Histogram,HeatMap,Area,BoxPlot,Bar
|
|
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, vplot
|
|
from bokeh.models import (
|
|
GMapPlot, GMapOptions, ColumnDataSource, Circle,
|
|
DataRange1d, PanTool, WheelZoomTool, BoxSelectTool,
|
|
SaveTool, ResizeTool, ResetTool, TapTool,CrosshairTool,BoxZoomTool,
|
|
Span, Label
|
|
)
|
|
from bokeh.models.glyphs import ImageURL
|
|
|
|
#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
|
|
|
|
from rowers.dataprep import rdata
|
|
import rowers.dataprep as dataprep
|
|
|
|
from rowers.metrics import axes,axlabels,yaxminima,yaxmaxima
|
|
|
|
from utils import lbstoN
|
|
import datautils
|
|
|
|
watermarkurl = "/static/img/logo7.png"
|
|
watermarksource = ColumnDataSource(dict(
|
|
url = [watermarkurl],))
|
|
|
|
watermarkrange = Range1d(start=0,end=1)
|
|
watermarkalpha = 0.6
|
|
watermarkx = 0.99
|
|
watermarky = 0.01
|
|
watermarkw = 184
|
|
watermarkh = 35
|
|
watermarkanchor = 'bottom_right'
|
|
|
|
def errorbar(fig, x, y, source=ColumnDataSource(),
|
|
xerr=False, yerr=False, color='black',
|
|
point_kwargs={}, error_kwargs={}):
|
|
|
|
|
|
xvalues = source.data[x]
|
|
yvalues = source.data[y]
|
|
|
|
xerrvalues = source.data['xerror']
|
|
yerrvalues = source.data['yerror']
|
|
try:
|
|
colorvalues = source.data['color']
|
|
except KeyError:
|
|
colorvalues = ["#%02x%02x%02x" % (255,0,0) for x in xvalues]
|
|
|
|
|
|
try:
|
|
a = xvalues[0]+1
|
|
if xerr:
|
|
x_err_x = []
|
|
x_err_y = []
|
|
err_color = []
|
|
for px, py, err, color in zip(xvalues, yvalues, xerrvalues, colorvalues):
|
|
x_err_x.append((px - err, px + err))
|
|
x_err_y.append((py, py))
|
|
(r, g, b) = tuple(int(color[i:i+2],16) for i in (1, 3, 5))
|
|
h,s,v = colorsys.rgb_to_hsv(r/255., g/255., b/255.)
|
|
v = v*0.8
|
|
r, g, b = colorsys.hsv_to_rgb(h, s, v)
|
|
color2 = "#%02x%02x%02x" % (int(255.*r), int(255.*g), int(255*b))
|
|
err_color.append(color2)
|
|
|
|
fig.multi_line(x_err_x, x_err_y, color=err_color,
|
|
name='xerr',
|
|
**error_kwargs)
|
|
except TypeError:
|
|
pass
|
|
|
|
try:
|
|
a = yvalues[0]+1
|
|
if yerr:
|
|
y_err_x = []
|
|
y_err_y = []
|
|
err_color = []
|
|
for px, py, err, color in zip(xvalues, yvalues, yerrvalues, colorvalues):
|
|
y_err_x.append((px, px))
|
|
y_err_y.append((py - err, py + err))
|
|
(r, g, b) = tuple(int(color[i:i+2],16) for i in (1, 3, 5))
|
|
h,s,v = colorsys.rgb_to_hsv(r/255., g/255., b/255.)
|
|
v = v*0.8
|
|
r, g, b = colorsys.hsv_to_rgb(h, s, v)
|
|
color2 = "#%02x%02x%02x" % (int(255.*r), int(255.*g), int(255*b))
|
|
err_color.append(color2)
|
|
|
|
fig.multi_line(y_err_x, y_err_y, color=err_color,
|
|
name='yerr',**error_kwargs)
|
|
except TypeError:
|
|
pass
|
|
|
|
fig.circle(x, y, source=source, name='data',color=color,
|
|
**point_kwargs)
|
|
|
|
|
|
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
|
|
|
|
|
|
from rowers.dataprep import nicepaceformat,niceformat
|
|
from rowers.dataprep import timedeltaconv
|
|
|
|
def interactive_boxchart(datadf,fieldname,extratitle=''):
|
|
|
|
if datadf.empty:
|
|
return '','It looks like there are no data matching your filter'
|
|
|
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,resize,hover'
|
|
|
|
plot = BoxPlot(datadf, values=fieldname, label='date',
|
|
legend=False,
|
|
title=axlabels[fieldname]+' '+extratitle,
|
|
outliers=False,
|
|
tools=TOOLS,
|
|
toolbar_location="above",
|
|
toolbar_sticky=False,
|
|
x_mapper_type='datetime')
|
|
|
|
yrange1 = Range1d(start=yaxminima[fieldname],end=yaxmaxima[fieldname])
|
|
plot.y_range = yrange1
|
|
|
|
plot.xaxis.axis_label = 'Date'
|
|
plot.yaxis.axis_label = axlabels[fieldname]
|
|
|
|
|
|
# add watermark
|
|
plot.extra_y_ranges = {"watermark": watermarkrange}
|
|
plot.extra_x_ranges = {"watermark": watermarkrange}
|
|
|
|
plot.image_url([watermarkurl],watermarkx,watermarky,
|
|
watermarkw,watermarkh,
|
|
global_alpha=watermarkalpha,
|
|
w_units='screen',
|
|
h_units='screen',
|
|
anchor=watermarkanchor,
|
|
dilate=True,
|
|
x_range_name = "watermark",
|
|
y_range_name = "watermark",
|
|
)
|
|
|
|
|
|
plot.xaxis.formatter = DatetimeTickFormatter(
|
|
days=["%d %B %Y"],
|
|
months=["%d %B %Y"],
|
|
years=["%d %B %Y"],
|
|
)
|
|
|
|
if fieldname == 'pace':
|
|
plot.yaxis[0].formatter = DatetimeTickFormatter(
|
|
seconds = ["%S"],
|
|
minutes = ["%M"]
|
|
)
|
|
|
|
|
|
plot.xaxis.major_label_orientation = pi/4
|
|
|
|
hover = plot.select(dict(type=HoverTool))
|
|
|
|
hover.tooltips = OrderedDict([
|
|
('Value','@y'),
|
|
('Date','@x'),
|
|
])
|
|
|
|
hover.mode = 'mouse'
|
|
|
|
script, div = components(plot)
|
|
|
|
return script,div
|
|
|
|
|
|
def interactive_activitychart(workouts,startdate,enddate):
|
|
if len(workouts) == 0:
|
|
return "",""
|
|
|
|
dates = []
|
|
types = []
|
|
durations = []
|
|
|
|
for w in workouts:
|
|
if w.privacy == 'visible':
|
|
dd = w.date.strftime('%m/%d')
|
|
du = w.duration.hour*60+w.duration.minute
|
|
dates.append(dd)
|
|
durations.append(du)
|
|
types.append(w.workouttype)
|
|
|
|
try:
|
|
d = utc.localize(startdate)
|
|
except ValueError:
|
|
d = startdate
|
|
|
|
try:
|
|
enddate = utc.localize(enddate)
|
|
except ValueError:
|
|
pass
|
|
|
|
|
|
while d<=enddate:
|
|
dates.append(d.strftime('%m/%d'))
|
|
durations.append(0)
|
|
types.append('rower')
|
|
d += datetime.timedelta(days=1)
|
|
|
|
|
|
df = pd.DataFrame({
|
|
'date':dates,
|
|
'duration':durations,
|
|
'type':types,
|
|
})
|
|
|
|
p = Bar(df,'date',values='duration',title='Activity',
|
|
stack='type',
|
|
plot_width=350,
|
|
plot_height=250,
|
|
toolbar_location = None,
|
|
)
|
|
|
|
for legend in p.legend:
|
|
new_items = []
|
|
for legend_item in legend.items:
|
|
it = legend_item.label['value']
|
|
tot = df[df['type']==it].duration.sum()
|
|
if tot != 0:
|
|
new_items.append(legend_item)
|
|
legend.items = new_items
|
|
|
|
|
|
p.legend.location = "top_left"
|
|
p.legend.background_fill_alpha = 0.7
|
|
|
|
p.yaxis.axis_label = 'Minutes'
|
|
|
|
script, div = components(p)
|
|
|
|
return script,div
|
|
|
|
def interactive_forcecurve(theworkouts,workstrokesonly=False):
|
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
|
|
|
|
ids = [int(w.id) for w in theworkouts]
|
|
|
|
boattype = theworkouts[0].boattype
|
|
|
|
columns = ['catch','slip','wash','finish','averageforce',
|
|
'peakforceangle','peakforce','spm','distance',
|
|
'workoutstate','driveenergy']
|
|
|
|
rowdata = dataprep.getsmallrowdata_db(columns,ids=ids)
|
|
|
|
rowdata.dropna(axis=1,how='all',inplace=True)
|
|
rowdata.dropna(axis=0,how='any',inplace=True)
|
|
|
|
workoutstateswork = [1,4,5,8,9,6,7]
|
|
workoutstatesrest = [3]
|
|
workoutstatetransition = [0,2,10,11,12,13]
|
|
|
|
if workstrokesonly:
|
|
try:
|
|
rowdata = rowdata[~rowdata['workoutstate'].isin(workoutstatesrest)]
|
|
except KeyError:
|
|
pass
|
|
|
|
if rowdata.empty:
|
|
return "","No Valid Data Available","",""
|
|
|
|
try:
|
|
catchav = rowdata['catch'].mean()
|
|
except KeyError:
|
|
catchav = 0
|
|
|
|
try:
|
|
finishav = rowdata['finish'].mean()
|
|
except KeyError:
|
|
finishav = 0
|
|
try:
|
|
washav = rowdata['wash'].mean()
|
|
except KeyError:
|
|
washav = 0
|
|
|
|
try:
|
|
slipav = rowdata['slip'].mean()
|
|
except KeyError:
|
|
slipav = 0
|
|
|
|
try:
|
|
peakforceav = rowdata['peakforce'].mean()
|
|
except KeyError:
|
|
peakforceav = 0
|
|
|
|
try:
|
|
averageforceav = rowdata['averageforce'].mean()
|
|
except KeyError:
|
|
averageforceav = 0
|
|
|
|
try:
|
|
peakforceangleav = rowdata['peakforceangle'].mean()
|
|
except KeyError:
|
|
peakforceangleav = 0
|
|
|
|
x = [catchav,
|
|
catchav+slipav,
|
|
peakforceangleav,
|
|
finishav-washav,
|
|
finishav]
|
|
|
|
thresholdforce = 100 if 'x' in boattype else 200
|
|
#thresholdforce /= 4.45 # N to lbs
|
|
y = [0,thresholdforce,
|
|
peakforceav,
|
|
thresholdforce,0]
|
|
|
|
source = ColumnDataSource(
|
|
data = dict(
|
|
x = x,
|
|
y = y,
|
|
))
|
|
|
|
|
|
source2 = ColumnDataSource(
|
|
rowdata
|
|
)
|
|
|
|
plot = Figure(tools=TOOLS,
|
|
toolbar_sticky=False)
|
|
|
|
# add watermark
|
|
plot.extra_y_ranges = {"watermark": watermarkrange}
|
|
plot.extra_x_ranges = {"watermark": watermarkrange}
|
|
|
|
plot.image_url([watermarkurl],watermarkx,watermarky,
|
|
watermarkw,watermarkh,
|
|
global_alpha=watermarkalpha,
|
|
w_units='screen',
|
|
h_units='screen',
|
|
anchor=watermarkanchor,
|
|
dilate=True,
|
|
x_range_name = "watermark",
|
|
y_range_name = "watermark",
|
|
)
|
|
|
|
avf = Span(location=averageforceav,dimension='width',line_color='blue',
|
|
line_dash=[6,6],line_width=2)
|
|
|
|
plot.line('x','y',source=source,color="red")
|
|
|
|
plot.add_layout(avf)
|
|
|
|
peakflabel = Label(x=455,y=530,x_units='screen',y_units='screen',
|
|
text="Fpeak: {peakforceav:6.2f}".format(peakforceav=peakforceav),
|
|
background_fill_alpha=.7,
|
|
background_fill_color='white',
|
|
text_color='blue',
|
|
)
|
|
|
|
avflabel = Label(x=465,y=500,x_units='screen',y_units='screen',
|
|
text="Favg: {averageforceav:6.2f}".format(averageforceav=averageforceav),
|
|
background_fill_alpha=.7,
|
|
background_fill_color='white',
|
|
text_color='blue',
|
|
)
|
|
|
|
catchlabel = Label(x=460,y=470,x_units='screen',y_units='screen',
|
|
text="Catch: {catchav:6.2f}".format(catchav=catchav),
|
|
background_fill_alpha=0.7,
|
|
background_fill_color='white',
|
|
text_color='red',
|
|
)
|
|
|
|
peakforceanglelabel = Label(x=420,y=440,x_units='screen',y_units='screen',
|
|
text="Peak angle: {peakforceangleav:6.2f}".format(peakforceangleav=peakforceangleav),
|
|
background_fill_alpha=0.7,
|
|
background_fill_color='white',
|
|
text_color='red',
|
|
)
|
|
|
|
finishlabel = Label(x=455,y=410,x_units='screen',y_units='screen',
|
|
text="Finish: {finishav:6.2f}".format(finishav=finishav),
|
|
background_fill_alpha=0.7,
|
|
background_fill_color='white',
|
|
text_color='red',
|
|
)
|
|
|
|
sliplabel = Label(x=470,y=380,x_units='screen',y_units='screen',
|
|
text="Slip: {slipav:6.2f}".format(slipav=slipav),
|
|
background_fill_alpha=0.7,
|
|
background_fill_color='white',
|
|
text_color='red',
|
|
)
|
|
|
|
washlabel = Label(x=460,y=350,x_units='screen',y_units='screen',
|
|
text="Wash: {washav:6.2f}".format(washav=washav),
|
|
background_fill_alpha=0.7,
|
|
background_fill_color='white',
|
|
text_color='red',
|
|
)
|
|
|
|
plot.add_layout(peakflabel)
|
|
plot.add_layout(peakforceanglelabel)
|
|
plot.add_layout(avflabel)
|
|
plot.add_layout(catchlabel)
|
|
plot.add_layout(sliplabel)
|
|
plot.add_layout(washlabel)
|
|
plot.add_layout(finishlabel)
|
|
|
|
plot.xaxis.axis_label = "Angle"
|
|
plot.yaxis.axis_label = "Force (N)"
|
|
plot.title.text = theworkouts[0].name
|
|
plot.title.text_font_size=value("1.0em")
|
|
|
|
yrange1 = Range1d(start=0,end=900)
|
|
plot.y_range = yrange1
|
|
|
|
xrange1 = Range1d(start=yaxmaxima['catch'],end=yaxmaxima['finish'])
|
|
plot.x_range = xrange1
|
|
|
|
callback = CustomJS(args = dict(
|
|
source=source,
|
|
source2=source2,
|
|
avf=avf,
|
|
avflabel=avflabel,
|
|
catchlabel=catchlabel,
|
|
finishlabel=finishlabel,
|
|
sliplabel=sliplabel,
|
|
washlabel=washlabel,
|
|
peakflabel=peakflabel,
|
|
peakforceanglelabel=peakforceanglelabel,
|
|
), code="""
|
|
var data = source.data
|
|
var data2 = source2.data
|
|
|
|
var x = data['x']
|
|
var y = data['y']
|
|
var spm1 = data2['spm']
|
|
var distance1 = data2['distance']
|
|
var driveenergy1 = data2['driveenergy']
|
|
|
|
var thresholdforce = y[1]
|
|
|
|
var c = source2.data['catch']
|
|
var finish = data2['finish']
|
|
var slip = data2['slip']
|
|
var wash = data2['wash']
|
|
var peakforceangle = data2['peakforceangle']
|
|
var peakforce = data2['peakforce']
|
|
var averageforce = data2['averageforce']
|
|
|
|
var minspm = minspm.value
|
|
var maxspm = maxspm.value
|
|
var mindist = mindist.value
|
|
var maxdist = maxdist.value
|
|
var minwork = minwork.value
|
|
var maxwork = maxwork.value
|
|
|
|
var catchav = 0
|
|
var finishav = 0
|
|
var slipav = 0
|
|
var washav = 0
|
|
var peakforceangleav = 0
|
|
var averageforceav = 0
|
|
var peakforceav = 0
|
|
var count = 0
|
|
|
|
|
|
for (i=0; i<c.length; i++) {
|
|
if (spm1[i]>=minspm && spm1[i]<=maxspm) {
|
|
if (distance1[i]>=mindist && distance1[i]<=maxdist) {
|
|
if (driveenergy1[i]>=minwork && driveenergy1[i]<=maxwork) {
|
|
catchav += c[i]
|
|
finishav += finish[i]
|
|
slipav += slip[i]
|
|
washav += wash[i]
|
|
peakforceangleav += peakforceangle[i]
|
|
averageforceav += averageforce[i]
|
|
peakforceav += peakforce[i]
|
|
count += 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
catchav /= count
|
|
finishav /= count
|
|
slipav /= count
|
|
washav /= count
|
|
peakforceangleav /= count
|
|
peakforceav /= count
|
|
averageforceav /= count
|
|
|
|
data['x'] = [catchav,catchav+slipav,peakforceangleav,finishav-washav,finishav]
|
|
data['y'] = [0,thresholdforce,peakforceav,thresholdforce,0]
|
|
|
|
avf.location = averageforceav
|
|
avflabel.text = 'Favg: '+averageforceav.toFixed(2)
|
|
catchlabel.text = 'Catch: '+catchav.toFixed(2)
|
|
finishlabel.text = 'Finish: '+finishav.toFixed(2)
|
|
sliplabel.text = 'Slip: '+slipav.toFixed(2)
|
|
washlabel.text = 'Wash: '+washav.toFixed(2)
|
|
peakflabel.text = 'Fpeak: '+peakforceav.toFixed(2)
|
|
peakforceanglelabel.text = 'Peak angle: '+peakforceangleav.toFixed(2)
|
|
|
|
source.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
|
|
|
|
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.)
|
|
|
|
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,
|
|
slider_work_min,
|
|
slider_work_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_histoall(theworkouts):
|
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
|
|
|
|
ids = [int(w.id) for w in theworkouts]
|
|
|
|
rowdata = dataprep.getsmallrowdata_db(['power'],ids=ids,doclean=True)
|
|
|
|
|
|
rowdata.dropna(axis=0,how='any',inplace=True)
|
|
|
|
if rowdata.empty:
|
|
return "","No Valid Data Available","",""
|
|
|
|
histopwr = rowdata['power'].values
|
|
if len(histopwr) == 0:
|
|
return "","No valid data available","",""
|
|
|
|
# throw out nans
|
|
histopwr = histopwr[~np.isinf(histopwr)]
|
|
histopwr = histopwr[histopwr > 25]
|
|
histopwr = histopwr[histopwr < 1000]
|
|
|
|
plot = Figure(tools=TOOLS,plot_width=900,
|
|
toolbar_sticky=False,
|
|
toolbar_location="above"
|
|
)
|
|
|
|
# add watermark
|
|
plot.extra_y_ranges = {"watermark": watermarkrange}
|
|
plot.extra_x_ranges = {"watermark": watermarkrange}
|
|
|
|
plot.image_url([watermarkurl],watermarkx,watermarky,
|
|
watermarkw,watermarkh,
|
|
global_alpha=watermarkalpha,
|
|
w_units='screen',
|
|
h_units='screen',
|
|
anchor=watermarkanchor,
|
|
dilate=True,
|
|
x_range_name = "watermark",
|
|
y_range_name = "watermark",
|
|
)
|
|
|
|
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 leaflet_chart(lat,lon,name=""):
|
|
if lat.empty or lon.empty:
|
|
return [0,"invalid coordinate data"]
|
|
|
|
latmean = lat.mean()
|
|
lonmean = lon.mean()
|
|
latbegin = lat[lat.index[0]]
|
|
longbegin = lon[lon.index[0]]
|
|
latend = lat[lat.index[-1]]
|
|
longend = lon[lon.index[-1]]
|
|
|
|
coordinates = zip(lat,lon)
|
|
|
|
scoordinates = "["
|
|
|
|
for x,y in coordinates:
|
|
scoordinates += """[{x},{y}],
|
|
""".format(
|
|
x=x,
|
|
y=y
|
|
)
|
|
|
|
scoordinates += "]"
|
|
|
|
script = """
|
|
<script>
|
|
|
|
|
|
var streets = L.tileLayer('https://api.tiles.mapbox.com/v4/{{id}}/{{z}}/{{x}}/{{y}}.png?access_token=pk.eyJ1Ijoic2FuZGVycm9vc2VuZGFhbCIsImEiOiJjajY3aTRkeWQwNmx6MzJvMTN3andlcnBlIn0.MFG8Xt0kDeSA9j7puZQ9hA', {{
|
|
maxZoom: 18,
|
|
id: 'mapbox.streets'
|
|
}}),
|
|
|
|
satellite = L.tileLayer('https://api.tiles.mapbox.com/v4/{{id}}/{{z}}/{{x}}/{{y}}.png?access_token=pk.eyJ1Ijoic2FuZGVycm9vc2VuZGFhbCIsImEiOiJjajY3aTRkeWQwNmx6MzJvMTN3andlcnBlIn0.MFG8Xt0kDeSA9j7puZQ9hA', {{
|
|
maxZoom: 18,
|
|
id: 'mapbox.satellite'
|
|
}}),
|
|
|
|
outdoors = L.tileLayer('https://api.tiles.mapbox.com/v4/{{id}}/{{z}}/{{x}}/{{y}}.png?access_token=pk.eyJ1Ijoic2FuZGVycm9vc2VuZGFhbCIsImEiOiJjajY3aTRkeWQwNmx6MzJvMTN3andlcnBlIn0.MFG8Xt0kDeSA9j7puZQ9hA', {{
|
|
maxZoom: 18,
|
|
id: 'mapbox.outdoors'
|
|
}});
|
|
|
|
|
|
|
|
var mymap = L.map('map_canvas', {{
|
|
center: [{latmean}, {lonmean}],
|
|
zoom: 13,
|
|
layers: [streets, satellite]
|
|
}}).setView([{latmean},{lonmean}], 13);
|
|
|
|
var osmUrl2='http://tiles.openseamap.org/seamark/{{z}}/{{x}}/{{y}}.png';
|
|
var osmUrl='http://{{s}}.tile.openstreetmap.org/{{z}}/{{x}}/{{y}}.png';
|
|
|
|
|
|
//create two TileLayer
|
|
var nautical=new L.TileLayer(osmUrl,{{
|
|
maxZoom:18}});
|
|
|
|
|
|
L.control.layers({{
|
|
"Streets": streets,
|
|
"Satellite": satellite,
|
|
"Outdoors": outdoors,
|
|
"Nautical": nautical,
|
|
}}).addTo(mymap);
|
|
|
|
var marker = L.marker([{latbegin}, {longbegin}]).addTo(mymap);
|
|
marker.bindPopup("<b>Start</b>");
|
|
var emarker = new L.marker([{latend}, {longend}]).addTo(mymap);
|
|
emarker.bindPopup("<b>End</b>");
|
|
|
|
var latlongs = {scoordinates}
|
|
var polyline = L.polyline(latlongs, {{color:'red'}}).addTo(mymap)
|
|
mymap.fitBounds(polyline.getBounds())
|
|
|
|
</script>
|
|
""".format(
|
|
latmean=latmean,
|
|
lonmean=lonmean,
|
|
latbegin = latbegin,
|
|
latend=latend,
|
|
longbegin=longbegin,
|
|
longend=longend,
|
|
scoordinates=scoordinates,
|
|
)
|
|
|
|
div = """
|
|
<div id="map_canvas" style="width: 100%; height: 400px;"><p> </p></div>
|
|
"""
|
|
|
|
|
|
|
|
return script,div
|
|
|
|
def leaflet_chart2(lat,lon,name=""):
|
|
if lat.empty or lon.empty:
|
|
return [0,"invalid coordinate data"]
|
|
|
|
latmean = lat.mean()
|
|
lonmean = lon.mean()
|
|
latbegin = lat[lat.index[0]]
|
|
longbegin = lon[lon.index[0]]
|
|
latend = lat[lat.index[-1]]
|
|
longend = lon[lon.index[-1]]
|
|
|
|
coordinates = zip(lat,lon)
|
|
|
|
scoordinates = "["
|
|
|
|
for x,y in coordinates:
|
|
scoordinates += """[{x},{y}],
|
|
""".format(
|
|
x=x,
|
|
y=y
|
|
)
|
|
|
|
scoordinates += "]"
|
|
|
|
script = """
|
|
<script>
|
|
|
|
|
|
var streets = L.tileLayer('https://api.tiles.mapbox.com/v4/{{id}}/{{z}}/{{x}}/{{y}}.png?access_token=pk.eyJ1Ijoic2FuZGVycm9vc2VuZGFhbCIsImEiOiJjajY3aTRkeWQwNmx6MzJvMTN3andlcnBlIn0.MFG8Xt0kDeSA9j7puZQ9hA', {{
|
|
maxZoom: 18,
|
|
id: 'mapbox.streets'
|
|
}}),
|
|
|
|
satellite = L.tileLayer('https://api.tiles.mapbox.com/v4/{{id}}/{{z}}/{{x}}/{{y}}.png?access_token=pk.eyJ1Ijoic2FuZGVycm9vc2VuZGFhbCIsImEiOiJjajY3aTRkeWQwNmx6MzJvMTN3andlcnBlIn0.MFG8Xt0kDeSA9j7puZQ9hA', {{
|
|
maxZoom: 18,
|
|
id: 'mapbox.satellite'
|
|
}}),
|
|
|
|
outdoors = L.tileLayer('https://api.tiles.mapbox.com/v4/{{id}}/{{z}}/{{x}}/{{y}}.png?access_token=pk.eyJ1Ijoic2FuZGVycm9vc2VuZGFhbCIsImEiOiJjajY3aTRkeWQwNmx6MzJvMTN3andlcnBlIn0.MFG8Xt0kDeSA9j7puZQ9hA', {{
|
|
maxZoom: 18,
|
|
id: 'mapbox.outdoors'
|
|
}});
|
|
|
|
|
|
|
|
var mymap = L.map('map_canvas', {{
|
|
center: [{latmean}, {lonmean}],
|
|
zoom: 13,
|
|
layers: [streets, satellite]
|
|
}}).setView([{latmean},{lonmean}], 13);
|
|
|
|
var navionics = new JNC.Leaflet.NavionicsOverlay({{
|
|
navKey: 'Navionics_webapi_03205',
|
|
chartType: JNC.NAVIONICS_CHARTS.NAUTICAL,
|
|
isTransparent: true,
|
|
zIndex: 1
|
|
}});
|
|
|
|
|
|
var osmUrl2='http://tiles.openseamap.org/seamark/{{z}}/{{x}}/{{y}}.png';
|
|
var osmUrl='http://{{s}}.tile.openstreetmap.org/{{z}}/{{x}}/{{y}}.png';
|
|
|
|
|
|
//create two TileLayer
|
|
var nautical=new L.TileLayer(osmUrl,{{
|
|
maxZoom:18}});
|
|
|
|
|
|
L.control.layers({{
|
|
"Streets": streets,
|
|
"Satellite": satellite,
|
|
"Outdoors": outdoors,
|
|
"Nautical": nautical,
|
|
}},{{
|
|
"Navionics":navionics,
|
|
}}).addTo(mymap);
|
|
|
|
var marker = L.marker([{latbegin}, {longbegin}]).addTo(mymap);
|
|
marker.bindPopup("<b>Start</b>");
|
|
var emarker = new L.marker([{latend}, {longend}]).addTo(mymap);
|
|
emarker.bindPopup("<b>End</b>");
|
|
|
|
var latlongs = {scoordinates}
|
|
var polyline = L.polyline(latlongs, {{color:'red'}}).addTo(mymap)
|
|
mymap.fitBounds(polyline.getBounds())
|
|
|
|
</script>
|
|
""".format(
|
|
latmean=latmean,
|
|
lonmean=lonmean,
|
|
latbegin = latbegin,
|
|
latend=latend,
|
|
longbegin=longbegin,
|
|
longend=longend,
|
|
scoordinates=scoordinates,
|
|
)
|
|
|
|
div = """
|
|
<div id="map_canvas" style="width: 100%; height: 400px;"><p> </p></div>
|
|
"""
|
|
|
|
|
|
|
|
return script,div
|
|
|
|
def googlemap_chart(lat,lon,name=""):
|
|
if lat.empty or lon.empty:
|
|
return [0,"invalid coordinate data"]
|
|
# plot tools
|
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,resize'
|
|
|
|
map_options = GMapOptions(lat = lat.mean(),lng=lon.mean(),
|
|
map_type="roadmap",zoom=13)
|
|
|
|
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_otwcpchart(powerdf,promember=0):
|
|
powerdf = powerdf[~(powerdf == 0).any(axis=1)]
|
|
# 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'
|
|
|
|
deltas = powerdf['Delta'].apply(lambda x: timedeltaconv(x))
|
|
powerdf['ftime'] = niceformat(deltas)
|
|
|
|
|
|
source = ColumnDataSource(
|
|
data = powerdf
|
|
)
|
|
|
|
# there is no Paul's law for OTW
|
|
|
|
thesecs = powerdf['Delta']
|
|
theavpower = powerdf['CP']
|
|
|
|
p1,fitt,fitpower,ratio = datautils.cpfit(powerdf)
|
|
|
|
message = ""
|
|
#if len(fitpower[fitpower<0]) > 0:
|
|
# message = "CP model fit didn't give correct results"
|
|
|
|
|
|
deltas = fitt.apply(lambda x: timedeltaconv(x))
|
|
ftime = niceformat(deltas)
|
|
|
|
sourcecomplex = ColumnDataSource(
|
|
data = dict(
|
|
CP = fitpower,
|
|
CPmax = ratio*fitpower,
|
|
duration = fitt,
|
|
ftime = ftime
|
|
)
|
|
)
|
|
|
|
# making the plot
|
|
plot = Figure(tools=TOOLS,x_axis_type=x_axis_type,
|
|
plot_width=900,
|
|
toolbar_location="above",
|
|
toolbar_sticky=False)
|
|
|
|
# add watermark
|
|
plot.extra_y_ranges = {"watermark": watermarkrange}
|
|
|
|
plot.image_url([watermarkurl],1.8*max(thesecs),watermarky,
|
|
watermarkw,watermarkh,
|
|
global_alpha=watermarkalpha,
|
|
w_units='screen',
|
|
h_units='screen',
|
|
anchor=watermarkanchor,
|
|
dilate=True,
|
|
y_range_name = "watermark",
|
|
)
|
|
|
|
plot.circle('Delta','CP',source=source,fill_color='red',size=15,
|
|
legend='Power Data')
|
|
plot.xaxis.axis_label = "Duration (seconds)"
|
|
plot.yaxis.axis_label = "Power (W)"
|
|
|
|
plot.y_range = Range1d(0,1.5*max(theavpower))
|
|
plot.x_range = Range1d(1,2*max(thesecs))
|
|
plot.legend.orientation = "vertical"
|
|
|
|
hover = plot.select(dict(type=HoverTool))
|
|
|
|
hover.tooltips = OrderedDict([
|
|
('Duration ','@ftime'),
|
|
('Power (W)','@CP{int}'),
|
|
('Power (W) upper','@CPmax{int}'),
|
|
])
|
|
|
|
hover.mode = 'mouse'
|
|
|
|
plot.line('duration','CP',source=sourcecomplex,legend="CP Model",
|
|
color='green')
|
|
|
|
plot.line('duration','CPmax',source=sourcecomplex,legend="CP Model",
|
|
color='red')
|
|
|
|
script, div = components(plot)
|
|
|
|
return [script,div,p1,ratio,message]
|
|
|
|
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'
|
|
|
|
thesecs = pd.Series(thesecs)
|
|
|
|
velo = thedistances/thesecs
|
|
p = pd.Series(500./velo)
|
|
|
|
p2 = p.fillna(method='ffill').apply(lambda x: timedeltaconv(x))
|
|
|
|
source = ColumnDataSource(
|
|
data = dict(
|
|
dist = thedistances,
|
|
duration = thesecs,
|
|
spm = 0*theavpower,
|
|
tim = niceformat(
|
|
thesecs.fillna(method='ffill').apply(lambda x: timedeltaconv(x))
|
|
),
|
|
|
|
power = theavpower,
|
|
fpace = 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 = pd.Series(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 = fitp.fillna(method='ffill').apply(lambda x: timedeltaconv(x))
|
|
|
|
|
|
sourcepaul = ColumnDataSource(
|
|
data = dict(
|
|
dist = 10**fitx,
|
|
duration = fitt,
|
|
power = fitpower,
|
|
spm = 0*fitpower,
|
|
tim = niceformat(
|
|
fitt.fillna(method='ffill').apply(lambda x: timedeltaconv(x))
|
|
),
|
|
fpace = 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 = pd.Series(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 = fitp.fillna(method='ffill').apply(lambda x: timedeltaconv(x))
|
|
|
|
sourcecomplex = ColumnDataSource(
|
|
data = dict(
|
|
dist = fitdist,
|
|
duration = fitt,
|
|
tim = niceformat(
|
|
fitt.fillna(method='ffill').apply(lambda x: timedeltaconv(x))
|
|
),
|
|
spm = 0*fitpower,
|
|
power = fitpower,
|
|
fpace = nicepaceformat(fitp2),
|
|
)
|
|
)
|
|
|
|
|
|
|
|
# making the plot
|
|
plot = Figure(tools=TOOLS,x_axis_type=x_axis_type,
|
|
plot_width=900,
|
|
toolbar_location="above",
|
|
toolbar_sticky=False)
|
|
|
|
# add watermark
|
|
plot.extra_y_ranges = {"watermark": watermarkrange}
|
|
|
|
plot.image_url([watermarkurl],1.8*max(thesecs),watermarky,
|
|
watermarkw,watermarkh,
|
|
global_alpha=watermarkalpha,
|
|
w_units='screen',
|
|
h_units='screen',
|
|
anchor=watermarkanchor,
|
|
dilate=True,
|
|
y_range_name = "watermark",
|
|
)
|
|
|
|
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 = p.fillna(method='ffill').apply(lambda x: timedeltaconv(x))
|
|
|
|
source2 = ColumnDataSource(
|
|
data = dict(
|
|
duration = cpdata['Delta'],
|
|
power = cpdata['CP'],
|
|
tim = niceformat(
|
|
cpdata['Delta'].fillna(method='ffill').apply(lambda x: timedeltaconv(x))
|
|
),
|
|
dist = cpdata['Distance'],
|
|
pace = nicepaceformat(p2),
|
|
)
|
|
)
|
|
|
|
plot.circle('duration','power',source=source2,
|
|
fill_color='blue',size=3,
|
|
legend = 'Power from segments')
|
|
|
|
hover = plot.select(dict(type=HoverTool))
|
|
|
|
hover.tooltips = OrderedDict([
|
|
('Duration ','@tim'),
|
|
('Power (W)','@power{int}'),
|
|
('Distance (m)','@dist{int}'),
|
|
('Pace (/500m)','@fpace'),
|
|
])
|
|
|
|
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.line('duration','power',source=sourcepaul,legend="Paul's Law")
|
|
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,ftp=r.ftp)
|
|
|
|
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,gzip=True)
|
|
dataprep.update_strokedata(id,rowdata.df)
|
|
|
|
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,ftp=r.ftp)
|
|
|
|
rowdata = rdata(f1,rower=rr)
|
|
if rowdata == 0:
|
|
return "","No Valid Data Available"
|
|
|
|
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,gzip=True)
|
|
dataprep.update_strokedata(id,rowdata.df)
|
|
|
|
# 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'
|
|
|
|
|
|
columns = ['time','pace','hr','fpace','ftime']
|
|
datadf = dataprep.getsmallrowdata_db(columns,ids=[id])
|
|
|
|
datadf.dropna(axis=0,how='any',inplace=True)
|
|
row = Workout.objects.get(id=id)
|
|
if datadf.empty:
|
|
return "","No Valid Data Available"
|
|
else:
|
|
datadf.sort_values(by='time',ascending=True,inplace=True)
|
|
|
|
#datadf,row = dataprep.getrowdata_db(id=id)
|
|
#if datadf.empty:
|
|
#return "","No Valid Data Available"
|
|
|
|
source = ColumnDataSource(
|
|
datadf
|
|
)
|
|
|
|
plot = Figure(x_axis_type="datetime",y_axis_type="datetime",
|
|
plot_width=400,
|
|
plot_height=400,
|
|
toolbar_sticky=False,
|
|
tools=TOOLS)
|
|
|
|
# add watermark
|
|
plot.extra_y_ranges = {"watermark": watermarkrange}
|
|
plot.extra_x_ranges = {"watermark": watermarkrange}
|
|
|
|
plot.image_url([watermarkurl],0.01,0.99,
|
|
0.5*watermarkw,0.5*watermarkh,
|
|
global_alpha=watermarkalpha,
|
|
w_units='screen',
|
|
h_units='screen',
|
|
anchor='top_left',
|
|
dilate=True,
|
|
x_range_name = "watermark",
|
|
y_range_name = "watermark",
|
|
)
|
|
|
|
plot.line('time','pace',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 = 90.
|
|
ymin = 150.
|
|
|
|
if row.workouttype == 'water':
|
|
ymax = 90.
|
|
ymin = 210.
|
|
|
|
plot.y_range = Range1d(1.e3*ymin,1.e3*ymax)
|
|
|
|
|
|
hover = plot.select(dict(type=HoverTool))
|
|
|
|
|
|
hover.tooltips = OrderedDict([
|
|
('Time','@ftime'),
|
|
('Pace','@fpace'),
|
|
('HR','@hr{int}'),
|
|
('SPM','@spm{1.1}'),
|
|
])
|
|
|
|
hover.mode = 'mouse'
|
|
|
|
plot.extra_y_ranges["hrax"] = Range1d(start=100,end=200)
|
|
plot.line('time','hr',source=source,color="red",
|
|
y_range_name="hrax", legend="Heart Rate")
|
|
plot.add_layout(LinearAxis(y_range_name="hrax",axis_label="HR"),'right')
|
|
|
|
plot.legend.location = "bottom_right"
|
|
|
|
script, div = components(plot)
|
|
|
|
return [script,div]
|
|
|
|
def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='',
|
|
ploterrorbars=False,
|
|
title=None,binsize=1,colorlegend=[]):
|
|
|
|
if datadf.empty:
|
|
return ['','<p>No non-zero data in selection</p>']
|
|
|
|
if xparam == 'workoutid':
|
|
xparamname = 'Workout'
|
|
else:
|
|
xparamname = axlabels[xparam]
|
|
|
|
if yparam == 'workoutid':
|
|
yparamname = 'Workout'
|
|
else:
|
|
yparamname = axlabels[yparam]
|
|
|
|
if groupby == 'workoutid':
|
|
groupname = 'Workout'
|
|
elif groupby == 'date':
|
|
groupname = 'Date'
|
|
else:
|
|
groupname = axlabels[groupby]
|
|
|
|
|
|
if title==None:
|
|
title = '{y} vs {x} grouped by {gr}'.format(
|
|
x = xparamname,
|
|
y = yparamname,
|
|
gr = groupname,
|
|
)
|
|
|
|
|
|
if xparam=='distance':
|
|
xaxmax = datadf[xparam].max()
|
|
xaxmin = datadf[xparam].min()
|
|
elif xparam=='time':
|
|
tseconds = datadf.ix[:,'time']
|
|
xaxmax = tseconds.max()
|
|
xaxmin = 0
|
|
elif xparam == 'workoutid':
|
|
xaxmax = datadf[xparam].max()-5
|
|
xaxmin = datadf[xparam].min()+5
|
|
else:
|
|
xaxmax = yaxmaxima[xparam]
|
|
xaxmin = yaxminima[xparam]
|
|
|
|
if yparam=='distance':
|
|
yaxmax = datadf[yparam].max()
|
|
yaxmin = datadf[yparam].min()
|
|
elif yparam=='time':
|
|
tseconds = datadf.ix[:,'time']
|
|
yaxmax = tseconds.max()
|
|
yaxmin = 0
|
|
elif yparam == 'workoutid':
|
|
yaxmax = datadf[yparam].max()-5
|
|
yaxmin = datadf[yparam].min()+5
|
|
else:
|
|
yaxmax = yaxmaxima[yparam]
|
|
yaxmin = yaxminima[yparam]
|
|
|
|
x_axis_type = 'linear'
|
|
y_axis_type = 'linear'
|
|
if xparam == 'time':
|
|
x_axis_type = 'datetime'
|
|
if yparam == 'pace':
|
|
y_axis_type = 'datetime'
|
|
|
|
source = ColumnDataSource(
|
|
datadf,
|
|
)
|
|
|
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,resize'
|
|
|
|
if groupby != 'date':
|
|
hover = HoverTool(names=['data'],
|
|
tooltips = [
|
|
(groupby,'@groupval{1.1}'),
|
|
(xparamname,'@x{1.1}'),
|
|
(yparamname,'@y')
|
|
])
|
|
else:
|
|
hover = HoverTool(names=['data'],
|
|
tooltips = [
|
|
(groupby,'@groupval'),
|
|
(xparamname,'@x{1.1}'),
|
|
(yparamname,'@y')
|
|
,
|
|
])
|
|
|
|
hover.mode = 'mouse'
|
|
TOOLS = [SaveTool(),PanTool(),BoxZoomTool(),WheelZoomTool(),
|
|
ResetTool(),TapTool(),ResizeTool(),hover]
|
|
|
|
|
|
plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type,
|
|
tools=TOOLS,
|
|
toolbar_location="above",
|
|
toolbar_sticky=False) #,plot_width=500,plot_height=500)
|
|
|
|
# add watermark
|
|
plot.extra_y_ranges = {"watermark": watermarkrange}
|
|
plot.extra_x_ranges = {"watermark": watermarkrange}
|
|
|
|
plot.title.text = title
|
|
plot.title.text_font_size=value("1.0em")
|
|
|
|
plot.image_url([watermarkurl],watermarkx,watermarky,
|
|
watermarkw,watermarkh,
|
|
global_alpha=watermarkalpha,
|
|
w_units='screen',
|
|
h_units='screen',
|
|
anchor=watermarkanchor,
|
|
dilate=True,
|
|
x_range_name = "watermark",
|
|
y_range_name = "watermark",
|
|
)
|
|
|
|
errorbar(plot,xparam,yparam,source=source,
|
|
xerr=ploterrorbars,
|
|
yerr=ploterrorbars,
|
|
point_kwargs={
|
|
'line_color':"#969696",
|
|
'size':"groupsize",
|
|
'fill_color':"color",
|
|
'fill_alpha':1.0,
|
|
},
|
|
)
|
|
|
|
|
|
|
|
for nr, gvalue, color in colorlegend:
|
|
box = BoxAnnotation(bottom=400+20*nr,left=550,top=420+20*nr,
|
|
right=570,
|
|
bottom_units='screen',
|
|
top_units='screen',
|
|
left_units='screen',
|
|
right_units='screen',
|
|
fill_color=color,
|
|
fill_alpha=1.0,
|
|
line_color=color)
|
|
legendlabel = Label(x=571,y=403+20*nr,x_units='screen',
|
|
y_units='screen',
|
|
text = "{gvalue:3.0f}".format(gvalue=gvalue),
|
|
background_fill_alpha=1.0,
|
|
text_color='black',
|
|
text_font_size=value("0.7em"))
|
|
plot.add_layout(box)
|
|
plot.add_layout(legendlabel)
|
|
|
|
if colorlegend:
|
|
legendlabel = Label(x=372,y=300,x_units='screen',
|
|
y_units='screen',
|
|
text = 'group legend',
|
|
text_color='black',
|
|
text_font_size=value("0.7em"),
|
|
angle=90,
|
|
angle_units='deg')
|
|
|
|
if xparam == 'workoutid':
|
|
plot.xaxis.axis_label = 'Workout'
|
|
else:
|
|
plot.xaxis.axis_label = axlabels[xparam]
|
|
|
|
if yparam == 'workoutid':
|
|
plot.xaxis.axis_label = 'Workout'
|
|
else:
|
|
plot.yaxis.axis_label = axlabels[yparam]
|
|
|
|
binlabel = Label(x=100,y=100,x_units='screen',
|
|
y_units='screen',
|
|
text="Bin size {binsize:3.1f}".format(binsize=binsize),
|
|
background_fill_alpha=0.7,
|
|
background_fill_color='white',
|
|
text_color='black',
|
|
)
|
|
|
|
plot.add_layout(binlabel)
|
|
|
|
yrange1 = Range1d(start=yaxmin,end=yaxmax)
|
|
plot.y_range = yrange1
|
|
|
|
xrange1 = Range1d(start=xaxmin,end=xaxmax)
|
|
plot.x_range = xrange1
|
|
|
|
if yparam == 'pace':
|
|
plot.yaxis[0].formatter = DatetimeTickFormatter(
|
|
seconds = ["%S"],
|
|
minutes = ["%M"]
|
|
)
|
|
|
|
|
|
script,div = components(plot)
|
|
|
|
|
|
return [script,div]
|
|
|
|
def interactive_cum_flex_chart2(theworkouts,promember=0,
|
|
xparam='spm',
|
|
yparam1='power',
|
|
yparam2='spm',
|
|
workstrokesonly=False):
|
|
|
|
# datadf = dataprep.smalldataprep(theworkouts,xparam,yparam1,yparam2)
|
|
ids = [int(w.id) for w in theworkouts]
|
|
columns = [xparam,yparam1,yparam2,'spm','driveenergy','distance']
|
|
datadf = dataprep.getsmallrowdata_db(columns,ids=ids,doclean=True,
|
|
workstrokesonly=workstrokesonly)
|
|
|
|
try:
|
|
tests = datadf[yparam2]
|
|
except KeyError:
|
|
yparam2 = 'None'
|
|
|
|
try:
|
|
tests = datadf[yparam1]
|
|
except KeyError:
|
|
yparam1 = 'None'
|
|
|
|
datadf.dropna(axis=1,how='all',inplace=True)
|
|
datadf.dropna(axis=0,how='any',inplace=True)
|
|
|
|
# test if we have drive energy
|
|
nowork = 1
|
|
try:
|
|
test = datadf['driveenergy'].mean()
|
|
nowork = 0
|
|
except KeyError:
|
|
datadf['driveenergy'] = 500.
|
|
|
|
# test if we have power
|
|
nopower = 1
|
|
try:
|
|
test = datadf['power'].mean()
|
|
nopower = 0
|
|
except KeyError:
|
|
datadf['power'] = 50.
|
|
|
|
|
|
yparamname1 = axlabels[yparam1]
|
|
if yparam2 != 'None':
|
|
yparamname2 = axlabels[yparam2]
|
|
|
|
|
|
# check if dataframe not empty
|
|
if datadf.empty:
|
|
return ['','<p>No non-zero data in selection</p>','','']
|
|
|
|
|
|
try:
|
|
datadf['x1'] = datadf.ix[:,xparam]
|
|
except KeyError:
|
|
datadf['x1'] = datadf['distance']
|
|
try:
|
|
datadf['y1'] = datadf.ix[:,yparam1]
|
|
except KeyError:
|
|
try:
|
|
datadf['y1'] = datadf['pace']
|
|
except KeyError:
|
|
return ['','<p>No non-zero data in selection</p>','','']
|
|
if yparam2 != 'None':
|
|
try:
|
|
datadf['y2'] = datadf.ix[:,yparam2]
|
|
except KeyError:
|
|
datadf['y2'] = datadf['y1']
|
|
else:
|
|
datadf['y2'] = datadf['y1']
|
|
|
|
|
|
if xparam=='distance':
|
|
xaxmax = datadf['x1'].max()
|
|
xaxmin = datadf['x1'].min()
|
|
else:
|
|
xaxmax = yaxmaxima[xparam]
|
|
xaxmin = yaxminima[xparam]
|
|
|
|
# average values
|
|
x1mean = datadf['x1'].mean()
|
|
|
|
y1mean = datadf['y1'].mean()
|
|
y2mean = datadf['y2'].mean()
|
|
|
|
|
|
xvals = pd.Series(xaxmin+np.arange(100)*(xaxmax-xaxmin)/100.)
|
|
|
|
x_axis_type = 'linear'
|
|
y_axis_type = 'linear'
|
|
if xparam == 'time':
|
|
x_axis_type = 'datetime'
|
|
|
|
if yparam1 == 'pace':
|
|
y_axis_type = 'datetime'
|
|
y1mean = datadf.ix[:,'pace'].mean()
|
|
|
|
datadf['xname'] = axlabels[xparam]
|
|
datadf['yname1'] = axlabels[yparam1]
|
|
if yparam2 != 'None':
|
|
datadf['yname2'] = axlabels[yparam2]
|
|
else:
|
|
datadf['yname2'] = axlabels[yparam1]
|
|
|
|
|
|
source = ColumnDataSource(
|
|
datadf
|
|
)
|
|
|
|
source2 = ColumnDataSource(
|
|
datadf.copy()
|
|
)
|
|
|
|
# Add hover to this comma-separated string and see what changes
|
|
if (promember==1):
|
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,resize,crosshair'
|
|
else:
|
|
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,crosshair'
|
|
|
|
plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type,
|
|
tools=TOOLS,
|
|
toolbar_location="above",
|
|
toolbar_sticky=False)
|
|
|
|
# add watermark
|
|
plot.extra_y_ranges = {"watermark": watermarkrange}
|
|
plot.extra_x_ranges = {"watermark": watermarkrange}
|
|
|
|
plot.image_url([watermarkurl],watermarkx,watermarky,
|
|
watermarkw,watermarkh,
|
|
global_alpha=watermarkalpha,
|
|
w_units='screen',
|
|
h_units='screen',
|
|
anchor=watermarkanchor,
|
|
dilate=True,
|
|
x_range_name = "watermark",
|
|
y_range_name = "watermark",
|
|
)
|
|
|
|
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
|
|
|
|
xlabel = Label(x=100,y=130,x_units='screen',y_units='screen',
|
|
text=axlabels[xparam]+": {x1mean:6.2f}".format(x1mean=x1mean),
|
|
background_fill_alpha=.7,
|
|
background_fill_color='white',
|
|
text_color='green',
|
|
)
|
|
|
|
|
|
plot.add_layout(x1means)
|
|
plot.add_layout(xlabel)
|
|
|
|
plot.add_layout(y1means)
|
|
|
|
y1label = Label(x=100,y=100,x_units='screen',y_units='screen',
|
|
text=axlabels[yparam1]+": {y1mean:6.2f}".format(y1mean=y1mean),
|
|
background_fill_alpha=.7,
|
|
background_fill_color='white',
|
|
text_color='blue',
|
|
)
|
|
|
|
if yparam1 != 'time' and yparam1 != 'pace':
|
|
plot.add_layout(y1label)
|
|
|
|
y2label = y1label
|
|
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
|
|
|
|
xrange1 = Range1d(start=yaxminima[xparam],end=yaxmaxima[xparam])
|
|
plot.x_range = xrange1
|
|
|
|
if yparam1 == 'pace':
|
|
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)
|
|
y2label = Label(x=100,y=70,x_units='screen',y_units='screen',
|
|
text=axlabels[yparam2]+": {y2mean:6.2f}".format(y2mean=y2mean),
|
|
background_fill_alpha=.7,
|
|
background_fill_color='white',
|
|
text_color='red',
|
|
)
|
|
if yparam2 != 'pace' and yparam2 != 'time':
|
|
plot.add_layout(y2label)
|
|
|
|
|
|
callback = CustomJS(args = dict(source=source,source2=source2,
|
|
x1means=x1means,
|
|
y1means=y1means,
|
|
y1label=y1label,
|
|
y2label=y2label,
|
|
xlabel=xlabel,
|
|
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 distance1 = data['distance']
|
|
var driveenergy1 = data['driveenergy']
|
|
var xname = data['xname'][0]
|
|
var yname1 = data['yname1'][0]
|
|
var yname2 = data['yname2'][0]
|
|
|
|
var minspm = minspm.value
|
|
var maxspm = maxspm.value
|
|
var mindist = mindist.value
|
|
var maxdist = maxdist.value
|
|
var minwork = minwork.value
|
|
var maxwork = maxwork.value
|
|
var xm = 0
|
|
var ym1 = 0
|
|
var ym2 = 0
|
|
|
|
data2['x1'] = []
|
|
data2['y1'] = []
|
|
data2['y2'] = []
|
|
data2['spm'] = []
|
|
data2['distance'] = []
|
|
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) {
|
|
if (driveenergy1[i]>=minwork && driveenergy1[i]<=maxwork) {
|
|
data2['x1'].push(x1[i])
|
|
data2['y1'].push(y1[i])
|
|
data2['y2'].push(y2[i])
|
|
data2['spm'].push(spm1[i])
|
|
data2['distance'].push(distance1[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
|
|
y1label.text = yname1+': '+(ym1).toFixed(2)
|
|
y2label.text = yname2+': '+(ym2).toFixed(2)
|
|
xlabel.text = xname+': '+(xm).toFixed(2)
|
|
|
|
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
|
|
|
|
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.)
|
|
|
|
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,
|
|
slider_work_min,
|
|
slider_work_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_chart2(id=0,promember=0,
|
|
xparam='time',
|
|
yparam1='pace',
|
|
yparam2='hr',
|
|
plottype='line',
|
|
workstrokesonly=False):
|
|
|
|
|
|
#rowdata,row = dataprep.getrowdata_db(id=id)
|
|
columns = [xparam,yparam1,yparam2,
|
|
'ftime','distance','fpace',
|
|
'power','hr','spm','driveenergy',
|
|
'time','pace','workoutstate','time']
|
|
|
|
rowdata = dataprep.getsmallrowdata_db(columns,ids=[id],doclean=True,
|
|
workstrokesonly=workstrokesonly)
|
|
|
|
if rowdata.empty:
|
|
rowdata = dataprep.getsmallrowdata_db(columns,ids=[id],doclean=True,
|
|
workstrokesonly=False)
|
|
workstrokesonly=False
|
|
|
|
try:
|
|
tests = rowdata[yparam2]
|
|
except KeyError:
|
|
yparam2 = 'None'
|
|
|
|
try:
|
|
tests = rowdata[yparam1]
|
|
except KeyError:
|
|
yparam1 = 'None'
|
|
|
|
rowdata.dropna(axis=1,how='all',inplace=True)
|
|
|
|
# test if we have drive energy
|
|
nowork = 1
|
|
try:
|
|
test = rowdata['driveenergy'].mean()
|
|
nowork = 0
|
|
except KeyError:
|
|
rowdata['driveenergy'] = 500.
|
|
|
|
# test if we have power
|
|
nopower = 1
|
|
try:
|
|
test = rowdata['power'].mean()
|
|
nopower = 0
|
|
except KeyError:
|
|
rowdata['power'] = 50.
|
|
|
|
row = Workout.objects.get(id=id)
|
|
if rowdata.empty:
|
|
return "","No valid data"
|
|
else:
|
|
try:
|
|
rowdata.sort_values(by='time',ascending=True,inplace=True)
|
|
except KeyError:
|
|
pass
|
|
|
|
workoutstateswork = [1,4,5,8,9,6,7]
|
|
workoutstatesrest = [3]
|
|
workoutstatetransition = [0,2,10,11,12,13]
|
|
|
|
if workstrokesonly:
|
|
try:
|
|
rowdata = rowdata[~rowdata['workoutstate'].isin(workoutstatesrest)]
|
|
except KeyError:
|
|
pass
|
|
|
|
try:
|
|
tseconds = rowdata.ix[:,'time']
|
|
except KeyError:
|
|
return '','No time data - cannot make flex plot','',''
|
|
|
|
|
|
try:
|
|
rowdata['x1'] = rowdata.ix[:,xparam]
|
|
except KeyError:
|
|
rowdata['x1'] = 0*rowdata.ix[:,'time']
|
|
|
|
try:
|
|
rowdata['y1'] = rowdata.ix[:,yparam1]
|
|
except KeyError:
|
|
rowdata['y1'] = 0*rowdata.ix[:,'time']
|
|
|
|
if yparam2 != 'None':
|
|
try:
|
|
rowdata['y2'] = rowdata.ix[:,yparam2]
|
|
except KeyError:
|
|
rowdata['y2'] = 0*rowdata.ix[:,'time']
|
|
else:
|
|
rowdata['y2'] = rowdata['y1']
|
|
|
|
if xparam=='time':
|
|
xaxmax = tseconds.max()
|
|
xaxmin = tseconds.min()
|
|
elif xparam=='distance' or xparam=='cumdist':
|
|
xaxmax = rowdata['x1'].max()
|
|
xaxmin = rowdata['x1'].min()
|
|
else:
|
|
xaxmax = yaxmaxima[xparam]
|
|
xaxmin = yaxminima[xparam]
|
|
|
|
# average values
|
|
if xparam != 'time':
|
|
try:
|
|
x1mean = rowdata['x1'].mean()
|
|
except TypeError:
|
|
x1mean = 0
|
|
else:
|
|
x1mean = 0
|
|
|
|
y1mean = rowdata['y1'].mean()
|
|
y2mean = rowdata['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 = rowdata['y1'].mean()*rowdata['x1'].mean()/xvals
|
|
|
|
x_axis_type = 'linear'
|
|
y_axis_type = 'linear'
|
|
if xparam == 'time':
|
|
x_axis_type = 'datetime'
|
|
|
|
if yparam1 == 'pace':
|
|
y_axis_type = 'datetime'
|
|
y1mean = rowdata.ix[:,'pace'].mean()
|
|
|
|
|
|
rowdata['xname'] = axlabels[xparam]
|
|
rowdata['yname1'] = axlabels[yparam1]
|
|
if yparam2 != 'None':
|
|
rowdata['yname2'] = axlabels[yparam2]
|
|
else:
|
|
rowdata['yname2'] = axlabels[yparam1]
|
|
|
|
|
|
# prepare data
|
|
source = ColumnDataSource(
|
|
rowdata
|
|
)
|
|
|
|
# second source for filtering
|
|
source2 = ColumnDataSource(
|
|
rowdata.copy()
|
|
)
|
|
|
|
# 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
|
|
)
|
|
|
|
|
|
|
|
# add watermark
|
|
plot.extra_y_ranges = {"watermark": watermarkrange}
|
|
plot.extra_x_ranges = {"watermark": watermarkrange}
|
|
|
|
plot.image_url([watermarkurl],watermarkx,watermarky,
|
|
watermarkw,watermarkh,
|
|
global_alpha=watermarkalpha,
|
|
w_units='screen',
|
|
h_units='screen',
|
|
anchor=watermarkanchor,
|
|
dilate=True,
|
|
x_range_name = "watermark",
|
|
y_range_name = "watermark",
|
|
)
|
|
|
|
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
|
|
|
|
xlabel = Label(x=100,y=130,x_units='screen',y_units='screen',
|
|
text=axlabels[xparam]+": {x1mean:6.2f}".format(x1mean=x1mean),
|
|
background_fill_alpha=.7,
|
|
background_fill_color='white',
|
|
text_color='green',
|
|
)
|
|
|
|
annolabel = Label(x=100,y=500,x_units='screen',y_units='screen',
|
|
text='',
|
|
background_fill_alpha=0.7,
|
|
background_fill_color='white',
|
|
text_color='black',
|
|
)
|
|
|
|
|
|
if (xparam != 'time') and (xparam != 'distance') and (xparam != 'cumdist'):
|
|
plot.add_layout(x1means)
|
|
plot.add_layout(xlabel)
|
|
|
|
|
|
plot.add_layout(y1means)
|
|
plot.add_layout(annolabel)
|
|
|
|
y1label = Label(x=100,y=100,x_units='screen',y_units='screen',
|
|
text=axlabels[yparam1]+": {y1mean:6.2f}".format(y1mean=y1mean),
|
|
background_fill_alpha=.7,
|
|
background_fill_color='white',
|
|
text_color='blue',
|
|
)
|
|
if yparam1 != 'time' and yparam1 != 'pace':
|
|
plot.add_layout(y1label)
|
|
y2label = y1label
|
|
|
|
# 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.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') and (xparam != 'cumdist'):
|
|
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.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
|
|
#= {"yax2": yrange2}
|
|
|
|
if plottype=='line':
|
|
plot.line('x1','y2',color="red",y_range_name="yax2",
|
|
legend=axlabels[yparam2],
|
|
source=source2)
|
|
|
|
elif plottype=='scatter':
|
|
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)
|
|
y2label = Label(x=100,y=70,x_units='screen',y_units='screen',
|
|
text=axlabels[yparam2]+": {y2mean:6.2f}".format(y2mean=y2mean),
|
|
background_fill_alpha=.7,
|
|
background_fill_color='white',
|
|
text_color='red',
|
|
)
|
|
if yparam2 != 'pace' and yparam2 != 'time':
|
|
plot.add_layout(y2label)
|
|
|
|
hover = plot.select(dict(type=HoverTool))
|
|
|
|
|
|
hover.tooltips = OrderedDict([
|
|
('Time','@ftime'),
|
|
('Distance','@distance{int}'),
|
|
('Pace','@fpace'),
|
|
('HR','@hr{int}'),
|
|
('SPM','@spm{1.1}'),
|
|
('Power','@power{int}'),
|
|
])
|
|
|
|
hover.mode = 'mouse'
|
|
|
|
|
|
callback = CustomJS(args = dict(source=source,source2=source2,
|
|
x1means=x1means,
|
|
y1means=y1means,
|
|
y1label=y1label,
|
|
y2label=y2label,
|
|
xlabel=xlabel,
|
|
annolabel=annolabel,
|
|
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 fpace1 = data['fpace']
|
|
var distance1 = data['distance']
|
|
var power1 = data['power']
|
|
var driveenergy1 = data['driveenergy']
|
|
var xname = data['xname'][0]
|
|
var yname1 = data['yname1'][0]
|
|
var yname2 = data['yname2'][0]
|
|
|
|
var annotation = annotation.value
|
|
var minspm = minspm.value
|
|
var maxspm = maxspm.value
|
|
var mindist = mindist.value
|
|
var maxdist = maxdist.value
|
|
var minwork = minwork.value
|
|
var maxwork = maxwork.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['fpace'] = []
|
|
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) {
|
|
if (driveenergy1[i]>=minwork && driveenergy1[i]<=maxwork) {
|
|
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['fpace'].push(fpace1[i])
|
|
data2['pace'].push(pace1[i])
|
|
data2['hr'].push(hr1[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
|
|
y1label.text = yname1+': '+ym1.toFixed(2)
|
|
y2label.text = yname2+': '+ym2.toFixed(2)
|
|
xlabel.text = xname+': '+xm.toFixed(2)
|
|
annolabel.text = annotation
|
|
|
|
source2.trigger('change');
|
|
""")
|
|
|
|
annotation = TextInput(title="Type your plot notes here", value="",
|
|
callback=callback)
|
|
callback.args["annotation"] = annotation
|
|
|
|
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
|
|
|
|
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.)
|
|
|
|
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,
|
|
slider_work_min,
|
|
slider_work_max,
|
|
annotation,
|
|
],
|
|
),
|
|
plot])
|
|
|
|
script, div = components(layout)
|
|
js_resources = INLINE.render_js()
|
|
css_resources = INLINE.render_css()
|
|
|
|
return [script,div,js_resources,css_resources,workstrokesonly]
|
|
|
|
|
|
def interactive_bar_chart(id=0,promember=0):
|
|
# check if valid ID exists (workout exists)
|
|
rowdata,row = dataprep.getrowdata_db(id=id)
|
|
rowdata.dropna(axis=1,how='all',inplace=True)
|
|
rowdata.dropna(axis=0,how='any',inplace=True)
|
|
|
|
if rowdata.empty:
|
|
return "","No Valid Data Available"
|
|
|
|
# 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(
|
|
rowdata
|
|
)
|
|
|
|
plot = Figure(x_axis_type="datetime",y_axis_type="datetime",
|
|
toolbar_sticky=False,
|
|
plot_width=920,
|
|
tools=TOOLS)
|
|
|
|
# add watermark
|
|
plot.extra_y_ranges = {"watermark": watermarkrange}
|
|
plot.extra_x_ranges = {"watermark": watermarkrange}
|
|
|
|
plot.image_url([watermarkurl],0.01,0.99,
|
|
watermarkw,watermarkh,
|
|
global_alpha=watermarkalpha,
|
|
w_units='screen',
|
|
h_units='screen',
|
|
anchor='top_left',
|
|
dilate=True,
|
|
x_range_name = "watermark",
|
|
y_range_name = "watermark",
|
|
)
|
|
|
|
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(
|
|
hours = "",
|
|
seconds = ["%S"],
|
|
minutes = ["%M"],
|
|
)
|
|
ymax = 1.0e3*90
|
|
ymin = 1.0e3*180
|
|
|
|
if row.workouttype == 'water':
|
|
ymax = 1.0e3*90
|
|
ymin = 1.0e3*210
|
|
|
|
plot.y_range = Range1d(ymin,ymax)
|
|
|
|
|
|
hover = plot.select(dict(type=HoverTool))
|
|
|
|
|
|
hover.tooltips = OrderedDict([
|
|
('Time','@ftime'),
|
|
('Pace','@fpace'),
|
|
('HR','@hr{int}'),
|
|
('SPM','@spm{1.1}'),
|
|
])
|
|
|
|
hover.mode = 'mouse'
|
|
|
|
plot.extra_y_ranges["hr"] = Range1d(start=100,end=200)
|
|
plot.quad(left='time',top='hr_ut2',bottom='hr_bottom',
|
|
right='x_right',source=source,color="gray",
|
|
y_range_name="hr", legend="<UT2")
|
|
plot.quad(left='time',top='hr_ut1',bottom='hr_bottom',
|
|
right='x_right',source=source,color="tan",
|
|
y_range_name="hr", legend="UT2")
|
|
plot.quad(left='time',top='hr_at',bottom='hr_bottom',
|
|
right='x_right',source=source,color="green",
|
|
y_range_name="hr", legend="UT1")
|
|
plot.quad(left='time',top='hr_tr',bottom='hr_bottom',
|
|
right='x_right',source=source,color="blue",
|
|
y_range_name="hr", legend="AT")
|
|
plot.quad(left='time',top='hr_an',bottom='hr_bottom',
|
|
right='x_right',source=source,color="violet",
|
|
y_range_name="hr", legend="TR")
|
|
plot.quad(left='time',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('time','pace',source=source,legend="Pace",color="black")
|
|
|
|
|
|
script, div = components(plot)
|
|
|
|
return [script,div]
|
|
|
|
def interactive_multiple_compare_chart(ids,xparam,yparam,plottype='line',
|
|
promember=0,
|
|
labeldict=None):
|
|
|
|
message = ''
|
|
errormessage = ''
|
|
|
|
columns = [xparam,yparam,
|
|
'ftime','distance','fpace',
|
|
'power','hr','spm',
|
|
'time','pace','workoutstate',
|
|
'workoutid']
|
|
|
|
datadf = dataprep.getsmallrowdata_db(columns,ids=ids)
|
|
datadf.dropna(axis=1,how='all',inplace=True)
|
|
datadf.dropna(axis=0,how='any',inplace=True)
|
|
|
|
nrworkouts = len(ids)
|
|
|
|
tseconds = datadf.ix[:,'time']
|
|
|
|
yparamname = axlabels[yparam]
|
|
|
|
#datadf = datadf[datadf[yparam] > 0]
|
|
|
|
#datadf = datadf[datadf[xparam] > 0]
|
|
|
|
# check if dataframe not empty
|
|
if datadf.empty:
|
|
return ['','<p>No non-zero data in selection</p>','','No non-zero data in selection']
|
|
|
|
if xparam != 'distance' and xparam != 'time' and xparam != 'cumdist':
|
|
xaxmax = yaxmaxima[xparam]
|
|
xaxmin = yaxminima[xparam]
|
|
elif xparam == 'time':
|
|
xaxmax = tseconds.max()
|
|
xaxmin = tseconds.min()
|
|
else:
|
|
xaxmax = datadf['distance'].max()
|
|
xaxmin = datadf['distance'].min()
|
|
|
|
if yparam == 'distance':
|
|
yaxmin = datadf['distance'].min()
|
|
yaxmax = datadf['distance'].max()
|
|
elif yparam == 'cumdist':
|
|
yaxmin = datadf['cumdist'].min()
|
|
yaxmax = datadf['cumdist'].max()
|
|
else:
|
|
yaxmin = yaxminima[yparam]
|
|
yaxmax = yaxmaxima[yparam]
|
|
|
|
x_axis_type = 'linear'
|
|
y_axis_type = 'linear'
|
|
|
|
# Add hover to this comma-separated string and see what changes
|
|
if (promember==1):
|
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,resize,crosshair'
|
|
else:
|
|
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,crosshair'
|
|
|
|
if yparam == 'pace':
|
|
y_axis_type = 'datetime'
|
|
yaxmax = 90.*1e3
|
|
yaxmin = 150.*1e3
|
|
|
|
if xparam == 'time':
|
|
x_axis_type = 'datetime'
|
|
|
|
if xparam != 'time':
|
|
xvals = xaxmin+np.arange(100)*(xaxmax-xaxmin)/100.
|
|
else:
|
|
xvals = np.arange(100)
|
|
|
|
plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type,
|
|
tools=TOOLS,
|
|
toolbar_location="above",
|
|
plot_width=920,
|
|
toolbar_sticky=False)
|
|
|
|
# add watermark
|
|
plot.extra_y_ranges = {"watermark": watermarkrange}
|
|
plot.extra_x_ranges = {"watermark": watermarkrange}
|
|
|
|
plot.image_url([watermarkurl],0.05,0.9,
|
|
watermarkw,watermarkh,
|
|
global_alpha=watermarkalpha,
|
|
w_units='screen',
|
|
h_units='screen',
|
|
anchor='top_left',
|
|
dilate=True,
|
|
x_range_name = "watermark",
|
|
y_range_name = "watermark",
|
|
)
|
|
|
|
colors = itertools.cycle(palette)
|
|
|
|
cntr = 0
|
|
|
|
for id,color in itertools.izip(ids,colors):
|
|
group = datadf[datadf['workoutid']==int(id)].copy()
|
|
group.sort_values(by='time',ascending=True,inplace=True)
|
|
try:
|
|
group['x'] = group[xparam]
|
|
except KeyError:
|
|
group['x'] = group['time']
|
|
errormessage = xparam+' has no values. Plot invalid'
|
|
try:
|
|
group['y'] = group[yparam]
|
|
except KeyError:
|
|
group['y'] = 0.0*group['x']
|
|
|
|
ymean = group['y'].mean()
|
|
ylabel = Label(x=100,y=60+nrworkouts*20-20*cntr,
|
|
x_units='screen',y_units='screen',
|
|
text=axlabels[yparam]+": {ymean:6.2f}".format(ymean=ymean),
|
|
background_fill_alpha=.7,
|
|
background_fill_color='white',
|
|
text_color=color,
|
|
)
|
|
if yparam != 'time' and yparam != 'pace':
|
|
plot.add_layout(ylabel)
|
|
|
|
source = ColumnDataSource(
|
|
group
|
|
)
|
|
|
|
TIPS = OrderedDict([
|
|
('time','@ftime'),
|
|
('pace','@fpace'),
|
|
('hr','@hr'),
|
|
('spm','@spm{1.1}'),
|
|
('distance','@distance{5}'),
|
|
])
|
|
|
|
hover = plot.select(type=HoverTool)
|
|
hover.tooltips = TIPS
|
|
|
|
if labeldict:
|
|
legend=labeldict[id]
|
|
else:
|
|
legend=str(id)
|
|
|
|
if plottype=='line':
|
|
l1 = plot.line('x','y',source=source,color=color,legend=legend)
|
|
else:
|
|
l1 = plot.scatter('x','y',source=source,color=color,legend=legend,
|
|
fill_alpha=0.4,line_color=None)
|
|
|
|
plot.add_tools(HoverTool(renderers=[l1],tooltips=TIPS))
|
|
cntr += 1
|
|
|
|
plot.legend.location='bottom_right'
|
|
plot.xaxis.axis_label = axlabels[xparam]
|
|
plot.yaxis.axis_label = axlabels[yparam]
|
|
|
|
if (xparam != 'time') and (xparam != 'distance') and (xparam != 'cumdist'):
|
|
xrange1 = Range1d(start=yaxminima[xparam],end=yaxmaxima[xparam])
|
|
plot.x_range = xrange1
|
|
|
|
yrange1 = Range1d(start=yaxmin,end=yaxmax)
|
|
plot.y_range = yrange1
|
|
|
|
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 yparam == 'pace':
|
|
plot.yaxis[0].formatter = DatetimeTickFormatter(
|
|
seconds = ["%S"],
|
|
minutes = ["%M"]
|
|
)
|
|
|
|
script, div = components(plot)
|
|
|
|
|
|
|
|
return [script,div,message,errormessage]
|
|
|
|
|
|
|
|
def interactive_comparison_chart(id1=0,id2=0,xparam='distance',yparam='spm',
|
|
promember=0,plottype='line'):
|
|
|
|
|
|
|
|
|
|
columns = [xparam,yparam,
|
|
'ftime','distance','fpace',
|
|
'power','hr','spm',
|
|
'time','pace','workoutstate']
|
|
|
|
# check if valid ID exists (workout exists)
|
|
#rowdata1,row1 = dataprep.getrowdata_db(id=id1)
|
|
#rowdata2,row2 = dataprep.getrowdata_db(id=id2)
|
|
|
|
rowdata1 = dataprep.getsmallrowdata_db(columns,ids=[id1])
|
|
rowdata2 = dataprep.getsmallrowdata_db(columns,ids=[id2])
|
|
for n in ['distance','power','hr','spm','time','pace','workoutstate']:
|
|
rowdata1[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)
|
|
rowdata2.dropna(axis=1,how='all',inplace=True)
|
|
rowdata2.dropna(axis=0,how='any',inplace=True)
|
|
|
|
row1 = Workout.objects.get(id=id1)
|
|
row2 = Workout.objects.get(id=id2)
|
|
|
|
if rowdata1.empty:
|
|
return "","No Valid Data Available"
|
|
else:
|
|
rowdata1.sort_values(by='time',ascending=True,inplace=True)
|
|
|
|
if rowdata2.empty:
|
|
return "","No Valid Data Available"
|
|
else:
|
|
rowdata2.sort_values(by='time',ascending=True,inplace=True)
|
|
|
|
try:
|
|
x1 = rowdata1.ix[:,xparam]
|
|
x2 = rowdata2.ix[:,xparam]
|
|
|
|
y1 = rowdata1.ix[:,yparam]
|
|
y2 = rowdata2.ix[:,yparam]
|
|
except KeyError:
|
|
return "","No valid Data Available"
|
|
|
|
x_axis_type = 'linear'
|
|
y_axis_type = 'linear'
|
|
if xparam == 'time':
|
|
x_axis_type = 'datetime'
|
|
|
|
if yparam == 'pace':
|
|
y_axis_type = 'datetime'
|
|
ymax = 1.0e3*90
|
|
ymin = 1.0e3*180
|
|
|
|
if row1.workouttype == 'water':
|
|
ymax = 1.0e3*90
|
|
ymin = 1.0e3*210
|
|
|
|
ftime1 = rowdata1.ix[:,'ftime']
|
|
ftime2 = rowdata2.ix[:,'ftime']
|
|
|
|
hr1 = rowdata1.ix[:,'hr']
|
|
hr2 = rowdata2.ix[:,'hr']
|
|
|
|
|
|
fpace1 = rowdata1.ix[:,'fpace']
|
|
fpace2 = rowdata2.ix[:,'fpace']
|
|
|
|
distance1 = rowdata1.ix[:,'distance']
|
|
distance2 = rowdata2.ix[:,'distance']
|
|
|
|
spm1 = rowdata1.ix[:,'spm']
|
|
spm2 = rowdata2.ix[:,'spm']
|
|
|
|
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'
|
|
|
|
|
|
data1 = pd.DataFrame(
|
|
dict(
|
|
x1=x1,
|
|
y1=y1,
|
|
ftime1=ftime1,
|
|
fpace1=fpace1,
|
|
hr1 = hr1,
|
|
spm1 = spm1,
|
|
distance1=distance1,
|
|
)
|
|
).dropna()
|
|
|
|
data2 = pd.DataFrame(
|
|
dict(
|
|
x2=x2,
|
|
y2=y2,
|
|
ftime2=ftime2,
|
|
fpace2=fpace2,
|
|
hr2 = hr2,
|
|
spm2 = spm2,
|
|
distance2=distance2,
|
|
)
|
|
).dropna()
|
|
|
|
|
|
|
|
source1 = ColumnDataSource(
|
|
data1
|
|
)
|
|
|
|
source2 = ColumnDataSource(
|
|
data2
|
|
)
|
|
|
|
ymean1 = data1['y1'].mean()
|
|
ymean2 = data2['y2'].mean()
|
|
|
|
# create interactive plot
|
|
plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type,
|
|
tools=TOOLS,
|
|
plot_width=920,
|
|
toolbar_sticky=False)
|
|
|
|
# add watermark
|
|
plot.extra_y_ranges = {"watermark": watermarkrange}
|
|
plot.extra_x_ranges = {"watermark": watermarkrange}
|
|
|
|
plot.image_url([watermarkurl],0.05,watermarky,
|
|
watermarkw,watermarkh,
|
|
global_alpha=watermarkalpha,
|
|
w_units='screen',
|
|
h_units='screen',
|
|
anchor='bottom_left',
|
|
dilate=True,
|
|
x_range_name = "watermark",
|
|
y_range_name = "watermark",
|
|
)
|
|
|
|
TIPS = OrderedDict([
|
|
('time','@ftime1'),
|
|
('pace','@fpace1'),
|
|
('hr','@hr1'),
|
|
('spm','@spm1{1.1}'),
|
|
('distance','@distance1{5}'),
|
|
])
|
|
TIPS2 = OrderedDict([
|
|
('time','@ftime2'),
|
|
('pace','@fpace2'),
|
|
('hr','@hr2'),
|
|
('spm','@spm2{1.1}'),
|
|
('distance','@distance2{5}'),
|
|
])
|
|
|
|
|
|
|
|
hover1 = plot.select(type=HoverTool)
|
|
hover1.tooltips = TIPS
|
|
hover2 = plot.select(type=HoverTool)
|
|
hover2.tooltips = TIPS2
|
|
|
|
|
|
if plottype=='line':
|
|
l1 = plot.line('x1','y1',source=source1,
|
|
color="blue",legend=row1.name,
|
|
)
|
|
l2 = plot.line('x2','y2',source=source2,
|
|
color="red",legend=row2.name,
|
|
)
|
|
elif plottype=='scatter':
|
|
l1 = plot.scatter('x1','y1',source=source1,legend=row1.name,
|
|
fill_alpha=0.4,
|
|
line_color=None)
|
|
l2 = plot.scatter('x2','y2',source=source2,legend=row2.name,
|
|
fill_alpha=0.4,
|
|
line_color=None,color="red")
|
|
|
|
plot.add_tools(HoverTool(renderers=[l1],tooltips=TIPS))
|
|
plot.add_tools(HoverTool(renderers=[l2],tooltips=TIPS2))
|
|
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]
|
|
|
|
ylabel1 = Label(x=100,y=90,x_units='screen',y_units='screen',
|
|
text=axlabels[yparam]+": {ymean1:6.2f}".format(
|
|
ymean1=ymean1
|
|
),
|
|
background_fill_alpha=.7,
|
|
background_fill_color='white',
|
|
text_color='blue'
|
|
)
|
|
ylabel2 = Label(x=100,y=110,x_units='screen',y_units='screen',
|
|
text=axlabels[yparam]+": {ymean2:6.2f}".format(
|
|
ymean2=ymean2
|
|
),
|
|
background_fill_alpha=.7,
|
|
background_fill_color='white',
|
|
text_color='red'
|
|
)
|
|
plot.add_layout(ylabel1)
|
|
plot.add_layout(ylabel2)
|
|
|
|
if xparam == 'time':
|
|
plot.xaxis[0].formatter = DatetimeTickFormatter(
|
|
hours = ["%H"],
|
|
minutes = ["%M"],
|
|
seconds = ["%S"],
|
|
days = ["0"],
|
|
months = [""],
|
|
years = [""]
|
|
)
|
|
|
|
|
|
if yparam == 'pace':
|
|
plot.yaxis[0].formatter = DatetimeTickFormatter(
|
|
seconds = ["%S"],
|
|
minutes = ["%M"]
|
|
)
|
|
|
|
plot.y_range = Range1d(ymin,ymax)
|
|
|
|
|
|
script, div = components(plot)
|
|
|
|
return [script,div]
|
|
|
|
def interactive_otw_advanced_pace_chart(id=0,promember=0):
|
|
# check if valid ID exists (workout exists)
|
|
rowdata,row = dataprep.getrowdata_db(id=id)
|
|
rowdata.dropna(axis=1,how='all',inplace=True)
|
|
rowdata.dropna(axis=0,how='any',inplace=True)
|
|
|
|
if rowdata.empty:
|
|
return "","No Valid Data Available"
|
|
|
|
# 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(
|
|
rowdata
|
|
)
|
|
|
|
|
|
plot = Figure(x_axis_type="datetime",y_axis_type="datetime",
|
|
tools=TOOLS,
|
|
plot_width=920,
|
|
toolbar_sticky=False)
|
|
|
|
# add watermark
|
|
plot.extra_y_ranges = {"watermark": watermarkrange}
|
|
plot.extra_x_ranges = {"watermark": watermarkrange}
|
|
|
|
plot.image_url([watermarkurl],watermarkx,watermarky,
|
|
watermarkw,watermarkh,
|
|
global_alpha=watermarkalpha,
|
|
w_units='screen',
|
|
h_units='screen',
|
|
anchor=watermarkanchor,
|
|
dilate=True,
|
|
x_range_name = "watermark",
|
|
y_range_name = "watermark",
|
|
)
|
|
|
|
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 = 1.0e3*90
|
|
ymin = 1.0e3*210
|
|
|
|
plot.y_range = Range1d(ymin,ymax)
|
|
|
|
|
|
hover = plot.select(dict(type=HoverTool))
|
|
|
|
plot.line('time','pace',source=source,legend="Pace",color="black")
|
|
plot.line('time','nowindpace',source=source,legend="Corrected Pace",color="red")
|
|
|
|
hover.tooltips = OrderedDict([
|
|
('Time','@ftime'),
|
|
('Pace','@fpace'),
|
|
('Corrected Pace','@fnowindpace'),
|
|
('HR','@hr{int}'),
|
|
('SPM','@spm{1.1}'),
|
|
])
|
|
|
|
hover.mode = 'mouse'
|
|
|
|
script, div = components(plot)
|
|
|
|
return [script,div]
|
|
|