From 53affc41e202339bb7b5e2270f5340dc98a21006 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 5 Sep 2018 22:19:18 +0200 Subject: [PATCH 1/3] better modality form in multi compare --- rowers/forms.py | 2 +- rowers/views.py | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/rowers/forms.py b/rowers/forms.py index 1ad57343..2bb383e7 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -540,7 +540,7 @@ class TrendFlexModalForm(forms.Form): initial = types.waterboattype) rankingonly = forms.BooleanField(initial=False, label='Only Ranking Pieces', - required=True) + required=False) # This form sets options for the summary stats page diff --git a/rowers/views.py b/rowers/views.py index c77ebef0..09f32763 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -5112,6 +5112,10 @@ def team_comparison_select(request, else: waterboattype = types.waterboattype + if 'rankingonly' in request.session: + rankingonly = request.session['rankingonly'] + else: + rankingonly = False if 'modalities' in request.session: modalities = request.session['modalities'] @@ -5130,8 +5134,6 @@ def team_comparison_select(request, enddate = dateform.cleaned_data['enddate'] startdatestring = startdate.strftime('%Y-%m-%d') enddatestring = enddate.strftime('%Y-%m-%d') - request.session['startdate'] = startdatestring - request.session['enddate'] = enddatestring else: dateform = DateRangeForm(initial={ 'startdate':startdate, @@ -5152,6 +5154,11 @@ def team_comparison_select(request, if modality != 'water': waterboattype = [b[0] for b in types.boattypes] + + if 'rankingonly' in modalityform.cleaned_data: + rankingonly = modalityform.cleaned_data['rankingonly'] + else: + rankingonly = False request.session['modalities'] = modalities request.session['waterboattype'] = waterboattype @@ -5203,6 +5210,9 @@ def team_comparison_select(request, startdatetime__lte=enddate, workouttype__in=modalities).order_by("-date", "-starttime").exclude(boattype__in=negtypes) + if rankingonly: + workouts = [w for w in workouts if w.rankingpiece] + query = request.GET.get('q') if query: query_list = query.split() @@ -5224,7 +5234,8 @@ def team_comparison_select(request, chartform = ChartParamChoiceForm(initial={'teamid':0}) modalityform = TrendFlexModalForm(initial={ 'modality':modality, - 'waterboattype':waterboattype + 'waterboattype':waterboattype, + 'rankingonly':rankingonly, }) From de4df4473cdd994f41230a9337282690319fd353 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 6 Sep 2018 12:29:36 +0200 Subject: [PATCH 2/3] reformatting stats page --- rowers/templates/workoutstats.html | 135 +++++++++++++++-------------- 1 file changed, 68 insertions(+), 67 deletions(-) diff --git a/rowers/templates/workoutstats.html b/rowers/templates/workoutstats.html index 3b1fbc5b..4c5f8644 100644 --- a/rowers/templates/workoutstats.html +++ b/rowers/templates/workoutstats.html @@ -8,7 +8,7 @@

Workout Statistics for {{ workout.name }}

- This is an experimental page which just lists a bunch of statistics for + This page lists a bunch of statistics for your workout. The mean is of a metric is the mean with equal weight for each stroke. The time weighted mean takes into account the stroke duration. @@ -43,76 +43,77 @@ If your data source allows, this will show or hide strokes taken during rest intervals.

-
-{% if stats %} -{% for key, value in stats.items %} -

{{ value.verbosename }}

- - - - - - - - - - - - - - - - - - - - - - - - - - -
MetricValue
Mean{{ value.mean|floatformat:-2 }}
Time Weighted Mean {{ value.wmean|floatformat:-2 }}
Minimum{{ value.min|floatformat:-2 }}
25%{{ value.firstq|floatformat:-2 }}
Median{{ value.median|floatformat:-2 }}
75%{{ value.thirdq|floatformat:-2 }}
Maximum{{ value.max|floatformat:-2 }}
Standard Deviation{{ value.std|floatformat:-2 }}
-{% endfor %} -{% endif %} -
-
+ +
+ +

rPower: Equivalent steady state power for the duration of the workout.

+

Heart Rate Drift: Comparing heart rate normalized for average power for the first and second half of the workout

+

TRIMP: TRaining IMPact. A way to combine duration and heart rate into a single number.

+

rScore: Score based on rPower and workout duration to estimate training effect

+

rScore (HR): Score based on heart rate, designed to give values comparable to rScore. Used instead of rScore for workouts without power data.

