diff --git a/rowers/forms.py b/rowers/forms.py index 5266bdc4..763d4fdb 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -739,6 +739,9 @@ class PerformanceManagerForm(forms.Form): doform = forms.BooleanField(required=False,initial=False, label='Freshness') + showtests = forms.BooleanField(required=False,initial=False, + label='Show my best workouts') + class FitnessFitForm(forms.Form): startdate = forms.DateField( initial=timezone.now()-datetime.timedelta(days=365), diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index b7a9f4ac..c4f5aeb5 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -120,6 +120,7 @@ def build_goldmedalstandards(workouts,kfitness): testduration = [] fatigues = [] fitnesses = [] + impulses = [] data = [] goldmedalstandards = [] @@ -185,8 +186,9 @@ def build_goldmedalstandards(workouts,kfitness): fatigues.append(np.nan) fitnesses.append(np.nan) + impulses.append(np.nan) - return dates, testpower, testduration, fatigues, fitnesses + return dates, testpower, testduration, fatigues, fitnesses,impulses def get_testpower(workouts,fitnesstestsecs,kfitness): @@ -1762,24 +1764,55 @@ def getfatigues( return fatigues,fitnesses,dates,testpower,testduration,impulses def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, - metricchoice='trimp',doform=False,dofatigue=False): + metricchoice='trimp',doform=False,dofatigue=False, + showtests=False): TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' TOOLS2 = 'box_zoom,hover' - fatigues = [] - fitnesses = [] - dates = [] - testpower = [] - testduration = [] - modelchoice = 'coggan' p0 = 0 k1 = 1 k2 = 1 + dates = [] + testpower = [] + fatigues = [] + fitnesses = [] + testduration = [] + if showtests: + workouts = Workout.objects.filter(user=user.rower,date__gte=startdate, + date__lte=enddate, + workouttype__in=mytypes.rowtypes, + duplicate=False) + dates,testpower,testduration,fatigues,fitnesses,impulses = build_goldmedalstandards( + workouts,kfitness + ) + + df = pd.DataFrame({ + 'date':dates, + 'testpower':testpower, + 'testduration':testduration, + 'fatigue':fatigues, + 'fitness':fitnesses, + 'impulse':impulses, + }) + df.sort_values(['date'],inplace=True) + df['testdup'] = df['testpower'].shift(1) + df['testpower'] = df.apply(lambda x: newtestpower(x),axis=1) + + try: + df['testpower'].iloc[-1] = df['testdup'].iloc[-1] + except IndexError: + pass + + dates = [d for d in df['date']] + testpower = df['testpower'].values.tolist() + fatigues = df['fatigue'].values.tolist() + fitnesses = df['fitness'].values.tolist() + testduration = df['testduration'].values.tolist() fatigues,fitnesses,dates,testpower,testduration,impulses = getfatigues(fatigues, fitnesses, @@ -1794,6 +1827,7 @@ def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, df = pd.DataFrame({ 'date':dates, 'testpower':testpower, + 'testduration': testduration, 'fatigue':fatigues, 'fitness':fitnesses, 'impulse':impulses, @@ -1819,6 +1853,7 @@ def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, source = ColumnDataSource( data = dict( testpower = df['testpower'], + testduration = df['testduration'].apply(lambda x:totaltime_sec_to_string(x,shorten=True)), date = df['date'], fdate = df['date'].map(lambda x: x.strftime('%d-%m-%Y')), fitness = df['fitness'], @@ -1881,8 +1916,9 @@ def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, yaxlabel = 'Fitness' - #plot.circle('date','testpower',source=source,fill_color='green',size=10, - # legend_label=legend_label.format(fitnesstest=fitnesstest)) + if showtests: + plot.circle('date','testpower',source=source,fill_color='green',size=10, + legend_label='Your best workouts') plot.xaxis.axis_label = None plot.yaxis.axis_label = yaxlabel @@ -1937,6 +1973,7 @@ def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, linked_crosshair = CrosshairTool(dimensions='height') + hover.tooltips = OrderedDict([ #(legend_label,'@testpower'), ('Date','@fdate'), @@ -1946,7 +1983,17 @@ def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, ('Impulse','@impulse{int}') ]) - + if showtests: + hover.tooltips = OrderedDict([ + #(legend_label,'@testpower'), + ('Date','@fdate'), + (fitlabel,'@fitness{int}'), + (fatiguelabel,'@fatigue{int}'), + (formlabel,'@form{int}'), + ('Impulse','@impulse{int}'), + ('Gold Medal Score','@testpower{int}'), + ('Test', '@testduration'), + ]) plot2 = Figure(tools=TOOLS2,x_axis_type='datetime', plot_width=900,plot_height=150, @@ -2007,7 +2054,7 @@ def fitnessfit_chart(workouts,user,workoutmode='water',startdate=None, workouts,fitnesstestsecs,kfitness ) else: - dates,testpower, testduration,fatigues,fitnesses = build_goldmedalstandards( + dates,testpower, testduration,fatigues,fitnesses,impulses = build_goldmedalstandards( workouts,kfitness ) # create CP data diff --git a/rowers/models.py b/rowers/models.py index aa31934e..54ceb777 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -891,6 +891,7 @@ class Rower(models.Model): kfatigue = models.IntegerField(default=7,verbose_name='Fatigue Time Decay Constant (days)') showfit = models.BooleanField(default=False) showfresh = models.BooleanField(default=False) + showtests = models.BooleanField(default=False) pw_ut2 = models.IntegerField(default=124,verbose_name="UT2 Power") pw_ut1 = models.IntegerField(default=171,verbose_name="UT1 Power") diff --git a/rowers/templates/performancemanager.html b/rowers/templates/performancemanager.html index 4136e750..e4a8a0f3 100644 --- a/rowers/templates/performancemanager.html +++ b/rowers/templates/performancemanager.html @@ -112,6 +112,11 @@ on the left. The model balances out after a few weeks of regular training, so don't make this chart shorter than a few months.

