diff --git a/rowers/forms.py b/rowers/forms.py index 7387d8fe..ae17b596 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -722,7 +722,8 @@ class TrendFlexModalForm(forms.Form): label='Only Ranking Pieces', required=False) - + + # This form sets options for the summary stats page class StatsOptionsForm(forms.Form): includereststrokes = forms.BooleanField(initial=False,label='Include Rest Strokes',required=False) @@ -1181,10 +1182,21 @@ class FlexOptionsForm(forms.Form): ('line','Line Plot'), ('scatter','Scatter Plot'), ) - plottype = forms.ChoiceField(choices=plotchoices,initial='scatter', + plottype = forms.ChoiceField(choices=plotchoices,initial='line', label='Chart Type') - +class ForceCurveOptionsForm(forms.Form): + includereststrokes = forms.BooleanField(initial=False, required = False, + label='Include Rest Strokes') + plotchoices = ( + ('line','Force Curve Collection Plot'), + ('scatter','Peak Force Scatter Plot'), + ('none','Only aggregrate data') + ) + plottype = forms.ChoiceField(choices=plotchoices,initial='scatter', + label='Individual Stroke Chart Type') + + class FlexAxesForm(forms.Form): axchoices = list( (ax[0],ax[1]) for ax in axes if ax[0] not in ['cumdist','None'] diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 2c499cee..d601d558 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -17,6 +17,7 @@ from math import pi from django.utils import timezone from bokeh.palettes import Dark2_8 as palette +from bokeh.models.glyphs import MultiLine import itertools from bokeh.plotting import figure, ColumnDataSource, Figure,curdoc from bokeh.models import CustomJS,Slider, TextInput,BoxAnnotation @@ -374,7 +375,7 @@ def interactive_activitychart(workouts,startdate,enddate,stack='type'): script,div = components(p) return script,div -def interactive_forcecurve(theworkouts,workstrokesonly=False): +def interactive_forcecurve(theworkouts,workstrokesonly=True,plottype='scatter'): TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair' ids = [int(w.id) for w in theworkouts] @@ -642,12 +643,20 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False): rowdata ) - sourcepoints = ColumnDataSource( - data = dict( - peakforceangle = rowdata['peakforceangle'], - peakforce = rowdata['peakforce'] + if plottype == 'scatter': + sourcepoints = ColumnDataSource( + data = dict( + peakforceangle = rowdata['peakforceangle'], + peakforce = rowdata['peakforce'] ) ) + else: + sourcepoints = ColumnDataSource( + data = dict( + peakforceangle = [], + peakforce = [] + )) + sourcerange = ColumnDataSource( data = dict( @@ -697,6 +706,50 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False): plot.circle('xslip','yslip',source=sourceslipwash,color="red") plot.circle('peakforceangle','peakforce',source=sourcepoints,color='black',alpha=0.1) + + if plottype == 'line': + multilinedatax = [] + multilinedatay = [] + for i in range(len(rowdata)): + x = [ + rowdata['catch'].values[i], + rowdata['slip'].values[i]+rowdata['catch'].values[i], + rowdata['peakforceangle'].values[i], + rowdata['finish'].values[i]-rowdata['wash'].values[i], + rowdata['finish'].values[i] + ] + + + y = [ + 0, + thresholdforce, + rowdata['peakforce'].values[i], + thresholdforce, + 0] + + multilinedatax.append(x) + multilinedatay.append(y) + + sourcemultiline = ColumnDataSource(dict( + x=multilinedatax, + y=multilinedatay, + )) + + sourcemultiline2 = ColumnDataSource(dict( + x=multilinedatax, + y=multilinedatay, + )) + + glyph = MultiLine(xs='x',ys='y',line_color='black',line_alpha=0.05) + plot.add_glyph(sourcemultiline,glyph) + else: + sourcemultiline = ColumnDataSource(dict( + x=[],y=[])) + + sourcemultiline2 = ColumnDataSource(dict( + x=[],y=[])) + + plot.line('x','y',source=source,color="red") plot.add_layout(avf) @@ -801,11 +854,20 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False): peakforceanglelabel=peakforceanglelabel, annolabel=annolabel, sliderlabel=sliderlabel, + plottype=plottype, + sourcemultiline=sourcemultiline, + sourcemultiline2=sourcemultiline2 ), code=""" var data = source.data var data2 = source2.data var dataslipwash = sourceslipwash.data var datapoints = sourcepoints.data + var multilines = sourcemultiline.data + var multilines2 = sourcemultiline2.data + var plottype = plottype + + var multilinesx = multilines['xs'] + var multilinesy = multilines['ys'] var x = data['x'] var y = data['y'] @@ -826,6 +888,9 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False): var peakforce = data2['peakforce'] var averageforce = data2['averageforce'] + var peakforcepoints = datapoints['peakforce'] + var peakforceanglepoints = datapoints['peakforceangle'] + var annotation = annotation.value var minspm = minspm.value var maxspm = maxspm.value @@ -849,13 +914,21 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False): datapoints['peakforceangle'] = [] datapoints['peakforce'] = [] + multilines['xs'] = [] + multilines['ys'] = [] for (i=0; i=minspm && spm1[i]<=maxspm) { if (distance1[i]>=mindist && distance1[i]<=maxdist) { if (driveenergy1[i]>=minwork && driveenergy1[i]<=maxwork) { - datapoints['peakforceangle'].push(peakforceangle[i]) - datapoints['peakforce'].push(peakforce[i]) + if (plottype=='scatter') { + datapoints['peakforceangle'].push(peakforceangle[i]) + datapoints['peakforce'].push(peakforce[i]) + } + if (plottype=='line') { + multilines['xs'].push(multilinesx[i]) + multilines['ys'].push(multilinesy[i]) + } catchav += c[i] finishav += finish[i] slipav += slip[i] @@ -897,6 +970,7 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False): source.change.emit(); sourceslipwash.change.emit() sourcepoints.change.emit(); + sourcemultiline.change.emit(); """) annotation = TextInput(title="Type your plot notes here", value="", diff --git a/rowers/templates/forcecurve_single.html b/rowers/templates/forcecurve_single.html index 2cebb103..2326aa0a 100644 --- a/rowers/templates/forcecurve_single.html +++ b/rowers/templates/forcecurve_single.html @@ -22,27 +22,24 @@

