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

-
-
-

- Delete -

-
-
-

- Export - -

-
-
-

- Advanced -

- Advanced Functionality (More interactive Charts) - -
-
+ {% if form.errors %} +

+ Please correct the error{{ form.errors|pluralize }} below. +

+ {% endif %} +

Edit Workout Data

+
+
+

+ Delete +

+
+
+

+ Export + +

+
+
+

+ Advanced +

+ Advanced Functionality (More interactive Charts) + +
+
+
+
+

+ Statistics +

+
+
{% 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

+
+

+ Edit Workout +

+
+
+

+ Export +

+ +
+
+

+ Advanced +

+
+
+
+ {% csrf_token %} + {% if workstrokesonly == True %} + + + {% else %} + + + {% endif %} + + If your data source allows, this will show or hide strokes taken during rest intervals. +
+
+
+

Stroke Rate

+
+ + + + + + + + + + + + + + + + + + + + + + + +
MetricValue
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

+ + + + + + + + + + + + + + + + + + + + + + + + +
MetricValue
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

+ + + + + + + + + + + + + + + + + + + + + + + + +
MetricValue
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, })