From 6f723cc66a00d4a891f6a7e0ad3a39232bae9bac Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 13 Sep 2022 19:58:01 +0200 Subject: [PATCH 1/3] trend line --- rowers/forms.py | 6 +++++ rowers/interactiveplots.py | 30 +++++++++++++++++++++- rowers/tasks.py | 4 +-- rowers/templates/user_analysis_select.html | 12 ++++++++- rowers/views/analysisviews.py | 4 +++ rowers/views/workoutviews.py | 13 ++++++++-- 6 files changed, 63 insertions(+), 6 deletions(-) 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) From 6b412afc61aede09de8fd24ff400de284268b699 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 13 Sep 2022 21:28:40 +0200 Subject: [PATCH 2/3] fix tests --- rowers/tests/test_analysis.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rowers/tests/test_analysis.py b/rowers/tests/test_analysis.py index 65c4e4f6..5a5d4fff 100644 --- a/rowers/tests/test_analysis.py +++ b/rowers/tests/test_analysis.py @@ -805,6 +805,7 @@ class WorkoutStatsTestNew(TestCase): 'startdate':startdate, 'enddate':enddate, 'plottype':'scatter', + 'trendline':True, 'spmmin':15, 'spmmax':55, 'workmin':0, From 8cdd1cdbf45678d6ddd65a0307a36db8b5fecd98 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 14 Sep 2022 16:20:01 +0200 Subject: [PATCH 3/3] ch --- rowers/interactiveplots.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 1b1ee65a..faad59d2 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -4911,6 +4911,7 @@ def interactive_cum_flex_chart2(theworkouts, promember=0, var yname1 = data['yname1'] var yname2 = data['yname2'] var workoutid1 = data['workoutid'] + var ytrend = data['ytrend'] var minspm = minspm.value var maxspm = maxspm.value @@ -4941,6 +4942,7 @@ def interactive_cum_flex_chart2(theworkouts, promember=0, data2['yname1'] = [] data2['yname2'] = [] data2['spm'] = [] + data2['ytrend'] = [] for (var i=0; i=minspm && spm1[i]<=maxspm) { @@ -4957,6 +4959,7 @@ def interactive_cum_flex_chart2(theworkouts, promember=0, data2['xname'].push(0) data2['yname1'].push(0) data2['yname2'].push(0) + data2['ytrend'].push(ytrend[i]) xm += x1[i] ym1 += y1[i] @@ -5834,6 +5837,7 @@ def interactive_flex_chart2(id, r, promember=0, var yname2 = data['yname2'] var workoutid1 = data['workoutid'] var workoutstate1 = data['workoutstate'] + var ytrend = data['ytrend'] var annotation = annotation.value var minspm = minspm.value @@ -5871,6 +5875,7 @@ def interactive_flex_chart2(id, r, promember=0, data2['xname'] = [] data2['yname1'] = [] data2['yname2'] = [] + data2['ytrend'] = [] for (var i=0; i