From 87b6684465c76f4ad0f50cd3fee72c734ac70ff3 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Fri, 1 Jan 2021 16:51:00 +0100
Subject: [PATCH] Adding gold medal standard to performance chart (optional)
---
rowers/forms.py | 3 +
rowers/interactiveplots.py | 71 ++++++++++++++++++++----
rowers/models.py | 1 +
rowers/templates/performancemanager.html | 5 ++
rowers/views/analysisviews.py | 7 ++-
5 files changed, 74 insertions(+), 13 deletions(-)
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(