Private
Public Access
1
0

Merge branch 'feature/boxchart' into develop

This commit is contained in:
Sander Roosendaal
2017-05-12 19:12:03 +02:00
6 changed files with 109 additions and 9 deletions
+13 -1
View File
@@ -272,7 +272,7 @@ def getstatsfields():
fielddict = {field.name:field.verbose_name for field in fields}
fielddict.pop('workoutid')
#fielddict.pop('workoutid')
fielddict.pop('ergpace')
fielddict.pop('hr_an')
fielddict.pop('hr_tr')
@@ -968,6 +968,12 @@ def read_cols_df_sql(ids,columns):
connection = engine.raw_connection()
df = pd.read_sql_query(query,engine)
df = df.fillna(value=0)
try:
df['peakforce'] = df['peakforce']*lbstoN
df['averageforce'] = df['averageforce']*lbstoN
except KeyError:
pass
engine.dispose()
return df
@@ -980,6 +986,12 @@ def read_df_sql(id):
engine.dispose()
df = df.fillna(value=0)
try:
df['peakforce'] = df['peakforce']*lbstoN
df['averageforce'] = df['averageforce']*lbstoN
except KeyError:
pass
return df
# Get the necessary data from the strokedata table in the DB.
+30 -2
View File
@@ -4,14 +4,14 @@ from rowingdata import main as rmain
from rowingdata import cumcpdata,histodata
from rowingdata import rowingdata as rrdata
from math import pi
from django.utils import timezone
from bokeh.palettes import Dark2_8 as palette
import itertools
from bokeh.plotting import figure, ColumnDataSource, Figure,curdoc
from bokeh.models import CustomJS,Slider, TextInput
from bokeh.charts import Histogram,HeatMap
from bokeh.charts import Histogram,HeatMap,Area,BoxPlot
from bokeh.resources import CDN,INLINE
from bokeh.embed import components
from bokeh.layouts import layout,widgetbox
@@ -87,6 +87,34 @@ def tailwind(bearing,vwind,winddir):
from rowers.dataprep import nicepaceformat,niceformat
from rowers.dataprep import timedeltaconv
def interactive_boxchart(datadf,fieldname):
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,resize'
plot = BoxPlot(datadf, values=fieldname, label='date',
legend=False,
title=axlabels[fieldname],
outliers=False,
tools=TOOLS,
x_mapper_type='datetime')
yrange1 = Range1d(start=yaxminima[fieldname],end=yaxmaxima[fieldname])
plot.y_range = yrange1
plot.xaxis.axis_label = 'Date'
plot.yaxis.axis_label = axlabels[fieldname]
plot.xaxis.formatter = DatetimeTickFormatter(
days=["%d %B %Y"],
months=["%d %B %Y"],
years=["%d %B %Y"],
)
plot.xaxis.major_label_orientation = pi/4
script, div = components(plot)
return script,div
def interactive_forcecurve(theworkouts,workstrokesonly=False):
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
+44 -3
View File
@@ -5,6 +5,35 @@
{% block title %}Workout Statistics{% endblock %}
{% block content %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ plotscript |safe }}
<script>
// Set things up to resize the plot on a window resize. You can play with
// the arguments of resize_width_height() to change the plot's behavior.
var plot_resize_setup = function () {
var plotid = Object.keys(Bokeh.index)[0]; // assume we have just one plot
var plot = Bokeh.index[plotid];
var plotresizer = function() {
// arguments: use width, use height, maintain aspect ratio
plot.resize_width_height(true, true, true);
};
window.addEventListener('resize', plotresizer);
plotresizer();
};
window.addEventListener('load', plot_resize_setup);
</script>
<style>
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
html, body {height: 100%; margin:5px;}
</style>
<div class="grid_12 alpha">
<h1>Workout Statistics</h1>
<p>
@@ -18,7 +47,7 @@
between {{ startdate|date }} and {{ enddate|date }}</p>
<p>Direct link for other Pro users:
<a href="/rowers/{{ id }}/cumstats/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}">https://rowsandall.com/rowers/{{ id }}/cumstats/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}</a>
<a href="/rowers/{{ id }}/cumstats/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}/p/{{ plotfield }}">https://rowsandall.com/rowers/{{ id }}/cumstats/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}/p/{{ plotfield }}</a>
</p>
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
@@ -66,7 +95,13 @@
<div class="grid_4 alpha">
{% if stats %}
{% for key, value in stats.items %}
<h2>{{ value.verbosename }}</h2>
<h2>{{ value.verbosename }}</h2>
<div class="grid_1">
<p>
<a class="button blue small" href="/rowers/{{ id }}/cumstats/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}/p/{{ key }}">Plot</a>
</p>
</div>
<table width="100%" class="listtable">
<thead>
<tr>
@@ -97,6 +132,7 @@
</div>
<div class="grid_8 omega">
{% if cordict %}
<div class="grid_8">
<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>
@@ -132,8 +168,13 @@
{% endfor %}
</tbody>
</table>
<div class="grid_8">
{% endif %}
</div>
<div class="grid_8">
{{ plotdiv|safe }}
</div>
</div>
</div>
{% endblock %}
+1 -1
View File
@@ -51,4 +51,4 @@
</div>
{% endblock %}
{% endblock %}
+2
View File
@@ -141,6 +141,8 @@ urlpatterns = [
url(r'^histo/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.histo),
url(r'^histo/(?P<deltadays>\d+)$',views.histo),
url(r'^histo/$',views.histo),
url(r'^cumstats/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)/p/(?P<plotfield>\w+.*)$',views.cumstats),
url(r'^(?P<theuser>\d+)/cumstats/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)/p/(?P<plotfield>\w+.*)$',views.cumstats),
url(r'^(?P<theuser>\d+)/cumstats/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.cumstats),
url(r'^(?P<theuser>\d+)/cumstats/(?P<deltadays>\d+)$',views.cumstats),
url(r'^cumstats/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.cumstats),
+19 -2
View File
@@ -3834,11 +3834,12 @@ def workout_geeky_view(request,id=0,message="",successmessage=""):
# Cumulative stats page
@login_required()
def cumstats(request,theuser=0,
startdate=timezone.now()-datetime.timedelta(days=365),
startdate=timezone.now()-datetime.timedelta(days=30),
enddate=timezone.now(),
deltadays=-1,
startdatestring="",
enddatestring="",
plotfield='spm',
options={
'includereststrokes':False,
'workouttypes':['rower','dynamic','slides']
@@ -3957,6 +3958,12 @@ def cumstats(request,theuser=0,
ids = [int(workout.id) for workout in allergworkouts]
datemapping = {
w.id:w.date for w in allergworkouts
}
fieldlist,fielddict = dataprep.getstatsfields()
# prepare data frame
@@ -3972,6 +3979,7 @@ def cumstats(request,theuser=0,
# Create stats
stats = {}
fielddict.pop('workoutstate')
fielddict.pop('workoutid')
for field,verbosename in fielddict.iteritems():
thedict = {
@@ -3998,8 +4006,14 @@ def cumstats(request,theuser=0,
except KeyError:
thedict[field2] = 0
cordict[field1] = thedict
cordict[field1] = thedict
# interactive box plot
datadf['workoutid'].replace(datemapping,inplace=True)
datadf.rename(columns={"workoutid":"date"},inplace=True)
datadf = datadf.sort_values(['date'])
script,div = interactive_boxchart(datadf,plotfield)
# set options form correctly
initial = {}
initial['includereststrokes'] = includereststrokes
@@ -4027,6 +4041,9 @@ def cumstats(request,theuser=0,
'deltaform':deltaform,
'optionsform':optionsform,
'cordict':cordict,
'plotscript':script,
'plotdiv':div,
'plotfield':plotfield,
})
request.session['options'] = options