Merge branch 'release/statspage'
This commit is contained in:
@@ -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))
|
||||
|
||||
@@ -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%>
|
||||
|
||||
127
rowers/templates/workoutstats.html
Normal file
127
rowers/templates/workoutstats.html
Normal 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%</td><td>{{ stats.spm.firstq|floatformat:-2 }}</td>
|
||||
</tr><tr>
|
||||
<td>Median</td><td>{{ stats.spm.median|floatformat:-2 }}</td>
|
||||
</tr><tr>
|
||||
<td>75%</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%</td><td>{{ stats.hr.firstq|floatformat:-2 }}</td>
|
||||
</tr><tr>
|
||||
<td>Median</td><td>{{ stats.hr.median|floatformat:-2 }}</td>
|
||||
</tr><tr>
|
||||
<td>75%</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%</td><td>{{ stats.power.firstq|floatformat:-2 }}</td>
|
||||
</tr><tr>
|
||||
<td>Median</td><td>{{ stats.power.median|floatformat:-2 }}</td>
|
||||
</tr><tr>
|
||||
<td>75%</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 %}
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user