diff --git a/rowers/forms.py b/rowers/forms.py index fae9c06e..8610dab5 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -1373,6 +1373,9 @@ class AnalysisChoiceForm(forms.Form): required=False, label='Include Rest Strokes') + trendline = forms.BooleanField(initial=False, required=False, + label='Trend Line') + def __init__(self, *args, **kwargs): super(AnalysisChoiceForm, self).__init__(*args, **kwargs) @@ -1808,6 +1811,9 @@ class FlexOptionsForm(forms.Form): plottype = forms.ChoiceField(choices=plotchoices, initial='line', label='Chart Type') + trendline = forms.BooleanField(initial=False, required=False, + label='Add Trend Line') + class ForceCurveOptionsForm(forms.Form): includereststrokes = forms.BooleanField(initial=False, required=False, diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index d0d87fd6..1b1ee65a 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -4658,7 +4658,8 @@ def interactive_cum_flex_chart2(theworkouts, promember=0, xparam='spm', yparam1='power', yparam2='spm', - workstrokesonly=False): + workstrokesonly=False, + trendline=False): # datadf = dataprep.smalldataprep(theworkouts,xparam,yparam1,yparam2) ids = [int(w.id) for w in theworkouts] @@ -4747,6 +4748,15 @@ def interactive_cum_flex_chart2(theworkouts, promember=0, else: # pragma: no cover datadf['yname2'] = axlabels[yparam1] + def func(x, a, b): + return a*x+b + + x1 = datadf['x1'] + y1 = datadf['y1'] + popt, pcov = optimize.curve_fit(func, x1, y1) + ytrend = func(x1, popt[0], popt[1]) + datadf['ytrend'] = ytrend + source = ColumnDataSource( datadf ) @@ -4848,6 +4858,10 @@ def interactive_cum_flex_chart2(theworkouts, promember=0, minutes=["%M"] ) + # trendline + if trendline: + plot.line('x1', 'ytrend', source=source2, legend_label=yparamname1+' (trend)') + if yparam2 != 'None': yrange2 = Range1d(start=yaxminima[yparam2], end=yaxmaxima[yparam2]) plot.extra_y_ranges["yax2"] = yrange2 @@ -5392,6 +5406,7 @@ def interactive_flex_chart2(id, r, promember=0, yparam2='hr', plottype='line', workstrokesonly=False, + trendline=False, mode='rower'): watermarkurl = "/static/img/logo7.png" @@ -5557,6 +5572,15 @@ def interactive_flex_chart2(id, r, promember=0, else: # pragma: no cover rowdata['yname2'] = rowdata['yname1'] + def func(x, a, b): + return a*x+b + + x1 = rowdata['x1'] + y1 = rowdata['y1'] + popt, pcov = optimize.curve_fit(func, x1, y1) + ytrend = func(x1, popt[0], popt[1]) + rowdata['ytrend'] = ytrend + # prepare data source = ColumnDataSource( rowdata @@ -5672,6 +5696,10 @@ def interactive_flex_chart2(id, r, promember=0, plot.line(xvals, yconstantpower, color="green", legend_label="Constant Power") + # trendline + if trendline: + plot.line('x1', 'ytrend', source=source2, legend_label=yaxlabel+' (trend)') + if plottype == 'line': plot.line('x1', 'y1', source=source2, legend_label=yaxlabel) elif plottype == 'scatter': # pragma: no cover diff --git a/rowers/tasks.py b/rowers/tasks.py index 6791029f..f6499009 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -3028,8 +3028,8 @@ def handle_nk_async_workout(alldata, userid, nktoken, nkid, delaysec, defaulttim jsonData = response.json() strokeData = jsonData[str(nkid)] - dologging('nklog.log', json.dumps(data)) - dologging('nklog.log', json.dumps(strokeData)) + #dologging('nklog.log', json.dumps(data)) + #dologging('nklog.log', json.dumps(strokeData)) seatNumber = 1 try: diff --git a/rowers/templates/user_analysis_select.html b/rowers/templates/user_analysis_select.html index f952b9b2..17d95609 100644 --- a/rowers/templates/user_analysis_select.html +++ b/rowers/templates/user_analysis_select.html @@ -89,6 +89,7 @@ var plottype = $("#id_plottype").parent().parent(); var reststrokes = $("#id_includereststrokes").parent().parent(); + var trendline = $("#id_trendline").parent().parent(); var piece = $("#id_piece").parent().parent(); var cpfit = $("#id_cpfit").parent().parent(); var cpoverlay = $("#id_cpoverlay").parent().parent(); @@ -116,6 +117,7 @@ cpfit.hide(); cpoverlay.hide(); piece.hide(); + trendline.hide(); if (functionfield.val() == 'boxplot') { plotfield.show(); @@ -138,6 +140,7 @@ xaxis.show(); yaxis1.show(); yaxis2.show(); + trendline.show(); } if (functionfield.val() == 'stats') { @@ -179,6 +182,7 @@ palette.hide(); binsize.hide(); errorbars.hide(); + trendline.hide(); xaxis.hide(); yaxis1.hide(); yaxis2.hide(); @@ -199,6 +203,7 @@ groupby.hide(); palette.hide(); binsize.hide(); + trendline.hide(); errorbars.hide(); xaxis.hide(); yaxis1.hide(); @@ -220,6 +225,7 @@ spmmin.show(); spmmax.show(); workmin.show(); + trendline.hide(); workmax.show(); plotfield.hide(); xaxis.hide(); @@ -240,6 +246,7 @@ y_param.hide(); groupby.hide(); spmmin.hide(); + trendline.show(); spmmax.hide(); workmin.hide(); workmax.hide(); @@ -265,6 +272,7 @@ binsize.hide(); errorbars.hide(); plottype.hide(); + trendline.hide(); reststrokes.show(); cpfit.hide(); cpoverlay.hide(); @@ -283,6 +291,7 @@ workmax.hide(); plotfield.hide(); palette.hide(); + trendline.hide(); binsize.hide(); plottype.show(); errorbars.hide(); @@ -301,6 +310,7 @@ workmax.hide(); x_param.hide(); y_param.hide(); + trendline.hide(); groupby.hide(); palette.hide(); binsize.hide(); @@ -392,7 +402,7 @@ and weight category ({{ weightcategory }}). The Gold Medal Standard power is derived from the - World Record for your category. + World Record for your category. The percentile lines are estimates of where the percentiles of the Concept2 rankings historically are for those of exactly your age, gender and weight class. diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py index af28f6cd..0c96f1c2 100644 --- a/rowers/views/analysisviews.py +++ b/rowers/views/analysisviews.py @@ -21,6 +21,7 @@ defaultoptions = { 'waterboattype': mytypes.waterboattype, 'function': 'boxplot', 'ranking': False, + 'trendline': False, } @@ -307,6 +308,7 @@ def trendflexdata(workouts, options, userid=0): ids = options['ids'] workstrokesonly = not includereststrokes + fieldlist, fielddict = dataprep.getstatsfields() fieldlist = [xparam, yparam, groupby, 'workoutid', 'spm', 'driveenergy', @@ -477,6 +479,7 @@ def flexalldata(workouts, options): xparam = options['xaxis'] yparam1 = options['yaxis1'] yparam2 = options['yaxis2'] + trendline = options['trendline'] promember = True workstrokesonly = not includereststrokes @@ -486,6 +489,7 @@ def flexalldata(workouts, options): yparam2=yparam2, promember=promember, workstrokesonly=workstrokesonly, + trendline=trendline, ) script = res[0] div = res[1] diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index b94816a9..2dd80ae6 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -3760,6 +3760,11 @@ def workout_flexchart3_view(request, *args, **kwargs): else: plottype = 'line' + if 'trendline' in kwargs: # pragma: no cover + trendline = kwargs['trendline'] + else: + trendline = False + if 'workstrokesonly' in kwargs: # pragma: no cover workstrokesonly = kwargs['workstrokesonly'] else: @@ -3790,6 +3795,7 @@ def workout_flexchart3_view(request, *args, **kwargs): cd = flexoptionsform.cleaned_data includereststrokes = cd['includereststrokes'] plottype = cd['plottype'] + trendline = cd['trendline'] workstrokesonly = not includereststrokes @@ -3832,7 +3838,9 @@ def workout_flexchart3_view(request, *args, **kwargs): xparam=xparam, yparam1=yparam1, yparam2=yparam2, promember=promember, plottype=plottype, - workstrokesonly=workstrokesonly, mode=row.workouttype + workstrokesonly=workstrokesonly, + trendline=trendline, + mode=row.workouttype ) axchoicesbasic = {ax[0]: ax[1] for ax in axes if ax[4] == 'basic'} @@ -3876,7 +3884,8 @@ def workout_flexchart3_view(request, *args, **kwargs): initial = { 'includereststrokes': not workstrokesonly, - 'plottype': plottype + 'plottype': plottype, + 'trendline': trendline, } flexoptionsform = FlexOptionsForm(initial=initial)