+

+ Optionally, the chart shows you workouts that represent your best performance for that period. + We automatically detect hard workout segments and tests and compare them to world class + ("Gold Medal") standards for your gender, weight and age category. +

For this chart to reflect your fitness and freshness, it is important to have all workouts on Rowsandall.com. You can automatically import workouts from other fitness platforms. Change diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py index e601d3fe..787f85ad 100644 --- a/rowers/views/analysisviews.py +++ b/rowers/views/analysisviews.py @@ -1567,6 +1567,7 @@ def performancemanager_view(request,userid=0,mode='rower', usegoldmedalstandard = False doform = therower.showfresh dofatigue = therower.showfit + showtests = therower.showtests if request.method == 'POST': form = PerformanceManagerForm(request.POST) @@ -1576,13 +1577,16 @@ def performancemanager_view(request,userid=0,mode='rower', metricchoice = form.cleaned_data['metricchoice'] dofatigue = form.cleaned_data['dofatigue'] doform = form.cleaned_data['doform'] + showtests = form.cleaned_data['showtests'] therower.showfresh = doform therower.showfatigue = dofatigue + therower.showtests = showtests therower.save() else: form = PerformanceManagerForm(initial={ 'doform':doform, 'dofatigue':dofatigue, + 'showtests':showtests, }) script, thediv, endfitness, endfatigue, endform = performance_chart( @@ -1592,6 +1596,7 @@ def performancemanager_view(request,userid=0,mode='rower', metricchoice = metricchoice, doform = doform, dofatigue = dofatigue, + showtests = showtests, ) breadcrumbs = [ @@ -1680,7 +1685,7 @@ def fitness_from_cp_view(request,userid=0,mode='rower', date__lte=enddate, workouttype__in=mytypes.rowtypes, duplicate=False) - + script,thediv = fitnessfit_chart(