OTW power CP graph working!
This commit is contained in:
@@ -608,6 +608,10 @@ def interactive_otwcpchart(powerdf,promember=0):
|
||||
x_axis_type = 'log'
|
||||
y_axis_type = 'linear'
|
||||
|
||||
deltas = powerdf['Delta'].apply(lambda x: timedeltaconv(x))
|
||||
powerdf['ftime'] = niceformat(deltas)
|
||||
|
||||
|
||||
source = ColumnDataSource(
|
||||
data = powerdf
|
||||
)
|
||||
@@ -668,7 +672,7 @@ def interactive_otwcpchart(powerdf,promember=0):
|
||||
)
|
||||
|
||||
plot.circle('Delta','CP',source=source,fill_color='red',size=15,
|
||||
legend='Power')
|
||||
legend='Power Data')
|
||||
plot.xaxis.axis_label = "Duration (seconds)"
|
||||
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.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",
|
||||
color='green')
|
||||
|
||||
@@ -77,8 +77,28 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid_12 alpha">
|
||||
<div class="grid_6 alpha">
|
||||
<p> </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>
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -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>
|
||||
</p>
|
||||
|
||||
<p>The table gives the best efforts achieved on the official Concept2 ranking pieces in the selected date range.</p>
|
||||
|
||||
<p>This page will evolve and try to give you guidance on where to improve.</p>
|
||||
<p>The table gives the OTW 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.
|
||||
</p>
|
||||
<p>At the bottom of the page, you will find predictions derived from the model.</p>
|
||||
</div>
|
||||
<div id="form" class="grid_6 omega">
|
||||
<p>Use this form to select a different date range:</p>
|
||||
@@ -98,6 +101,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div id="theplot" class="grid_12 alpha">
|
||||
|
||||
<h2>Critical Power Plot</h2>
|
||||
|
||||
{{ the_div|safe }}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="grid_12 alpha">
|
||||
|
||||
<h2>Ranking Piece Results</h2>
|
||||
@@ -109,6 +122,7 @@
|
||||
<tr>
|
||||
<th> Distance</th>
|
||||
<th> Duration</th>
|
||||
<th> Avg Power</th>
|
||||
<th> Date</th>
|
||||
<th> Avg HR </th>
|
||||
<th> Max HR </th>
|
||||
@@ -118,8 +132,9 @@
|
||||
<tbody>
|
||||
{% for workout in rankingworkouts %}
|
||||
<tr>
|
||||
<td> {{ workout.distance }} </td>
|
||||
<td> {{ workout.distance }} m</td>
|
||||
<td> {{ workout.duration |durationprint:"%H:%M:%S.%f" }} </td>
|
||||
<td> {{ avgpower|lookup:workout.id }} W</td>
|
||||
<td> {{ workout.date }} </td>
|
||||
<td> {{ workout.averagehr }} </td>
|
||||
<td> {{ workout.maxhr }} </td>
|
||||
@@ -137,63 +152,56 @@
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div id="theplot" class="grid_12 alpha">
|
||||
|
||||
<h2>Critical Power Plot</h2>
|
||||
|
||||
{{ the_div|safe }}
|
||||
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<div class="grid_4 alpha">
|
||||
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
|
||||
{{ form.value }} {{ form.pieceunit }}
|
||||
|
||||
{% csrf_token %}
|
||||
</div>
|
||||
<div class="grid_2 suffix_6 omega">
|
||||
<input name="piece" class="button green"
|
||||
formaction="/rowers/{{ id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}"
|
||||
type="submit" value="Add">
|
||||
</form>
|
||||
</div>
|
||||
<p>Add non-ranking piece using the form. The piece will be added in the prediction tables below. </p>
|
||||
|
||||
|
||||
|
||||
<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 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>
|
||||
|
||||
<div class="grid_3">
|
||||
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
|
||||
{{ form.value }} {{ form.pieceunit }}
|
||||
|
||||
{% csrf_token %}
|
||||
</div>
|
||||
<div class="grid_1">
|
||||
minutes
|
||||
</div>
|
||||
<div class="grid_2 omega">
|
||||
<input name="piece" class="button green"
|
||||
formaction="/rowers/{{ id }}/otw-bests/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}"
|
||||
type="submit" value="Add">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -292,14 +292,11 @@ def iscoachmember(user):
|
||||
return result
|
||||
|
||||
def getrower(user):
|
||||
if not user.is_anonymous():
|
||||
try:
|
||||
r = Rower.objects.get(user=user)
|
||||
except Rower.DoesNotExist:
|
||||
r = Rower(user=user)
|
||||
r.save()
|
||||
else:
|
||||
raise PermissionDenied("You need to log in to use this function")
|
||||
try:
|
||||
r = Rower.objects.get(user=user)
|
||||
except Rower.DoesNotExist:
|
||||
r = Rower(user=user)
|
||||
r.save()
|
||||
|
||||
return r
|
||||
|
||||
@@ -2900,8 +2897,10 @@ def otwrankings_view(request,theuser=0,
|
||||
|
||||
thesecs.append(timesecs)
|
||||
|
||||
|
||||
maxt = pd.Series(thesecs).max()
|
||||
if len(thesecs) != 0:
|
||||
maxt = pd.Series(thesecs).max()
|
||||
else:
|
||||
maxt = 1000.
|
||||
|
||||
maxlog10 = np.log10(maxt)
|
||||
logarr = np.arange(100)*maxlog10/100.
|
||||
@@ -2912,11 +2911,16 @@ def otwrankings_view(request,theuser=0,
|
||||
|
||||
delta = []
|
||||
cpvalue = []
|
||||
avgpower = {}
|
||||
|
||||
dfgrouped = df.groupby(['workoutid'])
|
||||
for id,group in dfgrouped:
|
||||
tt = group['time']
|
||||
ww = group['power']
|
||||
try:
|
||||
avgpower[id] = int(ww.mean())
|
||||
except ValueError:
|
||||
avgpower[id] = '---'
|
||||
if not np.isnan(ww.mean()):
|
||||
length = len(ww)
|
||||
dt = []
|
||||
@@ -2946,6 +2950,7 @@ def otwrankings_view(request,theuser=0,
|
||||
for d in logarr:
|
||||
delta.append(d)
|
||||
|
||||
print avgpower
|
||||
dt = pd.Series(delta,name='Delta')
|
||||
cpvalue = pd.Series(cpvalue,name='CP')
|
||||
|
||||
@@ -2955,13 +2960,15 @@ def otwrankings_view(request,theuser=0,
|
||||
'CP':cpvalue,
|
||||
})
|
||||
|
||||
powerdf.sort_values(['Delta','CP'],ascending=[1,0])
|
||||
powerdf.drop_duplicates(subset='Delta',keep='first')
|
||||
powerdf = powerdf[powerdf['CP']>0]
|
||||
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
|
||||
if len(powerdf) !=0 :
|
||||
res = interactive_otwcpchart(powerdf)
|
||||
res = interactive_otwcpchart(powerdf,promember=promember)
|
||||
script = res[0]
|
||||
div = res[1]
|
||||
p1 = res[2]
|
||||
@@ -2979,16 +2986,12 @@ def otwrankings_view(request,theuser=0,
|
||||
|
||||
if request.method == 'POST' and "piece" in request.POST:
|
||||
form = PredictedPieceForm(request.POST)
|
||||
if form.is_valid():
|
||||
value = form.cleaned_data['value']
|
||||
hourvalue,value = divmod(value,60)
|
||||
if hourvalue >= 24:
|
||||
hourvalue = 23
|
||||
pieceunit = form.cleaned_data['pieceunit']
|
||||
if pieceunit == 'd':
|
||||
rankingdistances.append(value)
|
||||
else:
|
||||
rankingdurations.append(datetime.time(minute=value,hour=hourvalue))
|
||||
clean = form.is_valid()
|
||||
value = form.cleaned_data['value']
|
||||
hourvalue,value = divmod(value,60)
|
||||
if hourvalue >= 24:
|
||||
hourvalue = 23
|
||||
rankingdurations.append(datetime.time(minute=value,hour=hourvalue))
|
||||
else:
|
||||
form = PredictedPieceForm()
|
||||
|
||||
@@ -3012,13 +3015,15 @@ def otwrankings_view(request,theuser=0,
|
||||
if pwr <= 0:
|
||||
pwr = 50.
|
||||
|
||||
a = {
|
||||
'duration':timedeltaconv(t),
|
||||
'power':int(pwr)}
|
||||
cpredictions.append(a)
|
||||
if not np.isnan(pwr):
|
||||
a = {
|
||||
'duration':timedeltaconv(t),
|
||||
'power':int(pwr)}
|
||||
cpredictions.append(a)
|
||||
|
||||
print cpredictions
|
||||
|
||||
del form.fields["pieceunit"]
|
||||
|
||||
messages.error(request,message)
|
||||
return render(request, 'otwrankings.html',
|
||||
{'rankingworkouts':theworkouts,
|
||||
@@ -3026,6 +3031,7 @@ def otwrankings_view(request,theuser=0,
|
||||
'the_div':div,
|
||||
'predictions':predictions,
|
||||
'cpredictions':cpredictions,
|
||||
'avgpower':avgpower,
|
||||
'form':form,
|
||||
'dateform':dateform,
|
||||
'deltaform':deltaform,
|
||||
|
||||
Reference in New Issue
Block a user