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 76674819..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=""):