diff --git a/rowers/models.py b/rowers/models.py
index da30197d..04896767 100644
--- a/rowers/models.py
+++ b/rowers/models.py
@@ -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))
diff --git a/rowers/templates/workout_form.html b/rowers/templates/workout_form.html
index 27965b5e..fcae276d 100644
--- a/rowers/templates/workout_form.html
+++ b/rowers/templates/workout_form.html
@@ -8,34 +8,40 @@
{% block content %}
- {% if form.errors %}
-
- Please correct the error{{ form.errors|pluralize }} below.
-
- {% endif %}
-
-
Edit Workout Data
-
+ {% if form.errors %}
+
+ Please correct the error{{ form.errors|pluralize }} below.
+
+ {% endif %}
+
Edit Workout Data
+
+
{% localtime on %}
diff --git a/rowers/templates/workoutstats.html b/rowers/templates/workoutstats.html
new file mode 100644
index 00000000..360c0071
--- /dev/null
+++ b/rowers/templates/workoutstats.html
@@ -0,0 +1,127 @@
+{% extends "base.html" %}
+{% load staticfiles %}
+{% load rowerfilters %}
+
+{% block title %}Workout Statistics{% endblock %}
+
+{% block content %}
+
+
Workout Statistics
+
+
+
+
+
+ If your data source allows, this will show or hide strokes taken during rest intervals.
+
+
+
+
Stroke Rate
+
+
+
+ | Metric |
+ Value |
+
+
+
+
+ | Mean | {{ stats.spm.mean|floatformat:-2 }} |
+
+ | Minimum | {{ stats.spm.min|floatformat:-2 }} |
+
+ | 25% | {{ stats.spm.firstq|floatformat:-2 }} |
+
+ | Median | {{ stats.spm.median|floatformat:-2 }} |
+
+ | 75% | {{ stats.spm.thirdq|floatformat:-2 }} |
+
+ | Maximum | {{ stats.spm.max|floatformat:-2 }} |
+
+ | Standard Deviation | {{ stats.spm.std|floatformat:-2 }} |
+
+
+
+
+
+
Heart Rate
+
+
+
+ | Metric |
+ Value |
+
+
+
+
+ | Mean | {{ stats.hr.mean|floatformat:-2 }} |
+
+ | Minimum | {{ stats.hr.min|floatformat:-2 }} |
+
+ | 25% | {{ stats.hr.firstq|floatformat:-2 }} |
+
+ | Median | {{ stats.hr.median|floatformat:-2 }} |
+
+ | 75% | {{ stats.hr.thirdq|floatformat:-2 }} |
+
+ | Maximum | {{ stats.hr.max|floatformat:-2 }} |
+
+ | Standard Deviation | {{ stats.hr.std|floatformat:-2 }} |
+
+
+
+
+
+
+
Power
+
+
+
+ | Metric |
+ Value |
+
+
+
+
+ | Mean | {{ stats.power.mean|floatformat:-2 }} |
+
+ | Minimum | {{ stats.power.min|floatformat:-2 }} |
+
+ | 25% | {{ stats.power.firstq|floatformat:-2 }} |
+
+ | Median | {{ stats.power.median|floatformat:-2 }} |
+
+ | 75% | {{ stats.power.thirdq|floatformat:-2 }} |
+
+ | Maximum | {{ stats.power.max|floatformat:-2 }} |
+
+ | Standard Deviation | {{ stats.power.std|floatformat:-2 }} |
+
+
+
+
+
+
+{% endblock %}
diff --git a/rowers/urls.py b/rowers/urls.py
index 13ba7bb3..c493656a 100644
--- a/rowers/urls.py
+++ b/rowers/urls.py
@@ -154,6 +154,7 @@ urlpatterns = [
url(r'^workout/(?P\d+)/advanced/s/(?P.+.*)$',views.workout_advanced_view),
url(r'^workout/(?P\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),
diff --git a/rowers/views.py b/rowers/views.py
index 5c8e77c6..6cf816ea 100644
--- a/rowers/views.py
+++ b/rowers/views.py
@@ -2604,6 +2604,84 @@ def workout_geeky_view(request,id=0,message="",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()
def workout_advanced_view(request,id=0,message="",successmessage=""):
@@ -4596,11 +4674,15 @@ def rower_edit_view(request,message=""):
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,
})
except Rower.DoesNotExist:
@@ -4612,9 +4694,13 @@ def rower_edit_view(request,message=""):
#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=""):
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.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,
})