Private
Public Access
1
0

Merge branch 'release/statspage'

This commit is contained in:
Sander Roosendaal
2017-02-05 21:39:09 +01:00
5 changed files with 263 additions and 28 deletions

View File

@@ -53,7 +53,7 @@ if settings.DEBUG or user=='':
# model for Power Zone names
class PowerZonesField(models.TextField):
__metaclass__ = models.SubfieldBase
# __metaclass__ = models.SubfieldBase
def __init__(self, *args, **kwargs):
self.token = kwargs.pop('token',',')
@@ -65,6 +65,13 @@ class PowerZonesField(models.TextField):
return value
return value.split(self.token)
def from_db_value(self,value, expression, connection, context):
if value is None:
return value
if isinstance(value, list):
return value
return value.split(self.token)
def get_db_prep_value(self, value, connection, prepared=False):
if not value: return
assert(isinstance(value, list) or isinstance(value, tuple))

View File

@@ -8,34 +8,40 @@
{% block content %}
<div id="workouts" class="grid_6 alpha">
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
</p>
{% endif %}
<h1>Edit Workout Data</h1>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
<a class="button red small" href="/rowers/workout/{{ workout.id }}/deleteconfirm">Delete</a>
</p>
</div>
<div class="grid_2">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/export">Export</a>
</p>
</div>
<div class="grid_2 omega tooltip">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/advanced">Advanced</a>
</p>
<span class="tooltiptext">Advanced Functionality (More interactive Charts)</span>
</div>
</div>
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
</p>
{% endif %}
<h1>Edit Workout Data</h1>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
<a class="button red small" href="/rowers/workout/{{ workout.id }}/deleteconfirm">Delete</a>
</p>
</div>
<div class="grid_2">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/export">Export</a>
</p>
</div>
<div class="grid_2 omega tooltip">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/advanced">Advanced</a>
</p>
<span class="tooltiptext">Advanced Functionality (More interactive Charts)</span>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 prefix_4 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/stats">Statistics</a>
</p>
</div>
</div>
{% localtime on %}
<table width=100%>

View File

@@ -0,0 +1,127 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}Workout Statistics{% endblock %}
{% block content %}
<div class="grid_12 alpha">
<h1>Workout Statistics</h1>
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/export">Export</a>
</p>
</div>
<div class="grid_2">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/advanced">Advanced</a>
</p>
</div>
<div class="grid_2 omega suffix_4 tooltip">
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
{% csrf_token %}
{% if workstrokesonly == True %}
<input class="grid_2 alpha button blue small" type="hidden" name="workstrokesonly" value="False">
<input class="grid_2 alpha button blue small" value="Include Rest Strokes" type="Submit">
{% else %}
<input class="grid_2 alpha button blue small" type="hidden" name="workstrokesonly" value="True">
<input class="grid_2 alpha button blue small" value="Remove Rest Strokes" type="Submit">
{% endif %}
</form>
<span class="tooltiptext">If your data source allows, this will show or hide strokes taken during rest intervals.</span>
</div>
</div>
<div class="grid_6 alpha">
<h2>Stroke Rate</h2>
<table width="100%" class="listtable">
<thead>
<tr>
<th>Metric</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Mean</td><td>{{ stats.spm.mean|floatformat:-2 }}</td>
</tr><tr>
<td>Minimum</td><td>{{ stats.spm.min|floatformat:-2 }}</td>
</tr><tr>
<td>25&#37;</td><td>{{ stats.spm.firstq|floatformat:-2 }}</td>
</tr><tr>
<td>Median</td><td>{{ stats.spm.median|floatformat:-2 }}</td>
</tr><tr>
<td>75&#37;</td><td>{{ stats.spm.thirdq|floatformat:-2 }}</td>
</tr><tr>
<td>Maximum</td><td>{{ stats.spm.max|floatformat:-2 }}</td>
</tr><tr>
<td>Standard Deviation</td><td>{{ stats.spm.std|floatformat:-2 }}</td>
</tr>
</tbody>
</table>
</div>
<div class="grid_6 alpha">
<h2>Heart Rate</h2>
<table width="100%" class="listtable">
<thead>
<tr>
<th>Metric</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Mean</td><td>{{ stats.hr.mean|floatformat:-2 }}</td>
</tr><tr>
<td>Minimum</td><td>{{ stats.hr.min|floatformat:-2 }}</td>
</tr><tr>
<td>25&#37;</td><td>{{ stats.hr.firstq|floatformat:-2 }}</td>
</tr><tr>
<td>Median</td><td>{{ stats.hr.median|floatformat:-2 }}</td>
</tr><tr>
<td>75&#37;</td><td>{{ stats.hr.thirdq|floatformat:-2 }}</td>
</tr><tr>
<td>Maximum</td><td>{{ stats.hr.max|floatformat:-2 }}</td>
</tr><tr>
<td>Standard Deviation</td><td>{{ stats.hr.std|floatformat:-2 }}</td>
</tr>
</tbody>
</table>
</div>
<div class="grid_6 omega">
<div class="grid_6 alpha">
<h2>Power</h2>
<table width="100%" class="listtable">
<thead>
<tr>
<th>Metric</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Mean</td><td>{{ stats.power.mean|floatformat:-2 }}</td>
</tr><tr>
<td>Minimum</td><td>{{ stats.power.min|floatformat:-2 }}</td>
</tr><tr>
<td>25&#37;</td><td>{{ stats.power.firstq|floatformat:-2 }}</td>
</tr><tr>
<td>Median</td><td>{{ stats.power.median|floatformat:-2 }}</td>
</tr><tr>
<td>75&#37;</td><td>{{ stats.power.thirdq|floatformat:-2 }}</td>
</tr><tr>
<td>Maximum</td><td>{{ stats.power.max|floatformat:-2 }}</td>
</tr><tr>
<td>Standard Deviation</td><td>{{ stats.power.std|floatformat:-2 }}</td>
</tr>
</tbody>
</table>
</div>
</div>
{% endblock %}

