diff --git a/rowers/forms.py b/rowers/forms.py index 3fbc75fb..ab8c261f 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -738,6 +738,13 @@ class FitnessFitForm(forms.Form): initial='rscore', label='Workload Metric') + # temporary + k1 = forms.FloatField(required=True,initial=1.0, + label='k1') + k2 = forms.FloatField(required=True,initial=1.0, + label='k2') + p0 = forms.IntegerField(required=True,initial=100,label='Unfit Performance') + mode = forms.ChoiceField(required=True, choices=modechoices, initial='rower', @@ -745,7 +752,9 @@ class FitnessFitForm(forms.Form): ) class Meta: - fields = ['startdate','enddate','mode','fitnesstest','kfitness','kfatigue','metricchoice'] + fields = ['startdate','enddate','mode','fitnesstest', + 'kfitness','kfatigue','metricchoice', + 'k1','k2','p0'] class SessionDateShiftForm(forms.Form): shiftstartdate = forms.DateField( diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 97b2bded..525c09b8 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -1528,18 +1528,23 @@ def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'): return [script,div,js_resources,css_resources] def fitnessfit_chart(workouts,user,workoutmode='water',startdate=None, - enddate=None,kfitness=42,kfatigue=7,fitnesstest=20): + enddate=None,kfitness=42,kfatigue=7,fitnesstest=20, + metricchoice='rscore', + k1=1,k2=1,p0=100): TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' dates = [] testpower = [] + fatigues = [] + fitnesses = [] workouts = workouts.order_by('date') data = [] fitnesstestsecs = fitnesstest*60 + # create CP data for w in workouts: cpfile = 'media/cpdata_{id}.parquet.gz'.format(id=w.id) try: @@ -1559,6 +1564,7 @@ def fitnessfit_chart(workouts,user,workoutmode='water',startdate=None, errfunc = lambda pars,x,y: fitfunc(pars,x)-y for w in workouts: + # Create CP data point for date range ids = [w.id for w in workouts.filter(date__gte=w.date-datetime.timedelta(days=kfitness), date__lte=w.date)] @@ -1590,12 +1596,32 @@ def fitnessfit_chart(workouts,user,workoutmode='water',startdate=None, dates.append(datetime.datetime.combine(w.date,datetime.datetime.min.time())) testpower.append(powertest) + # create Fitness and Fatigue number + fatigue = 0 + fitness = 0 + previousworkouts = workouts.filter(date__lte=w.date) + for ww in previousworkouts: + delta = (w.date-ww.date).days + weight = getattr(ww,metricchoice) + fatigue += weight*math.exp(-delta/kfatigue) + fitness += weight*math.exp(-delta/kfitness) + + fatigues.append(fatigue) + fitnesses.append(fitness) + + df = pd.DataFrame({ 'date':dates, 'testpower':testpower, + 'fatigue':fatigues, + 'fitness':fitnesses, }) + df['fatigue'] = k2*df['fatigue'] + df['fitness'] = p0+k1*df['fitness'] + df['form'] = df['fitness']-df['fatigue'] + df.sort_values(['date'],inplace=True) @@ -1606,14 +1632,14 @@ def fitnessfit_chart(workouts,user,workoutmode='water',startdate=None, - - - source = ColumnDataSource( data = dict( testpower = df['testpower'], date = df['date'], - fdate = df['date'].map(lambda x: x.strftime('%d-%m-%Y')) + fdate = df['date'].map(lambda x: x.strftime('%d-%m-%Y')), + fitness = df['fitness'], + fatigue = df['fatigue'], + form = df['form'], ) ) @@ -1654,6 +1680,13 @@ def fitnessfit_chart(workouts,user,workoutmode='water',startdate=None, plot.circle('date','testpower',source=source,fill_color='green',size=10, legend_label='{fitnesstest} min power'.format(fitnesstest=fitnesstest)) + plot.line('date','fitness',source=source,color='yellow', + legend_label='fitness') + plot.line('date','fatigue',source=source,color='red', + legend_label='fatigue') + plot.line('date','form',source=source,color='green', + legend_label='form') + plot.xaxis.axis_label = 'Date' plot.yaxis.axis_label = 'Power (W)' diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py index 0788b16a..6537fdc9 100644 --- a/rowers/views/analysisviews.py +++ b/rowers/views/analysisviews.py @@ -1555,6 +1555,12 @@ def fitness_from_cp_view(request,userid=0,mode='rower', kfitness = 42 kfatigue = 7 fitnesstest = 20 + metricchoice = 'rscore' + + # temp fit parameters + k1 = 1 + k2 = 1 + p0 = 100. if request.method == 'POST': form = FitnessFitForm(request.POST) @@ -1565,6 +1571,11 @@ def fitness_from_cp_view(request,userid=0,mode='rower', kfitness = form.cleaned_data['kfitness'] kfatigue = form.cleaned_data['kfatigue'] fitnesstest = form.cleaned_data['fitnesstest'] + metricchoice = form.cleaned_data['metricchoice'] + # temporary manual "fit" parameters + k1 = form.cleaned_data['k1'] + k2 = form.cleaned_data['k2'] + p0 = form.cleaned_data['p0'] else: form = FitnessFitForm() @@ -1584,6 +1595,8 @@ def fitness_from_cp_view(request,userid=0,mode='rower', kfitness=kfitness, kfatigue=kfatigue, fitnesstest=fitnesstest, + metricchoice=metricchoice, + k1=k1,k2=k2,p0=p0 ) breadcrumbs = [