Merge branch 'feature/magicintervals' into develop
This commit is contained in:
@@ -468,7 +468,6 @@ def getfastest(df,thevalue,mode='distance'):
|
|||||||
starttime = griddata(restime,starttimes,[thevalue*60*1000],method='linear',rescale=True)
|
starttime = griddata(restime,starttimes,[thevalue*60*1000],method='linear',rescale=True)
|
||||||
duration = griddata(restime,restime,[thevalue*60*1000],method='linear',rescale=True)
|
duration = griddata(restime,restime,[thevalue*60*1000],method='linear',rescale=True)
|
||||||
endtime = starttime+duration
|
endtime = starttime+duration
|
||||||
print(distance,starttime,endtime )
|
|
||||||
return distance[0],starttime[0]/1000.,endtime[0]/1000.
|
return distance[0],starttime[0]/1000.,endtime[0]/1000.
|
||||||
|
|
||||||
return 0 # pragma: no cover
|
return 0 # pragma: no cover
|
||||||
|
|||||||
@@ -41,24 +41,75 @@
|
|||||||
title="Jump to following workout"><em>Next</em></a>
|
title="Jump to following workout"><em>Next</em></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<table width=100%>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th><td>{{ workout.name }}</td>
|
||||||
|
</tr><tr>
|
||||||
|
<th>Distance:</th><td>{{ workout.distance }}m</td>
|
||||||
|
</tr><tr>
|
||||||
|
<th>Duration:</th><td>{{ workout.duration |durationprint:"%H:%M:%S.%f" }}</td>
|
||||||
|
</tr><tr>
|
||||||
|
<th>Public link to this workout</th>
|
||||||
|
<td>
|
||||||
|
<a href="/rowers/workout/{{ workout.id|encode }}/">https://rowsandall.com/rowers/workout/{{ workout.id|encode }}</a>
|
||||||
|
<td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</p>
|
||||||
|
|
||||||
<h1>Edit Workout Interval Data</h1>
|
<h1>Edit Workout Interval Data</h1>
|
||||||
<ul class="main-content">
|
<ul class="main-content">
|
||||||
<li class="grid_2">
|
<li class="grid_2">
|
||||||
<table width=100%>
|
<h1>Updated Summary</h1>
|
||||||
<tr>
|
<form enctype="multipart/form-data" action="/rowers/workout/{{ workout.id|encode }}/editintervals/" method="post">
|
||||||
<th>Name</th><td>{{ workout.name }}</td>
|
{% csrf_token %}
|
||||||
</tr><tr>
|
<input type="hidden" name="{{ savebutton }}" value="{{ intervalstring }}">
|
||||||
<th>Distance:</th><td>{{ workout.distance }}m</td>
|
<input type="hidden" name="nrintervals" value={{ nrintervals }}>
|
||||||
</tr><tr>
|
{% for key,value in formvalues.items %}
|
||||||
<th>Duration:</th><td>{{ workout.duration |durationprint:"%H:%M:%S.%f" }}</td>
|
<input type="hidden" name="{{ key }}" value="{{ value|safe }}">
|
||||||
</tr><tr>
|
{% endfor %}
|
||||||
<th>Public link to this workout</th>
|
<p>
|
||||||
<td>
|
<input type="submit" value="Save">
|
||||||
<a href="/rowers/workout/{{ workout.id|encode }}/">https://rowsandall.com/rowers/workout/{{ workout.id|encode }}</a>
|
</p>
|
||||||
<td>
|
<span>
|
||||||
</tr>
|
<a href="">Reset to last saved</a>
|
||||||
</table>
|
|
||||||
|
<a href="/rowers/workout/{{ workout.id|encode }}/restore/">Restore Original data</a>
|
||||||
|
</span>
|
||||||
|
</form>
|
||||||
|
<p>
|
||||||
|
<pre>
|
||||||
|
{{ intervalstats }}
|
||||||
|
</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>
|
<h1>Interval Shorthand</h1>
|
||||||
<p>
|
<p>
|
||||||
See the how-to <a href="#howto">at the bottom of this page</a> for details on how to use this form.
|
See the how-to <a href="#howto">at the bottom of this page</a> for details on how to use this form.
|
||||||
@@ -70,6 +121,8 @@
|
|||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input class="button" type="submit" value="Update">
|
<input class="button" type="submit" value="Update">
|
||||||
</form>
|
</form>
|
||||||
|
</li>
|
||||||
|
<li class="grid_2">
|
||||||
<h1>Intervals by Power/Pace</h1>
|
<h1>Intervals by Power/Pace</h1>
|
||||||
|
|
||||||
<p>With this form, you can specify a power or pace level. Everything faster/harder than the
|
<p>With this form, you can specify a power or pace level. Everything faster/harder than the
|
||||||
@@ -92,40 +145,6 @@
|
|||||||
<input class="button" type="submit" value="Submit">
|
<input class="button" type="submit" value="Submit">
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</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>Updated Summary</h1>
|
|
||||||
<form enctype="multipart/form-data" action="/rowers/workout/{{ workout.id|encode }}/editintervals/" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<input type="hidden" name="{{ savebutton }}" value="{{ intervalstring }}">
|
|
||||||
<input type="hidden" name="nrintervals" value={{ nrintervals }}>
|
|
||||||
{% for key,value in formvalues.items %}
|
|
||||||
<input type="hidden" name="{{ key }}" value="{{ value|safe }}">
|
|
||||||
{% endfor %}
|
|
||||||
<p>
|
|
||||||
<input type="submit" value="Save">
|
|
||||||
</p>
|
|
||||||
<span>
|
|
||||||
<a href="">Reset to last saved</a>
|
|
||||||
|
|
||||||
<a href="/rowers/workout/{{ workout.id|encode }}/restore/">Restore Original data</a>
|
|
||||||
</span>
|
|
||||||
</form>
|
|
||||||
<p>
|
|
||||||
<pre>
|
|
||||||
{{ intervalstats }}
|
|
||||||
</pre>
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li class="grid_2">
|
<li class="grid_2">
|
||||||
<h1 id="howto">Interval Shorthand How-To</h1>
|
<h1 id="howto">Interval Shorthand How-To</h1>
|
||||||
<p>This is a quick way to enter the intervals using a special mini-language.</p>
|
<p>This is a quick way to enter the intervals using a special mini-language.</p>
|
||||||
|
|||||||
BIN
Binary file not shown.
@@ -18,6 +18,8 @@ from rowers.utils import intervals_to_string
|
|||||||
from urllib.parse import urlparse, parse_qs
|
from urllib.parse import urlparse, parse_qs
|
||||||
from json.decoder import JSONDecodeError
|
from json.decoder import JSONDecodeError
|
||||||
|
|
||||||
|
import ruptures as rpt
|
||||||
|
|
||||||
def default(o): # pragma: no cover
|
def default(o): # pragma: no cover
|
||||||
if isinstance(o, numpy.int64): return int(o)
|
if isinstance(o, numpy.int64): return int(o)
|
||||||
if isinstance(o, numpy.int32): 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)
|
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
|
# We have submitted the mini language interpreter
|
||||||
if request.method == 'POST' and "intervalstring" in request.POST:
|
if request.method == 'POST' and "intervalstring" in request.POST:
|
||||||
form = SummaryStringForm(request.POST)
|
form = SummaryStringForm(request.POST)
|
||||||
|
|||||||
Reference in New Issue
Block a user