+
{% if otherstats %} -
-   -
-
- -

rPower: Equivalent steady state power for the duration of the workout.

-

Heart Rate Drift: Comparing heart rate normalized for average power for the first and second half of the workout

-

TRIMP: TRaining IMPact. A way to combine duration and heart rate into a single number.

-

rScore: Score based on rPower and workout duration to estimate training effect

-

rScore (HR): Score based on heart rate, designed to give values comparable to rScore. Used instead of rScore for workouts without power data.

-
-

Other Stats

- - - - - - - - - - {% for key, value in otherstats.items %} - - - - - - {% endfor %} - -
MetricValueUnit
{{ value.verbose_name }}{{ value.value }}{{ value.unit }}
+
+

Workout Metrics

+ + + + + + + + + + {% for key, value in otherstats.items %} + + + + + + {% endfor %} + +
MetricValueUnit
{{ value.verbose_name }}{{ value.value }}{{ value.unit }}
{% endif %} +
-
+
+ {% if stats %} +

Statistics

+ + + + + + + + + + + + + + + + {% for key, value in stats.items %} + + + + + + + + + + + + {% endfor %} + +
MetricMeanTime Weighted MeanMinimum25%Median75%MaximumStandard Deviation
{{ value.verbosename }}{{ value.mean|floatformat:-2 }} {{ value.wmean|floatformat:-2 }}{{ value.min|floatformat:-2 }}{{ value.firstq|floatformat:-2 }}{{ value.median|floatformat:-2 }}{{ value.thirdq|floatformat:-2 }}{{ value.max|floatformat:-2 }}{{ value.std|floatformat:-2 }}
+ {% endif %} +
+ +
{% if cordict %}

Correlation matrix

This matrix indicates a positive (+) or negative (-) correlation between two parameters. The Spearman correlation coefficient has values between +1 and -1. Positive correlation between two metrics means that if one metric increases, the other value is also likely to increase. Negative is the opposite. The further from zero, the higher the likelyhood. From 1567b24900026f57a10d4927d1944a42de86247f Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 6 Sep 2018 15:40:10 +0200 Subject: [PATCH 3/3] createplan page --- rowers/models.py | 37 +++++-- rowers/templates/trainingplan_create.html | 117 ++++++++++++++++++++++ rowers/urls.py | 2 + rowers/views.py | 61 ++++++++++- 4 files changed, 207 insertions(+), 10 deletions(-) create mode 100644 rowers/templates/trainingplan_create.html diff --git a/rowers/models.py b/rowers/models.py index 4e877fd3..38151ee3 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -935,17 +935,31 @@ class TrainingTarget(models.Model): default=half_year_from_now) notes = models.TextField(max_length=300,blank=True) + def __unicode__(self): + date = self.date + name = self.name + ownerfirst = self.rower.user.first_name + ownerlast = self.rower.user.last_name + + stri = u'{ownerfirst} {ownerlast} {d} {n}'.format( + ownerfirst = ownerfirst, + ownerlast = ownerlast, + d = date.strftime('%Y-%m-%d'), + n = name + ) + + return stri + class TrainingTargetForm(ModelForm): class Meta: model = TrainingTarget fields = ['name','date','notes'] widgets = { - 'date': SelectDateWidget( - years=range( - timezone.now().year-1,timezone.now().year+1)), + 'date': AdminDateWidget() } + # SportTracks has a TrainingGoal like this #class TrainingGoal(models.Model): # rower = models.ForeignKey(Rower) @@ -976,14 +990,19 @@ class TrainingPlanForm(ModelForm): fields = ['name','target','startdate','enddate'] widgets = { - 'startdate': SelectDateWidget( - years=range( - timezone.now().year-1,timezone.now().year+1)), - 'enddate': SelectDateWidget( - years=range( - timezone.now().year-1,timezone.now().year+1)), + 'startdate': AdminDateWidget(), + 'enddate': AdminDateWidget() } + def __init__(self,*args, **kwargs): + targets = kwargs.pop('targets',None) + super(TrainingPlanForm, self).__init__(*args, **kwargs) + + if targets: + targetchoices = [(x.id,x) for x in targets] + targetchoices.append((None,'---')) + self.fields['target'].choices = targetchoices + cycletypechoices = ( ('filler','System Defined'), diff --git a/rowers/templates/trainingplan_create.html b/rowers/templates/trainingplan_create.html new file mode 100644 index 00000000..1a4f2c29 --- /dev/null +++ b/rowers/templates/trainingplan_create.html @@ -0,0 +1,117 @@ +{% extends "base.html" %} +{% load staticfiles %} +{% load rowerfilters %} + +{% block title %}Rowsandall Training Plans{% endblock %} + +{% block scripts %} + +{% endblock %} + +{% block content %} + + +

