Private
Public Access
1
0

OTW power CP graph working!

This commit is contained in:
Sander Roosendaal
2017-06-08 14:19:21 +02:00
parent 8b42de8a53
commit 832d6f8878
4 changed files with 130 additions and 84 deletions

View File

@@ -608,6 +608,10 @@ def interactive_otwcpchart(powerdf,promember=0):
x_axis_type = 'log' x_axis_type = 'log'
y_axis_type = 'linear' y_axis_type = 'linear'
deltas = powerdf['Delta'].apply(lambda x: timedeltaconv(x))
powerdf['ftime'] = niceformat(deltas)
source = ColumnDataSource( source = ColumnDataSource(
data = powerdf data = powerdf
) )
@@ -668,7 +672,7 @@ def interactive_otwcpchart(powerdf,promember=0):
) )
plot.circle('Delta','CP',source=source,fill_color='red',size=15, plot.circle('Delta','CP',source=source,fill_color='red',size=15,
legend='Power') legend='Power Data')
plot.xaxis.axis_label = "Duration (seconds)" plot.xaxis.axis_label = "Duration (seconds)"
plot.yaxis.axis_label = "Power (W)" plot.yaxis.axis_label = "Power (W)"
@@ -676,6 +680,14 @@ def interactive_otwcpchart(powerdf,promember=0):
plot.x_range = Range1d(1,2*max(thesecs)) plot.x_range = Range1d(1,2*max(thesecs))
plot.legend.orientation = "vertical" plot.legend.orientation = "vertical"
hover = plot.select(dict(type=HoverTool))
hover.tooltips = OrderedDict([
('Duration ','@ftime'),
('Power (W)','@CP{int}'),
])
hover.mode = 'mouse'
plot.line('duration','power',source=sourcecomplex,legend="CP Model", plot.line('duration','power',source=sourcecomplex,legend="CP Model",
color='green') color='green')

View File