Empower Force Curve

    -
  • - {% if user.is_authenticated and mayedit %} -
    - {% csrf_token %} - {% if workstrokesonly %} - - - {% else %} - - -
    - {% endif %} - If your data source allows, this will show or hide strokes taken during rest intervals. - {% endif %} -
  • {{ the_div|safe }}
  • +
  • +
    + {% csrf_token %} + + {{ form.as_table }} +
    +

    + +

    +
    +
diff --git a/rowers/views/statements.py b/rowers/views/statements.py index 76691ab1..2179bebb 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -48,6 +48,7 @@ from django.http import ( ) from django.contrib.auth import authenticate, login, logout from rowers.forms import ( + ForceCurveOptionsForm, LoginForm,DocumentsForm,UploadOptionsForm,ImageForm,CourseForm, TeamUploadOptionsForm,WorkFlowLeftPanelForm,WorkFlowMiddlePanelForm, WorkFlowLeftPanelElement,WorkFlowMiddlePanelElement, diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index c976e6f7..8b3ac052 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -26,15 +26,24 @@ def workout_forcecurve_view(request,id=0,workstrokesonly=False): if not promember: return HttpResponseRedirect("/rowers/about/") - if request.method == 'POST' and 'workstrokesonly' in request.POST: - workstrokesonly = request.POST['workstrokesonly'] - if workstrokesonly == 'True': - workstrokesonly = True + if request.method == 'POST': + form = ForceCurveOptionsForm(request.POST) + if form.is_valid(): + includereststrokes = form.cleaned_data['includereststrokes'] + plottype = form.cleaned_data['plottype'] + workstrokesonly = not includereststrokes else: - workstrokesonly = False + workstrokesonly = True + plottype = 'line' + else: + form = ForceCurveOptionsForm() + plottype = 'line' - script,div,js_resources,css_resources = interactive_forcecurve([row], - workstrokesonly=workstrokesonly) + script,div,js_resources,css_resources = interactive_forcecurve( + [row], + workstrokesonly=workstrokesonly, + plottype=plottype, + ) breadcrumbs = [ { @@ -53,12 +62,13 @@ def workout_forcecurve_view(request,id=0,workstrokesonly=False): ] r = getrower(request.user) - + return render(request, 'forcecurve_single.html', { 'the_script':script, 'rower':r, + 'form':form, 'workout':row, 'breadcrumbs':breadcrumbs, 'active':'nav-workouts', @@ -67,7 +77,6 @@ def workout_forcecurve_view(request,id=0,workstrokesonly=False): 'css_res':css_resources, 'id':id, 'mayedit':mayedit, - 'workstrokesonly': not workstrokesonly, 'teams':get_my_teams(request.user), })