Private
Public Access
1
0

slightly working prototype

only boxplot implemented, doesn't change y param
This commit is contained in:
Sander Roosendaal
2019-04-28 15:46:34 +02:00
parent d1ec7854d4
commit f1888ab9fa
5 changed files with 534 additions and 1 deletions

View File

@@ -721,6 +721,17 @@ class HistoForm(forms.Form):
histoparam = forms.ChoiceField(choices=parchoices,initial='power',
label='Metric')
class AnalysisOptionsForm(forms.Form):
modality = forms.ChoiceField(choices=workouttypes,
label='Workout Type',
initial='all')
waterboattype = forms.MultipleChoiceField(choices=boattypes,
label='Water Boat Type',
initial = mytypes.waterboattype)
rankingonly = forms.BooleanField(initial=False,
label='Only Ranking Pieces',
required=False)
# form to select modality and boat type for trend flex
class TrendFlexModalForm(forms.Form):
@@ -809,6 +820,29 @@ class PlannedSessionMultipleCloneForm(forms.Form):
label='Planned Sessions'
)
analysischoices = (
('boxplot','Box Chart'),
('trendflex','Trend Flex'),
)
class AnalysisChoiceForm(forms.Form):
function = forms.ChoiceField(choices=analysischoices,initial='boxplot',
label='Analysis')
yparam = forms.ChoiceField(choices=parchoices,initial='spm',
label='Metric')
spmmin = forms.FloatField(initial=15,
required=False,label = 'Min SPM')
spmmax = forms.FloatField(initial=55,
required=False,label = 'Max SPM')
workmin = forms.FloatField(initial=0,
required=False,label = 'Min Work per Stroke')
workmax = forms.FloatField(initial=1500,
required=False,label = 'Max Work per Stroke')
includereststrokes = forms.BooleanField(initial=False,
required=False,
label='Include Rest Strokes')
class BoxPlotChoiceForm(forms.Form):
yparam = forms.ChoiceField(choices=parchoices,initial='spm',

View File

@@ -0,0 +1,158 @@
{% extends "newbase.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}Workouts{% endblock %}
{% block main %}
<script>
function toggle(source) {
checkboxes = document.querySelectorAll("input[name='workouts']");
for(var i=0, n=checkboxes.length;i<n;i++) {
checkboxes[i].checked = source.checked;
}
}
</script>
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script>
$(function() {
// Get the form fields and hidden div
var modality = $("#id_modality");
var hidden = $("#id_waterboattype");
// Hide the fields.
// Use JS to do this in case the user doesn't have JS
// enabled.
hidden.hide();
if (modality.val() == 'water') {
hidden.show();
}
// Setup an event listener for when the state of the
// checkbox changes.
modality.change(function() {
// Check to see if the checkbox is checked.
// If it is, show the fields and populate the input.
// If not, hide the fields.
var Value = modality.val();
if (Value=='water') {
// Show the hidden fields.
hidden.show();
} else {
// Make sure that the hidden fields are indeed
// hidden.
hidden.hide();
// You may also want to clear the value of the
// hidden fields here. Just in case somebody
// shows the fields, enters data to them and then
// unticks the checkbox.
//
// This would do the job:
//
// $("#hidden_field").val("");
}
});
});
</script>
<script src="https://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
<div id="id_script">
</div>
<ul class="main-content">
<li class="grid_4">
<div id="id_chart">
{{ the_div|safe }}
</div>
</li>
<li class="grid_4">
<p>You can use the date and search forms to search through all
workouts from this team.</p>
<p>TIP: Agree with your team members to put tags (e.g. '8x500m') in the notes section of
your workouts. That makes it easy to search.</p>
</li>
<li class="grid_2 maxheight">
<form id="searchform" action=""
method="get" accept-charset="utf-8">
{{ searchform }}
<input type="submit" value="GO"></input>
</form>
<form enctype="multipart/form-data" action="" method="post">
{% if workouts %}
<input type="checkbox" onClick="toggle(this)" /> Toggle All<br/>
<table width="100%" class="listtable">
{{ form.as_table }}
</table>
{% else %}
<p> No workouts found </p>
{% endif %}
</li>
<li class="grid_2">
<p>Select two or more workouts, set your plot settings below,
and press submit
</p>
{% csrf_token %}
<table>
{{ chartform.as_table }}
</table>
</li>
<li class="grid_2">
<form enctype="multipart/form-data" method="post">
<table>
{{ dateform.as_table }}
</table>
<table>
{{ optionsform.as_table }}
</table>
{% csrf_token %}
<input name='optionsform' type="submit" value="Submit">
</form>
</li>
</ul>
{% endblock %}
{% block scripts %}
{% if request.method == 'POST' %}
<script type='text/javascript'
src='https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'>
</script>
<script>
$(function($) {
console.log('loading script');
$.getJSON(window.location.protocol + '//'+window.location.host + '/rowers/analysisdata/', function(json) {
var counter=0;
var script = json.script;
var div = json.div;
$("#id_sitready").remove();
$("#id_chart").append(div);
console.log(div);
$("#id_script").append("<script>"+script+"</s"+"cript>");
});
});
</script>
{% endif %}
{% endblock %}
{% block sidebar %}
{% include 'menu_analytics.html' %}
{% endblock %}

View File

@@ -223,6 +223,10 @@ urlpatterns = [
re_path(r'^workouts-join-select/user/(?P<userid>\d+)/$',views.workouts_join_select,name='workouts_join_select'),
re_path(r'^user-boxplot-select/user/(?P<userid>\d+)/$',views.user_boxplot_select,name='user_boxplot_select'),
re_path(r'^user-boxplot-select/$',views.user_boxplot_select,name='user_boxplot_select'),
re_path(r'^user-analysis-select/(?P<function>\w.*)/user/(?P<userid>\d+)/$',views.analysis_new,name='analysis_new'),
re_path(r'^user-analysis-select/(?P<function>\w.*)/$',views.analysis_new,name='analysis_new'),
re_path(r'^user-analysis-select/user/(?P<userid>\d+)/$',views.analysis_new,name='analysis_new'),
re_path(r'^user-analysis-select/$',views.analysis_new,name='analysis_new'),
# re_path(r'^user-multiflex-select/user/(?P<userid>\d+)/(?P<startdatestring>\d+-\d+-\d+)/(?P<enddatestring>\d+-\d+-\d+)/$',views.user_multiflex_select,name='user_multiflex_select'),
re_path(r'^user-multiflex-select/user/(?P<userid>\d+)/$',views.user_multiflex_select,name='user_multiflex_select'),
# re_path(r'^user-multiflex-select/(?P<startdatestring>\d+-\d+-\d+)/(?P<enddatestring>\d+-\d+-\d+)/$',views.user_multiflex_select,name='user_multiflex_select'),
@@ -259,6 +263,7 @@ urlpatterns = [
# re_path(r'^flexall/(?P<xparam>\w+.*)/(?P<yparam1>\w+.*)/(?P<yparam2>\w+.*)/(?P<startdatestring>\d+-\d+-\d+)/(?P<enddatestring>\d+-\d+-\d+)/user/(?P<theuser>\d+)/$',views.cum_flex,name='cum_flex'),
# re_path(r'^flexall/(?P<xparam>\w+.*)/(?P<yparam1>\w+.*)/(?P<yparam2>\w+.*)/(?P<startdatestring>\d+-\d+-\d+)/(?P<enddatestring>\d+-\d+-\d+)/$',views.cum_flex,name='cum_flex'),
re_path(r'^flexall/(?P<xparam>\w+.*)/(?P<yparam1>\w+.*)/(?P<yparam2>\w+.*)/$',views.cum_flex,name='cum_flex'),
re_path(r'^analysisdata/$',views.analysis_view_data,name='analysis_view_data'),
re_path(r'^flexall/user/(?P<theuser>\d+)/$',views.cum_flex,name='cum_flex'),
re_path(r'^flexall/$',views.cum_flex,name='cum_flex'),
re_path(r'^flexalldata/$',views.cum_flex_data,name='cum_flex_data'),

View File

@@ -5,6 +5,341 @@ from __future__ import unicode_literals
from __future__ import unicode_literals, absolute_import
from rowers.views.statements import *
# generic Analysis view -
defaultoptions = {
'includereststrokes': False,
'workouttypes':['rower','dynamic','slides'],
'waterboattype': mytypes.waterboattype,
'rankingonly': False,
'function':'boxplot'
}
@user_passes_test(ispromember, login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def analysis_new(request,userid=0,function='boxplot'):
r = getrequestrower(request, userid=userid)
user = r.user
userid = user.id
if 'options' in request.session:
options = request.session['options']
else:
options=defaultoptions
try:
workouttypes = options['workouttypes']
except KeyError:
workouttypes = ['rower','dynamic','slides']
try:
rankingonly = options['rankingonly']
except KeyError:
rankingonly = False
try:
includereststrokes = options['includereststrokes']
except KeyError:
includereststrokes = False
if 'startdate' in request.session:
startdate = iso8601.parse_date(request.session['startdate'])
if 'enddate' in request.session:
enddate = iso8601.parse_date(request.session['enddate'])
workstrokesonly = not includereststrokes
waterboattype = mytypes.waterboattype
if request.method == 'POST':
dateform = DateRangeForm(request.POST)
if dateform.is_valid():
startdate = dateform.cleaned_data['startdate']
enddate = dateform.cleaned_data['enddate']
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
request.session['startdate'] = startdatestring
request.session['enddate'] = enddatestring
optionsform = AnalysisOptionsForm(request.POST)
if optionsform.is_valid():
for key, value in optionsform.cleaned_data.items():
options[key] = value
modality = optionsform.cleaned_data['modality']
waterboattype = optionsform.cleaned_data['waterboattype']
if modality == 'all':
modalities = [m[0] for m in mytypes.workouttypes]
else:
modalities = [modality]
if modality != 'water':
waterboattype = [b[0] for b in mytypes.boattypes]
if 'rankingonly' in optionsform.cleaned_data:
rankingonly = optionsform.cleaned_data['rankingonly']
else:
rankingonly = False
options['modalities'] = modalities
options['waterboattype'] = waterboattype
chartform = AnalysisChoiceForm(request.POST)
if chartform.is_valid():
for key, value in chartform.cleaned_data.items():
options[key] = value
form = WorkoutMultipleCompareForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
selectedworkouts = cd['workouts']
ids = [int(w.id) for w in selectedworkouts]
options['ids'] = ids
else:
ids = []
options['ids'] = ids
else:
dateform = DateRangeForm(initial={
'startdate':startdate,
'enddate':enddate,
})
if 'modalities' in request.session:
modalities = request.session['modalities']
if len(modalities) > 1:
modality = 'all'
else:
modality = modalities[0]
else:
modalities = [m[0] for m in mytypes.workouttypes]
modality = 'all'
negtypes = []
for b in mytypes.boattypes:
if b[0] not in waterboattype:
negtypes.append(b[0])
startdate = datetime.datetime.combine(startdate,datetime.time())
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
if enddate < startdate:
s = enddate
enddate = startdate
startdate = s
negtypes = []
for b in mytypes.boattypes:
if b[0] not in waterboattype:
negtypes.append(b[0])
workouts = Workout.objects.filter(user=r,
startdatetime__gte=startdate,
startdatetime__lte=enddate,
workouttype__in=modalities,
).order_by(
"-date", "-starttime"
).exclude(boattype__in=negtypes)
if rankingonly:
workouts = workouts.exclude(rankingpiece=False)
query = request.GET.get('q')
if query:
query_list = query.split()
workouts = workouts.filter(
reduce(operator.and_,
(Q(name__icontains=q) for q in query_list)) |
reduce(operator.and_,
(Q(notes__icontains=q) for q in query_list))
)
searchform = SearchForm(initial={'q':query})
else:
searchform = SearchForm()
if request.method != 'POST':
form = WorkoutMultipleCompareForm()
chartform = AnalysisChoiceForm()
selectedworkouts = Workout.objects.none()
else:
selectedworkouts = Workout.objects.filter(id__in=ids)
form.fields["workouts"].queryset = workouts | selectedworkouts
optionsform = AnalysisOptionsForm(initial={
'modality':modality,
'waterboattype':waterboattype,
'rankingonly':rankingonly,
})
startdatestring = startdate.strftime('%Y-%m-%d')
enddatestring = enddate.strftime('%Y-%m-%d')
request.session['startdate'] = startdatestring
request.session['enddate'] = enddatestring
request.session['options'] = options
breadcrumbs = [
{
'url':'/rowers/analysis',
'name':'Analysis'
},
{
'url':reverse('analysis_new',kwargs={'userid':userid}),
'name': 'Analysis Select'
},
]
return render(request, 'user_analysis_select.html',
{'workouts': workouts,
'dateform':dateform,
'startdate':startdate,
'enddate':enddate,
'rower':r,
'breadcrumbs':breadcrumbs,
'theuser':user,
'form':form,
'active':'nav-analysis',
'chartform':chartform,
'searchform':searchform,
'optionsform':optionsform,
'teams':get_my_teams(request.user),
})
def boxplotdata(workouts,options):
try:
includereststrokes = options['includereststrokes']
spmmin = options['spmmin']
spmmax = options['spmmax']
workmin = options['workmin']
workmax = options['workmax']
ids = options['ids']
userid = options['userid']
plotfield = options['plotfield']
function = options['function']
except KeyError:
includereststrokes = False
spmmin = 15
spmmax = 55
workmin = 0
workmax = 55
ids = []
userid = 0
plotfield = 'spm'
function = 'boxplot'
workstrokesonly = not includereststrokes
labeldict = {
int(w.id): w.__str__() for w in workouts
}
datemapping = {
w.id:w.date for w in workouts
}
fieldlist,fielddict = dataprep.getstatsfields()
fieldlist = [plotfield,'workoutid','spm','driveenergy',
'workoutstate']
ids = [w.id for w in workouts]
# prepare data frame
datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)
datadf = dataprep.filter_df(datadf,'spm',spmmin,
largerthan=True)
datadf = dataprep.filter_df(datadf,'spm',spmmax,
largerthan=False)
datadf = dataprep.filter_df(datadf,'driveenergy',workmin,
largerthan=True)
datadf = dataprep.filter_df(datadf,'driveneergy',workmax,
largerthan=False)
datadf.dropna(axis=0,how='any',inplace=True)
datadf['workoutid'].replace(datemapping,inplace=True)
datadf.rename(columns={"workoutid":"date"},inplace=True)
datadf = datadf.sort_values(['date'])
if userid == 0:
extratitle = ''
else:
u = User.objects.get(id=userid)
extratitle = ' '+u.first_name+' '+u.last_name
script,div = interactive_boxchart(datadf,plotfield,
extratitle=extratitle,
spmmin=spmmin,spmmax=spmmax,workmin=workmin,workmax=workmax)
scripta = script.split('\n')[2:-1]
script = ''.join(scripta)
return(script,div)
@user_passes_test(ispromember,login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher",
redirect_field_name=None)
def analysis_view_data(request,userid=0):
if 'options' in request.session:
options = request.session['options']
else:
options = defaultoptions
if userid==0:
userid = request.user.id
workouts = []
ids = options['ids']
function = options['function']
if not ids:
return JSONResponse({
"script":'',
"div":'No data found'
})
for id in ids:
try:
workouts.append(Workout.objects.get(id=id))
except Workout.DoesNotExist:
pass
if function == 'boxplot':
script, div = boxplotdata(workouts,options)
else:
script = ''
div = 'Unknown analysis functions'
return JSONResponse({
"script":script,
"div":div,
})
# Histogram for a date/time range
@user_passes_test(ispromember,login_url="/rowers/paidplans",
message="This functionality requires a Pro plan or higher",
@@ -2769,7 +3104,7 @@ def multiflex_view(request,userid=0,
options['spmmax'] = spmmax
options['workmin'] = workmin
options['workmax'] = workmax
options['ids'] = ids
options['idso'] = ids
request.session['options'] = options

View File

@@ -80,6 +80,7 @@ from rowers.forms import (
UpdateStreamForm,WorkoutMultipleCompareForm,ChartParamChoiceForm,
FusionMetricChoiceForm,BoxPlotChoiceForm,MultiFlexChoiceForm,
TrendFlexModalForm,WorkoutSplitForm,WorkoutJoinParamForm,
AnalysisOptionsForm, AnalysisChoiceForm,
PlannedSessionMultipleCloneForm,SessionDateShiftForm,
)
from rowers.models import (