@@ -77,8 +77,28 @@
</div> </div>
</div> </div>
<div class="grid_12 alpha">
<div class="grid_6 alpha">
<p>&nbsp;</p>
</div>
<div class="grid_6 omega">
<div class="grid_2 suffix_4 alpha">
<p>
{% if user.rower.rowerplan == 'pro' or user.rower.rowerplan == 'coach' %}
<a class="button blue small" href="/rowers/otw-bests">OTW Ranking Pieces</a>
{% else %}
<a class="button blue small" href="/rowers/promembership">OTW Ranking Pieces</a>
{% endif %}
</p>
<p>
Analyse power vs piece duration to make predictions.
</p>
</div>
</div>
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -67,9 +67,12 @@
<a href="/rowers/{{ id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}">https://rowsandall.com/rowers/{{ id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}</a> <a href="/rowers/{{ id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}">https://rowsandall.com/rowers/{{ id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}</a>
</p> </p>
<p>The table gives the best efforts achieved on the official Concept2 ranking pieces in the selected date range.</p> <p>The table gives the OTW efforts you marked as Ranking Piece.
The graph shows the best segments from those pieces, plotted as
<p>This page will evolve and try to give you guidance on where to improve.</p> average power (over the segment) vs the duration of the segment/
In other words: How long you can hold that power.
</p>
<p>At the bottom of the page, you will find predictions derived from the model.</p>
</div> </div>
<div id="form" class="grid_6 omega"> <div id="form" class="grid_6 omega">
<p>Use this form to select a different date range:</p> <p>Use this form to select a different date range:</p>
@@ -98,6 +101,16 @@
</div> </div>
</div> </div>
<div id="theplot" class="grid_12 alpha">
<h2>Critical Power Plot</h2>
{{ the_div|safe }}
</div>
<div class="grid_12 alpha"> <div class="grid_12 alpha">
<h2>Ranking Piece Results</h2> <h2>Ranking Piece Results</h2>
@@ -109,6 +122,7 @@
<tr> <tr>
<th> Distance</th> <th> Distance</th>
<th> Duration</th> <th> Duration</th>
<th> Avg Power</th>
<th> Date</th> <th> Date</th>
<th> Avg HR </th> <th> Avg HR </th>
<th> Max HR </th> <th> Max HR </th>
@@ -118,8 +132,9 @@
<tbody> <tbody>
{% for workout in rankingworkouts %} {% for workout in rankingworkouts %}
<tr> <tr>
<td> {{ workout.distance }} </td> <td> {{ workout.distance }} m</td>
<td> {{ workout.duration |durationprint:"%H:%M:%S.%f" }} </td> <td> {{ workout.duration |durationprint:"%H:%M:%S.%f" }} </td>
<td> {{ avgpower|lookup:workout.id }} W</td>
<td> {{ workout.date }} </td> <td> {{ workout.date }} </td>
<td> {{ workout.averagehr }} </td> <td> {{ workout.averagehr }} </td>
<td> {{ workout.maxhr }} </td> <td> {{ workout.maxhr }} </td>
@@ -137,63 +152,56 @@
</div> </div>
<div id="theplot" class="grid_12 alpha">
<h2>Critical Power Plot</h2>
{{ the_div|safe }}
</div>
<div id="predictions" class="grid_12 alpha"> <div id="predictions" class="grid_12 alpha">
<h2>Pace predictions for Ranking Pieces</h2> <h2>Pace predictions for Ranking Pieces</h2>
<p>Add non-ranking piece using the form. The piece will be added in the prediction tables below. </p> <p>Add non-ranking piece using the form. The piece will be added in the prediction tables below. </p>
<div class="grid_4 alpha">
<div id="cpmodel" class="grid_6 alpha">
<table width="70%" class="listtable">
<thead>
<tr>
<th> Duration</th>
<th> Power </th>
<tr>
</thead>
<tbody>
{% for pred in cpredictions %}
<tr>
{% for key, value in pred.items %}
{% if key == "power" %}
<td> {{ value }} W </td>
{% endif %}
{% if key == "duration" %}
<td> {{ value |deltatimeprint }} </td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="grid_3">
<form enctype="multipart/form-data" action="{{ formloc }}" method="post"> <form enctype="multipart/form-data" action="{{ formloc }}" method="post">
{{ form.value }} {{ form.pieceunit }} {{ form.value }} {{ form.pieceunit }}
{% csrf_token %} {% csrf_token %}
</div> </div>
<div class="grid_2 suffix_6 omega"> <div class="grid_1">
<input name="piece" class="button green" minutes
formaction="/rowers/{{ id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}" </div>
type="submit" value="Add"> <div class="grid_2 omega">
</form> <input name="piece" class="button green"
</div> formaction="/rowers/{{ id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}"
type="submit" value="Add">
</form>
</div>
<div id="paul" class="grid_6 alpha">
No Paul Data
</div>
<div id="cpmodel" class="grid_6 omega">
<h3>CP Model</h3>
<table width="70%" class="listtable">
<thead>
<tr>
<th> Duration</th>
<th> Power </th>
<tr>
</thead>
<tbody>
{% for pred in cpredictions %}
<tr>
{% for key, value in pred.items %}
{% if key == "power" %}
<td> {{ value }} W </td>
{% endif %}
{% if key == "duration" %}
<td> {{ value |deltatimeprint }} </td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -292,14 +292,11 @@ def iscoachmember(user):
return result return result
def getrower(user): def getrower(user):
if not user.is_anonymous(): try:
try: r = Rower.objects.get(user=user)
r = Rower.objects.get(user=user) except Rower.DoesNotExist:
except Rower.DoesNotExist: r = Rower(user=user)
r = Rower(user=user) r.save()
r.save()
else:
raise PermissionDenied("You need to log in to use this function")
return r return r
@@ -2900,8 +2897,10 @@ def otwrankings_view(request,theuser=0,
thesecs.append(timesecs) thesecs.append(timesecs)
if len(thesecs) != 0:
maxt = pd.Series(thesecs).max() maxt = pd.Series(thesecs).max()
else:
maxt = 1000.
maxlog10 = np.log10(maxt) maxlog10 = np.log10(maxt)
logarr = np.arange(100)*maxlog10/100. logarr = np.arange(100)*maxlog10/100.
@@ -2912,11 +2911,16 @@ def otwrankings_view(request,theuser=0,
delta = [] delta = []
cpvalue = [] cpvalue = []
avgpower = {}
dfgrouped = df.groupby(['workoutid']) dfgrouped = df.groupby(['workoutid'])
for id,group in dfgrouped: for id,group in dfgrouped:
tt = group['time'] tt = group['time']
ww = group['power'] ww = group['power']
try:
avgpower[id] = int(ww.mean())
except ValueError:
avgpower[id] = '---'
if not np.isnan(ww.mean()): if not np.isnan(ww.mean()):
length = len(ww) length = len(ww)
dt = [] dt = []
@@ -2946,6 +2950,7 @@ def otwrankings_view(request,theuser=0,
for d in logarr: for d in logarr:
delta.append(d) delta.append(d)
print avgpower
dt = pd.Series(delta,name='Delta') dt = pd.Series(delta,name='Delta')
cpvalue = pd.Series(cpvalue,name='CP') cpvalue = pd.Series(cpvalue,name='CP')
@@ -2955,13 +2960,15 @@ def otwrankings_view(request,theuser=0,
'CP':cpvalue, 'CP':cpvalue,
}) })
powerdf.sort_values(['Delta','CP'],ascending=[1,0]) powerdf = powerdf[powerdf['CP']>0]
powerdf.drop_duplicates(subset='Delta',keep='first') powerdf.dropna(axis=0,inplace=True)
powerdf.sort_values(['Delta','CP'],ascending=[1,0],inplace=True)
powerdf.drop_duplicates(subset='Delta',keep='first',inplace=True)
# create interactive plot # create interactive plot
if len(powerdf) !=0 : if len(powerdf) !=0 :
res = interactive_otwcpchart(powerdf) res = interactive_otwcpchart(powerdf,promember=promember)
script = res[0] script = res[0]
div = res[1] div = res[1]
p1 = res[2] p1 = res[2]
@@ -2979,16 +2986,12 @@ def otwrankings_view(request,theuser=0,
if request.method == 'POST' and "piece" in request.POST: if request.method == 'POST' and "piece" in request.POST:
form = PredictedPieceForm(request.POST) form = PredictedPieceForm(request.POST)
if form.is_valid(): clean = form.is_valid()
value = form.cleaned_data['value'] value = form.cleaned_data['value']
hourvalue,value = divmod(value,60) hourvalue,value = divmod(value,60)
if hourvalue >= 24: if hourvalue >= 24:
hourvalue = 23 hourvalue = 23
pieceunit = form.cleaned_data['pieceunit'] rankingdurations.append(datetime.time(minute=value,hour=hourvalue))
if pieceunit == 'd':
rankingdistances.append(value)
else:
rankingdurations.append(datetime.time(minute=value,hour=hourvalue))
else: else:
form = PredictedPieceForm() form = PredictedPieceForm()
@@ -3012,12 +3015,14 @@ def otwrankings_view(request,theuser=0,
if pwr <= 0: if pwr <= 0:
pwr = 50. pwr = 50.
a = { if not np.isnan(pwr):
'duration':timedeltaconv(t), a = {
'power':int(pwr)} 'duration':timedeltaconv(t),
cpredictions.append(a) 'power':int(pwr)}
cpredictions.append(a)
print cpredictions
del form.fields["pieceunit"]
messages.error(request,message) messages.error(request,message)
return render(request, 'otwrankings.html', return render(request, 'otwrankings.html',
@@ -3026,6 +3031,7 @@ def otwrankings_view(request,theuser=0,
'the_div':div, 'the_div':div,
'predictions':predictions, 'predictions':predictions,
'cpredictions':cpredictions, 'cpredictions':cpredictions,
'avgpower':avgpower,
'form':form, 'form':form,
'dateform':dateform, 'dateform':dateform,
'deltaform':deltaform, 'deltaform':deltaform,