From 40313f9680469af96276bac19754c2f71c4197d9 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 18 May 2017 15:23:09 +0200 Subject: [PATCH] adding OTW power slack --- rowers/models.py | 5 +- rowers/templates/rower_form.html | 12 ++++- rowers/views.py | 88 +++++++++++++++++++++++++------- 3 files changed, 83 insertions(+), 22 deletions(-) diff --git a/rowers/models.py b/rowers/models.py index fa0c839d..15046a7a 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -173,13 +173,14 @@ class Rower(models.Model): # Power Zone Data ftp = models.IntegerField(default=226,verbose_name="Functional Threshold Power") + otwslack = models.IntegerField(default=15,verbose_name="OTW Power slack") pw_ut2 = models.IntegerField(default=124,verbose_name="UT2 Power") pw_ut1 = models.IntegerField(default=171,verbose_name="UT1 Power") pw_at = models.IntegerField(default=203,verbose_name="AT Power") pw_tr = models.IntegerField(default=237,verbose_name="TR Power") pw_an = models.IntegerField(default=273,verbose_name="AN Power") - + powerzones = PowerZonesField(default=['Rest', 'Pwr UT2', 'Pwr UT1', @@ -576,7 +577,7 @@ class AdvancedWorkoutForm(ModelForm): class RowerPowerForm(ModelForm): class Meta: model = Rower - fields = ['ftp'] + fields = ['ftp','otwslack'] # Form to set rower's Power zones, including test routines # to enable consistency diff --git a/rowers/templates/rower_form.html b/rowers/templates/rower_form.html index c495cb23..9fc7b380 100644 --- a/rowers/templates/rower_form.html +++ b/rowers/templates/rower_form.html @@ -7,6 +7,7 @@

Heart Rate Zones

+

Set your heart rate zones with this form.

{% if form.errors %}

Please correct the error{{ form.errors|pluralize }} below. @@ -27,6 +28,8 @@

Power Zones

+

The power zones are defined relative to power as measured by the + indoor rower.

{% if powerzonesform.errors %}

@@ -127,10 +130,15 @@

-

Functional Threshold Power

+

Functional Threshold Power and OTW Slack

Use this form to quickly change your zones based on the power of a recent - full out 60 minutes effort. It will update all zones defined above.

+ full out 60 minutes effort on the ergometer. + It will update all zones defined above.

+

The OTW Power Slack is the percentage drop of your On-the-water + rowing power + vs the erg power. Typical values are around 15%. This will lower + the power zones for your OTW workouts.