View File

@@ -154,6 +154,7 @@ urlpatterns = [
url(r'^workout/(?P<id>\d+)/advanced/s/(?P<successmessage>.+.*)$',views.workout_advanced_view),
url(r'^workout/(?P<id>\d+)/geeky$',views.workout_geeky_view),
url(r'^workout/(\d+)/advanced$',views.workout_advanced_view),
url(r'^workout/(\d+)/stats$',views.workout_stats_view),
url(r'^workout/(\d+)/otwsetpower$',views.workout_otwsetpower_view),
url(r'^workout/(\d+)/interactiveotwplot$',views.workout_otwpowerplot_view),
url(r'^workout/(\d+)/wind$',views.workout_wind_view),

View File

@@ -2604,6 +2604,84 @@ def workout_geeky_view(request,id=0,message="",successmessage=""):
'successmessage': successmessage,
'interactiveplot':script,
'the_div':div})
# Stats page
@login_required()
def workout_stats_view(request,id=0,message="",successmessage=""):
workstrokesonly = True
if request.method == 'POST' and 'workstrokesonly' in request.POST:
workstrokesonly = request.POST['workstrokesonly']
row = Workout.objects.get(id=id)
if (checkworkoutuser(request.user,row)==False):
message = "You are not allowed to see the stats of this workout"
url = reverse(workouts_view,args=[str(message)])
return HttpResponseRedirect(url)
columns = ['hr','spm','power','workoutstate']
datadf = dataprep.getsmallrowdata_db(columns,ids=[id])
if datadf.empty:
return HttpResponse("CSV data file not found")
workoutstateswork = [1,4,5,8,9,6,7]
workoutstatesrest = [3]
workoutstatetransition = [0,2,10,11,12,13]
if workstrokesonly=='True' or workstrokesonly==True:
try:
datadf = datadf[~datadf['workoutstate'].isin(workoutstatesrest)]
except:
pass
workstrokesonly = True
stats = {}
# SPM
spmdict = {
'mean':datadf['spm'].mean(),
'max': datadf['spm'].max(),
'min': datadf['spm'].min(),
'std': datadf['spm'].std(),
'median': datadf['spm'].median(),
'firstq':datadf['spm'].quantile(q=0.25),
'thirdq':datadf['spm'].quantile(q=0.75),
}
stats['spm'] = spmdict
# HR
hrdict = {
'mean':datadf['hr'].mean(),
'max': datadf['hr'].max(),
'min': datadf['hr'].min(),
'std': datadf['hr'].std(),
'median': datadf['hr'].median(),
'firstq':datadf['hr'].quantile(q=0.25),
'thirdq':datadf['hr'].quantile(q=0.75),
}
stats['hr'] = hrdict
# Power
powerdict = {
'mean':datadf['power'].mean(),
'max': datadf['power'].max(),
'min': datadf['power'].min(),
'std': datadf['power'].std(),
'median': datadf['power'].median(),
'firstq':datadf['power'].quantile(q=0.25),
'thirdq':datadf['power'].quantile(q=0.75),
}
stats['power'] = powerdict
return render(request,
'workoutstats.html',
{
'stats':stats,
'workout':row,
'workstrokesonly':workstrokesonly,
})
# The Advanced edit page
@login_required()
@@ -4596,11 +4674,15 @@ def rower_edit_view(request,message=""):
successmessage = "Your Heart Rate data were changed"
form = RowerForm(instance=r)
powerform = RowerPowerForm(instance=r)
powerzonesform = RowerPowerZonesForm(instance=r)
accountform = AccountRowerForm(instance=r)
userform = UserForm(instance=request.user)
return render(request, 'rower_form.html',
{'form':form,
'powerzonesform':powerzonesform,
'powerform':powerform,
'rower':r,
'accountform':accountform,
'userform':userform,
'successmessage':successmessage,
})
@@ -4612,9 +4694,13 @@ def rower_edit_view(request,message=""):
message = HttpResponse("invalid form")
#form = RowerForm(instance=r)
powerform = RowerPowerForm(instance=r)
powerzonesform = RowerPowerZonesForm(instance=r)
userform = UserForm(instance=request.user)
accountform = AccountRowerForm(instance=r)
return render(request, 'rower_form.html',
{'form':form,
'powerzonesform':powerzonesform,
'userform':userform,
'accountform':accountform,
'powerform':powerform,
'rower':r,
@@ -4654,10 +4740,14 @@ def rower_edit_view(request,message=""):
message = HttpResponse("invalid form")
form = RowerForm(instance=r)
#powerform = RowerPowerForm(instance=r)
powerzonesform = RowerPowerZonesForm(instance=r)
userform = UserForm(instance=request.user)
accountform = AccountRowerForm(instance=r)
return render(request, 'rower_form.html',
{'form':form,
'powerform':powerform,
'rower':r,
'userform':userform,
'accountform':accountform,
})
@@ -4689,12 +4779,16 @@ def rower_edit_view(request,message=""):
r.powerzones = powerzones
r.save()
successmessage = "Your Power Zone data were changed"
form = RowerForm(instance=r)
accountform = AccountRowerForm(instance=r)
userform = UserForm(instance=request.user)
powerform = RowerPowerForm(instance=r)
powerzonesform = RowerPowerZonesForm(instance=r)
return render(request, 'rower_form.html',
{'form':form,
'powerzonesform':powerzonesform,
'powerform':powerform,
'userform':userform,
'accountform':accountform,
'rower':r,
'successmessage':successmessage,