From fdd7622af42291e718a3389480dfa5b4fd63d8b9 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 25 Jan 2021 19:22:47 +0100 Subject: [PATCH 1/3] adding customizable HR zones --- rowers/models.py | 198 ++++++++++++++++++++---- rowers/templates/rower_preferences.html | 40 ++++- rowers/views/statements.py | 2 +- rowers/views/userviews.py | 15 +- 4 files changed, 215 insertions(+), 40 deletions(-) diff --git a/rowers/models.py b/rowers/models.py index 0f9e0ab4..745e9386 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -906,6 +906,14 @@ class Rower(models.Model): 'Pwr TR', 'Pwr AN']) + hrzones = PowerZonesField(default=['Rest', + 'UT2', + 'UT1', + 'AT', + 'TR', + 'AN','max']) + + emailalternatives = AlternativeEmails(default=[],null=True,blank=True,verbose_name='Alternative Email addresses (separate with ",")') # Site Settings @@ -3654,6 +3662,160 @@ class RowerCPForm(ModelForm): model = Rower fields = ['cprange','kfit','kfatigue'] + +# Form to set rower's Power zones, including test routines +# to enable consistency +class RowerHRZonesForm(ModelForm): + + hrzones = ['Rest','UT2','UT1','AT','TR','AN','Max'] + hrrestname = forms.CharField(initial=hrzones[0]) + hrut2name = forms.CharField(initial=hrzones[1]) + hrut1name = forms.CharField(initial=hrzones[2]) + hratname = forms.CharField(initial=hrzones[3]) + hrtrname = forms.CharField(initial=hrzones[4]) + hranname = forms.CharField(initial=hrzones[5]) + hrmaxname = forms.CharField(initial=hrzones[6]) + + def __init__(self, *args,**kwargs): + super(RowerHRZonesForm, self).__init__(*args, **kwargs) + + if 'instance' in kwargs: + hrzones = kwargs['instance'].hrzones + else: + hrzones = ['Rest','UT2','UT1','AT','TR','AN','Max'] + + self.fields['hrrestname'].initial = hrzones[0] + self.fields['hrut2name'].initial = hrzones[1] + self.fields['hrut1name'].initial = hrzones[2] + self.fields['hratname'].initial = hrzones[3] + self.fields['hrtrname'].initial = hrzones[4] + self.fields['hranname'].initial = hrzones[5] + self.fields['hrmaxname'].initial = hrzones[6] + + class Meta: + model = Rower + fields = ['rest','ut2','ut1','at','tr','an','max'] + + def clean(self): + cleaned_data = super(RowerHRZonesForm, self).clean() + + try: + rest = cleaned_data['rest'] + except KeyError: + raise ValidationError("Value cannot be empty") + except: + rest = int(self.data['rest']) + + try: + ut2 = cleaned_data['ut2'] + except KeyError: + raise ValidationError("Value cannot be empty") + except: + ut2 = int(self.data['ut2']) + try: + ut1 = cleaned_data['ut1'] + except KeyError: + raise ValidationError("Value cannot be empty") + except: + ut1 = int(self.data['ut1']) + try: + at = cleaned_data['at'] + except KeyError: + raise ValidationError("Value cannot be empty") + except: + at = int(self.data['at']) + try: + tr = cleaned_data['tr'] + except KeyError: + raise ValidationError("Value cannot be empty") + except: + tr = int(self.data['tr']) + try: + an = cleaned_data['an'] + except KeyError: + raise ValidationError("Value cannot be empty") + except: + an = int(self.data['an']) + + try: + max = cleaned_data['max'] + except KeyError: + raise ValidationError("Value cannot be empty") + except: + max = int(self.data['max']) + + try: + hrrestname = cleaned_data['hrrestname'] + except: + hrrestname = 'Rest' + cleaned_data['hrut3name'] = 'Rest' + try: + hrut2name = cleaned_data['hrut2name'] + except: + hrut2name = 'UT2' + cleaned_data['hrut2name'] = 'UT2' + try: + hrut1name = cleaned_data['hrut1name'] + except: + hrut1name = 'UT1' + cleaned_data['hrut1name'] = 'UT1' + try: + hratname = cleaned_data['hratname'] + except: + hratname = 'AT' + cleaned_data['hratname'] = 'AT' + try: + hrtrname = cleaned_data['hrtrname'] + except: + hrtrname = 'TR' + cleaned_data['hrtrname'] = 'TR' + try: + hranname = cleaned_data['hranname'] + except: + hranname = 'AN' + cleaned_data['hranname'] = 'AN' + + + if rest >= ut2: + e = "{ut2name} should be higher than {restname}".format( + restname=hrrestname, + ut2name=hrut2name + ) + raise forms.ValidationError(e) + + if ut1 <= ut2: + e = "{ut1name} should be higher than {ut2name}".format( + ut1name = hrut1name, + ut2name= hrut2name, + ) + raise forms.ValidationError(e) + if at <= ut1: + e = "{atname} should be higher than {ut1name}".format( + atname = hratname, + ut1name= hrut1name, + ) + raise forms.ValidationError(e) + if tr <= at: + e = "{trname} should be higher than {atname}".format( + atname = hratname, + trname= hrtrname, + ) + raise forms.ValidationError(e) + if an <= tr: + e = "{anname} should be higher than {trname}".format( + anname = hranname, + trname= hrtrname, + ) + raise forms.ValidationError(e) + + if max <= an: + e = "{anname} should be lower than {maxname}".format( + anname=hranname, + maxname=hrmaxname, + ) + + return cleaned_data + # Form to set rower's Power zones, including test routines # to enable consistency class RowerPowerZonesForm(ModelForm): @@ -3743,44 +3905,12 @@ class RowerPowerZonesForm(ModelForm): trname = cleaned_data['trname'] except: trname = 'TR' - cleaned_data['ut1name'] = 'TR' + cleaned_data['trname'] = 'TR' try: anname = cleaned_data['anname'] except: anname = 'AN' - cleaned_data['ut1name'] = 'AN' - - - try: - ut3name = cleaned_data['ut3name'] - except: - ut2name = 'UT3' - cleaned_data['ut3name'] = 'UT3' - try: - ut2name = cleaned_data['ut2name'] - except: - ut2name = 'UT2' - cleaned_data['ut2name'] = 'UT2' - try: - ut1name = cleaned_data['ut1name'] - except: - ut1name = 'UT1' - cleaned_data['ut1name'] = 'UT1' - try: - atname = cleaned_data['atname'] - except: - atname = 'AT' - cleaned_data['atname'] = 'AT' - try: - trname = cleaned_data['trname'] - except: - trname = 'TR' - cleaned_data['ut1name'] = 'TR' - try: - anname = cleaned_data['anname'] - except: - anname = 'AN' - cleaned_data['ut1name'] = 'AN' + cleaned_data['anname'] = 'AN' if pw_ut1 <= pw_ut2: diff --git a/rowers/templates/rower_preferences.html b/rowers/templates/rower_preferences.html index 434336f4..cce14a1f 100644 --- a/rowers/templates/rower_preferences.html +++ b/rowers/templates/rower_preferences.html @@ -14,15 +14,49 @@

