NK Tools Force Curve
This commit is contained in:
@@ -125,7 +125,177 @@ def tailwind(bearing,vwind,winddir):
|
|||||||
from rowers.dataprep import nicepaceformat,niceformat
|
from rowers.dataprep import nicepaceformat,niceformat
|
||||||
from rowers.dataprep import timedeltaconv
|
from rowers.dataprep import timedeltaconv
|
||||||
|
|
||||||
|
def interactive_forcecurve(theworkouts):
|
||||||
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
|
||||||
|
|
||||||
|
ids = [int(w.id) for w in theworkouts]
|
||||||
|
|
||||||
|
boattype = theworkouts[0].boattype
|
||||||
|
|
||||||
|
columns = ['catch','slip','wash','finish','averageforce',
|
||||||
|
'peakforceangle','peakforce','spm','distance']
|
||||||
|
|
||||||
|
rowdata = dataprep.getsmallrowdata_db(columns,ids=ids)
|
||||||
|
|
||||||
|
catchav = rowdata['catch'].mean()
|
||||||
|
finishav = rowdata['finish'].mean()
|
||||||
|
washav = rowdata['wash'].mean()
|
||||||
|
slipav = rowdata['slip'].mean()
|
||||||
|
peakforceav = rowdata['peakforce'].mean()
|
||||||
|
averageforceav = rowdata['averageforce'].mean()
|
||||||
|
peakforceangleav = rowdata['peakforceangle'].mean()
|
||||||
|
|
||||||
|
x = [catchav,
|
||||||
|
catchav+slipav,
|
||||||
|
peakforceangleav,
|
||||||
|
finishav-washav,
|
||||||
|
finishav]
|
||||||
|
|
||||||
|
thresholdforce = 100 if 'x' in boattype else 200
|
||||||
|
thresholdforce /= 4.45 # N to lbs
|
||||||
|
y = [0,thresholdforce,
|
||||||
|
peakforceav,
|
||||||
|
thresholdforce,0]
|
||||||
|
|
||||||
|
source = ColumnDataSource(
|
||||||
|
data = dict(
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
source2 = ColumnDataSource(
|
||||||
|
rowdata
|
||||||
|
)
|
||||||
|
|
||||||
|
plot = Figure(tools=TOOLS,
|
||||||
|
toolbar_sticky=False)
|
||||||
|
|
||||||
|
avf = Span(location=averageforceav,dimension='width',line_color='blue',
|
||||||
|
line_dash=[6,6],line_width=2)
|
||||||
|
|
||||||
|
plot.line('x','y',source=source,color="red")
|
||||||
|
|
||||||
|
plot.add_layout(avf)
|
||||||
|
|
||||||
|
plot.xaxis.axis_label = "Angle"
|
||||||
|
plot.yaxis.axis_label = "Force (lbs)"
|
||||||
|
plot.title.text = theworkouts[0].name
|
||||||
|
plot.title.text_font_size=value("1.0em")
|
||||||
|
|
||||||
|
yrange1 = Range1d(start=0,end=200)
|
||||||
|
plot.y_range = yrange1
|
||||||
|
|
||||||
|
xrange1 = Range1d(start=yaxmaxima['catch'],end=yaxmaxima['finish'])
|
||||||
|
plot.x_range = xrange1
|
||||||
|
|
||||||
|
callback = CustomJS(args = dict(
|
||||||
|
source=source,
|
||||||
|
source2=source2,
|
||||||
|
avf=avf,
|
||||||
|
), code="""
|
||||||
|
var data = source.data
|
||||||
|
var data2 = source2.data
|
||||||
|
|
||||||
|
var x = data['x']
|
||||||
|
var y = data['y']
|
||||||
|
var spm1 = data2['spm']
|
||||||
|
var distance1 = data2['distance']
|
||||||
|
|
||||||
|
var thresholdforce = y[1]
|
||||||
|
|
||||||
|
var c = source2.data['catch']
|
||||||
|
var finish = data2['finish']
|
||||||
|
var slip = data2['slip']
|
||||||
|
var wash = data2['wash']
|
||||||
|
var peakforceangle = data2['peakforceangle']
|
||||||
|
var peakforce = data2['peakforce']
|
||||||
|
var averageforce = data2['averageforce']
|
||||||
|
|
||||||
|
var minspm = minspm.value
|
||||||
|
var maxspm = maxspm.value
|
||||||
|
var mindist = mindist.value
|
||||||
|
var maxdist = maxdist.value
|
||||||
|
|
||||||
|
var catchav = 0
|
||||||
|
var finishav = 0
|
||||||
|
var slipav = 0
|
||||||
|
var washav = 0
|
||||||
|
var peakforceangleav = 0
|
||||||
|
var averageforceav = 0
|
||||||
|
var peakforceav = 0
|
||||||
|
var count = 0
|
||||||
|
|
||||||
|
|
||||||
|
for (i=0; i<c.length; i++) {
|
||||||
|
if (spm1[i]>=minspm && spm1[i]<=maxspm) {
|
||||||
|
if (distance1[i]>=mindist && distance1[i]<=maxdist) {
|
||||||
|
catchav += c[i]
|
||||||
|
finishav += finish[i]
|
||||||
|
slipav += slip[i]
|
||||||
|
washav += wash[i]
|
||||||
|
peakforceangleav += peakforceangle[i]
|
||||||
|
averageforceav += averageforce[i]
|
||||||
|
peakforceav += peakforce[i]
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
catchav /= count
|
||||||
|
finishav /= count
|
||||||
|
slipav /= count
|
||||||
|
washav /= count
|
||||||
|
peakforceangleav /= count
|
||||||
|
peakforceav /= count
|
||||||
|
averageforceav /= count
|
||||||
|
|
||||||
|
data['x'] = [catchav,catchav+slipav,peakforceangleav,finishav-washav,finishav]
|
||||||
|
data['y'] = [0,thresholdforce,peakforceav,thresholdforce,0]
|
||||||
|
|
||||||
|
avf.location = averageforceav
|
||||||
|
|
||||||
|
source.trigger('change');
|
||||||
|
""")
|
||||||
|
|
||||||
|
slider_spm_min = Slider(start=15.0, end=55,value=15.0, step=.1,
|
||||||
|
title="Min SPM",callback=callback)
|
||||||
|
callback.args["minspm"] = slider_spm_min
|
||||||
|
|
||||||
|
|
||||||
|
slider_spm_max = Slider(start=15.0, end=55,value=55.0, step=.1,
|
||||||
|
title="Max SPM",callback=callback)
|
||||||
|
callback.args["maxspm"] = slider_spm_max
|
||||||
|
|
||||||
|
distmax = 100+100*int(rowdata['distance'].max()/100.)
|
||||||
|
|
||||||
|
slider_dist_min = Slider(start=0,end=distmax,value=0,step=1,
|
||||||
|
title="Min Distance",callback=callback)
|
||||||
|
callback.args["mindist"] = slider_dist_min
|
||||||
|
|
||||||
|
slider_dist_max = Slider(start=0,end=distmax,value=distmax,
|
||||||
|
step=1,
|
||||||
|
title="Max Distance",callback=callback)
|
||||||
|
callback.args["maxdist"] = slider_dist_max
|
||||||
|
|
||||||
|
layout = layoutrow([layoutcolumn([slider_spm_min,
|
||||||
|
slider_spm_max,
|
||||||
|
slider_dist_min,
|
||||||
|
slider_dist_max,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
plot])
|
||||||
|
|
||||||
|
script, div = components(layout)
|
||||||
|
js_resources = INLINE.render_js()
|
||||||
|
css_resources = INLINE.render_css()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return [script,div,js_resources,css_resources]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def interactive_histoall(theworkouts):
|
def interactive_histoall(theworkouts):
|
||||||
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
|
||||||
|
|
||||||
|
|||||||
57
rowers/templates/forcecurve_single.html
Normal file
57
rowers/templates/forcecurve_single.html
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% load rowerfilters %}
|
||||||
|
{% load tz %}
|
||||||
|
|
||||||
|
{% block title %} Force Curve Plot {% endblock %}
|
||||||
|
|
||||||
|
{% localtime on %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{{ js_res | safe }}
|
||||||
|
{{ css_res| safe }}
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
|
||||||
|
<script type="text/javascript" src="/static/js/bokeh-widgets-0.12.3.min.js"></script>
|
||||||
|
<script async="true" type="text/javascript">
|
||||||
|
Bokeh.set_log_level("info");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{{ the_script |safe }}
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
|
||||||
|
html, body {height: 100%; margin:5px;}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div id="navigation" class="grid_12 alpha">
|
||||||
|
{% if user.is_authenticated and mayedit %}
|
||||||
|
<div class="grid_2 alpha">
|
||||||
|
<p>
|
||||||
|
<a class="button gray small" href="/rowers/workout/{{ id }}/edit">Edit Workout</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="grid_2 suffix_8 omega">
|
||||||
|
<p>
|
||||||
|
<a class="button gray small" href="/rowers/workout/{{ id }}/advanced">Advanced Edit</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<p> </p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div id="theplot" class="grid_12 alpha">
|
||||||
|
|
||||||
|
{{ the_div|safe }}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
{% endlocaltime %}
|
||||||
@@ -82,6 +82,7 @@ urlpatterns = [
|
|||||||
url(r'^workout/upload/$',views.workout_upload_view),
|
url(r'^workout/upload/$',views.workout_upload_view),
|
||||||
url(r'^workout/upload/(.+.*)$',views.workout_upload_view),
|
url(r'^workout/upload/(.+.*)$',views.workout_upload_view),
|
||||||
url(r'^workout/(?P<id>\d+)/histo$',views.workout_histo_view),
|
url(r'^workout/(?P<id>\d+)/histo$',views.workout_histo_view),
|
||||||
|
url(r'^workout/(?P<id>\d+)/forcecurve$',views.workout_forcecurve_view),
|
||||||
url(r'^workout/(?P<id>\d+)/export/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.workout_export_view),
|
url(r'^workout/(?P<id>\d+)/export/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.workout_export_view),
|
||||||
url(r'^workout/(?P<id>\d+)/export/c/(?P<message>\w+.*)$',views.workout_export_view),
|
url(r'^workout/(?P<id>\d+)/export/c/(?P<message>\w+.*)$',views.workout_export_view),
|
||||||
url(r'^workout/(?P<id>\d+)/export/s/(?P<successmessage>\w+.*)$',views.workout_export_view),
|
url(r'^workout/(?P<id>\d+)/export/s/(?P<successmessage>\w+.*)$',views.workout_export_view),
|
||||||
|
|||||||
@@ -1390,6 +1390,35 @@ def cum_flex(request,theuser=0,
|
|||||||
'promember':promember,
|
'promember':promember,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@user_passes_test(promember,login_url="/",redirect_field_name=None)
|
||||||
|
def workout_forcecurve_view(request,id=0):
|
||||||
|
row = Workout.objects.get(id=id)
|
||||||
|
promember=0
|
||||||
|
mayedit=0
|
||||||
|
if not request.user.is_anonymous():
|
||||||
|
r = Rower.objects.get(user=request.user)
|
||||||
|
result = request.user.is_authenticated() and r.rowerplan=='pro'
|
||||||
|
if result:
|
||||||
|
promember=1
|
||||||
|
if request.user == row.user.user:
|
||||||
|
mayedit=1
|
||||||
|
|
||||||
|
if not promember:
|
||||||
|
return HttpResponseRedirect("/rowers/about/")
|
||||||
|
|
||||||
|
script,div,js_resources,css_resources = interactive_forcecurve([row])
|
||||||
|
|
||||||
|
return render(request,
|
||||||
|
'forcecurve_single.html',
|
||||||
|
{
|
||||||
|
'the_script':script,
|
||||||
|
'the_div':div,
|
||||||
|
'js_res': js_resources,
|
||||||
|
'css_res':css_resources,
|
||||||
|
'id':id,
|
||||||
|
'mayedit':mayedit,
|
||||||
|
})
|
||||||
|
|
||||||
@login_required()
|
@login_required()
|
||||||
def workout_histo_view(request,id=0):
|
def workout_histo_view(request,id=0):
|
||||||
row = Workout.objects.get(id=id)
|
row = Workout.objects.get(id=id)
|
||||||
@@ -1406,9 +1435,6 @@ def workout_histo_view(request,id=0):
|
|||||||
if not promember:
|
if not promember:
|
||||||
return HttpResponseRedirect("/rowers/about/")
|
return HttpResponseRedirect("/rowers/about/")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
res = interactive_histoall([row])
|
res = interactive_histoall([row])
|
||||||
script = res[0]
|
script = res[0]
|
||||||
div = res[1]
|
div = res[1]
|
||||||
|
|||||||
Reference in New Issue
Block a user