diff --git a/rowers/forms.py b/rowers/forms.py index 763d4fdb..f2d73740 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -739,8 +739,6 @@ 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( diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 3ccc776d..1809be10 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -114,6 +114,15 @@ def newtestpower(x): return x['testpower'] +def newtestpowerid(x): + try: + if np.isnan(x['testpower']): + return np.nan + except (AttributeError,TypeError): + return np.nan + + return x['id'] + def build_goldmedalstandards(workouts,kfitness): dates = [] testpower = [] @@ -125,11 +134,14 @@ def build_goldmedalstandards(workouts,kfitness): data = [] goldmedalstandards = [] goldmedaldurations = [] - ids = [] workoutdt = [] + ids = [] + + outids = [] + for w in workouts: - goldmedalstandard,goldmedalseconds = dataprep.workout_goldmedalstandard(w) ids.append(w.id) + goldmedalstandard,goldmedalseconds = dataprep.workout_goldmedalstandard(w) if goldmedalseconds > 60: goldmedalstandards.append(goldmedalstandard) goldmedaldurations.append(goldmedalseconds) @@ -170,6 +182,7 @@ def build_goldmedalstandards(workouts,kfitness): powerdf = df[df['workout'].isin(ids)] indexmax = powerdf['goldmedalstandard'].idxmax() + theid = powerdf.loc[indexmax,'workout'] powertest = powerdf['goldmedalstandard'].max() durationtest = powerdf.loc[indexmax,'goldmedalduration'] @@ -178,15 +191,17 @@ def build_goldmedalstandards(workouts,kfitness): if powertest > 0: testpower.append(powertest) testduration.append(durationtest) + outids.append(theid) else: testpower.append(np.nan) testduration.append(np.nan) + outids.append(np.nan) fatigues.append(np.nan) fitnesses.append(np.nan) impulses.append(np.nan) - return dates, testpower, testduration, fatigues, fitnesses,impulses + return dates, testpower, testduration, fatigues, fitnesses,impulses,outids def get_testpower(workouts,fitnesstestsecs,kfitness): @@ -1760,6 +1775,7 @@ def getfatigues( testduration.append(np.nan) + return fatigues,fitnesses,dates,testpower,testduration,impulses def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, @@ -1787,16 +1803,19 @@ def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, testduration = [] impulses = [] + outids = [] + 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( + dates,testpower,testduration,fatigues,fitnesses,impulses, outids = build_goldmedalstandards( workouts,kfitness ) df = pd.DataFrame({ + 'id': outids, 'date':dates, 'testpower':testpower, 'testduration':testduration, @@ -1807,11 +1826,12 @@ def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, df.sort_values(['date'],inplace=True) df['testdup'] = df['testpower'].shift(1) df['testpower'] = df.apply(lambda x: newtestpower(x),axis=1) + df['id'] = df.apply(lambda x: newtestpowerid(x),axis=1) - try: - df['testpower'].iloc[-1] = df['testdup'].iloc[-1] - except IndexError: - pass + #try: + # df['testpower'].iloc[-1] = df['testdup'].iloc[-1] + #except IndexError: + # pass dates = [d for d in df['date']] @@ -1820,6 +1840,7 @@ def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, fitnesses = df['fitness'].values.tolist() testduration = df['testduration'].values.tolist() impulses = df['impulse'].tolist() + outids = df['id'].unique() fatigues,fitnesses,dates,testpower,testduration,impulses = getfatigues(fatigues, fitnesses, @@ -1859,6 +1880,7 @@ def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, df = df.groupby(['date']).max() df['date'] = df.index.values + #for row in df.iterrows(): # print(row) @@ -1928,9 +1950,9 @@ def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, yaxlabel = 'Fitness' - if showtests: - plot.circle('date','testpower',source=source,fill_color='green',size=10, - legend_label='Your best workouts') + #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 @@ -1938,13 +1960,13 @@ def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, y2rangemin = df.loc[:,['form']].min().min() y2rangemax = df.loc[:,['form']].max().max() - if dofatigue and showtests: - y1rangemin = df.loc[:,['testpower','fitness','fatigue']].min().min() - y1rangemax = df.loc[:,['testpower','fitness','fatigue']].max().max()*1.02 - elif showtests: - y1rangemin = df.loc[:,['testpower','fitness']].min().min() - y1rangemax = df.loc[:,['testpower','fitness']].max().max()*1.02 - elif dofatigue: + #if dofatigue and showtests: + # y1rangemin = df.loc[:,['testpower','fitness','fatigue']].min().min() + # y1rangemax = df.loc[:,['testpower','fitness','fatigue']].max().max()*1.02 + #elif showtests: + # y1rangemin = df.loc[:,['testpower','fitness']].min().min() + # y1rangemax = df.loc[:,['testpower','fitness']].max().max()*1.02 + if dofatigue: y1rangemin = df.loc[:,['fitness','fatigue']].min().min() y1rangemax = df.loc[:,['fitness','fatigue']].max().max()*1.02 else: @@ -2024,6 +2046,7 @@ def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, plot2.y_range = Range1d(0,df['impulse'].max()) plot2.vbar(x = df['date'], top = df['impulse'],color='gray') + plot2.vbar(x = df['date'], top = 0*df['testpower']+df['impulse'], color='red') plot2.sizing_mode = 'scale_both' plot2.yaxis.axis_label = 'Impulse' @@ -2045,10 +2068,10 @@ def performance_chart(user,startdate=None,enddate=None,kfitness=42,kfatigue=7, nrworkouts = workouts.count(), nrdata = len(df), e = e, - ) + ),0,0,0,[] ) - return [script,div,endfitness,endfatigue,endform] + return [script,div,endfitness,endfatigue,endform,outids] def fitnessfit_chart(workouts,user,workoutmode='water',startdate=None, diff --git a/rowers/models.py b/rowers/models.py index 54ceb777..aa31934e 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -891,7 +891,6 @@ 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 e4a8a0f3..06908470 100644 --- a/rowers/templates/performancemanager.html +++ b/rowers/templates/performancemanager.html @@ -113,9 +113,12 @@ 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. + The bottom chart shows the training impulse of each individual workout. A gray bar + denotes a regular workout. The red bars denote workouts that stand out in terms + of your power/time performance for that period. This is only available for workouts + where Power (Watts) is measured. How well you performed is expressed as a + Gold Medal Score, where 100 means you are as good as the world class + athletes of your gender, weight and age category.

For this chart to reflect your fitness and freshness, it is important to have all workouts on @@ -148,6 +151,37 @@

+ {% if bestworkouts %} +

Marker Workouts

+
  • + + + + + + + + + + + {% for w in bestworkouts %} + + + + + + + {% endfor %} + +
    DateWorkoutGold Medal ScoreDuration
    {{ w.date }} + {{ w.name }} + + {{ w.goldmedalstandard|floatformat:"0" }} % + + {{ w.goldmedalseconds|secondstotimestring }} +
    +
  • + {% endif %} diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py index b6edc61f..dd33bcf1 100644 --- a/rowers/templatetags/rowerfilters.py +++ b/rowers/templatetags/rowerfilters.py @@ -270,6 +270,7 @@ def strfdeltah(tdelta): return res +@register.filter def secondstotimestring(tdelta): hours, rest = divmod(tdelta,3600) minutes,seconds = divmod(rest,60) diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py index 787f85ad..f879150c 100644 --- a/rowers/views/analysisviews.py +++ b/rowers/views/analysisviews.py @@ -1567,7 +1567,6 @@ 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) @@ -1577,28 +1576,30 @@ 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( + script, thediv, endfitness, endfatigue, endform, ids = performance_chart( theuser,startdate=startdate,enddate=enddate, kfitness = kfitness, kfatigue = kfatigue, metricchoice = metricchoice, doform = doform, dofatigue = dofatigue, - showtests = showtests, + showtests = True, ) + ids = pd.Series(ids).dropna().values + + bestworkouts = Workout.objects.filter(id__in=ids).order_by('date') + + breadcrumbs = [ { 'url':'/rowers/analysis', @@ -1634,6 +1635,7 @@ def performancemanager_view(request,userid=0,mode='rower', 'endfitness':int(endfitness), 'endfatigue':int(endfatigue), 'endform':int(endform), + 'bestworkouts':bestworkouts, })