{{ powerform.as_table }} diff --git a/rowers/views.py b/rowers/views.py index 1b421e1f..2bead2fc 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -4394,7 +4394,11 @@ def cumstats(request,theuser=0, def workout_stats_view(request,id=0,message="",successmessage=""): r = Rower.objects.get(user=request.user) - + try: + w = Workout.objects.get(id=id) + except Workout.DoesNotExist: + raise Http404("Workout doesn't exist") + workstrokesonly = True if request.method == 'POST' and 'workstrokesonly' in request.POST: workstrokesonly = request.POST['workstrokesonly'] @@ -4462,9 +4466,13 @@ def workout_stats_view(request,id=0,message="",successmessage=""): pwr4 = datadf['power']**(4) normp = (pwr4.mean())**(0.25) if not np.isnan(normp): - intensityfactor = datadf['power'].mean()/float(r.ftp) - intensityfactor = normp/float(r.ftp) - tss = 100.*((duration*normp*intensityfactor)/(3600.*r.ftp)) + ftp = float(r.ftp) + if w.workouttype == 'water': + ftp = ftp*(100.-r.otwslack)/100. + + intensityfactor = datadf['power'].mean()/float(ftp) + intensityfactor = normp/float(ftp) + tss = 100.*((duration*normp*intensityfactor)/(3600.*ftp)) if not np.isnan(tss): otherstats['tss'] = { @@ -5298,7 +5306,11 @@ def workout_add_otw_powerplot_view(request,id): r.pw_at, r.pw_tr,r.pw_an])/r.ftp - + + ftp = r.ftp + if w.workouttype == 'water': + ftp = ftp*(100.-r.otwslack)/100. + hrpwrdata = { 'hrmax':r.max, 'hrut2':r.ut2, @@ -5306,7 +5318,7 @@ def workout_add_otw_powerplot_view(request,id): 'hrat':r.at, 'hrtr':r.tr, 'hran':r.an, - 'ftp':r.ftp, + 'ftp':ftp, 'powerperc':serialize_list(powerperc), 'powerzones':serialize_list(r.powerzones), } @@ -5356,6 +5368,9 @@ def workout_add_piechart_view(request,id): r.pw_at, r.pw_tr,r.pw_an])/r.ftp + ftp = float(r.ftp) + if w.workouttype == 'water': + ftp = ftp*(100.-r.otwslack)/100. hrpwrdata = { 'hrmax':r.max, @@ -5364,7 +5379,7 @@ def workout_add_piechart_view(request,id): 'hrat':r.at, 'hrtr':r.tr, 'hran':r.an, - 'ftp':r.ftp, + 'ftp':ftp, 'powerperc':serialize_list(powerperc), 'powerzones':serialize_list(r.powerzones), } @@ -5414,6 +5429,9 @@ def workout_add_power_piechart_view(request,id): r.pw_at, r.pw_tr,r.pw_an])/r.ftp + ftp = float(r.ftp) + if w.workouttype == 'water': + ftp = ftp*(100.-r.otwslack)/100. hrpwrdata = { 'hrmax':r.max, @@ -5422,7 +5440,7 @@ def workout_add_power_piechart_view(request,id): 'hrat':r.at, 'hrtr':r.tr, 'hran':r.an, - 'ftp':r.ftp, + 'ftp':ftp, 'powerperc':serialize_list(powerperc), 'powerzones':serialize_list(r.powerzones), } @@ -5469,6 +5487,9 @@ def workout_add_timeplot_view(request,id): r.pw_at, r.pw_tr,r.pw_an])/r.ftp + ftp = float(r.ftp) + if w.workouttype == 'water': + ftp = ftp*(100.-r.otwslack)/100. hrpwrdata = { 'hrmax':r.max, @@ -5477,7 +5498,7 @@ def workout_add_timeplot_view(request,id): 'hrat':r.at, 'hrtr':r.tr, 'hran':r.an, - 'ftp':r.ftp, + 'ftp':ftp, 'powerperc':serialize_list(powerperc), 'powerzones':serialize_list(r.powerzones), } @@ -5526,6 +5547,9 @@ def workout_add_distanceplot_view(request,id): r.pw_at, r.pw_tr,r.pw_an])/r.ftp + ftp = float(r.ftp) + if w.workouttype == 'water': + ftp = ftp*(100.-r.otwslack)/100. hrpwrdata = { 'hrmax':r.max, @@ -5534,7 +5558,7 @@ def workout_add_distanceplot_view(request,id): 'hrat':r.at, 'hrtr':r.tr, 'hran':r.an, - 'ftp':r.ftp, + 'ftp':ftp, 'powerperc':serialize_list(powerperc), 'powerzones':serialize_list(r.powerzones), } @@ -5581,6 +5605,9 @@ def workout_add_distanceplot2_view(request,id): r.pw_at, r.pw_tr,r.pw_an])/r.ftp + ftp = float(r.ftp) + if w.workouttype == 'water': + ftp = ftp*(100.-r.otwslack)/100. hrpwrdata = { 'hrmax':r.max, @@ -5589,7 +5616,7 @@ def workout_add_distanceplot2_view(request,id): 'hrat':r.at, 'hrtr':r.tr, 'hran':r.an, - 'ftp':r.ftp, + 'ftp':ftp, 'powerperc':serialize_list(powerperc), 'powerzones':serialize_list(r.powerzones), } @@ -5638,6 +5665,9 @@ def workout_add_timeplot2_view(request,id): r.pw_at, r.pw_tr,r.pw_an])/r.ftp + ftp = float(r.ftp) + if w.workouttype == 'water': + ftp = ftp*(100.-r.otwslack)/100. hrpwrdata = { 'hrmax':r.max, @@ -5646,7 +5676,7 @@ def workout_add_timeplot2_view(request,id): 'hrat':r.at, 'hrtr':r.tr, 'hran':r.an, - 'ftp':r.ftp, + 'ftp':ftp, 'powerperc':serialize_list(powerperc), 'powerzones':serialize_list(r.powerzones), } @@ -6387,6 +6417,10 @@ def workout_upload_view(request, r.pw_at, r.pw_tr,r.pw_an])/r.ftp + ftp = float(r.ftp) + if w.workouttype == 'water': + ftp = ftp*(100.-r.otwslack)/100. + hrpwrdata = { 'hrmax':r.max, 'hrut2':r.ut2, @@ -6394,7 +6428,7 @@ def workout_upload_view(request, 'hrat':r.at, 'hrtr':r.tr, 'hran':r.an, - 'ftp':r.ftp, + 'ftp':ftp, 'powerperc':serialize_list(powerperc), 'powerzones':serialize_list(r.powerzones), } @@ -6631,6 +6665,10 @@ def team_workout_upload_view(request,message="", r.pw_at, r.pw_tr,r.pw_an])/r.ftp + ftp = float(r.ftp) + if w.workouttype == 'water': + ftp = ftp*(100.-r.otwslack)/100. + hrpwrdata = { 'hrmax':r.max, 'hrut2':r.ut2, @@ -6638,7 +6676,7 @@ def team_workout_upload_view(request,message="", 'hrat':r.at, 'hrtr':r.tr, 'hran':r.an, - 'ftp':r.ftp, + 'ftp':ftp, 'powerperc':serialize_list(powerperc), 'powerzones':serialize_list(r.powerzones), } @@ -6840,9 +6878,13 @@ def workout_summary_restore_view(request,id,message="",successmessage=""): r.pw_at, r.pw_tr,r.pw_an])/r.ftp + ftp = float(r.ftp) + if row.workouttype == 'water': + ftp = ftp*(100.-r.otwslack)/100. + rr = rrower(hrmax=r.max,hrut2=r.ut2, hrut1=r.ut1,hrat=r.at, - hrtr=r.tr,hran=r.an,ftp=r.ftp, + hrtr=r.tr,hran=r.an,ftp=ftp, powerperc=powerperc,powerzones=r.powerzones) rowdata = rdata(f1,rower=rr) if rowdata == 0: @@ -6951,9 +6993,13 @@ def workout_summary_edit_view(request,id,message="",successmessage="" r.pw_at, r.pw_tr,r.pw_an])/r.ftp + ftp = float(r.ftp) + if row.workouttype == 'water': + ftp = ftp*(100.-r.otwslack)/100. + rr = rrower(hrmax=r.max,hrut2=r.ut2, hrut1=r.ut1,hrat=r.at, - hrtr=r.tr,hran=r.an,ftp=r.ftp, + hrtr=r.tr,hran=r.an,ftp=ftp, powerperc=powerperc,powerzones=r.powerzones) rowdata = rdata(f1,rower=rr) if rowdata == 0: @@ -7272,6 +7318,7 @@ def rower_edit_view(request,message=""): if powerform.is_valid(): cd = powerform.cleaned_data ftp = cd['ftp'] + otwslack = cd['otwslack'] try: r = Rower.objects.get(user=request.user) powerfrac = 100*np.array([r.pw_ut2, @@ -7279,6 +7326,7 @@ def rower_edit_view(request,message=""): r.pw_at, r.pw_tr,r.pw_an])/r.ftp r.ftp = max(min(ftp,650),50) + r.otwslack = max(min(otwslack,50),0) ut2,ut1,at,tr,an = (r.ftp*powerfrac/100.).astype(int) r.pw_ut2 = ut2 r.pw_ut1 = ut1 @@ -7286,7 +7334,7 @@ def rower_edit_view(request,message=""): r.pw_tr = tr r.pw_an = an r.save() - message = "Functional Threshold Value Changed" + message = "FTP and/or OTW slack values changed." messages.info(request,message) url = reverse(rower_edit_view) response = HttpResponseRedirect(url) @@ -7635,9 +7683,13 @@ def strokedatajson(request,id): r.pw_at, r.pw_tr,r.pw_an])/r.ftp + ftp = float(r.ftp) + if row.workouttype == 'water': + ftp = ftp*(100.-r.otwslack)/100. + rr = rrower(hrmax=r.max,hrut2=r.ut2, hrut1=r.ut1,hrat=r.at, - hrtr=r.tr,hran=r.an,ftp=r.ftp, + hrtr=r.tr,hran=r.an,ftp=ftp, powerperc=powerperc,powerzones=r.powerzones) rowdata = rdata(row.csvfilename,rower=rr).df