+
+

Training Targets

+ + {% if targets %} + + + + + + + + + + {% for target in targets %} + + + + + + {% endfor %} + +
Target DateNameNotes
{{ target.date }} {{ target.name }} {{ target.notes }}
+ {% else %} + No training targets found + {% endif %} +
+
+
+

Add a target

+ +
+ + {{ targetform.as_table }} +
+ {% csrf_token %} +
+ +
+
+ +
+
+
+ + +
+ +
+

Plans

+ + {% if plans %} + + + + + + + + + + {% for plan in plans %} + + + + + + {% endfor %} + +
Start Date End Date Name
{{ plan.startdate }} {{ plan.enddate }} {{ plan.name }}
+ {% else %} +

No plans found

+ {% endif %} + +
+ + + +
+
+

Add a plan

+ +
+ + {{ form.as_table }} +
+ {% csrf_token %} +
+ +
+
+ +
+
+ + +
+ {% endblock %} diff --git a/rowers/urls.py b/rowers/urls.py index a423bb70..5e0b41e0 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -419,6 +419,8 @@ urlpatterns = [ url(r'^workout/compare/(?P\d+)/(?P\d+)/(?P\w+.*)/(?P[\w\ ]+.*)/(?P[\w\ ]+.*)$',views.workout_comparison_view2), url(r'^workout/compare/(?P\d+)/(?P\d+)/(?P\w+.*)/(?P[\w\ ]+.*)/$',views.workout_comparison_view2), url(r'^test\_callback',views.rower_process_testcallback), + url(r'^createplan$',views.rower_create_trainingplan), + url(r'^createplan/(?P\d+)$',views.rower_create_trainingplan), url(r'^workout/(?P\d+)/test\_strokedata$',views.strokedataform), url(r'^sessions/teamcreate$',views.plannedsession_teamcreate_view), diff --git a/rowers/views.py b/rowers/views.py index 09f32763..9a1bc520 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -64,7 +64,10 @@ from rowers.forms import ( ) from rowers.models import ( Workout, User, Rower, WorkoutForm,FavoriteChart, - PlannedSession, DeactivateUserForm,DeleteUserForm + PlannedSession, DeactivateUserForm,DeleteUserForm, + TrainingPlan,TrainingPlanForm,TrainingTarget,TrainingTargetForm, + TrainingMacroCycle,TrainingMesoCycle,TrainingMicroCycle, + TrainingTarget,TrainingTargetForm, ) from rowers.models import ( RowerPowerForm,RowerForm,GraphImage,AdvancedWorkoutForm, @@ -14167,5 +14170,61 @@ def virtualevent_submit_result_view(request,id=0): 'w_form':w_form, }) +@user_passes_test(hasplannedsessions,login_url="/", redirect_field_name=None) +def rower_create_trainingplan(request,id=0): + + therower = getrequestrower(request,userid=id) + theuser = therower.user + + + if request.method == 'POST' and 'date' in request.POST: + targetform = TrainingTargetForm(request.POST) + if targetform.is_valid(): + name = targetform.cleaned_data['name'] + date = targetform.cleaned_data['date'] + notes = targetform.cleaned_data['notes'] + + t = TrainingTarget(rower=therower, + name=name, + date=date, + notes=notes) + + t.save() + + elif request.method == 'POST' and 'startdate' in request.POST: + form = TrainingPlanForm(request.POST) + if form.is_valid(): + name = form.cleaned_data['name'] + target = form.cleaned_data['target'] + startdate = form.cleaned_data['startdate'] + enddate = form.cleaned_data['enddate'] + + p = TrainingPlan( + name=name, + rower=therower, + target=target, + startdate=startdate, + enddate=enddate, + ) + + p.save() + + + + targets = TrainingTarget.objects.filter(rower=therower).order_by("date") + targetform = TrainingTargetForm() + + plans = TrainingPlan.objects.filter(rower=therower).order_by("-startdate") + form = TrainingPlanForm(targets=targets) + + + return render(request,'trainingplan_create.html', + { + 'form':form, + 'plans':plans, + 'targets':targets, + 'targetform':targetform, + }) +