Merge branch 'release/v9.83'
This commit is contained in:
+1
-1
@@ -157,7 +157,7 @@ ratelim==0.1.6
|
||||
redis==3.2.1
|
||||
requests==2.21.0
|
||||
requests-oauthlib==1.2.0
|
||||
rowingdata==2.2.7
|
||||
rowingdata==2.3.1
|
||||
rowingphysics==0.5.0
|
||||
rq==1.0
|
||||
rq-dashboard==0.4.0
|
||||
|
||||
+11
-5
@@ -221,12 +221,15 @@ def filter_df(datadf, fieldname, value, largerthan=True):
|
||||
except KeyError:
|
||||
return datadf
|
||||
|
||||
if largerthan:
|
||||
mask = datadf[fieldname] < value
|
||||
else:
|
||||
mask = datadf[fieldname] >= value
|
||||
try:
|
||||
if largerthan:
|
||||
mask = datadf[fieldname] < value
|
||||
else:
|
||||
mask = datadf[fieldname] >= value
|
||||
|
||||
datadf.loc[mask, fieldname] = np.nan
|
||||
datadf.loc[mask, fieldname] = np.nan
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
return datadf
|
||||
|
||||
@@ -2182,6 +2185,9 @@ def dataprep(rowdatadf, id=0, bands=True, barchart=True, otwpower=True,
|
||||
else:
|
||||
drivenergy = drivelength * averageforce
|
||||
|
||||
if driveenergy.mean() == 0 and driveenergy.std() == 0:
|
||||
driveenergy = 0*driveenergy+100
|
||||
|
||||
distance = rowdatadf.loc[:, 'cum_dist']
|
||||
velo = 500. / p
|
||||
|
||||
|
||||
+98
-16
@@ -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):
|
||||
@@ -810,22 +821,6 @@ class PlannedSessionMultipleCloneForm(forms.Form):
|
||||
)
|
||||
|
||||
|
||||
class BoxPlotChoiceForm(forms.Form):
|
||||
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')
|
||||
|
||||
grouplabels = axlabels.copy()
|
||||
grouplabels['date'] = 'Date'
|
||||
grouplabels['workoutid'] = 'Workout'
|
||||
@@ -842,6 +837,93 @@ from rowers.utils import palettes
|
||||
|
||||
palettechoices = tuple((p,p) for p in palettes.keys())
|
||||
|
||||
analysischoices = (
|
||||
('boxplot','Box Chart'),
|
||||
('trendflex','Trend Flex'),
|
||||
('histo','Histogram'),
|
||||
('flexall','Cumulative Flex Chart'),
|
||||
('stats','Statistics'),
|
||||
)
|
||||
|
||||
|
||||
|
||||
class AnalysisChoiceForm(forms.Form):
|
||||
axchoices = list(
|
||||
(ax[0],ax[1]) for ax in axes if ax[0] not in ['cumdist','None']
|
||||
)
|
||||
axchoices = dict((x,y) for x,y in axchoices)
|
||||
axchoices = list(sorted(axchoices.items(), key = lambda x:x[1]))
|
||||
|
||||
|
||||
yaxchoices = list((ax[0],ax[1]) for ax in axes if ax[0] not in ['cumdist','distance','time'])
|
||||
yaxchoices = dict((x,y) for x,y in yaxchoices)
|
||||
yaxchoices = list(sorted(yaxchoices.items(), key = lambda x:x[1]))
|
||||
|
||||
|
||||
yaxchoices2 = list(
|
||||
(ax[0],ax[1]) for ax in axes if ax[0] not in ['cumdist','distance','time']
|
||||
)
|
||||
yaxchoices2 = dict((x,y) for x,y in yaxchoices2)
|
||||
yaxchoices2 = list(sorted(yaxchoices2.items(), key = lambda x:x[1]))
|
||||
|
||||
function = forms.ChoiceField(choices=analysischoices,initial='boxplot',
|
||||
label='Analysis')
|
||||
xaxis = forms.ChoiceField(
|
||||
choices=axchoices,label='X-Axis',required=True,initial='spm')
|
||||
yaxis1 = forms.ChoiceField(
|
||||
choices=yaxchoices,label='Left Axis',required=True,initial='power')
|
||||
yaxis2 = forms.ChoiceField(
|
||||
choices=yaxchoices2,label='Right Axis',required=True,initial='None')
|
||||
|
||||
plotfield = forms.ChoiceField(choices=parchoices,initial='spm',
|
||||
label='Metric')
|
||||
xparam = forms.ChoiceField(choices=parchoicesmultiflex,
|
||||
initial='hr',
|
||||
label='X axis')
|
||||
yparam = forms.ChoiceField(choices=parchoicesmultiflex,
|
||||
initial='pace',
|
||||
label='Y axis')
|
||||
|
||||
groupby = forms.ChoiceField(choices=groupchoices,initial='spm',
|
||||
label='Group By')
|
||||
binsize = forms.FloatField(initial=1,required=False,label = 'Bin Size')
|
||||
|
||||
ploterrorbars = forms.BooleanField(initial=False,
|
||||
required=False,
|
||||
label='Plot Error Bars')
|
||||
|
||||
palette = forms.ChoiceField(choices=palettechoices,
|
||||
label = 'Color Scheme',
|
||||
initial='monochrome_blue')
|
||||
|
||||
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',
|
||||
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 MultiFlexChoiceForm(forms.Form):
|
||||
xparam = forms.ChoiceField(choices=parchoicesmultiflex,
|
||||
|
||||
@@ -1214,7 +1214,9 @@ def fitnessmetric_chart(fitnessmetrics,user,workoutmode='rower',startdate=None,
|
||||
|
||||
return [script,div]
|
||||
|
||||
def interactive_histoall(theworkouts,histoparam,includereststrokes):
|
||||
def interactive_histoall(theworkouts,histoparam,includereststrokes,
|
||||
spmmin=0,spmmax=55,
|
||||
workmin=0,workmax=1500):
|
||||
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
|
||||
|
||||
ids = [int(w.id) for w in theworkouts]
|
||||
@@ -1224,15 +1226,21 @@ def interactive_histoall(theworkouts,histoparam,includereststrokes):
|
||||
|
||||
rowdata.dropna(axis=0,how='any',inplace=True)
|
||||
|
||||
rowdata = dataprep.filter_df(rowdata,'spm',spmmin,largerthan=True)
|
||||
rowdata = dataprep.filter_df(rowdata,'spm',spmmax,largerthan=False)
|
||||
|
||||
rowdata = dataprep.filter_df(rowdata,'driveenergy',workmin,largerthan=True)
|
||||
rowdata = dataprep.filter_df(rowdata,'driveenergy',workmax,largerthan=False)
|
||||
|
||||
if rowdata.empty:
|
||||
return "","No Valid Data Available","",""
|
||||
return "","No Valid Data Available"
|
||||
|
||||
try:
|
||||
histopwr = rowdata[histoparam].values
|
||||
except KeyError:
|
||||
return "","No data","",""
|
||||
return "","No data"
|
||||
if len(histopwr) == 0:
|
||||
return "","No valid data available","",""
|
||||
return "","No valid data available"
|
||||
|
||||
# throw out nans
|
||||
histopwr = histopwr[~np.isinf(histopwr)]
|
||||
@@ -1341,7 +1349,6 @@ def interactive_histoall(theworkouts,histoparam,includereststrokes):
|
||||
script = ''
|
||||
div = ''
|
||||
|
||||
|
||||
return [script,div]
|
||||
|
||||
def course_map(course):
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
<p>Rower: {{ rower.user.first_name }}</p>
|
||||
|
||||
<a href="/rowers/user-analysis-select">Be adventurous and try our new Analysis page</a>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
|
||||
{% if stats %}
|
||||
<h2>Statistics</h2>
|
||||
<table width="100%" class="listtable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Metric</th>
|
||||
<th>Mean</th>
|
||||
<th>Minimum</th>
|
||||
<th>25%</th>
|
||||
<th>Median</th>
|
||||
<th>75%</th>
|
||||
<th>Maximum</th>
|
||||
<th>Standard Deviation</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, value in stats.items() %}
|
||||
<tr>
|
||||
<td>{{ value.verbosename }}</td>
|
||||
<td>{{ value.mean|floatformat }}</td>
|
||||
<td>{{ value.min|floatformat }}</td>
|
||||
<td>{{ value.firstq|floatformat }}</td>
|
||||
<td>{{ value.median|floatformat }}</td>
|
||||
<td>{{ value.thirdq|floatformat }}</td>
|
||||
<td>{{ value.max|floatformat }}</td>
|
||||
<td>{{ value.std|floatformat }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% if cordict %}
|
||||
<h2> Correlation matrix</h2>
|
||||
<p>This matrix indicates a positive (+) or negative (-) correlation between two parameters. The Spearman correlation coefficient has values between +1 and -1. Positive correlation between two metrics means that if one metric increases, the other value is also likely to increase. Negative is the opposite. The further from zero, the higher the likelyhood.
|
||||
</p>
|
||||
<table width="90%" class="cortable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
{% for key,value in cordict.items() %}
|
||||
<th class="rotate"><div><span>{{ key }}</span></div></th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, thedict in cordict.items() %}
|
||||
<tr>
|
||||
<th> {{ key }}</th>
|
||||
{% for key2,value in thedict.items() %}
|
||||
<td>
|
||||
{% if value > 0.5 %}
|
||||
<div class="poscor">{{ value|floatformat }}</div>
|
||||
{% elif value > 0.1 %}
|
||||
<div class="weakposcor">{{ value|floatformat }}</div>
|
||||
{% elif value < -0.5 %}
|
||||
<div class="negcor">{{ value|floatformat }}</div>
|
||||
{% elif value < -0.1 %}
|
||||
<div class="weaknegcor">{{ value|floatformat }}</div>
|
||||
{% else %}
|
||||
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
{% 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>
|
||||
// script for chart options form
|
||||
$(function() {
|
||||
|
||||
// Get the form fields and hidden div
|
||||
var functionfield = $("#id_function");
|
||||
var plotfield = $("#id_plotfield").parent().parent();
|
||||
var x_param = $("#id_xparam").parent().parent();
|
||||
var y_param = $("#id_yparam").parent().parent();
|
||||
|
||||
var groupby = $("#id_groupby").parent().parent();
|
||||
var binsize = $("#id_binsize").parent().parent();
|
||||
var errorbars = $("#id_ploterrorbars").parent().parent();
|
||||
var palette = $("#id_palette").parent().parent();
|
||||
var spmmin = $("#id_spmmin").parent().parent();
|
||||
var spmmax = $("#id_spmmax").parent().parent();
|
||||
var workmin = $("#id_workmin").parent().parent();
|
||||
var workmax = $("#id_workmax").parent().parent();
|
||||
|
||||
var xaxis = $("#id_xaxis").parent().parent();
|
||||
var yaxis1 = $("#id_yaxis1").parent().parent();
|
||||
var yaxis2 = $("#id_yaxis2").parent().parent();
|
||||
|
||||
// Hide the fields.
|
||||
// Use JS to do this in case the user doesn't have JS
|
||||
// enabled.
|
||||
plotfield.hide();
|
||||
x_param.hide();
|
||||
y_param.hide();
|
||||
groupby.hide();
|
||||
errorbars.hide();
|
||||
palette.hide();
|
||||
binsize.hide();
|
||||
xaxis.hide();
|
||||
yaxis1.hide();
|
||||
yaxis2.hide();
|
||||
|
||||
if (functionfield.val() == 'boxplot') {
|
||||
plotfield.show();
|
||||
};
|
||||
|
||||
if (functionfield.val() == 'histo') {
|
||||
plotfield.show()
|
||||
};
|
||||
|
||||
if (functionfield.val() == 'trendflex') {
|
||||
x_param.show();
|
||||
y_param.show();
|
||||
groupby.show();
|
||||
palette.show();
|
||||
binsize.show();
|
||||
errorbars.show();
|
||||
};
|
||||
|
||||
if (functionfield.val() == 'flexall') {
|
||||
xaxis.show();
|
||||
yaxis1.show();
|
||||
yaxis2.show();
|
||||
}
|
||||
|
||||
if (functionfield.val() == 'stats') {
|
||||
plotfield.hide();
|
||||
}
|
||||
|
||||
|
||||
// Setup an event listener for when the state of the
|
||||
// checkbox changes.
|
||||
functionfield.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 = functionfield.val();
|
||||
if (Value=='boxplot') {
|
||||
// Show the hidden fields.
|
||||
plotfield.show();
|
||||
spmmin.show();
|
||||
spmmax.show();
|
||||
workmin.show();
|
||||
workmax.show();
|
||||
x_param.hide();
|
||||
y_param.hide();
|
||||
groupby.hide();
|
||||
palette.hide();
|
||||
binsize.hide();
|
||||
errorbars.hide();
|
||||
xaxis.hide();
|
||||
yaxis1.hide();
|
||||
yaxis2.hide();
|
||||
}
|
||||
else if (Value=='histo') {
|
||||
plotfield.show();
|
||||
spmmin.show();
|
||||
spmmax.show();
|
||||
workmin.show();
|
||||
workmax.show();
|
||||
x_param.hide();
|
||||
y_param.hide();
|
||||
groupby.hide();
|
||||
palette.hide();
|
||||
binsize.hide();
|
||||
errorbars.hide();
|
||||
xaxis.hide();
|
||||
yaxis1.hide();
|
||||
yaxis2.hide();
|
||||
|
||||
}
|
||||
else if (Value=='trendflex') {
|
||||
x_param.show();
|
||||
y_param.show();
|
||||
groupby.show();
|
||||
palette.show();
|
||||
binsize.show();
|
||||
errorbars.show();
|
||||
spmmin.show();
|
||||
spmmax.show();
|
||||
workmin.show();
|
||||
workmax.show();
|
||||
plotfield.hide();
|
||||
xaxis.hide();
|
||||
yaxis1.hide();
|
||||
yaxis2.hide();
|
||||
|
||||
}
|
||||
else if (Value=='flexall') {
|
||||
xaxis.show();
|
||||
yaxis1.show();
|
||||
yaxis2.show();
|
||||
x_param.hide();
|
||||
y_param.hide();
|
||||
groupby.hide();
|
||||
spmmin.hide();
|
||||
spmmax.hide();
|
||||
workmin.hide();
|
||||
workmax.hide();
|
||||
plotfield.hide();
|
||||
palette.hide();
|
||||
binsize.hide();
|
||||
errorbars.hide();
|
||||
}
|
||||
else if (Value=='stats') {
|
||||
xaxis.hide();
|
||||
yaxis1.hide();
|
||||
yaxis2.hide();
|
||||
x_param.hide();
|
||||
y_param.hide();
|
||||
groupby.hide();
|
||||
plotfield.hide();
|
||||
palette.hide();
|
||||
binsize.hide();
|
||||
errorbars.hide();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<div id="id_css_res">
|
||||
<link rel="stylesheet" href="https://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.css" type="text/css" />
|
||||
<link rel="stylesheet" href="https://cdn.pydata.org/bokeh/release/bokeh-widgets-1.0.4.min.css" type="text/css" />
|
||||
</div>
|
||||
<div id="id_js_res">
|
||||
<script src="https://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
|
||||
<script src="https://cdn.pydata.org/bokeh/release/bokeh-widgets-1.0.4.min.js"></script>
|
||||
</div>
|
||||
|
||||
<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 %}
|
||||
BIN
Binary file not shown.
@@ -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'),
|
||||
|
||||
+674
-12
@@ -5,6 +5,663 @@ from __future__ import unicode_literals
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
from rowers.views.statements import *
|
||||
|
||||
from jinja2 import Template,Environment,FileSystemLoader
|
||||
|
||||
def floatformat(x,prec=2):
|
||||
return '{x}'.format(x=round(x,prec))
|
||||
|
||||
|
||||
env = Environment(loader = FileSystemLoader(["rowers/templates"]))
|
||||
env.filters['floatformat'] = floatformat
|
||||
|
||||
|
||||
from django.contrib.staticfiles import finders
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
options['userid'] = userid
|
||||
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':
|
||||
thediv = get_call()
|
||||
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:
|
||||
thediv = ''
|
||||
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,
|
||||
'the_div':thediv,
|
||||
'form':form,
|
||||
'active':'nav-analysis',
|
||||
'chartform':chartform,
|
||||
'searchform':searchform,
|
||||
'optionsform':optionsform,
|
||||
'teams':get_my_teams(request.user),
|
||||
})
|
||||
|
||||
def trendflexdata(workouts, options,userid=0):
|
||||
|
||||
includereststrokes = options['includereststrokes']
|
||||
palette = options['palette']
|
||||
groupby = options['groupby']
|
||||
binsize = options['binsize']
|
||||
xparam = options['xparam']
|
||||
yparam = options['yparam']
|
||||
spmmin = options['spmmin']
|
||||
spmmax = options['spmmax']
|
||||
workmin = options['workmin']
|
||||
workmax = options['workmax']
|
||||
ploterrorbars = options['ploterrorbars']
|
||||
ids = options['ids']
|
||||
workstrokesonly = not includereststrokes
|
||||
|
||||
labeldict = {
|
||||
int(w.id): w.__str__() for w in workouts
|
||||
}
|
||||
|
||||
fieldlist,fielddict = dataprep.getstatsfields()
|
||||
fieldlist = [xparam,yparam,groupby,
|
||||
'workoutid','spm','driveenergy',
|
||||
'workoutstate']
|
||||
|
||||
# prepare data frame
|
||||
datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)
|
||||
|
||||
if xparam == groupby:
|
||||
datadf['groupby'] = datadf[xparam]
|
||||
groupy = 'groupby'
|
||||
|
||||
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)
|
||||
|
||||
|
||||
datemapping = {
|
||||
w.id:w.date for w in workouts
|
||||
}
|
||||
|
||||
datadf['date'] = datadf['workoutid']
|
||||
datadf['date'].replace(datemapping,inplace=True)
|
||||
|
||||
today = datetime.date.today()
|
||||
datadf['days ago'] = map(lambda x : x.days, datadf.date - today)
|
||||
|
||||
if groupby != 'date':
|
||||
try:
|
||||
bins = np.arange(datadf[groupby].min()-binsize,
|
||||
datadf[groupby].max()+binsize,
|
||||
binsize)
|
||||
groups = datadf.groupby(pd.cut(datadf[groupby],bins,labels=False))
|
||||
except ValueError:
|
||||
messages.error(
|
||||
request,
|
||||
"Unable to compete. Probably not enough data selected"
|
||||
)
|
||||
url = reverse(user_multiflex_select)
|
||||
return HttpResponseRedirect(url)
|
||||
else:
|
||||
bins = np.arange(datadf['days ago'].min()-binsize,
|
||||
datadf['days ago'].max()+binsize,
|
||||
binsize,
|
||||
)
|
||||
groups = datadf.groupby(pd.cut(datadf['days ago'], bins,
|
||||
labels=False))
|
||||
|
||||
|
||||
xvalues = groups.mean()[xparam]
|
||||
yvalues = groups.mean()[yparam]
|
||||
xerror = groups.std()[xparam]
|
||||
yerror = groups.std()[yparam]
|
||||
groupsize = groups.count()[xparam]
|
||||
|
||||
mask = groupsize <= min([0.01*groupsize.sum(),0.2*groupsize.mean()])
|
||||
xvalues.loc[mask] = np.nan
|
||||
|
||||
yvalues.loc[mask] = np.nan
|
||||
xerror.loc[mask] = np.nan
|
||||
yerror.loc[mask] = np.nan
|
||||
groupsize.loc[mask] = np.nan
|
||||
|
||||
xvalues.dropna(inplace=True)
|
||||
yvalues.dropna(inplace=True)
|
||||
xerror.dropna(inplace=True)
|
||||
yerror.dropna(inplace=True)
|
||||
groupsize.dropna(inplace=True)
|
||||
|
||||
if len(groupsize) == 0:
|
||||
messages.error(request,'No data in selection')
|
||||
url = reverse(user_multiflex_select)
|
||||
return HttpResponseRedirect(url)
|
||||
else:
|
||||
groupsize = 30.*np.sqrt(groupsize/float(groupsize.max()))
|
||||
|
||||
df = pd.DataFrame({
|
||||
xparam:xvalues,
|
||||
yparam:yvalues,
|
||||
'x':xvalues,
|
||||
'y':yvalues,
|
||||
'xerror':xerror,
|
||||
'yerror':yerror,
|
||||
'groupsize':groupsize,
|
||||
})
|
||||
|
||||
|
||||
if yparam == 'pace':
|
||||
df['y'] = dataprep.paceformatsecs(df['y']/1.0e3)
|
||||
|
||||
aantal = len(df)
|
||||
|
||||
if groupby != 'date':
|
||||
try:
|
||||
df['groupval'] = groups.mean()[groupby]
|
||||
df['groupval'].loc[mask] = np.nan
|
||||
|
||||
groupcols = df['groupval']
|
||||
except ValueError:
|
||||
df['groupval'] = groups.mean()[groupby].fillna(value=0)
|
||||
df['groupval'].loc[mask] = np.nan
|
||||
groupcols = df['groupval']
|
||||
except KeyError:
|
||||
messages.error(request,'Data selection error')
|
||||
url = reverse(user_multiflex_select)
|
||||
return HttpResponseRedirect(url)
|
||||
else:
|
||||
try:
|
||||
dates = groups.min()[groupby]
|
||||
dates.loc[mask] = np.nan
|
||||
dates.dropna(inplace=True)
|
||||
df['groupval'] = [x.strftime("%Y-%m-%d") for x in dates]
|
||||
df['groupval'].loc[mask] = np.nan
|
||||
groupcols = 100.*np.arange(aantal)/float(aantal)
|
||||
except AttributeError:
|
||||
df['groupval'] = groups.mean()['days ago'].fillna(value=0)
|
||||
groupcols = 100.*np.arange(aantal)/float(aantal)
|
||||
|
||||
|
||||
groupcols = (groupcols-groupcols.min())/(groupcols.max()-groupcols.min())
|
||||
|
||||
if aantal == 1:
|
||||
groupcols = np.array([1.])
|
||||
|
||||
|
||||
colors = range_to_color_hex(groupcols,palette=palette)
|
||||
|
||||
df['color'] = colors
|
||||
|
||||
clegendx = np.arange(0,1.2,.2)
|
||||
legcolors = range_to_color_hex(clegendx,palette=palette)
|
||||
if groupby != 'date':
|
||||
clegendy = df['groupval'].min()+clegendx*(df['groupval'].max()-df['groupval'].min())
|
||||
else:
|
||||
clegendy = df.index.min()+clegendx*(df.index.max()-df.index.min())
|
||||
|
||||
|
||||
|
||||
colorlegend = zip(range(6),clegendy,legcolors)
|
||||
|
||||
|
||||
if userid == 0:
|
||||
extratitle = ''
|
||||
else:
|
||||
u = User.objects.get(id=userid)
|
||||
extratitle = ' '+u.first_name+' '+u.last_name
|
||||
|
||||
|
||||
|
||||
script,div = interactive_multiflex(df,xparam,yparam,
|
||||
groupby,
|
||||
extratitle=extratitle,
|
||||
ploterrorbars=ploterrorbars,
|
||||
binsize=binsize,
|
||||
colorlegend=colorlegend,
|
||||
spmmin=spmmin,spmmax=spmmax,
|
||||
workmin=workmin,workmax=workmax)
|
||||
|
||||
scripta= script.split('\n')[2:-1]
|
||||
script = ''.join(scripta)
|
||||
|
||||
return(script,div)
|
||||
|
||||
def flexalldata(workouts, options):
|
||||
includereststrokes = options['includereststrokes']
|
||||
xparam = options['xaxis']
|
||||
yparam1 = options['yaxis1']
|
||||
yparam2 = options['yaxis2']
|
||||
promember=True
|
||||
|
||||
workstrokesonly = not includereststrokes
|
||||
|
||||
res = interactive_cum_flex_chart2(workouts, xparam=xparam,
|
||||
yparam1=yparam1,
|
||||
yparam2=yparam2,
|
||||
promember=promember,
|
||||
workstrokesonly=workstrokesonly,
|
||||
)
|
||||
script = res[0]
|
||||
div = res[1]
|
||||
|
||||
scripta = script.split('\n')[2:-1]
|
||||
script = ''.join(scripta)
|
||||
|
||||
return(script,div)
|
||||
|
||||
def histodata(workouts, options):
|
||||
includereststrokes = options['includereststrokes']
|
||||
plotfield = options['plotfield']
|
||||
function = options['function']
|
||||
spmmin = options['spmmin']
|
||||
spmmax = options['spmmax']
|
||||
workmin = options['workmin']
|
||||
workmax = options['workmax']
|
||||
|
||||
|
||||
workstrokesonly = not includereststrokes
|
||||
|
||||
script, div = interactive_histoall(workouts,plotfield,includereststrokes,
|
||||
spmmin=spmmin,spmmax=spmmax,workmin=workmin,workmax=workmax)
|
||||
|
||||
|
||||
scripta = script.split('\n')[2:-1]
|
||||
script = ''.join(scripta)
|
||||
|
||||
return(script,div)
|
||||
|
||||
def statsdata(workouts, options):
|
||||
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']
|
||||
|
||||
workstrokesonly = not includereststrokes
|
||||
|
||||
ids = [w.id for w in workouts]
|
||||
|
||||
datamapping = {
|
||||
w.id:w.date for w in workouts
|
||||
}
|
||||
|
||||
fieldlist,fielddict = dataprep.getstatsfields()
|
||||
|
||||
# prepare data frame
|
||||
datadf,extracols = dataprep.read_cols_df_sql(ids,fieldlist)
|
||||
|
||||
datadf = dataprep.clean_df_stats(datadf,workstrokesonly=workstrokesonly)
|
||||
|
||||
# Create stats
|
||||
stats = {}
|
||||
fielddict.pop('workoutstate')
|
||||
fielddict.pop('workoutid')
|
||||
|
||||
for field,verbosename in fielddict.items():
|
||||
thedict = {
|
||||
'mean':datadf[field].mean(),
|
||||
'min': datadf[field].min(),
|
||||
'std': datadf[field].std(),
|
||||
'max': datadf[field].max(),
|
||||
'median': datadf[field].median(),
|
||||
'firstq':datadf[field].quantile(q=0.25),
|
||||
'thirdq':datadf[field].quantile(q=0.75),
|
||||
'verbosename':verbosename,
|
||||
}
|
||||
stats[field] = thedict
|
||||
|
||||
# Create a dict with correlation values
|
||||
cor = datadf.corr(method='spearman')
|
||||
cor.fillna(value=0,inplace=True)
|
||||
cordict = {}
|
||||
for field1,verbosename in fielddict.items():
|
||||
thedict = {}
|
||||
for field2,verbosename in fielddict.items():
|
||||
try:
|
||||
thedict[field2] = cor.loc[field1,field2]
|
||||
except KeyError:
|
||||
thedict[field2] = 0
|
||||
|
||||
cordict[field1] = thedict
|
||||
|
||||
context = {
|
||||
'stats':stats,
|
||||
'cordict':cordict,
|
||||
}
|
||||
|
||||
htmly = env.get_template('statsdiv.html')
|
||||
html_content = htmly.render(context)
|
||||
|
||||
return('',html_content)
|
||||
|
||||
def boxplotdata(workouts,options):
|
||||
|
||||
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']
|
||||
|
||||
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)
|
||||
elif function == 'trendflex':
|
||||
script, div = trendflexdata(workouts, options,userid=userid)
|
||||
elif function == 'histo':
|
||||
script, div = histodata(workouts, options)
|
||||
elif function == 'flexall':
|
||||
script,div = flexalldata(workouts,options)
|
||||
elif function == 'stats':
|
||||
script,div = statsdata(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",
|
||||
@@ -2412,6 +3069,8 @@ def multiflex_data(request,userid=0,
|
||||
'ploterrorbars':False,
|
||||
}):
|
||||
|
||||
def_options = options
|
||||
|
||||
if 'options' in request.session:
|
||||
options = request.session['options']
|
||||
|
||||
@@ -2436,16 +3095,16 @@ def multiflex_data(request,userid=0,
|
||||
userid = request.user.id
|
||||
|
||||
|
||||
palette = options['palette']
|
||||
groupby = options['groupby']
|
||||
binsize = options['binsize']
|
||||
xparam = options['xparam']
|
||||
yparam = options['yparam']
|
||||
spmmin = options['spmmin']
|
||||
spmmax = options['spmmax']
|
||||
workmin = options['workmin']
|
||||
workmax = options['workmax']
|
||||
ids = options['ids']
|
||||
palette = keyvalue_get_default('palette',options, def_options)
|
||||
groupby = keyvalue_get_default('groupby',options, def_options)
|
||||
binsize = keyvalue_get_default('binsize',options, def_options)
|
||||
xparam = keyvalue_get_default('xparam',options, def_options)
|
||||
yparam = keyvalue_get_default('yparam',options, def_options)
|
||||
spmmin = keyvalue_get_default('spmmin',options, def_options)
|
||||
spmmax = keyvalue_get_default('spmmax',options, def_options)
|
||||
workmin = keyvalue_get_default('workmin',options, def_options)
|
||||
workmax = keyvalue_get_default('workmax',options, def_options)
|
||||
ids = keyvalue_get_default('ids',options, def_options)
|
||||
|
||||
workouts = []
|
||||
|
||||
@@ -2496,7 +3155,10 @@ def multiflex_data(request,userid=0,
|
||||
datadf['date'].replace(datemapping,inplace=True)
|
||||
|
||||
today = datetime.date.today()
|
||||
datadf['days ago'] = map(lambda x : x.days, datadf.date - today)
|
||||
try:
|
||||
datadf['days ago'] = map(lambda x : x.days, datadf.date - today)
|
||||
except TypeError:
|
||||
datadf['days ago'] = 0
|
||||
|
||||
if groupby != 'date':
|
||||
try:
|
||||
@@ -2769,7 +3431,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
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -1798,11 +1798,12 @@ def workout_downloadwind_view(request,id=0,
|
||||
windspeed = winddata[0]
|
||||
windbearing = winddata[1]
|
||||
message = winddata[2]
|
||||
try:
|
||||
row.notes += "\n"+message
|
||||
except TypeError:
|
||||
if message and row.notes:
|
||||
row.notes += message
|
||||
if message is not None:
|
||||
try:
|
||||
row.notes += "\n"+message
|
||||
except TypeError:
|
||||
if message is not None and row.notes is not None:
|
||||
row.notes += message
|
||||
|
||||
row.save()
|
||||
rowdata.add_wind(windspeed,windbearing)
|
||||
|
||||
Reference in New Issue
Block a user