diff --git a/rowers/forms.py b/rowers/forms.py index 0566c38c..3763b132 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -298,6 +298,7 @@ formaxlabels = axlabels.copy() formaxlabels.pop('None') parchoices = list(sorted(formaxlabels.items(), key = lambda x:x[1])) + class BoxPlotChoiceForm(forms.Form): yparam = forms.ChoiceField(choices=parchoices,initial='spm', label='Metric') @@ -313,6 +314,36 @@ class BoxPlotChoiceForm(forms.Form): includereststrokes = forms.BooleanField(initial=False, required=False, label='Include Rest Strokes') + +grouplabels = axlabels.copy() +grouplabels['date'] = 'Date' +grouplabels['workoutid'] = 'Workout' +grouplabels.pop('None') +grouplabels.pop('time') +groupchoices = list(sorted(grouplabels.items(), key = lambda x:x[1])) + +class MultiFlexChoiceForm(forms.Form): + xparam = forms.ChoiceField(choices=parchoices,initial='spm', + label='X axis') + yparam = forms.ChoiceField(choices=parchoices,initial='power', + label='Y axis') + groupby = forms.ChoiceField(choices=groupchoices,initial='date', + label='Group By') + binsize = forms.FloatField(initial=1,required=False,label = 'Bin Size') + spmmin = forms.FloatField(initial=15, + required=False,label = 'Min SPM') + spmmax = forms.FloatField(initial=55, + required=False,label = 'Max SPM') + workmin = forms.FloatField(initial=0, + required=False,label = 'Min Work per Stroke') + workmax = forms.FloatField(initial=1500, + required=False,label = 'Max Work per Stroke') + ploterrorbars = forms.BooleanField(initial=False, + required=False, + label='Plot Error Bars') + includereststrokes = forms.BooleanField(initial=False, + required=False, + label='Include Rest Strokes') class ChartParamChoiceForm(forms.Form): plotchoices = ( diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index a8e3f38a..eb524261 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -69,27 +69,46 @@ watermarkw = 184 watermarkh = 35 watermarkanchor = 'bottom_right' -def errorbar(fig, x, y, xerr=None, yerr=None, color='red', +def errorbar(fig, x, y, source=ColumnDataSource(), + xerr=False, yerr=False, color='red', point_kwargs={}, error_kwargs={}): - fig.circle(x, y, color=color, **point_kwargs) - - if xerr: - x_err_x = [] - x_err_y = [] - for px, py, err in zip(x, y, xerr): - x_err_x.append((px - err, px + err)) - x_err_y.append((py, py)) - fig.multi_line(x_err_x, x_err_y, color=color, **error_kwargs) + fig.circle(x, y, source=source, name='data',color=color, **point_kwargs) - if yerr: - y_err_x = [] - y_err_y = [] - for px, py, err in zip(x, y, yerr): - y_err_x.append((px, px)) - y_err_y.append((py - err, py + err)) - fig.multi_line(y_err_x, y_err_y, color=color, **error_kwargs) - + xvalues = source.data[x] + yvalues = source.data[y] + + xerrvalues = source.data['xerror'] + yerrvalues = source.data['yerror'] + + + try: + a = xvalues[0]+1 + if xerr: + x_err_x = [] + x_err_y = [] + for px, py, err in zip(xvalues, yvalues, xerrvalues): + x_err_x.append((px - err, px + err)) + x_err_y.append((py, py)) + fig.multi_line(x_err_x, x_err_y, color=color, + name='xerr', + **error_kwargs) + except TypeError: + pass + + try: + a = yvalues[0]+1 + if yerr: + y_err_x = [] + y_err_y = [] + for px, py, err in zip(xvalues, yvalues, yerrvalues): + y_err_x.append((px, px)) + y_err_y.append((py - err, py + err)) + fig.multi_line(y_err_x, y_err_y, color=color, + name='yerr',**error_kwargs) + except TypeError: + pass + def tailwind(bearing,vwind,winddir): """ Calculates head-on head/tailwind in direction of rowing @@ -1144,6 +1163,98 @@ def interactive_chart(id=0,promember=0): return [script,div] +def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', + ploterrorbars=False): + if datadf.empty: + return ['','
No non-zero data in selection
'] + + + xparamname = axlabels[xparam] + yparamname = axlabels[yparam] + + if xparam=='distance': + xaxmax = datadf['x1'].max() + xaxmin = datadf['x1'].min() + else: + xaxmax = yaxmaxima[xparam] + xaxmin = yaxminima[xparam] + + 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,hover' + + 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", + ) + + errorbar(plot,xparam,yparam,source=source, + xerr=ploterrorbars, + yerr=ploterrorbars, + point_kwargs={ + 'line_color':None, + 'legend':yparamname, + 'size':10, + }) + + plot.xaxis.axis_label = axlabels[xparam] + plot.yaxis.axis_label = axlabels[yparam] + + + yrange1 = Range1d(start=yaxminima[yparam],end=yaxmaxima[yparam]) + plot.y_range = yrange1 + + xrange1 = Range1d(start=yaxminima[xparam],end=yaxmaxima[xparam]) + plot.x_range = xrange1 + + if yparam == 'pace': + plot.yaxis[0].formatter = DatetimeTickFormatter( + seconds = ["%S"], + minutes = ["%M"] + ) + + hover = plot.select(dict(type=HoverTool)) + + if groupby != 'date': + hover.tooltips = OrderedDict([ + (groupby,'@groupval{1.1}'), + ]) + else: + hover.tooltips = OrderedDict([ + (groupby,'@groupval'), + ]) + + hover.mode = 'mouse' + + script,div = components(plot) + + + return [script,div] + def interactive_cum_flex_chart2(theworkouts,promember=0, xparam='spm', yparam1='power', diff --git a/rowers/templates/multiflex.html b/rowers/templates/multiflex.html new file mode 100644 index 00000000..62666379 --- /dev/null +++ b/rowers/templates/multiflex.html @@ -0,0 +1,70 @@ +{% extends "base.html" %} +{% load staticfiles %} +{% load rowerfilters %} + +{% block title %}View Comparison {% endblock %} + +{% block content %} + + + + +{{ interactiveplot |safe }} + + + + + ++ You can use the form above to change the metric or filter the data. + Set Min SPM and Max SPM to select only strokes in a certain range of + stroke rates. + Set Work per Stroke to a minimum value to remove "paddle" strokes or turns. +
+