diff --git a/rowers/templates/oterankings.html b/rowers/templates/oterankings.html new file mode 100644 index 00000000..9fdc910d --- /dev/null +++ b/rowers/templates/oterankings.html @@ -0,0 +1,232 @@ +{% extends "base.html" %} +{% load staticfiles %} +{% load rowerfilters %} + +{% block scripts %} +{% include "monitorjobs.html" %} +{% endblock %} + +{% block title %}Workouts{% endblock %} + +{% block content %} + + + + + {{ interactiveplot |safe }} + + + + + +
+
+ {% if theuser %} +

{{ theuser.first_name }}'s Ranking Pieces

+ {% else %} +

{{ user.first_name }}'s Ranking Pieces

+ {% endif %} +
+
+ {% if user.is_authenticated and user|is_manager %} + +
+ +
+

Summary for {{ theuser.first_name }} {{ theuser.last_name }} + between {{ startdate|date }} and {{ enddate|date }}

+ +

Direct link for other users: + {% if workouttype == 'water' %} + https://rowsandall.com/rowers/{{ id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }} + {% else %} + https://rowsandall.com/rowers/{{ id }}/ote-ranking/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }} + {% endif %} +

+ +

The table gives the efforts you marked as Ranking Piece. + The graph shows the best segments from those pieces, plotted as + average power (over the segment) vs the duration of the segment/ + In other words: How long you can hold that power. +

+ +

When you change the date range, the algorithm calculates new + parameters in a background process. You may have to reload the + page to get an updated prediction.

+

At the bottom of the page, you will find predictions derived from the model.

+
+
+

Use this form to select a different date range:

+

+ Select start and end date for a date range: +

+ +
+ + + {{ dateform.as_table }} +
+ {% csrf_token %} +
+
+ +
+
+
+ Or use the last {{ deltaform }} days. +
+
+ {% csrf_token %} + + +
+
+ + + +
+ +

Critical Power Plot

+ + {{ the_div|safe }} + +
+ +
+ +

Ranking Piece Results

+ + {% if rankingworkouts %} + + + + + + + + + + + + + + + {% for workout in rankingworkouts %} + + + + + + + + + + + + {% endfor %} + +
Distance Duration Avg Power Date Avg HR Max HR Edit
{{ workout.distance }} m {{ workout.duration |durationprint:"%H:%M:%S.%f" }} {{ avgpower|lookup:workout.id }} W {{ workout.date }} {{ workout.averagehr }} {{ workout.maxhr }} + {{ workout.name }}
+ {% else %} +

No ranking workouts found

+ {% endif %} + +
+ +
+

Pace predictions for Ranking Pieces

+ +

Add non-ranking piece using the form. The piece will be added in the prediction tables below.

+ + + +
+ + + + + + + + + + + + {% for pred in cpredictions %} + + {% for key, value in pred.items %} + {% if key == "power" or key == "upper" %} + + {% endif %} + {% if key == "duration" %} + + {% endif %} + {% if key == "distance" %} + + {% endif %} + {% if key == 'pace' %} + + {% endif %} + {% endfor %} + + {% endfor %} + +
Duration Distance Pace (upper) Power     Power (upper)
{{ value }} W {{ value |deltatimeprint }} {{ value }} m {{ value|paceprint }}
+ +
+ +
+
+ {{ form.value }} {{ form.pieceunit }} + + {% csrf_token %} +
+
+ minutes +
+
+ + +
+ + +
+ +{% endblock %} diff --git a/rowers/views.py b/rowers/views.py index 8fe5e197..13c9cee9 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -3779,6 +3779,8 @@ def oterankings_view(request,theuser=0, rankingdurations.append(datetime.time(hour=1)) rankingdurations.append(datetime.time(hour=1,minute=15)) + rankingdistances = [100,500,1000,2000,5000,6000,10000,21097,42195,100000] + thedistances = [] theworkouts = [] thesecs = [] @@ -3874,21 +3876,29 @@ def oterankings_view(request,theuser=0, form = PredictedPieceForm(request.POST) clean = form.is_valid() value = form.cleaned_data['value'] - hourvalue,value = divmod(value,60) + hourvalue,tvalue = divmod(value,60) hourvalue = int(hourvalue) - minutevalue = int(value) - value = int(60*(value-minutevalue)) + minutevalue = int(tvalue) + tvalue = int(60*(tvalue-minutevalue)) if hourvalue >= 24: hourvalue = 23 - rankingdurations.append(datetime.time(minute=minutevalue, - hour=hourvalue, - second=value)) + pieceunit = form.cleaned_data['pieceunit'] + if pieceunit == 'd': + rankingdistances.append(value) + else: + rankingdurations.append(datetime.time( + minute=minutevalue, + hour=hourvalue, + second=tvalue + )) else: form = PredictedPieceForm() cpredictions = [] + + for rankingduration in rankingdurations: t = 3600.*rankingduration.hour t += 60.*rankingduration.minute @@ -3900,6 +3910,9 @@ def oterankings_view(request,theuser=0, pwr = p1[0]/(1+t/p1[2]) pwr += p1[1]/(1+t/p1[3]) + velo = (pwr/2.8)**(1./3.) + p = 500./velo + d = t*velo if pwr <= 0: pwr = 50. @@ -3912,16 +3925,78 @@ def oterankings_view(request,theuser=0, pwr2 = pwr a = { + 'distance':int(d), 'duration':timedeltaconv(t), 'power':int(pwr), - 'upper':int(pwr2)} + 'upper':int(pwr2), + 'pace':timedeltaconv(p)} + cpredictions.append(a) - del form.fields["pieceunit"] + # initiation - get 10 min power, then use Paul's law + + t_10 = 600. + power_10 = p1[0]/(1+t_10/p1[2]) + power_10 += p1[1]/(1+t_10/p1[3]) + + velo_10 = (power_10/2.8)**(1./3.) + pace_10 = 500./velo_10 + distance_10 = t_10*velo_10 + + paulslope = 5. + + for rankingdistance in rankingdistances: + + delta = paulslope * np.log(rankingdistance/distance_10)/np.log(2) + + + p = pace_10+delta + velo = 500./p + t = rankingdistance/velo + + pwr2 = p1[0]/(1+t/p1[2]) + pwr2 += p1[1]/(1+t/p1[3]) + pwr2 *= ratio + + if pwr2 <= 0: + pwr2 = 50. + + velo2 = (pwr2/2.8)**(1./3.) + + if np.isnan(velo2) or velo2 <= 0: + velo2 = 1.0 + + t2 = rankingdistance/velo2 + + pwr3 = p1[0]/(1+t2/p1[2]) + pwr3 += p1[1]/(1+t2/p1[3]) + pwr3 *= ratio + + + if pwr3 <= 0: + pwr3 = 50. + + velo3 = (pwr3/2.8)**(1./3.) + if np.isnan(velo3) or velo3 <= 0: + velo3 = 1.0 + + t3 = rankingdistance/velo3 + p3 = 500./velo3 + + a = { + 'distance':rankingdistance, + 'duration':timedeltaconv(t3), + 'power':'--', + 'upper':int(pwr3), + 'pace':timedeltaconv(p3)} + + cpredictions.append(a) + + # del form.fields["pieceunit"] messages.error(request,message) - return render(request, 'otwrankings.html', + return render(request, 'oterankings.html', {'rankingworkouts':theworkouts, 'interactiveplot':script, 'the_div':div, diff --git a/static/img/image.png b/static/img/image.png new file mode 100644 index 00000000..d6acd417 Binary files /dev/null and b/static/img/image.png differ