Private
Public Access
1
0

Merge branch 'feature/magicintervals' into develop

This commit is contained in:
Sander Roosendaal
2021-05-28 10:24:01 +02:00
4 changed files with 106 additions and 50 deletions
-1
View File
@@ -468,7 +468,6 @@ def getfastest(df,thevalue,mode='distance'):
starttime = griddata(restime,starttimes,[thevalue*60*1000],method='linear',rescale=True)
duration = griddata(restime,restime,[thevalue*60*1000],method='linear',rescale=True)
endtime = starttime+duration
print(distance,starttime,endtime )
return distance[0],starttime[0]/1000.,endtime[0]/1000.
return 0 # pragma: no cover
+64 -45
View File
@@ -41,10 +41,7 @@
title="Jump to following workout"><em>Next</em></a>
{% endif %}
</p>
<h1>Edit Workout Interval Data</h1>
<ul class="main-content">
<li class="grid_2">
<p>
<table width=100%>
<tr>
<th>Name</th><td>{{ workout.name }}</td>
@@ -59,49 +56,10 @@
<td>
</tr>
</table>
<h1>Interval Shorthand</h1>
<p>
See the how-to <a href="#howto">at the bottom of this page</a> for details on how to use this form.
</p>
<form enctype="multipart/form-data" action="/rowers/workout/{{ workout.id|encode }}/editintervals/" method="post">
<table width=100%>
{{ form.as_table }}
</table>
{% csrf_token %}
<input class="button" type="submit" value="Update">
</form>
<h1>Intervals by Power/Pace</h1>
<p>With this form, you can specify a power or pace level. Everything faster/harder than the
specified pace/power will become a work interval. Everything slower will become a rest
interval. Use the slider to limit the active range. Everything outside the active range will
become rest (warming up and cooling down).
</p>
<form ecntype="multipart/form-data" method="post">
<div id="slider-range"></div>
<p>
<label for="amount">Active Range:</label>
<input type="text" id="amount" readonly style="border:0; color:#1c75bc; font-weight:bold;">
</p>
<table>
{{ powerupdateform.as_table }}
</table>
{% csrf_token %}
<input class="button" type="submit" value="Submit">
</form>
</li>
<li class="grid_2">
<script src="https://cdn.pydata.org/bokeh/release/bokeh-2.2.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ interactiveplot |safe }}
{{ the_div |safe }}
</li>
<h1>Edit Workout Interval Data</h1>
<ul class="main-content">
<li class="grid_2">
<h1>Updated Summary</h1>
<form enctype="multipart/form-data" action="/rowers/workout/{{ workout.id|encode }}/editintervals/" method="post">
@@ -126,6 +84,67 @@
</pre>
</p>
</li>
<li class="grid_2">
<script src="https://cdn.pydata.org/bokeh/release/bokeh-2.2.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ interactiveplot |safe }}
{{ the_div |safe }}
</li>
<li class="grid_2">
<h1>Feeling lucky?</h1>
<p>This new, experimental feature tries to find intervals by looking for patterns in the data. It does not
autodetect rest and work intervals yet. The resulting "interval string" is copied to the Interval Shorthand
section below, where you can edit it.
<form enctype="multipart/form-data" method="post">
<input type="hidden" name="ruptures" value="ruptures">
<table width=100%>
{{ ruptureform.as_table }}
</table>
{% csrf_token %}
<input class="button" type="submit" value="I'm feeling lucky">
</form>
</p>
</li>
<li class="grid_2">
<h1>Interval Shorthand</h1>
<p>
See the how-to <a href="#howto">at the bottom of this page</a> for details on how to use this form.
</p>
<form enctype="multipart/form-data" action="/rowers/workout/{{ workout.id|encode }}/editintervals/" method="post">
<table width=100%>
{{ form.as_table }}
</table>
{% csrf_token %}
<input class="button" type="submit" value="Update">
</form>
</li>
<li class="grid_2">
<h1>Intervals by Power/Pace</h1>
<p>With this form, you can specify a power or pace level. Everything faster/harder than the
specified pace/power will become a work interval. Everything slower will become a rest
interval. Use the slider to limit the active range. Everything outside the active range will
become rest (warming up and cooling down).
</p>
<form ecntype="multipart/form-data" method="post">
<div id="slider-range"></div>
<p>
<label for="amount">Active Range:</label>
<input type="text" id="amount" readonly style="border:0; color:#1c75bc; font-weight:bold;">
</p>
<table>
{{ powerupdateform.as_table }}
</table>
{% csrf_token %}
<input class="button" type="submit" value="Submit">
</form>
</li>
<li class="grid_2">
<h1 id="howto">Interval Shorthand How-To</h1>
<p>This is a quick way to enter the intervals using a special mini-language.</p>
Binary file not shown.
+38
View File
@@ -18,6 +18,8 @@ from rowers.utils import intervals_to_string
from urllib.parse import urlparse, parse_qs
from json.decoder import JSONDecodeError
import ruptures as rpt
def default(o): # pragma: no cover
if isinstance(o, numpy.int64): return int(o)
if isinstance(o, numpy.int32): return int(o)
@@ -6144,6 +6146,42 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
powerupdateform = PowerIntervalUpdateForm(initial=data)
# feeling lucky / ruptures
if request.method == 'POST' and "ruptures" in request.POST:
df = pd.DataFrame({
'spm':rowdata.df[' Cadence (stokes/min)'],
'power':rowdata.df[' Power (watts)'],
'v':rowdata.df[' AverageBoatSpeed (m/s)']
})
algo = rpt.Pelt(model="rbf").fit(df.values)
result = algo.predict(pen=10)
df['time'] = rowdata.df['TimeStamp (sec)'].values
timeprev = int(df['time'].values[0])
timenext = int(df['time'].values[result[0]])
s = '{delta}sec'.format(delta=timenext-timeprev)
for i in range(len(result)-1):
timeprev = int(df['time'].values[result[i]-1])
timenext = int(df['time'].values[result[i+1]-1])
interval = '+{delta}sec'.format(delta=timenext-timeprev)
s += interval
try:
rowdata.updateinterval_string(s)
except:
messages.error(request,"Nope, you were not lucky")
intervalstats = rowdata.allstats()
itime, idist, itype = rowdata.intervalstats_values()
nrintervals = len(idist)
savebutton = 'savestringform'
intervalString = s
form = SummaryStringForm(initial={'intervalstring':intervalString})
# We have submitted the mini language interpreter
if request.method == 'POST' and "intervalstring" in request.POST:
form = SummaryStringForm(request.POST)