From 47d8dd63d320ee4b9fb83e894c033cfcc9bab584 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 15 Dec 2022 22:10:37 +0100 Subject: [PATCH] better power zones --- rowers/dataroutines.py | 1 - rowers/interactiveplots.py | 16 +- rowers/models.py | 13 +- rowers/templates/rower_preferences.html | 187 +++++++++++++----------- rowers/templatetags/rowerfilters.py | 5 + rowers/views/userviews.py | 15 +- 6 files changed, 134 insertions(+), 103 deletions(-) diff --git a/rowers/dataroutines.py b/rowers/dataroutines.py index 31129951..2e8e4c5c 100644 --- a/rowers/dataroutines.py +++ b/rowers/dataroutines.py @@ -1259,7 +1259,6 @@ def getsmallrowdata_db(columns, ids=[], doclean=True, workstrokesonly=True, comp df = pd.concat(data, axis=0) except ValueError: # pragma: no cover return pd.DataFrame() - # df = dd.concat(data,axis=0) else: try: diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index a3dc27bf..c1fa8eaf 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -6283,7 +6283,15 @@ def thumbnails_set(r, id, favorites): columns += ['time'] rowdata = dataprep.getsmallrowdata_db(columns, ids=[id], doclean=True) - rowdata.dropna(axis=1, how='all', inplace=True) + try: + rowdata.dropna(axis=1, how='all', inplace=True) + except TypeError: + return [ + {'script': "", + 'div': "", + 'notes': "" + }] + if rowdata.empty: try: @@ -6303,12 +6311,6 @@ def thumbnails_set(r, id, favorites): 'notes': "" }] - # else: - # try: - # rowdata.sort_values(by='time',ascending=True,inplace=True) - # except KeyError: - # pass - lengte = len(rowdata) maxlength = 50 if lengte > maxlength: diff --git a/rowers/models.py b/rowers/models.py index bcbfdf6e..e533f129 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -964,6 +964,7 @@ class Rower(models.Model): # Power Zone Data ftp = models.IntegerField( default=226, verbose_name="Functional Threshold Power") + cogganzones = models.BooleanField(verbose_name='Use Default Power Zones',default=True) p0 = models.FloatField(default=1.0, verbose_name="CP p1") p1 = models.FloatField(default=1.0, verbose_name="CP p2") @@ -997,11 +998,11 @@ class Rower(models.Model): pw_an = models.IntegerField(default=273, verbose_name="AN Power") powerzones = PowerZonesField(default=['Rest', - 'Pwr UT2', - 'Pwr UT1', - 'Pwr AT', - 'Pwr TR', - 'Pwr AN']) + 'Active Recovery', + 'Endurance', + 'Tempo', + 'Threshold', + 'Anaerobic']) hrzones = PowerZonesField(default=['Rest', 'UT2', @@ -4224,7 +4225,7 @@ class RowerExportForm(ModelForm): class RowerPowerForm(ModelForm): class Meta: model = Rower - fields = ['hrftp', 'ftp', 'otwslack'] + fields = ['hrftp', 'ftp', 'otwslack','cogganzones'] class RowerCPForm(ModelForm): diff --git a/rowers/templates/rower_preferences.html b/rowers/templates/rower_preferences.html index 28f27dc2..ba9456ad 100644 --- a/rowers/templates/rower_preferences.html +++ b/rowers/templates/rower_preferences.html @@ -10,6 +10,103 @@

Need help? Click to read the tutorial

    +
  • +
    +

    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 on the ergometer. It + will update all zones defined.

    +

    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.

    +

    If you use default power zones, we will use the namees and + values as defined by + Coggan.

    + + {{ powerform.as_table }} +
    + {% csrf_token %} + +
    +
  • +
  • +

    Power Zones

    +

    Use this form if you want to rename and redefine your power + zones independent of the value of FTP. We recommend, however, that + you leave these values as they are and only change the FTP and OTW + slack values.

    +
    + {% if powerzonesform.errors %} +

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

    + {% endif %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ID Zone NameLower Boundary (Watt) Lower Boundary (Watt)
    IndoorOTW
    1{{ powerzonesform.ut3name }}
    2{{ powerzonesform.ut2name }}{{ powerzonesform.pw_ut2 }}{{ user.rower.pw_ut2|otwslack:user }}
    3{{ powerzonesform.ut1name }}{{ powerzonesform.pw_ut1 }}{{ user.rower.pw_ut1|otwslack:user }}
    4{{ powerzonesform.atname }}{{ powerzonesform.pw_at }}{{ user.rower.pw_at|otwslack:user }}
    5{{ powerzonesform.trname }}{{ powerzonesform.pw_tr }}{{ user.rower.pw_tr|otwslack:user }}
    6{{ powerzonesform.anname }}{{ powerzonesform.pw_an }}{{ user.rower.pw_an|otwslack:user }}
    + {% csrf_token %} +
    + +
    +
    +
  • +
  • +
    +

    Fitness and Performance Manager Settings

    +

    Use this form to change the parameters affecting your performance management.

    +

    A shorter range for the CP calculations + will give you notifications of fitness improvements more often, + but will also quickly forget those breakthrough workouts. Shorter decay + time constants for the performance manager will model a quicker building up + of fitness and a faster recovery. Recommended values are 42 days (fitness) + and 7 days (fatigue).

    + + {{ cpform.as_table }} +
    + {% csrf_token %} + +
    +
  • Heart Rate Zones

    @@ -22,7 +119,7 @@

    {% endif %} - + @@ -52,7 +149,7 @@ - + @@ -62,92 +159,6 @@ -
  • -

    Power Zones

    -

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

    -
    - {% if powerzonesform.errors %} -

    - Please correct the error{{ powerzonesform.errors|pluralize }} below. - {{ powerzonesform.non_field_errors }} - -

    - {% endif %} -
  • IDZone NameLower Boundary (BPM)
    6{{ form.hranname }} {{ form.an }}
    7{{ form.hrmaxname }} {{ form.max }}
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    IDZone NameLower Boundary (Watt)
    1{{ powerzonesform.ut3name }}
    2{{ powerzonesform.ut2name }}{{ powerzonesform.pw_ut2 }}
    3{{ powerzonesform.ut1name }}{{ powerzonesform.pw_ut1 }}
    4{{ powerzonesform.atname }}{{ powerzonesform.pw_at }}
    5{{ powerzonesform.trname }}{{ powerzonesform.pw_tr }}
    6{{ powerzonesform.anname }}{{ powerzonesform.pw_an }}
    - {% csrf_token %} -
    - -
    - -
  • -
  • -
    -

    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 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 }} -
    - {% csrf_token %} - -
    -
  • -
  • -
    -

    Fitness and Performance Manager Settings

    -

    Use this form to change the parameters affecting your performance management.

    -

    A shorter range for the CP calculations - will give you notifications of fitness improvements more often, - but will also quickly forget those breakthrough workouts. Shorter decay - time constants for the performance manager will model a quicker building up -of fitness and a faster recovery. Recommended values are 42 days (fitness) -and 7 days (fatigue).

    - - {{ cpform.as_table }} -
    - {% csrf_token %} - -
    -
diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py index a1e234ab..40924683 100644 --- a/rowers/templatetags/rowerfilters.py +++ b/rowers/templatetags/rowerfilters.py @@ -1039,3 +1039,8 @@ def previousworkout(workout, user): return encoder.encode_hex(ws[0].id) else: return 0 + +@register.filter +def otwslack(value, user): + r = Rower.objects.get(user=user) + return int(value*(100.-r.otwslack)/100.) diff --git a/rowers/views/userviews.py b/rowers/views/userviews.py index 6f1492e9..cd744ffd 100644 --- a/rowers/views/userviews.py +++ b/rowers/views/userviews.py @@ -618,13 +618,23 @@ def rower_prefs_view(request, userid=0, message=""): hrftp = int((r.an+r.tr)/2.) ftp = cd['ftp'] otwslack = cd['otwslack'] + cogganzones = cd['cogganzones'] powerfrac = 100*np.array([r.pw_ut2, r.pw_ut1, r.pw_at, r.pw_tr, r.pw_an])/r.ftp + if cogganzones: + powerfrac = np.array([55.,75.,90.,105.,120.]) + powerzones = ['Rest', + 'Active Recovery', + 'Endurance', + 'Tempo', + 'Threshold', + 'Anaerobic'] + r.powerzones = powerzones + 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 @@ -632,7 +642,10 @@ def rower_prefs_view(request, userid=0, message=""): r.pw_tr = tr r.pw_an = an r.hrftp = hrftp + r.otwslack = max(min(otwslack, 50), 0) r.save() + powerzonesform = RowerPowerZonesForm(instance=r) + message = "FTP and/or OTW slack values changed." messages.info(request, message)