From 0e6fac88b5e83ed615bbc85ec04a29b5c8f84c21 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 13 Dec 2016 10:44:02 +0100 Subject: [PATCH] NK Tools Force Curve --- rowers/interactiveplots.py | 170 ++++++++++++++++++++++++ rowers/templates/forcecurve_single.html | 57 ++++++++ rowers/urls.py | 1 + rowers/views.py | 32 ++++- 4 files changed, 257 insertions(+), 3 deletions(-) create mode 100644 rowers/templates/forcecurve_single.html diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index e1ee9af7..04c2ee23 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -125,7 +125,177 @@ def tailwind(bearing,vwind,winddir): from rowers.dataprep import nicepaceformat,niceformat 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=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): TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair' diff --git a/rowers/templates/forcecurve_single.html b/rowers/templates/forcecurve_single.html new file mode 100644 index 00000000..2f7e83b1 --- /dev/null +++ b/rowers/templates/forcecurve_single.html @@ -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 }} + + + + + + {{ the_script |safe }} + + + + + + + +

 

+ + + +
+ + {{ the_div|safe }} + +
+ + +{% endblock %} +{% endlocaltime %} diff --git a/rowers/urls.py b/rowers/urls.py index 7c3d8e06..9e2c2e8d 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -82,6 +82,7 @@ urlpatterns = [ url(r'^workout/upload/$',views.workout_upload_view), url(r'^workout/upload/(.+.*)$',views.workout_upload_view), url(r'^workout/(?P\d+)/histo$',views.workout_histo_view), + url(r'^workout/(?P\d+)/forcecurve$',views.workout_forcecurve_view), url(r'^workout/(?P\d+)/export/c/(?P\w+.*)/s/(?P\w+.*)$',views.workout_export_view), url(r'^workout/(?P\d+)/export/c/(?P\w+.*)$',views.workout_export_view), url(r'^workout/(?P\d+)/export/s/(?P\w+.*)$',views.workout_export_view), diff --git a/rowers/views.py b/rowers/views.py index 3b483290..beda5436 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -1390,6 +1390,35 @@ def cum_flex(request,theuser=0, '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() def workout_histo_view(request,id=0): row = Workout.objects.get(id=id) @@ -1406,9 +1435,6 @@ def workout_histo_view(request,id=0): if not promember: return HttpResponseRedirect("/rowers/about/") - - - res = interactive_histoall([row]) script = res[0] div = res[1]