Heart Rate Zones

Set your heart rate zones with this form.

+
{% if form.errors %}

Please correct the error{{ form.errors|pluralize }} below. + {{ form.non_field_errors }}

{% endif %} - - - {{ form.as_table }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IDZone NameLower Boundary (BPM)
1{{ form.hrrestname }}{{ form.rest }}
2{{ form.hrut2name }}{{ form.ut2 }}
3{{ form.hrut1name }}{{ form.ut1 }}
4{{ form.hratname }}{{ form.at }}
5{{ form.hrtrname }}{{ form.tr }}
6{{ form.hranname }}{{ form.an }}
7{{ form.hrmaxname }}{{ form.max }}
{% csrf_token %} diff --git a/rowers/views/statements.py b/rowers/views/statements.py index c11e3c26..409dab68 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -121,7 +121,7 @@ from rowers.models import ( VirtualRaceFollower, ) from rowers.models import ( - RowerPowerForm,RowerForm,RowerCPForm,GraphImage,AdvancedWorkoutForm, + RowerPowerForm,RowerHRZonesForm,RowerForm,RowerCPForm,GraphImage,AdvancedWorkoutForm, RowerPowerZonesForm,AccountRowerForm,UserForm, Team,TeamForm,TeamInviteForm,TeamInvite,TeamRequest, WorkoutComment,WorkoutCommentForm,RowerExportForm, diff --git a/rowers/views/userviews.py b/rowers/views/userviews.py index f181db1a..d9069f82 100644 --- a/rowers/views/userviews.py +++ b/rowers/views/userviews.py @@ -489,13 +489,13 @@ def rower_prefs_view(request,userid=0,message=""): } ] - form = RowerForm(instance=r) + form = RowerHRZonesForm(instance=r) powerform = RowerPowerForm(instance=r) powerzonesform = RowerPowerZonesForm(instance=r) cpform = RowerCPForm(instance=r) if request.method == 'POST' and "ut2" in request.POST: - form = RowerForm(request.POST) + form = RowerHRZonesForm(request.POST) if form.is_valid(): # something cd = form.cleaned_data @@ -507,6 +507,16 @@ def rower_prefs_view(request,userid=0,message=""): an = cd['an'] rest = cd['rest'] + hrrestname = cd['hrrestname'] + hrut2name = cd['hrut2name'] + hrut1name = cd['hrut1name'] + hratname = cd['hratname'] + hrtrname = cd['hrtrname'] + hranname = cd['hranname'] + hrmaxname = cd['hrmaxname'] + hrzones = [hrrestname,hrut2name,hrut1name,hratname,hrtrname,hranname,hrmaxname] + + r.max = max(min(hrmax,250),10) r.ut2 = max(min(ut2,250),10) r.ut1 = max(min(ut1,250),10) @@ -514,6 +524,7 @@ def rower_prefs_view(request,userid=0,message=""): r.tr = max(min(tr,250),10) r.an = max(min(an,250),10) r.rest = max(min(rest,250),10) + r.hrzones = hrzones r.save() successmessage = "Your Heart Rate data were changed" messages.info(request,successmessage) From 1ea99a8101e5d99a70e53fc67b5708b3710e5a2d Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 26 Jan 2021 08:11:24 +0100 Subject: [PATCH 2/3] upgrading static plots with flexible HR zone names --- rowers/tasks.py | 4 +++- rowers/uploads.py | 1 + rowers/views/workoutviews.py | 9 ++++++--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/rowers/tasks.py b/rowers/tasks.py index 2947d679..1bc6e09e 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -2123,13 +2123,15 @@ def handle_makeplot(f1, f2, t, hrdata, plotnr, imagename, hran = hrdata['hran'] ftp = hrdata['ftp'] powerzones = deserialize_list(hrdata['powerzones']) + hrzones = deserialize_list(hrdata['hrzones']) powerperc = np.array(deserialize_list(hrdata['powerperc'])).astype(float) rr = rowingdata.rower(hrmax=hrmax, hrut2=hrut2, hrut1=hrut1, hrat=hrat, hrtr=hrtr, hran=hran, ftp=ftp, powerperc=powerperc, - powerzones=powerzones) + powerzones=powerzones, + hrzones=hrzones) try: row = rdata(csvfile=f2, rower=rr) except IOError: diff --git a/rowers/uploads.py b/rowers/uploads.py index 4c9809b4..849efcd0 100644 --- a/rowers/uploads.py +++ b/rowers/uploads.py @@ -433,6 +433,7 @@ def make_plot(r,w,f1,f2,plottype,title,imagename='',plotnr=0): 'ftp':ftp, 'powerperc':serialize_list(powerperc), 'powerzones':serialize_list(r.powerzones), + 'hrzones':serialize_list(r.hrzones), } # make plot - asynchronous task diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 73fe9be7..ea822a21 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -1290,7 +1290,8 @@ def remove_power_view(request,id=0): rr = rrower(hrmax=r.max, hrut2=r.ut2, hrut1=r.ut1, hrat=r.at, hrtr=r.tr, hran=r.an, ftp=r.ftp, - powerperc=powerperc, powerzones=r.powerzones) + powerperc=powerperc, powerzones=r.powerzones, + hrzones=r.hrzones) row = rdata(f,rower=rr) row.df[' Power (watts)'] = 0 row.write_csv(f) @@ -5837,7 +5838,8 @@ def workout_summary_restore_view(request,id,message="",successmessage=""): rr = rrower(hrmax=r.max,hrut2=r.ut2, hrut1=r.ut1,hrat=r.at, hrtr=r.tr,hran=r.an,ftp=ftp, - powerperc=powerperc,powerzones=r.powerzones) + powerperc=powerperc,powerzones=r.powerzones, + hrzones=r.hrzones) rowdata = rdata(f1,rower=rr) if rowdata == 0: raise Http404("Error: CSV Data File Not Found") @@ -6094,7 +6096,8 @@ def workout_summary_edit_view(request,id,message="",successmessage="" rr = rrower(hrmax=r.max,hrut2=r.ut2, hrut1=r.ut1,hrat=r.at, hrtr=r.tr,hran=r.an,ftp=ftp, - powerperc=powerperc,powerzones=r.powerzones) + powerperc=powerperc,powerzones=r.powerzones, + hrzones=r.hrzones) rowdata = rdata(f1,rower=rr) if rowdata == 0: return HttpResponse("Error: CSV Data File Not Found") From 6a58b3f4f26ef673a0bdd7fcbdd4ff73df10b739 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 26 Jan 2021 08:24:17 +0100 Subject: [PATCH 3/3] pip freeze --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 34437479..ae7cf418 100644 --- a/requirements.txt +++ b/requirements.txt @@ -112,7 +112,7 @@ keyring==18.0.0 kiwisolver==1.0.1 kombu==4.5.0 llvmlite==0.33.0 -lxml==4.3.2 +lxml==4.6.2 Markdown==3.0.1 MarkupSafe==1.1.1 matplotlib==3.0.3 @@ -180,7 +180,7 @@ ratelim==0.1.6 redis==3.5.3 requests==2.23.0 requests-oauthlib==1.2.0 -rowingdata==3.0.6 +rowingdata==3.1.1 rowingphysics==0.5.0 rq==0.13.0 rules==2.1