From c1dea26080c31d4bd6689fc4e23da52da2b95909 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 24 Sep 2018 08:39:40 +0200 Subject: [PATCH 01/95] cosmetic change --- rowers/dataprepnodjango.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rowers/dataprepnodjango.py b/rowers/dataprepnodjango.py index 7dad7bb2..486a4336 100644 --- a/rowers/dataprepnodjango.py +++ b/rowers/dataprepnodjango.py @@ -241,6 +241,7 @@ def add_c2_stroke_data_db(strokedata,workoutid,starttimeunix,csvfilename, hr = strokedata.ix[:,'hr'] except KeyError: hr = 0*spm + pace = strokedata.ix[:,'p']/10. pace = np.clip(pace,0,1e4) pace = pace.replace(0,300) From b2d8f0ff2c39db1eb166794802c0c70013a49447 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 26 Sep 2018 15:04:00 +0200 Subject: [PATCH 02/95] hover behavior in nav bar --- static/css/styles2.css | 31 ++++++++++++++++++++++++------- templates/newbase.html | 2 +- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/static/css/styles2.css b/static/css/styles2.css index 00c2282b..75ad43ce 100644 --- a/static/css/styles2.css +++ b/static/css/styles2.css @@ -10,7 +10,12 @@ .wrapper > * { /* border: 2px solid #f08c00; */ border 0; - padding: 5px; + padding: 5px; + } + + .wrapper nav { + padding-top: 0; + padding-bottom: 0; } a { @@ -232,19 +237,31 @@ } nav li { - margin: 0.2em; + margin: 0; + padding: 0.2em; } - nav a { - color: white; - transition: all .2s ease-in-out; - } - + nav a, nav a i { color: white; } + nav li:hover, + nav li:hover a, + nav li:hover a i { + color: #1c75bc; + background: white; + } + .nav-active, + .nav-active a, + .nav-active a i { + color: #1c75bc; + background: white; + } + + + .main-head { grid-area: header; background: #ededed; diff --git a/templates/newbase.html b/templates/newbase.html index 04a0c6c8..d96a2335 100644 --- a/templates/newbase.html +++ b/templates/newbase.html @@ -183,7 +183,7 @@  Workouts -
  • +
  • +
  • +

    Closest Airport Weather: {{ airport }} + ({{ airportdistance | floatformat:-1 }} km) + Airport Data

    +

    + Dark Sky Data +

    + Download wind speed and bearing from The Dark Sky +

    +
  • +
  • - - -
    +
  • +
  • - - + + - {{ interactiveplot |safe }} + {{ interactiveplot |safe }} - - - - -
    -
    - {{ the_div |safe }} -
    -
    - -
    -
    + {{ the_div |safe }} +
  • +
  • +
    {{ gmapdiv |safe }} {{ gmap |safe }}
    - +
  • + {% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/views.py b/rowers/views.py index 79416dfb..aa4ff2da 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -6947,6 +6947,22 @@ def workout_downloadmetar_view(request,id=0, @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_wind_view(request,id=0,message="",successmessage=""): row = get_workout(id) + r = getrower(request.user) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(row.id) + }, + { + 'url':reverse(workout_wind_view,kwargs={'id':id}), + 'name': 'Wind' + } + + ] if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" @@ -7050,6 +7066,9 @@ def workout_wind_view(request,id=0,message="",successmessage=""): return render(request, 'windedit.html', {'workout':row, + 'rower':r, + 'breadcrumbs':breadcrumbs, + 'active':'nav-active', 'teams':get_my_teams(request.user), 'interactiveplot':script, 'form':form, From add3f4167e411e0aba035d24ddb2257211ad8ca6 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 8 Oct 2018 21:43:38 +0200 Subject: [PATCH 52/95] addimage --- rowers/templates/image_form.html | 53 +++++++++++++++--------------- rowers/templates/menu_workout.html | 5 +++ rowers/views.py | 19 +++++++++++ 3 files changed, 51 insertions(+), 26 deletions(-) diff --git a/rowers/templates/image_form.html b/rowers/templates/image_form.html index e5454d4e..82e08395 100644 --- a/rowers/templates/image_form.html +++ b/rowers/templates/image_form.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -15,36 +15,33 @@ {% endblock %} -{% block content %} - -
    -
    -
    -

    Upload Image

    - {% if form.errors %} -

    - Please correct the error{{ form.errors|pluralize }} below. +{% block main %} +

    Upload Image

    + +
      +
    • + +
      + + {% if form.errors %} +

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

      - {% endif %} + {% endif %} - {{ form.as_table }} + {{ form.as_table }}
      {% csrf_token %} -
      - -
      -
      - - - - -
    • +

      + +

      +
    + + {% endblock %} {% block scripts %} @@ -169,7 +166,7 @@ }); $("#id_drop-files").replaceWith( - '
    ' + '
    ' ); $.ajax({ data: data, @@ -250,3 +247,7 @@ {% endblock %} + + {% block sidebar %} + {% include 'menu_workout.html' %} + {% endblock %} diff --git a/rowers/templates/menu_workout.html b/rowers/templates/menu_workout.html index db23a003..d8d4ceb6 100644 --- a/rowers/templates/menu_workout.html +++ b/rowers/templates/menu_workout.html @@ -102,6 +102,11 @@ {% endif %} +
  • + +  Upload Image + +
  • diff --git a/rowers/views.py b/rowers/views.py index aa4ff2da..c5c4f970 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -9275,6 +9275,22 @@ def workout_uploadimage_view(request,id): w = get_workout(id) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(w.id) + }, + { + 'url':reverse(workout_uploadimage_view,kwargs={'id':id}), + 'name': 'Upload Image' + } + + ] + if not checkworkoutuser(request.user,w): raise PermissionDenied("You are not allowed to edit this workout") @@ -9342,6 +9358,9 @@ def workout_uploadimage_view(request,id): form = ImageForm() return render(request,'image_form.html', {'form':form, + 'rower':r, + 'active':'nav-active', + 'breadcrumbs':breadcrumbs, 'teams':get_my_teams(request.user), 'workout': w, }) From 5ba1e45a93063de2498d88eb6faafd74e16f2a83 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 8 Oct 2018 21:53:35 +0200 Subject: [PATCH 53/95] stream view --- rowers/templates/streamedit.html | 99 ++++++++------------------------ rowers/views.py | 24 +++++++- 2 files changed, 47 insertions(+), 76 deletions(-) diff --git a/rowers/templates/streamedit.html b/rowers/templates/streamedit.html index b12def3c..8a0b00c3 100644 --- a/rowers/templates/streamedit.html +++ b/rowers/templates/streamedit.html @@ -1,48 +1,20 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Advanced Features {% endblock %} -{% block content %} -
    - -
    -

    Stream Editor

    -
    -
    -
    -

    - Edit Workout -

    -
    -
    -

    - Advanced Edit -

    - -
    -
    -

    OTW Power

    - Run calculations to get power values for your row. - -
    -
    -
    - -
    -

    Wind Edit

    -
    -
    -
    +{% block main %} +

    Stream Editor

    +
      +
    • Edit river Stream between turning points in your row. Use positive (+) values to denote rowing with the stream, negative (-) values to denote rowing against the stream.

      - +
    • +
    • {% if form.errors %}

      @@ -54,47 +26,26 @@ {{ form.as_table }} {% csrf_token %} -

    -
    - -
    - - - -
    - -
    - - - - - {{ interactiveplot |safe }} - - + - + + {{ interactiveplot |safe }} + + {{ the_div |safe }} +
  • -
    - {{ the_div |safe }} -
    + {% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/views.py b/rowers/views.py index c5c4f970..ef39d0d4 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -7068,7 +7068,7 @@ def workout_wind_view(request,id=0,message="",successmessage=""): {'workout':row, 'rower':r, 'breadcrumbs':breadcrumbs, - 'active':'nav-active', + 'active':'nav-workouts', 'teams':get_my_teams(request.user), 'interactiveplot':script, 'form':form, @@ -7083,6 +7083,7 @@ def workout_wind_view(request,id=0,message="",successmessage=""): @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_stream_view(request,id=0,message="",successmessage=""): row = get_workout(id) + r = getrower(request.user) if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" @@ -7135,11 +7136,30 @@ def workout_stream_view(request,id=0,message="",successmessage=""): script = res[0] div = res[1] + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(row.id) + }, + { + 'url':reverse(workout_stream_view,kwargs={'id':id}), + 'name': 'Stream' + } + + ] + messages.info(request,successmessage) messages.error(request,message) return render(request, 'streamedit.html', {'workout':row, + 'rower':r, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', 'teams':get_my_teams(request.user), 'interactiveplot':script, 'form':form, @@ -9359,7 +9379,7 @@ def workout_uploadimage_view(request,id): return render(request,'image_form.html', {'form':form, 'rower':r, - 'active':'nav-active', + 'active':'nav-workouts', 'breadcrumbs':breadcrumbs, 'teams':get_my_teams(request.user), 'workout': w, From 4b52a9080647e3c31ca6e80a8b04a2015737e6bb Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 8 Oct 2018 22:00:42 +0200 Subject: [PATCH 54/95] crewnerd summary --- rowers/templates/cn_form.html | 38 ++++++++++++++--------------- rowers/templatetags/rowerfilters.py | 5 +++- rowers/views.py | 24 ++++++++++++++++++ 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/rowers/templates/cn_form.html b/rowers/templates/cn_form.html index 354a1694..5a32d235 100644 --- a/rowers/templates/cn_form.html +++ b/rowers/templates/cn_form.html @@ -1,25 +1,25 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% block title %}CrewNerd Summary loading{% endblock %} -{% block content %} -
    -
    -

    Upload Workout Summary File (CrewNerd)

    - {% if form.errors %} -

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

    - {% endif %} - - - {{ form.as_table }} -
    - {% csrf_token %} -
    - -
    -
    +{% block main %} + +

    Upload Workout Summary File (CrewNerd)

    + {% if form.errors %} +

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

    + {% endif %} + + + {{ form.as_table }} +
    + {% csrf_token %} +
    {% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py index 51629331..e3d86059 100644 --- a/rowers/templatetags/rowerfilters.py +++ b/rowers/templatetags/rowerfilters.py @@ -52,7 +52,10 @@ def secondstotimestring(tdelta): @register.filter def water(workout): - return workout.workouttype in otwtypes + try: + return workout.workouttype in otwtypes + except AttributeError: + return False @register.filter def ddays(ddelta): diff --git a/rowers/views.py b/rowers/views.py index ef39d0d4..53525b4e 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -6763,6 +6763,22 @@ def workout_smoothenpace_view(request,id=0,message="",successmessage=""): @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""): row = get_workout(id) + r = getrower(request.user) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(row.id) + }, + { + 'url':reverse(workout_crewnerd_summary_view,kwargs={'id':id}), + 'name': 'CrewNerd Summary' + } + + ] if request.method == 'POST': form = CNsummaryForm(request.POST,request.FILES) @@ -6799,6 +6815,10 @@ def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""): return render(request, "cn_form.html", {'form':form, + 'active':'nav-workouts', + 'rower':r, + 'workout':row, + 'breadcrumbs':breadcrumbs, 'teams':get_my_teams(request.user), 'id':row.id}) else: @@ -6807,6 +6827,10 @@ def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""): return render(request, "cn_form.html", {'form':form, + 'active':'nav-workouts', + 'rower':r, + 'workout':row, + 'breadcrumbs':breadcrumbs, 'teams':get_my_teams(request.user), 'id':row.id}) From 50321b850a9125c84c4f5cbd471a072c2ff1c816 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 9 Oct 2018 08:40:57 +0200 Subject: [PATCH 55/95] edit intervals --- rowers/templates/summary_edit.html | 298 ++++++++++------------------- rowers/urls.py | 2 +- rowers/views.py | 19 ++ 3 files changed, 121 insertions(+), 198 deletions(-) diff --git a/rowers/templates/summary_edit.html b/rowers/templates/summary_edit.html index a5b44e92..a23238f1 100644 --- a/rowers/templates/summary_edit.html +++ b/rowers/templates/summary_edit.html @@ -1,129 +1,61 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% load tz %} {% block title %}Change Workout {% endblock %} -{% block content %} -
    - - {% if form.errors %} -

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

    - {% endif %} - -

    Edit Workout Interval Data

    -
    -
    -

    - Edit -

    -
    -
    -

    - Workflow View - -

    -
    -
    -

    - Advanced -

    - Advanced Functionality (More interactive Charts) - -
    -
    - - - {% localtime on %} - - - - - - - - - - - - -
    Date/Time:{{ workout.startdatetime }}
    Distance:{{ workout.distance }}m
    Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
    Public link to this workout - https://rowsandall.com/rowers/workout/{{ workout.id }} - -
    Public link to interactive chart - https://rowsandall.com/rowers/workout/{{ workout.id }}/interactiveplot - -
    - {% endlocaltime %} -

    Interval Translator

    -

    This is a quick way to enter the intervals using a special mini-language.

    -

    You enter something like 8x500m/3min, press "Update" and the site will interpret this for you and update the summary on the right. If you're happy with the result, press the green Save button to update the values. Nothing will be changed permanently until you hit Save.

    - -

    Special characters are x (times), + and / (denotes a rest interval), as well as ( and ). Units are min (minutes), sec (seconds), m (meters) and km (km).

    - -

    A typical interval is described as "10min/5min", with the work part before the "/" and the rest part after it. A zero rest can be omitted, so a single 1000m piece could be described either as "1km" or "1000m". The basic units can be combined with "+" and "Nx". You can use parentheses as in the example below.

    - -

    Here are a few examples.

    - - - - - - - - - - - - - - - - - - - - - -
    8x500m/2min8 times 500m with 2 minutes rest
    10kmSingle distance of 10km
    6min/3min + 5min/3min + 3min/3min + 3min Four intervals of 6, 5, 3 and 3 minutes length, 3 minutes rest
    8x500m/3:30min 8 times 500m with 3 minutes 30 seconds rest
    4x((500m+500m)/5min)4 times 1km, but each km is reported as two 500m intervals without rest. Note the nested parentheses.
    2x500m/500mA 2k rowed as 500m "on", 500m "off"
    - -
    +{% block main %} +

    Edit Workout Interval Data

    +
      +
    • - {{ form.as_table }} + + + + + + + + + + +
      Name{{ workout.name }}
      Distance:{{ workout.distance }}m
      Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
      Public link to this workout + https://rowsandall.com/rowers/workout/{{ workout.id }} + +
      Public link to interactive chart + https://rowsandall.com/rowers/workout/{{ workout.id }}/interactiveplot + +
      - {% csrf_token %} -
      +

      Interval Shorthand

      +

      + See the how-to at the bottom of this page for details on how to use this form. +

      + + + {{ form.as_table }} +
      + {% csrf_token %} -
      -
    • - -

      Intervals by Power/Pace

      - -

      With this form, you can specify a power or pace level. Everything faster/harder than the - specified pace/power will become a work interval. Everything slower will become a rest - interval. -

      - -
      - - {{ powerupdateform.as_table }} -
      - - {% csrf_token %} -
      - -
      -
      + +

      Intervals by Power/Pace

      - -
    - -
    -
    +

    With this form, you can specify a power or pace level. Everything faster/harder than the + specified pace/power will become a work interval. Everything slower will become a rest + interval. +

    +
    + + {{ powerupdateform.as_table }} +
    + + {% csrf_token %} + +
    + +
  • - - - -
    - {{ the_div |safe }} -
    -
  • - - -
    + {{ the_div |safe }} + +
  • Updated Summary

    {% csrf_token %} @@ -170,61 +77,58 @@ {% for field in detailform %} {{ field.as_hidden }} {% endfor %} -
    - -
    - - -
    -

    -

    -          {{ intervalstats }}
    -        
    +

    -
    -
  • - -
    -

    Detailed Summary Edit

    -

    This is still experimental and there are known bugs. Use at your own risk. Nothing is stored permanently until you hit Save on the summary above. You can use the restore original button to restore the original values.

    -
    -
    #
    -
    - Time -
    -
    - Distance -
    -
    - Type -
    -
    -
    - {% for i in nrintervals|times %} -
    -
    {{ i }}
    -
    - {% get_field_id i "intervalt_" detailform %} -
    -
    - {% get_field_id i "intervald_" detailform %} -
    -
    - {% get_field_id i "type_" detailform %} -
    -
    - {% endfor %} - {% csrf_token %} -
    - - -
    + + Reset to last saved +   + Restore Original data +
    -
    -
    +

    +

    +        {{ intervalstats }}
    +      
    +

    + +
  • +

    Interval Shorthand How-To

    +

    This is a quick way to enter the intervals using a special mini-language.

    +

    You enter something like 8x500m/3min, press "Update" and the site will interpret this for you and update the summary on the right. If you're happy with the result, press the green Save button to update the values. Nothing will be changed permanently until you hit Save.

    + +

    Special characters are x (times), + and / (denotes a rest interval), as well as ( and ). Units are min (minutes), sec (seconds), m (meters) and km (km).

    + +

    A typical interval is described as "10min/5min", with the work part before the "/" and the rest part after it. A zero rest can be omitted, so a single 1000m piece could be described either as "1km" or "1000m". The basic units can be combined with "+" and "Nx". You can use parentheses as in the example below.

    + +

    Here are a few examples.

    + + + + + + + + + + + + + + + + + + + + + +
    8x500m/2min8 times 500m with 2 minutes rest
    10kmSingle distance of 10km
    6min/3min + 5min/3min + 3min/3min + 3min Four intervals of 6, 5, 3 and 3 minutes length, 3 minutes rest
    8x500m/3:30min 8 times 500m with 3 minutes 30 seconds rest
    4x((500m+500m)/5min)4 times 1km, but each km is reported as two 500m intervals without rest. Note the nested parentheses.
    2x500m/500mA 2k rowed as 500m "on", 500m "off"
    +
  • + +{% endblock %} + + +{% block sidebar %} +{% include 'menu_workout.html' %} {% endblock %} diff --git a/rowers/urls.py b/rowers/urls.py index 25a267ee..a0c06061 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -255,7 +255,7 @@ urlpatterns = [ url(r'^workout/(?P\d+)/darkskywind$',views.workout_downloadwind_view), url(r'^workout/(?P\d+)/metar/(?P\w+)$',views.workout_downloadmetar_view), url(r'^workout/(?P\d+)/stream$',views.workout_stream_view), - url(r'^workout/(?P\d+)/crewnerdsummary$',views.workout_crewnerd_summary_view), +# url(r'^workout/(?P\d+)/crewnerdsummary$',views.workout_crewnerd_summary_view), url(r'^workout/(?P\d+)/editintervals$',views.workout_summary_edit_view), url(r'^workout/(?P\d+)/restore$',views.workout_summary_restore_view), url(r'^workout/(?P\d+)/split$',views.workout_split_view), diff --git a/rowers/views.py b/rowers/views.py index 53525b4e..9bcf32b5 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -11066,6 +11066,22 @@ def workout_fusion_view(request,id1=0,id2=1): def workout_summary_edit_view(request,id,message="",successmessage="" ): row = get_workout_permitted(request.user,id) + r = getrower(request.user) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,row.id), + 'name': str(row.id) + }, + { + 'url':reverse(workout_summary_edit_view,kwargs={'id':id}), + 'name': 'Edit Intervals' + } + + ] s = "" # still here - this is a workout we may edit @@ -11443,6 +11459,9 @@ def workout_summary_edit_view(request,id,message="",successmessage="" 'detailform':detailform, 'powerupdateform':powerupdateform, 'workout':row, + 'rower':r, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', 'teams':get_my_teams(request.user), 'intervalstats':intervalstats, 'nrintervals':nrintervals, From 864ea2883d5f2b46ee369b7b8e3ec5d32e635d56 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 9 Oct 2018 10:14:07 +0200 Subject: [PATCH 56/95] splitworkout --- rowers/templates/splitworkout.html | 174 ++++++++++------------------- rowers/templates/summary_edit.html | 5 - rowers/urls.py | 2 +- rowers/views.py | 18 +++ 4 files changed, 79 insertions(+), 120 deletions(-) diff --git a/rowers/templates/splitworkout.html b/rowers/templates/splitworkout.html index a264f626..0a186a56 100644 --- a/rowers/templates/splitworkout.html +++ b/rowers/templates/splitworkout.html @@ -1,131 +1,77 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% load tz %} {% block title %}Change Workout {% endblock %} -{% block content %} -
    - - {% if form.errors %} -

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

    - {% endif %} - +{% block main %}

    Edit Workout Interval Data

    -
    -
    -

    - Edit -

    -
    -
    -

    - Export - -

    -
    -
    -

    - Advanced -

    - Advanced Functionality (More interactive Charts) - -
    -
    - {% localtime on %} - -

    -

    - - {{ form.as_table }} -
    - {% csrf_token %} -
    - -
    -
    -

    -

    Workout Split

    -

    Split your workout in half at the given time using the form above. - Use the chart on - the right to find the point where you want to split.

    -

    Use any of the following formats: -

      -
    • H:MM:SS.d, e.g. 1:45:00.0 for one hour and 45 minutes
    • -
    • H:MM:SS, e.g. 1:45:00 for one hour and 45 minutes
    • -
    • MM:SS.d, e.g. 30:00.0 for thirty minutes
    • -
    • MM, e.g. 30 for thirty minutes
    • -
    -

    -

    Warning: If you deselect, "Keep Original", the original workout - cannot be restored afterwards.

    - -
    - -
    -
    +
      +
    • +

      +

      + + {{ form.as_table }} +
      + {% csrf_token %} + +
      +

      +
    • +
    • - - - - {{ thescript |safe }} - - - - - -
      + + + + {{ thescript |safe }} + {{ thediv |safe }} -
      - - - - - - - - - - - -
      Date/Time:{{ workout.startdatetime }}
      Distance:{{ workout.distance }}m
      Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
      Public link to this workout - https://rowsandall.com/rowers/workout/{{ workout.id }} - -
      Public link to interactive chart - https://rowsandall.com/rowers/workout/{{ workout.id }}/interactiveplot - -
      - {% endlocaltime %} + + Date/Time:{{ workout.startdatetime }} + + Distance:{{ workout.distance }}m + + Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }} + + Public link to this workout + + https://rowsandall.com/rowers/workout/{{ workout.id }} + + + + {% endlocaltime %} +
    • +
    • +

      Workout Split

      +

      Split your workout in half at the given time using the form above. + Use the chart on + the right to find the point where you want to split.

      +

      Use any of the following formats: +

        +
      • H:MM:SS.d, e.g. 1:45:00.0 for one hour and 45 minutes
      • +
      • H:MM:SS, e.g. 1:45:00 for one hour and 45 minutes
      • +
      • MM:SS.d, e.g. 30:00.0 for thirty minutes
      • +
      • MM, e.g. 30 for thirty minutes
      • +
      +

      +

      + Warning: If you deselect, "Keep Original", the original workout + cannot be restored afterwards. +

      +
    • +
    -
    -
    - +{% endblock %} + + {% block sidebar %} +{% include 'menu_workout.html' %} {% endblock %} diff --git a/rowers/templates/summary_edit.html b/rowers/templates/summary_edit.html index a23238f1..d4de5063 100644 --- a/rowers/templates/summary_edit.html +++ b/rowers/templates/summary_edit.html @@ -21,11 +21,6 @@ https://rowsandall.com/rowers/workout/{{ workout.id }} - - Public link to interactive chart - - https://rowsandall.com/rowers/workout/{{ workout.id }}/interactiveplot -

    Interval Shorthand

    diff --git a/rowers/urls.py b/rowers/urls.py index a0c06061..fad10bd2 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -259,7 +259,7 @@ urlpatterns = [ url(r'^workout/(?P\d+)/editintervals$',views.workout_summary_edit_view), url(r'^workout/(?P\d+)/restore$',views.workout_summary_restore_view), url(r'^workout/(?P\d+)/split$',views.workout_split_view), - url(r'^workout/(?P\d+)/interactiveplot$',views.workout_biginteractive_view), +# url(r'^workout/(?P\d+)/interactiveplot$',views.workout_biginteractive_view), url(r'^workout/(?P\d+)/view$',views.workout_view), url(r'^workout/(?P\d+)$',views.workout_view), url(r'^workout/fusion/(?P\d+)/(?P\d+)$',views.workout_fusion_view), diff --git a/rowers/views.py b/rowers/views.py index 9bcf32b5..a8c39f98 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -10933,6 +10933,21 @@ def workout_split_view(request,id=id): r = row.user + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,row.id), + 'name': str(row.id) + }, + { + 'url':reverse(graph_show_view,kwargs={'id':id}), + 'name': 'Chart' + } + + ] if request.method == 'POST': form = WorkoutSplitForm(request.POST) if form.is_valid(): @@ -10993,6 +11008,9 @@ def workout_split_view(request,id=id): return render(request, 'splitworkout.html', {'form':form, + 'rower':r, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', 'workout':row, 'thediv':script, 'thescript':div, From ffa523851e3c34bce386953df13bd882b5d822f0 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 9 Oct 2018 10:44:35 +0200 Subject: [PATCH 57/95] workout_view --- rowers/templates/splitworkout.html | 2 +- rowers/templates/workout_view.html | 236 +++++++++++------------------ rowers/views.py | 155 +++++++++---------- 3 files changed, 164 insertions(+), 229 deletions(-) diff --git a/rowers/templates/splitworkout.html b/rowers/templates/splitworkout.html index 0a186a56..86756bc8 100644 --- a/rowers/templates/splitworkout.html +++ b/rowers/templates/splitworkout.html @@ -6,7 +6,7 @@ {% block title %}Change Workout {% endblock %} {% block main %} -

    Edit Workout Interval Data

    +

    Split Workout

    {% localtime on %}
      diff --git a/rowers/templates/workout_view.html b/rowers/templates/workout_view.html index ea0fc977..9e2bae74 100644 --- a/rowers/templates/workout_view.html +++ b/rowers/templates/workout_view.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block scripts %} @@ -16,183 +16,117 @@ {% for graph in graphs1 %} {% block og_image %} {% if graphs1 %} -{% for graph in graphs1 %} - - - - - {% endfor %} - {% else %} - - - {% endif %} +{% for graph in graphs %} + + + + +{% endfor %} +{% else %} + + +{% endif %} {% endblock %} {% block image_src %} -{% for graph in graphs1 %} - +{% for graph in graphs %} + {% endfor %} {% endblock %} {% endfor %} -{% block content %} -
      +{% block main %} - -

      {{ workout.name }}

      -
      - -
      -
      - -
      - - - +

      {{ workout.name }}

      +
      + - - + + - - + + - + - + - + - + - + - - {% if user.is_authenticated %} - - + + {% if user.is_authenticated %} + + - - {% endif %} -
      Rower:{{ first_name }} {{ last_name }}
      Name:{{ workout.name }}
      Date:{{ workout.date }}
      Time:{{ workout.starttime }}
      Distance:{{ workout.distance }}m
      Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
      Type:{{ workout.workouttype }}
      Weight Category:{{ workout.weightcategory }}
      Comments
      Comments Comment ({{ aantalcomments }})
      - -

      Workout Summary

      - -

      -

      -{{ workout.summary }}
      -
      -

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

      Workout Summary

      +

      +

      +        {{ workout.summary }}
      +      
      +

      +
    • +
    • +
      {{ mapdiv|safe }} {{ mapscript|safe }} - -
      -
      - -

      Images linked to this workout

      - {% if graphs1 %} - - {% for graph in graphs1 %} - {% if forloop.counter == 1 %} -
      - - {{ graph.filename }} -
      - {% elif forloop.counter == 3 %} -
      - - {{ graph.filename }} -
      - - {% else %} -
      - - {{ graph.filename }} -
      - {% endif %} - {% endfor %} - - - {% for graph in graphs2 %} - {% if forloop.counter == 1 %} -
      - - {{ graph.filename }} -
      - {% elif forloop.counter == 3 %} -
      - - {{ graph.filename }} -
      - - {% else %} -
      - - {{ graph.filename }} -
      - {% endif %} - {% endfor %} - - - - - {% else %} -

      No graphs found

      - {% endif %} -
      - - - - - {{ interactiveplot |safe }} - - + - + + {{ interactiveplot |safe }} - -
      {{ the_div|safe }} -
      +
    • + {% for graph in graphs %} +
    • + + {{ graph.filename }} + +
    • + {% endfor %} +
    {% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/views.py b/rowers/views.py index a8c39f98..774dd29d 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -6596,99 +6596,100 @@ def workout_fusion_list(request,id=0,message='',successmessage='', except Rower.DoesNotExist: raise Http404("User has no rower instance") -# Basic 'EDIT' view of workout +# Basic view of workout def workout_view(request,id=0): request.session['referer'] = absolute(request)['PATH'] - try: - # check if valid ID exists (workout exists) - row = Workout.objects.get(id=id) - comments = WorkoutComment.objects.filter(workout=row) - - aantalcomments = len(comments) - - - if row.privacy == 'private': - raise PermissionDenied("Access denied") + if not request.user.is_anonymous(): + rower = getrower(request.user) + else: + rower = None - g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime") - for i in g: - try: - width,height = Image.open(i.filename).size - i.width = width - i.height = height - i.save() - except: - pass + try: + row = Workout.objects.get(id=id) + except Workout.DoesNotExist: + raise Http404("Workout doesn't exist") + + comments = WorkoutComment.objects.filter(workout=row) + + aantalcomments = len(comments) + + + if row.privacy == 'private' and not checkworkoutuser(request.user,row): + raise PermissionDenied("Access denied") + + g = GraphImage.objects.filter(workout=row).order_by("-creationdatetime") + for i in g: + try: + width,height = Image.open(i.filename).size + i.width = width + i.height = height + i.save() + except: + pass - r = Rower.objects.get(id=row.user.id) - u = User.objects.get(id=r.user.id) - # create interactive plot - res = interactive_chart(id) - script = res[0] - div = res[1] + # create interactive plot + res = interactive_chart(id) + script = res[0] + div = res[1] - # create map - f1 = row.csvfilename - u = row.user.user - r = getrower(u) - rowdata = rdata(f1) - hascoordinates = 1 - if rowdata != 0: - try: - latitude = rowdata.df[' latitude'] - if not latitude.std(): - hascoordinates = 0 - except KeyError,AttributeError: + # create map + f1 = row.csvfilename + rowdata = rdata(f1) + hascoordinates = 1 + if rowdata != 0: + try: + latitude = rowdata.df[' latitude'] + if not latitude.std(): hascoordinates = 0 - - else: + except KeyError,AttributeError: hascoordinates = 0 + else: + hascoordinates = 0 - if hascoordinates: - mapscript,mapdiv = leaflet_chart(rowdata.df[' latitude'], - rowdata.df[' longitude'], - row.name) + + if hascoordinates: + mapscript,mapdiv = leaflet_chart(rowdata.df[' latitude'], + rowdata.df[' longitude'], + row.name) - else: - mapscript = "" - mapdiv = "" + else: + mapscript = "" + mapdiv = "" - - # render page - if (len(g)<=3): - return render(request, 'workout_view.html', - {'workout':row, - 'graphs1':g[0:3], - 'last_name':u.last_name, - 'first_name':u.first_name, - 'interactiveplot':script, - 'aantalcomments':aantalcomments, - 'mapscript':mapscript, - 'mapdiv':mapdiv, - 'teams':get_my_teams(request.user), - 'the_div':div}) - else: - return render(request, 'workout_view.html', - {'workout':row, - 'graphs1':g[0:3], - 'graphs2':g[3:6], - 'last_name':u.last_name, - 'first_name':u.first_name, - 'teams':get_my_teams(request.user), - 'aantalcomments':aantalcomments, - 'mapscript':mapscript, - 'mapdiv':mapdiv, - 'interactiveplot':script, - 'the_div':div}) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':reverse(workout_view,kwargs={'id':id}), + 'name': id, + } + + ] - - except Workout.DoesNotExist: - raise Http404("Workout doesn't exist") + u = row.user.user + + return render(request, 'workout_view.html', + {'workout':row, + 'rower':rower, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', + 'graphs':g, + 'last_name':u.last_name, + 'first_name':u.first_name, + 'interactiveplot':script, + 'aantalcomments':aantalcomments, + 'mapscript':mapscript, + 'mapdiv':mapdiv, + 'teams':get_my_teams(request.user), + 'the_div':div}) + # Resets stroke data to raw data (pace) @user_passes_test(ispromember,login_url="/",redirect_field_name=None) From aae0bb5cdbf42ce1343ab61bb89c93426c40c508 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 9 Oct 2018 11:26:15 +0200 Subject: [PATCH 58/95] fusion done --- rowers/templates/fusion.html | 56 ++++---- rowers/templates/fusion_list.html | 214 +++++++++++++++++------------- rowers/views.py | 189 +++++++++++++++----------- 3 files changed, 263 insertions(+), 196 deletions(-) diff --git a/rowers/templates/fusion.html b/rowers/templates/fusion.html index 52de737e..89a11a4b 100644 --- a/rowers/templates/fusion.html +++ b/rowers/templates/fusion.html @@ -1,22 +1,34 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Workouts{% endblock %} -{% block content %} +{% block main %} -
    -

    Fusion Editor

    -
    -
    -
    +

    Fusion Editor

    +
      +
    • + +
      +

      + + {{ form.as_table }} +
      +

      +

      + {% csrf_token %} + +

      +
      +
    • +
    • - Adding sensor data from workout {{ workout2.id }} into workout {{ workout1.id }}. - This will create a new workout. After you submit the form, you will be - taken to the newly created workout. If you are happy with the result, you - can delete the two original workouts manually. + Adding sensor data from workout {{ workout2.id }} into workout {{ workout1.id }}. + This will create a new workout. After you submit the form, you will be + taken to the newly created workout. If you are happy with the result, you + can delete the two original workouts manually.

      Workout 1: {{ workout1.name }} @@ -24,21 +36,13 @@

      Workout 2: {{ workout2.name }}

      -

      On the right hand side, please select the columns from workout 2 that +

      Please select the columns from workout 2 that you want to replace the equivalent columns in workout 1.

      -
    -
    - -
    - - - {{ form.as_table }} -
    - {% csrf_token %} -
    -
    - -
    -
    + + {% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/fusion_list.html b/rowers/templates/fusion_list.html index b7f285d7..8da7c441 100644 --- a/rowers/templates/fusion_list.html +++ b/rowers/templates/fusion_list.html @@ -1,36 +1,34 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Workouts{% endblock %} -{% block content %} -
    -
    -

    Workout {{ id }}

    - - +{% block main %} +

    Workout {{ id }} Sensor Fusion

    +
      +
    • +
    + - - + + - - + + - + - + - + - + - + - -
    Rower:{{ first_name }} {{ last_name }}
    Name:{{ workout.name }}
    Date:{{ workout.date }}
    Time:{{ workout.starttime }}
    Distance:{{ workout.distance }}m
    Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
    Type:{{ workout.workouttype }}
    Weight Category:{{ workout.weightcategory }}
    -
    -
    + +

    @@ -40,82 +38,108 @@

    -
    - - Select start and end date for a date range: -
    -

    -

    - - - {{ dateform.as_table }} -
    - {% csrf_token %} -
    -
    +

    + Select start and end date for a date range: +

    +

    + + + + {{ dateform.as_table }} +
    + {% csrf_token %} +

    -

    -
    + +
  • +

    Fuse this workout with data from:

    + {% if workouts %} +

    + + {% if workouts.has_previous %} + {% if request.GET.q %} + + + + + + + {% else %} + + + + + + + {% endif %} + {% endif %} + + + Page {{ workouts.number }} of {{ workouts.paginator.num_pages }}. + + + {% if workouts.has_next %} + {% if request.GET.q %} + + + + + + + {% else %} + + + + + + + {% endif %} + {% endif %} + +

    - -
  • - -
    -

    Fuse this workout with data from:

    - {% if workouts %} - - - - - - - - - - - - - - - - {% for cworkout in workouts %} - - - - - - - - - - {% if id == cworkout.id %} - - {% else %} - - {% endif %} - - - {% endfor %} - -
    Date Time Name Type Distance Duration Avg HR Max HR Fusion
    {{ cworkout.date }} {{ cworkout.starttime }} {{ cworkout.name }} {{ cworkout.workouttype }} {{ cworkout.distance }}m {{ cworkout.duration |durationprint:"%H:%M:%S.%f" }} {{ cworkout.averagehr }} {{ cworkout.maxhr }}   Fusion
    - {% else %} -

    No workouts found

    - {% endif %} - -
    - - {% if workouts.has_previous %} - < + + + + + + + + + + + + + + + + {% for cworkout in workouts %} + + + + + + + + + + {% if id == cworkout.id %} + + {% else %} + + {% endif %} + + + {% endfor %} + +
    Date Time Name Type Distance Duration Avg HR Max HR Fusion
    {{ cworkout.date }} {{ cworkout.starttime }} {{ cworkout.name }} {{ cworkout.workouttype }} {{ cworkout.distance }}m {{ cworkout.duration |durationprint:"%H:%M:%S.%f" }} {{ cworkout.averagehr }} {{ cworkout.maxhr }}   Fusion
    + {% else %} +

    No workouts found

    {% endif %} - - - Page {{ workouts.number }} of {{ workouts.paginator.num_pages }}. - - - {% if workouts.has_next %} - > - {% endif %} -
    -
    -
    + + +{% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} {% endblock %} diff --git a/rowers/views.py b/rowers/views.py index 774dd29d..8a3e650b 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -6528,73 +6528,93 @@ def workout_fusion_list(request,id=0,message='',successmessage='', try: r = getrower(request.user) - u = User.objects.get(id=r.user.id) - if request.method == 'POST': - dateform = DateRangeForm(request.POST) - if dateform.is_valid(): - startdate = dateform.cleaned_data['startdate'] - enddate = dateform.cleaned_data['enddate'] - else: - dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) - - if startdatestring: - startdate = iso8601.parse_date(startdatestring) - if enddatestring: - enddate = iso8601.parse_date(enddatestring) - - startdate = datetime.datetime.combine(startdate,datetime.time()) - enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) - #enddate = enddate+datetime.timedelta(days=1) - - if enddate < startdate: - s = enddate - enddate = startdate - startdate = s - - workouts = Workout.objects.filter(user=r, - startdatetime__gte=startdate, - startdatetime__lte=enddate).order_by("-date", "-starttime").exclude(id=id) - - query = request.GET.get('q') - if query: - query_list = query.split() - workouts = workouts.filter( - reduce(operator.and_, - (Q(name__icontains=q) for q in query_list)) | - reduce(operator.and_, - (Q(notes__icontains=q) for q in query_list)) - ) - - paginator = Paginator(workouts,15) # show 25 workouts per page - page = request.GET.get('page') - - try: - workouts = paginator.page(page) - except PageNotAnInteger: - workouts = paginator.page(1) - except EmptyPage: - workouts = paginator.page(paginator.num_pages) - row = get_workout(id) - - messages.info(request,successmessage) - messages.error(request,message) - - return render(request, 'fusion_list.html', - {'id':int(id), - 'workout':row, - 'workouts': workouts, - 'last_name':u.last_name, - 'first_name':u.first_name, - 'dateform':dateform, - 'startdate':startdate, - 'enddate':enddate, - 'teams':get_my_teams(request.user), - }) except Rower.DoesNotExist: raise Http404("User has no rower instance") + + u = User.objects.get(id=r.user.id) + if request.method == 'POST': + dateform = DateRangeForm(request.POST) + if dateform.is_valid(): + startdate = dateform.cleaned_data['startdate'] + enddate = dateform.cleaned_data['enddate'] + else: + dateform = DateRangeForm(initial={ + 'startdate':startdate, + 'enddate':enddate, + }) + + if startdatestring: + startdate = iso8601.parse_date(startdatestring) + if enddatestring: + enddate = iso8601.parse_date(enddatestring) + + startdate = datetime.datetime.combine(startdate,datetime.time()) + enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) + #enddate = enddate+datetime.timedelta(days=1) + + if enddate < startdate: + s = enddate + enddate = startdate + startdate = s + + workouts = Workout.objects.filter(user=r, + startdatetime__gte=startdate, + startdatetime__lte=enddate).order_by("-date", "-starttime").exclude(id=id) + + query = request.GET.get('q') + if query: + query_list = query.split() + workouts = workouts.filter( + reduce(operator.and_, + (Q(name__icontains=q) for q in query_list)) | + reduce(operator.and_, + (Q(notes__icontains=q) for q in query_list)) + ) + + paginator = Paginator(workouts,15) # show 25 workouts per page + page = request.GET.get('page') + + try: + workouts = paginator.page(page) + except PageNotAnInteger: + workouts = paginator.page(1) + except EmptyPage: + workouts = paginator.page(paginator.num_pages) + row = get_workout(id) + + messages.info(request,successmessage) + messages.error(request,message) + + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,row.id), + 'name': str(row.id) + }, + { + 'url':reverse(workout_fusion_list,kwargs={'id':id}), + 'name': 'Sensor Fusion' + } + + ] + + return render(request, 'fusion_list.html', + {'id':int(id), + 'workout':row, + 'rower':r, + 'active':'nav-workouts', + 'breadcrumbs':breadcrumbs, + 'workouts': workouts, + 'last_name':u.last_name, + 'first_name':u.first_name, + 'dateform':dateform, + 'startdate':startdate, + 'enddate':enddate, + 'teams':get_my_teams(request.user), + }) # Basic view of workout def workout_view(request,id=0): @@ -11021,6 +11041,9 @@ def workout_split_view(request,id=id): # Fuse two workouts @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def workout_fusion_view(request,id1=0,id2=1): + + r = getrower(request.user) + try: w1 = Workout.objects.get(id=id1) w2 = Workout.objects.get(id=id2) @@ -11061,20 +11084,36 @@ def workout_fusion_view(request,id1=0,id2=1): }) return HttpResponseRedirect(url) - else: - return render(request, 'fusion.html', - {'form':form, - 'teams':get_my_teams(request.user), - 'workout1':w1, - 'workout2':w2, - }) - - - form = FusionMetricChoiceForm(instance=w2) + else: + form = FusionMetricChoiceForm(instance=w2) + + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,w1.id), + 'name': str(w1.id) + }, + { + 'url':reverse(workout_fusion_list,kwargs={'id':id1}), + 'name': 'Sensor Fusion' + }, + { + 'url':reverse(workout_fusion_view,kwargs={'id1':id1,'id2':id2}), + 'name': 'Sensor Fusion' + } + + ] return render(request, 'fusion.html', {'form':form, 'teams':get_my_teams(request.user), + 'workout':w1, + 'rower':r, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', 'workout1':w1, 'workout2':w2, }) From 8b5a0f2e5c96e4e54aeb91ae51e6e1cde371bf8f Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 9 Oct 2018 12:33:44 +0200 Subject: [PATCH 59/95] help page --- rowers/templates/help.html | 158 +++++++++++++++++++++++++++++++- rowers/templates/menu_help.html | 12 +-- rowers/urls.py | 24 ----- 3 files changed, 161 insertions(+), 33 deletions(-) diff --git a/rowers/templates/help.html b/rowers/templates/help.html index 70c97ca2..894db99e 100644 --- a/rowers/templates/help.html +++ b/rowers/templates/help.html @@ -3,11 +3,163 @@ {% load rowerfilters %} {% block main %} -

    Main

    -

    Vestibulum consectetur sit amet nisi ut consectetur. Praesent efficitur, nibh vitae fringilla scelerisque, est neque faucibus quam, in iaculis purus libero eget mauris. Curabitur et luctus sapien, ac gravida orci. Aliquam erat volutpat. In hac habitasse platea dictumst. Aenean commodo, arcu a commodo efficitur, libero dolor mollis turpis, non posuere orci leo eget enim. Curabitur sit amet elementum orci, pulvinar dignissim urna. Morbi id ex eu ex congue laoreet. Aenean tincidunt dolor justo, semper pretium libero luctus nec. Ut vulputate metus accumsan leo imperdiet tincidunt. Phasellus nec rutrum dolor. Cras imperdiet sollicitudin arcu, id interdum nibh fermentum in. +

    Welcome to Rowsandall.com

    +
      +
    • +

      + Rowsandall.com is an online tool for rowers to analyze data from On The Water + (OTW) and On The Erg (OTE) workouts. It accepts workout data from a + number of devices and applications. It analyzes the data to provide + valuable insights about your training, and enables you to share + data with many common online training tracking systems.

      -

      Vestibulum consectetur sit amet nisi ut consectetur. Praesent efficitur, nibh vitae fringilla scelerisque, est neque faucibus quam, in iaculis purus libero eget mauris. Curabitur et luctus sapien, ac gravida orci. Aliquam erat volutpat. In hac habitasse platea dictumst. Aenean commodo, arcu a commodo efficitur, libero dolor mollis turpis, non posuere orci leo eget enim. Curabitur sit amet elementum orci, pulvinar dignissim urna. Morbi id ex eu ex congue laoreet. Aenean tincidunt dolor justo, semper pretium libero luctus nec. Ut vulputate metus accumsan leo imperdiet tincidunt. Phasellus nec rutrum dolor. Cras imperdiet sollicitudin arcu, id interdum nibh fermentum in. +

      Indoor Rowing

      +

      + rowsandall.com is designed to work with all models of the Concept2 + Indoor rower. Using applications like ergstick, rowpro, ergdata or + painsled; a user can collect stroke by stroke data from the Concept2 + Performance Monitor (Models PM3, PM4 or PM5). Workout data can be exported + from these applications, usually in CSV format, and uploaded to + rowsandall.com. Users can upload workouts either through the online + interface, for by emailing workouts to the site to simplify the + process for some applications.

      +

      On The Water Rowing

      +

      + On the water rowers use either dedicated devices like Speedcoaches or + smartphone applications to collect data on their workouts. All of these + devices and applications provide a method to export workout data in CSV, + TCX or FIT format files. Workout data in these formats can be uploaded + to rowsandall.com. Users can upload workouts either through the online + interface, for by emailing workouts to the site to simplify the process + for some applications. +

      +

      Basic Analysis

      +

      + Many athletes use training approaches that use heart rate as a key metric. In general, HR training is managed by defining different training zones for different purposes. Rowsandall.com uses heart rate zone definitions that are consistent with the Concept2 Training Guide. +

      + +

      + After a user defines their training zones, any training files that are uploaded with HR data can be analyzed to provide time in HR zone pie charts. +

      + +

      + The tools also provide the ability to review a row, stroke by stroke in plots versus time or distance. Basic plots in include HR, Pace, Stroke rate, and power for the erg. +

      + + +

      + The tools also provide a text summary of the row. +

      + +

      Workout Export

      + +

      + rowsandall.com provides the ability to easily export workouts from the erg or boat to the Concept2 online logbook. Export to other sport tracking sites like Strava and SportTracks is also supported. Users can also export workout data by email. +

      + +

      Import Compatibility

      + +

      + Rowsandall.com tries to be compatible with the most important tools that rowers use to capture the data (both indoor and OTW). The list of supported tools + continues to be expanded. +

      +
    • +
    • +

      Getting your data on rowsandall.com

      + +

      + To start using the tool, you first need to get some workouts in it. + There are basically two ways. The first method works with a workout file + (CSV, FIT, TCX, etc). The details are described in this + blog post. + A straightforward way to upload your data is to use the + Upload Page. +

      + +

      + The second way to get data into the tool is by importing them + from other workout tracking portals like Strava, SportTracks, etc. + To do this, you use the Import menu on the left of the + Workouts List page. For this + to work, you need to have coupled the external fitness tracking + site with rowsandall.com. You can do this in your + User Profile. Don't worry, the site + will guide you through the process. +

      + +

      User settings

      + +

      Talking about the user profile, + we recommend that you look + at what parameters can be set there. To get the most out of + the site, we recommend you set the heart rate and power zones, + as well as check that your age and gender are correct. Exercise + data make much more sense when this context is taken into account. +

      + +

      Exploring a workout

      + +

      + In the Workouts List, + you can click on the name of a workout to open it. + Once you're on a workout page, the menu on the left will + give you all possibilities you have to edit, manipulate, chart, + or analyze the workout data. +

      + +

      + When you are about to do something irreversible (like deleting a workout) + the site will ask for a confirmation, so don't hesitate to explore. +

      + +

      Analysis

      + +

      + Some of our functionality is not related to a single workout, but instead + looks at comparisons, trends, statistics, and other. You can find all + that under the Analysis Tab. +

      + + +

      On-line Racing

      + +

      + On-line racing is a + fun way to race other Rowsandall.com users + rowing on the same stretch of water. +

      + +

      Training Plan

      + +

      + Under the Plan tab, you + will find your training plan and functionality to see how + you are progressing towards your goals. +

      + +

      Teams

      + +

      + The Teams tab brings you to + functionality related to interaction with your team, if you + are part of one. +

      + + +
    • +
    • +

      Need more help?

      + +

      + The links in the menu on the left bring you to our blog, where we + regularly publish how-to's and articles about new functionality, to + our Facebook group where you can discuss with other users, and to + a contact page where you can learn how to contact the developers + of this site. +

      +
    + {% endblock %} {% block sideheader %} diff --git a/rowers/templates/menu_help.html b/rowers/templates/menu_help.html index c7017f7e..9e88aa21 100644 --- a/rowers/templates/menu_help.html +++ b/rowers/templates/menu_help.html @@ -1,23 +1,23 @@

    Help

    diff --git a/rowers/urls.py b/rowers/urls.py index fad10bd2..dabd72ad 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -265,33 +265,9 @@ urlpatterns = [ url(r'^workout/fusion/(?P\d+)/(?P\d+)$',views.workout_fusion_view), url(r'^workout/fusion/(?P\d+)/$',views.workout_fusion_list), url(r'^workout/fusion/(?P\d+)/(?P\d+-\d+-\d+)/(?P\w+.*)$',views.workout_fusion_list), - url(r'^redesign$',TemplateView.as_view( - template_name='redesign.html'),name='redesign' - ), - url(r'^new_workouts$',TemplateView.as_view( - template_name='redesign_workouts.html'),name='redesign' - ), - url(r'^new_workout$',TemplateView.as_view( - template_name='redesign_workout.html'),name='redesign' - ), - url(r'^new_plan$',TemplateView.as_view( - template_name='redesign_plan.html'),name='redesign' - ), - url(r'^new_profile$',TemplateView.as_view( - template_name='redesign_profile.html'),name='redesign' - ), - url(r'^new_racing$',TemplateView.as_view( - template_name='redesign_racing.html'),name='redesign' - ), - url(r'^new_teams$',TemplateView.as_view( - template_name='redesign_teams.html'),name='redesign' - ), url(r'^help$',TemplateView.as_view( template_name='help.html'),name='help' ), - url(r'^new_analysis$',TemplateView.as_view( - template_name='redesign_analysis.html'),name='redesign' - ), url(r'^physics$',TemplateView.as_view(template_name='physics.html'),name='physics'), url(r'^partners$',TemplateView.as_view(template_name='partners.html'),name='partners'), url(r'^workout/(?P\d+)/$',views.workout_view), From a5e98dd69c30b2cde383484f04295ef20e0e4db4 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 9 Oct 2018 13:03:50 +0200 Subject: [PATCH 60/95] help & physics --- rowers/templates/help.html | 1 + rowers/templates/menu_workout.html | 12 +- rowers/templates/physics.html | 270 ++++++++++++++--------------- rowers/urls.py | 1 - rowers/views.py | 16 +- 5 files changed, 155 insertions(+), 145 deletions(-) diff --git a/rowers/templates/help.html b/rowers/templates/help.html index 894db99e..a755360d 100644 --- a/rowers/templates/help.html +++ b/rowers/templates/help.html @@ -6,6 +6,7 @@

    Welcome to Rowsandall.com

    • +

      What is it?

      Rowsandall.com is an online tool for rowers to analyze data from On The Water (OTW) and On The Erg (OTE) workouts. It accepts workout data from a diff --git a/rowers/templates/menu_workout.html b/rowers/templates/menu_workout.html index d8d4ceb6..edbdc5c6 100644 --- a/rowers/templates/menu_workout.html +++ b/rowers/templates/menu_workout.html @@ -76,28 +76,28 @@

      • - +  Time
      • - +  Distance
      • - +  Power (Pie) -
      • +
      • - +  Heart Rate (Pie)
      • {% if workout|water %}
      • - +  OTW Power
      • diff --git a/rowers/templates/physics.html b/rowers/templates/physics.html index efc5757d..bdea3627 100644 --- a/rowers/templates/physics.html +++ b/rowers/templates/physics.html @@ -1,166 +1,164 @@ +{% extends "newbase.html" %} +{% block title %}About us{% endblock title %} +{% block main %} - {% extends "base.html" %} - {% block title %}About us{% endblock title %} - {% block content %} +

        How we calculate things

        -
        -

        How we calculate things

        -

        You are reading this because you want to understand how the wind/stream conversion and the conversion from OTW pace to OTE pace works.

        - -

        The conversions are done using a one-dimensional mechanical model that is -introduced here. -The model takes into account, among others, the following parameters: -

          +
            +
          • +

            You are reading this because you want to understand how the wind/stream conversion and the conversion from OTW pace to OTE pace works.

            + +

            The conversions are done using a one-dimensional mechanical model that is + introduced here. + The model takes into account, among others, the following parameters: +

            • Stroke rate
            • Stroke length
            • Rigging parameters
            • Rower and boat weight
            • -
            -For this site, we use "standard" rigging parameters, blade shapes, and FISA minimum boat weights. For the eight, I add the weight of a cox (at the FISA minimum weight). The stroke length is also set at a fixed value, but in the future I -will allow you to adjust to your own stroke length. -

            -

            -Knowing boat type (rigging), pace and stroke rate, and taking into account -the influence of wind and stream (if provided), I am able to find the -mechanical power that you provide to the rowing system by a reverse -calculation. That is, I vary the input force until I find the one that -corresponds to your actual pace at that stroke rate. -

            -

            -Knowing the power, I can calculate how fast you would have gone without -external wind and stream influences by running the calculation in a forward -way, using the power and force profile found. This is the wind/stream -corrected pace, which I think is useful to know and be able to compare -from training to training and between different rowing venues. -

            +
          + For this site, we use "standard" rigging parameters, blade shapes, and FISA minimum boat weights. For the eight, I add the weight of a cox (at the FISA minimum weight). The stroke length is also set at a fixed value, but in the future I + will allow you to adjust to your own stroke length. +

          +

          + Knowing boat type (rigging), pace and stroke rate, and taking into account + the influence of wind and stream (if provided), I am able to find the + mechanical power that you provide to the rowing system by a reverse + calculation. That is, I vary the input force until I find the one that + corresponds to your actual pace at that stroke rate. +

          +

          + Knowing the power, I can calculate how fast you would have gone without + external wind and stream influences by running the calculation in a forward + way, using the power and force profile found. This is the wind/stream + corrected pace, which I think is useful to know and be able to compare + from training to training and between different rowing venues. +

          + +

          + Using another algorithm to calculate total mechanical power on an erg, I can + calculate what the erg display would show you if you rowed on the erg with + the same average power, at the same stroke rate. The calculations are done + for a statical Concept2 erg with a fairly standard drag factor. I cannot + take into account the fact that you may use different technique or would + row at a different stroke rate on the erg. +

          -

          -Using another algorithm to calculate total mechanical power on an erg, I can -calculate what the erg display would show you if you rowed on the erg with -the same average power, at the same stroke rate. The calculations are done -for a statical Concept2 erg with a fairly standard drag factor. I cannot -take into account the fact that you may use different technique or would -row at a different stroke rate on the erg. -

          - -

          -It is important to understand that the Power display on the erg is not showing -you the complete picture. In my calculations, I use my proprietary algorithms -to calculate the additional power that goes into moving your body weight up -and down the slide on a static erg. To get the most accurate results, it is -important to be honest about your weight and set it independently for each -workout. -

          - -

          Not taken into account are the following factors: -

            +

            + It is important to understand that the Power display on the erg is not showing + you the complete picture. In my calculations, I use my proprietary algorithms + to calculate the additional power that goes into moving your body weight up + and down the slide on a static erg. To get the most accurate results, it is + important to be honest about your weight and set it independently for each + workout. +

            + +

            Not taken into account are the following factors: +

            • Water Temperature
            • Heavier/shorter/wider boats than the ones used by the elite
            • Bungees, weed, or other artefacts slowing down the boat
            • Boat stopping technique flaws
            • Effect of wave height or cross-wind
            • -
            -The water temperature has a small but measurable effect on the water density -(and thus on the drag). I am using the value at 20 degrees C, which is a -good average over the OTW season for a lake in a temperate climate. All -the other elements result in an equivalent erg pace that is probably slower -than what you can achieve on the erg. So look at it as an incentive to -improve your technique (big effect) and/or buy a faster boat (minor effect). -If your OTW to OTE pace conversion results in numbers close to what you -normally achieve on the erg, you are rowing like an elite rower (but possibly -at a lower power)! When I get around it, I will try to model the effect of cross-wind. -

            +
          + The water temperature has a small but measurable effect on the water density + (and thus on the drag). I am using the value at 20 degrees C, which is a + good average over the OTW season for a lake in a temperate climate. All + the other elements result in an equivalent erg pace that is probably slower + than what you can achieve on the erg. So look at it as an incentive to + improve your technique (big effect) and/or buy a faster boat (minor effect). + If your OTW to OTE pace conversion results in numbers close to what you + normally achieve on the erg, you are rowing like an elite rower (but possibly + at a lower power)! When I get around it, I will try to model the effect of cross-wind. +

          + +

          + I have checked the model both from a Physics perspective (I have a degree in + Physics, if you are interested) and compared with the data available. + An important data set has been published here by Dr Kleshnev. For sculling, + my algorithms are extremely close in reproducing that data set. For sweep + rowing, I am still fine tuning some parameters, but I am close for a pair and + a four. I had to make assumptions about Kleshnev's data, especially about the + stroke rate, but as I got realistic stroke rates (35 and higher) for world + record performance, I am quite confident. +

          + +

          + On top of that I am constantly comparing the model's results to my own sculling + and rowing, and I will be the first to admit flaws and correct them. So please + contact me if there are any inconsistencies, suspicions, questions or simply + if you want to chat about Rowing Physics. +

          -

          -I have checked the model both from a Physics perspective (I have a degree in -Physics, if you are interested) and compared with the data available. -An important data set has been published here by Dr Kleshnev. For sculling, -my algorithms are extremely close in reproducing that data set. For sweep -rowing, I am still fine tuning some parameters, but I am close for a pair and -a four. I had to make assumptions about Kleshnev's data, especially about the -stroke rate, but as I got realistic stroke rates (35 and higher) for world -record performance, I am quite confident. -

          + +
        • +

          Manual

          -

          -On top of that I am constantly comparing the model's results to my own sculling -and rowing, and I will be the first to admit flaws and correct them. So please -contact me if there are any inconsistencies, suspicions, questions or simply -if you want to chat about Rowing Physics. -

          - - -
        -
        -

        Manual

        - -

        Here's the best way - in my mind - to use the Rowing Physics -functionality. I am assuming you have successfully uploaded or imported -a rowing workout. You must have position data (lat/long) with your row. A -TCX from CrewNerd or RiM or a workout imported from SportTracks or Strava -(where you see a map of your workout on those sites) should have those -data. I am working on adding the FIT file format that is used by SpeedCoach -GPS. For now, export the data to Strava and then import them here. -

        - -

        Recipe for success: -

          +

          Here's the best way - in my mind - to use the Rowing Physics + functionality. I am assuming you have successfully uploaded or imported + a rowing workout. You must have position data (lat/long) with your row. A + TCX from CrewNerd or RiM or a workout imported from SportTracks or Strava + (where you see a map of your workout on those sites) should have those + data. I am working on adding the FIT file format that is used by SpeedCoach + GPS. For now, export the data to Strava and then import them here. +

          + +

          Recipe for success: +

          1. Click on the workout. This will bring you to the workout Edit view
          2. -
          3. Click on the "Advanced" button
          4. -
          5. Click on "Geeky Stuff"
          6. -
          7. Look for three buttons labelled "Edit Wind Data", "Edit Stream +
          8. Look for three menu items labelled "Edit Wind Data", "Edit Stream Data" and "OTW Power"
          9. If you have wind or stream data, click on the appropriate - button and enter your data.
          10. -
          11. If you have both wind and stream, click the shortcut button - on the respective page to take you to the other parameter
          12. + menu item and enter your data.
          13. Click on OTW Power
          14. Select the boat type and enter the average weight per crew member. Do not use the crew total weight.
          15. Click "Update & Run"
          16. Go do something else. You will receive an email when the calculations are finished. The calculation itself will take about 10 minutes for an -hour long row, but there may be other people's calculations in the queue, so + hour long row, but there may be other people's calculations in the queue, so it may take longer.
          17. Progress can be monitored by clicking on "here" in the message at the top of the page advising that the calculation has begun.
          18. -
          19. - When the calculation is complete, go back to the "Geeky Stuff" page - and click on "Corrected Pace Plot" to see the result. From here, you can re-run the calculation with different parameters.
          20. -
          -

          +
        +

        + +

        + Once you have run the calculation, the boat type, average crew weight, + Power and corrected pace data are stored permanently on the site. If you would + export the data to Strava or SportTracks now, those sites will have the + Power data. +

        + +

        Why does the calculation take so much time?

        -

        -Once you have run the calculation, the boat type, average crew weight, -Power and corrected pace data are stored permanently on the site. If you would -export the data to Strava or SportTracks now, those sites will have the -Power data. -

        +

        I am running the calculations from a first principles base, so for + each data point that I am calculating, I am finding the stroke average + force, then calculating corrected pace (wind/stream) and finding the + corresponding erg power. I am not taking any shortcuts. The advantage + of this approach is that I can give you numbers irrespective of your + weight, speed, stroke rate, sex, etc. The model can deal with circumstances + it has not encountered before. The downside is that it takes time.

        + +

        + A much faster approach would be to simply take pre-calculated data from + a table and interpolate. The advantage of this approach is is speed. The + disadvantage is that extrapolation outside the limits of the available + data is dangerous and will lead to erroneous results.

        + +

        Future versions of this site will use a hybrid approach + but only for pace/wind/stream/stroke rate/weight combinations that I consider + well validated. For that, I need to collect data, so keep the workouts coming! +

        + + + + +
      -

      Why does the calculation take so much time?

      +{% endblock %} -

      I am running the calculations from a first principles base, so for -each data point that I am calculating, I am finding the stroke average -force, then calculating corrected pace (wind/stream) and finding the -corresponding erg power. I am not taking any shortcuts. The advantage -of this approach is that I can give you numbers irrespective of your -weight, speed, stroke rate, sex, etc. The model can deal with circumstances -it has not encountered before. The downside is that it takes time.

      - -

      -A much faster approach would be to simply take pre-calculated data from -a table and interpolate. The advantage of this approach is is speed. The -disadvantage is that extrapolation outside the limits of the available -data is dangerous and will lead to erroneous results.

      - -

      Future versions of this site will use a hybrid approach -but only for pace/wind/stream/stroke rate/weight combinations that I consider -well validated. For that, I need to collect data, so keep the workouts coming! -

      - - - -
    - - {% endblock content %} +{% block sidebar %} +{% include 'menu_help.html' %} +{% endblock %} diff --git a/rowers/urls.py b/rowers/urls.py index dabd72ad..149a5bc1 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -270,7 +270,6 @@ urlpatterns = [ ), url(r'^physics$',TemplateView.as_view(template_name='physics.html'),name='physics'), url(r'^partners$',TemplateView.as_view(template_name='partners.html'),name='partners'), - url(r'^workout/(?P\d+)/$',views.workout_view), # keeping the old URLs for retrofit url(r'^workout/(?P\d+)/addtimeplot$', views.workout_add_chart_view, diff --git a/rowers/views.py b/rowers/views.py index 8a3e650b..78945187 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -6717,6 +6717,7 @@ def workout_undo_smoothenpace_view( request,id=0,message="",successmessage="" ): row = get_workout(id) + r = getrower(request.user) if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" @@ -6737,7 +6738,12 @@ def workout_undo_smoothenpace_view( row.write_csv(filename,gzip=True) dataprep.update_strokedata(id,row.df) - url = "/rowers/workout/"+str(id)+"/advanced" + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':id, + } + ) + return HttpResponseRedirect(url) @@ -6747,6 +6753,8 @@ def workout_undo_smoothenpace_view( def workout_smoothenpace_view(request,id=0,message="",successmessage=""): row = get_workout(id) + r = getrower(request.user) + if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) @@ -6776,7 +6784,11 @@ def workout_smoothenpace_view(request,id=0,message="",successmessage=""): row.write_csv(filename,gzip=True) dataprep.update_strokedata(id,row.df) - url = "/rowers/workout/"+str(id)+"/advanced" + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':id, + } + ) return HttpResponseRedirect(url) From f8b09b7b6d21dae785567679f2c2acadfef929cf Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 9 Oct 2018 18:21:28 +0200 Subject: [PATCH 61/95] export & import views - line 321 urls.py --- rowers/templates/c2_list_import2.html | 118 ++--- rowers/templates/menu_workout.html | 114 ++++- rowers/templates/menu_workouts.html | 6 +- rowers/templates/polar_list_import.html | 8 +- rowers/templates/runkeeper_list_import.html | 64 +-- rowers/templates/sporttracks_list_import.html | 87 ++-- rowers/templates/strava_list_import.html | 59 +-- rowers/templatetags/rowerfilters.py | 27 ++ rowers/urls.py | 10 +- rowers/views.py | 418 +++++++++++------- 10 files changed, 590 insertions(+), 321 deletions(-) diff --git a/rowers/templates/c2_list_import2.html b/rowers/templates/c2_list_import2.html index 033994e4..c88715aa 100644 --- a/rowers/templates/c2_list_import2.html +++ b/rowers/templates/c2_list_import2.html @@ -1,68 +1,74 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Workouts{% endblock %} -{% block content %} +{% block main %}

    Available on C2 Logbook

    -{% if workouts %} - -
    -

    This imports all workouts that have not been imported to rowsandall.com. +

      + {% if workouts %} +
    • + Import all NEW +

      This imports all workouts that have not been imported to rowsandall.com. The action may take a longer time to process, so please be patient. Click on Import in the list below to import an individual workout.

      -
    - -
    - {% if page > 1 %} - < + +
  • +

    + + {% if page > 1 %} + + + + {% endif %} + + + + +

    +
  • +
  • + + + + + + + + + + + + + + + {% for workout in workouts %} + + + + + + + + + + + + {% endfor %} + +
    Import Date/Time Duration Total Distance Type Source Comment New
    + Import{{ workout|lookup:'starttime' }}{{ workout|lookup:'duration' }}{{ workout|lookup:'distance' }}{{ workout|lookup:'rowtype' }}{{ workout|lookup:'source' }}{{ workout|lookup:'comment' }} + {{ workout|lookup:'new' }} +
    +
  • {% else %} -   +

    No workouts found

    {% endif %} -
    -
    - > -
    - -
    - - - - - - - - - - - - - - - {% for workout in workouts %} - - - - - - - - - - - - {% endfor %} - -
    Import Date/Time Duration Total Distance Type Source Comment New
    - Import{{ workout|lookup:'starttime' }}{{ workout|lookup:'duration' }}{{ workout|lookup:'distance' }}{{ workout|lookup:'rowtype' }}{{ workout|lookup:'source' }}{{ workout|lookup:'comment' }} - {{ workout|lookup:'new' }} -
    -
    -{% else %} -

    No workouts found

    -{% endif %} + +{% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} {% endblock %} diff --git a/rowers/templates/menu_workout.html b/rowers/templates/menu_workout.html index edbdc5c6..c045c36d 100644 --- a/rowers/templates/menu_workout.html +++ b/rowers/templates/menu_workout.html @@ -113,15 +113,111 @@
  • diff --git a/rowers/templates/menu_workouts.html b/rowers/templates/menu_workouts.html index 852dc0be..0370e37a 100644 --- a/rowers/templates/menu_workouts.html +++ b/rowers/templates/menu_workouts.html @@ -28,7 +28,7 @@
      -
    • Concept2
    • +
    • Concept2
    • Strava
    • RunKeeper
    • SportTracks
    • @@ -47,12 +47,12 @@
        {% for member in user|team_members %} - + {% if member == rower.user %} • {% else %} -   +   {% endif %} {{ member.first_name }} {{ member.last_name }} diff --git a/rowers/templates/polar_list_import.html b/rowers/templates/polar_list_import.html index 3e61acf2..9e6a3817 100644 --- a/rowers/templates/polar_list_import.html +++ b/rowers/templates/polar_list_import.html @@ -1,10 +1,10 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Workouts{% endblock %} -{% block content %} +{% block main %}

        New Workouts Imported From Polar Flow

        Due to a limitation in Polar Flow's API, we can only access new workouts. We @@ -47,3 +47,7 @@

        No new workouts found

        {% endif %} {% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} +{% endblock %} diff --git a/rowers/templates/runkeeper_list_import.html b/rowers/templates/runkeeper_list_import.html index 5cda8c0d..8af34266 100644 --- a/rowers/templates/runkeeper_list_import.html +++ b/rowers/templates/runkeeper_list_import.html @@ -1,37 +1,41 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Workouts{% endblock %} -{% block content %} +{% block main %}

        Available on Runkeeper

        - {% if workouts %} - - - - - - - - - - - - {% for workout in workouts %} - - - - - - - - - {% endfor %} - -
        Import Date/Time Duration Total Distance Type
        -Import{{ workout|lookup:'starttime' }}{{ workout|lookup:'duration' }} {{ workout|lookup:'distance' }} m{{ workout|lookup:'type' }}
        - {% else %} -

        No workouts found

        - {% endif %} +{% if workouts %} + + + + + + + + + + + + {% for workout in workouts %} + + + + + + + + + {% endfor %} + +
        Import Date/Time Duration Total Distance Type
        + Import{{ workout|lookup:'starttime' }}{{ workout|lookup:'duration' }} {{ workout|lookup:'distance' }} m{{ workout|lookup:'type' }}
        +{% else %} +

        No workouts found

        +{% endif %} +{% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} {% endblock %} diff --git a/rowers/templates/sporttracks_list_import.html b/rowers/templates/sporttracks_list_import.html index b547efc8..8400d5ff 100644 --- a/rowers/templates/sporttracks_list_import.html +++ b/rowers/templates/sporttracks_list_import.html @@ -1,52 +1,59 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Workouts{% endblock %} -{% block content %} +{% block main %}

        Available on SportTracks

        -{% if workouts %} - -
        -

        This imports all workouts that have not been imported to rowsandall.com. - The action may take a longer time to process, so please be patient. Click on Import in the list below to import an individual workout. -

        -
        -
        - - - - - - - - - - - - - - {% for workout in workouts %} - - - - - - - - - +{% if workouts %} +
          +
        • + Import all NEW +
        • +
        • +

          This imports all workouts that have not been imported to rowsandall.com. + The action may take a longer time to process, so please be patient. Click on Import in the list below to import an individual workout. +

          +
        • + +
        • +
        Import Name Date/Time Duration Total Distance Type New
        - Import{{ workout|lookup:'name' }}{{ workout|lookup:'starttime' }}{{ workout|lookup:'duration' }} {{ workout|lookup:'distance' }} m{{ workout|lookup:'type' }}{{ workout|lookup:'new' }}
        + + + + + + + + + + + + + {% for workout in workouts %} + + + + + + + + + - {% endfor %} - -
        Import Name Date/Time Duration Total Distance Type New
        + Import{{ workout|lookup:'name' }}{{ workout|lookup:'starttime' }}{{ workout|lookup:'duration' }} {{ workout|lookup:'distance' }} m{{ workout|lookup:'type' }}{{ workout|lookup:'new' }}
        -
        + {% endfor %} + + + +
      {% else %}

      No workouts found

      {% endif %} {% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} +{% endblock %} diff --git a/rowers/templates/strava_list_import.html b/rowers/templates/strava_list_import.html index f55ec5c6..a3c1c97b 100644 --- a/rowers/templates/strava_list_import.html +++ b/rowers/templates/strava_list_import.html @@ -17,36 +17,37 @@

      -
    • - - - - - - - - - - +
    • +
    • Import Name Date Duration Distance Type New
      + + + + + + + + + + + + + {% for workout in workouts %} + + + + + + + + - - - {% for workout in workouts %} - - - - - - - - - - - {% endfor %} - -
      Import Name Date Duration Distance Type New
      + Import{{ workout|lookup:'name' }}{{ workout|lookup:'starttime' }}{{ workout|lookup:'duration' }} {{ workout|lookup:'distance' }} m{{ workout|lookup:'type' }}{{ workout|lookup:'new' }}
      - Import{{ workout|lookup:'name' }}{{ workout|lookup:'starttime' }}{{ workout|lookup:'duration' }} {{ workout|lookup:'distance' }} m{{ workout|lookup:'type' }}{{ workout|lookup:'new' }}
      -
    • + + {% endfor %} + + + +
    {% else %}

    No workouts found diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py index e3d86059..6a8289c0 100644 --- a/rowers/templatetags/rowerfilters.py +++ b/rowers/templatetags/rowerfilters.py @@ -13,7 +13,12 @@ from rowers.plannedsessions import ( race_can_register, race_can_submit,race_rower_status ) +from rowers import c2stuff, runkeeperstuff +from rowers.c2stuff import c2_open +from rowers.runkeeperstuff import runkeeper_open + from rowers.types import otwtypes +from rowers.utils import NoTokenError def strfdelta(tdelta): minutes,seconds = divmod(tdelta.seconds,60) @@ -86,6 +91,28 @@ def deltatimeprint(d): else: return strfdeltah(d) +@register.filter +def c2userid(user): + try: + thetoken = c2_open(user) + except NoTokenError: + return 0 + + c2userid = c2stuff.get_userid(thetoken) + + return c2userid + +@register.filter +def rkuserid(user): + try: + thetoken = runkeeper_open(user) + except NoTokenError: + return 0 + + rkuserid = runkeeperstuff.get_userid(thetoken) + + return rkuserid + @register.filter def courselength(course): return course_length(course) diff --git a/rowers/urls.py b/rowers/urls.py index 149a5bc1..ac80f4a7 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -138,8 +138,8 @@ urlpatterns = [ url(r'^list-workouts/team/(?P\d+)/$',views.workouts_view), url(r'^(?P\d+)/list-workouts/$',views.workouts_view), url(r'^(?P\d+)/list-workouts/(?P\w+.*)/(?P\w+.*)$',views.workouts_view), - url(r'^u/(?P\d+)/list-workouts/$',views.workouts_view), - url(r'^u/(?P\d+)/list-workouts/(?P\w+.*)/(?P\w+.*)$',views.workouts_view), + url(r'^list-workouts/user/(?P\d+)/$',views.workouts_view), + url(r'^list-workouts/(?P\w+.*)/(?P\w+.*)/user/(?P\d+)/$',views.workouts_view), url(r'^list-workouts/(?P\w+.*)/(?P\w+.*)$',views.workouts_view), url(r'^virtualevents$',views.virtualevents_view), url(r'^virtualevent/create$',views.virtualevent_create_view), @@ -302,16 +302,22 @@ urlpatterns = [ url(r'^workout/c2import/$',views.workout_c2import_view), url(r'^workout/c2list/$',views.workout_c2import_view), url(r'^workout/c2list/(?P\d+)$',views.workout_c2import_view), + url(r'^workout/c2list/user/(?P\d+)$',views.workout_c2import_view), + url(r'^workout/c2list/(?P\d+)/user/(?P\d+)$',views.workout_c2import_view), url(r'^workout/stravaimport/$',views.workout_stravaimport_view), + url(r'^workout/stravaimport/user/(?P\d+)$',views.workout_stravaimport_view), url(r'^workout/c2import/all/$',views.workout_getc2workout_all), url(r'^workout/c2import/all/(?P\d+)$',views.workout_getc2workout_all), url(r'^workout/(?P\w+.*)import/(?P\d+)/$',views.workout_getimportview), url(r'^workout/stravaimport/all/$',views.workout_getstravaworkout_all), url(r'^workout/stravaimport/next/$',views.workout_getstravaworkout_next), url(r'^workout/sporttracksimport/$',views.workout_sporttracksimport_view), + url(r'^workout/sporttracksimport/user/(?P\d+)$',views.workout_sporttracksimport_view), url(r'^workout/sporttracksimport/all/$',views.workout_getsporttracksworkout_all), url(r'^workout/polarimport/$',views.workout_polarimport_view), + url(r'^workout/polarimport/user/(?P\d+)/',views.workout_polarimport_view), url(r'^workout/runkeeperimport/$',views.workout_runkeeperimport_view), + url(r'^workout/runkeeperimport/user/(?P\d+)$',views.workout_runkeeperimport_view), url(r'^workout/underarmourimport/$',views.workout_underarmourimport_view), # url(r'^workout/(?P\d+)/deleteconfirm$',views.workout_delete_confirm_view), url(r'^workout/(?P\d+)/c2uploadw/$',views.workout_c2_upload_view), diff --git a/rowers/views.py b/rowers/views.py index 78945187..e86ec07f 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -1277,22 +1277,24 @@ def workout_tcxemail_view(request,id=0): if r.emailbounced: message = "Please check your email address first. Email to this address bounced." messages.error(request, message) - return HttpResponseRedirect( - reverse(workout_export_view, - kwargs = { - 'id':str(w.id), - }) - ) + + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':int(id) + }) + + return HttpResponseRedirect(url) + tcxfile,tcxmessg = stravastuff.createstravaworkoutdata(w,dozip=False) if tcxfile == 0: message = "Something went wrong (TCX export) "+tcxmessg messages.error(request,message) - url = reverse(workout_export_view, - kwargs = { - 'id':str(w.id), - }) + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':int(id) + }) return HttpResponseRedirect(url) if tcxfile: res = myqueue(queuehigh,handle_sendemailtcx, @@ -1307,19 +1309,19 @@ def workout_tcxemail_view(request,id=0): messages.info(request,successmessage) - url = reverse(workout_export_view, - kwargs = { - 'id':str(w.id), - }) + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':int(id) + }) response = HttpResponseRedirect(url) else: message = "You are not allowed to export this workout" messages.error(request,message) - url = reverse(workout_export_view, - kwargs = { - 'id':str(w.id), - }) + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':int(id) + }) response = HttpResponseRedirect(url) return response @@ -1375,12 +1377,12 @@ def workout_gpxemail_view(request,id=0): if r.emailbounced: message = "Please check your email address first. Email to this address bounced." messages.error(request, message) - return HttpResponseRedirect( - reverse(workout_export_view, - kwargs = { - 'id':str(w.id), - }) - ) + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':int(id) + }) + + return HttpResponseRedirect(url) w = get_workout(id) @@ -1399,17 +1401,17 @@ def workout_gpxemail_view(request,id=0): successmessage = "The GPX file was sent to you per email" messages.info(request,successmessage) - url = reverse(workout_export_view, - kwargs = { - 'id':str(w.id), - }) + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':int(id) + }) response = HttpResponseRedirect(url) else: message = "You are not allowed to export this workout" messages.error(request,message) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1425,7 +1427,7 @@ def workouts_summaries_email_view(request): message = "Please check your email address first. Email to this address bounced." messages.error(request, message) return HttpResponseRedirect( - reverse(workout_export_view, + reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1468,7 +1470,7 @@ def workout_csvemail_view(request,id=0): message = "Please check your email address first. Email to this address bounced." messages.error(request, message) return HttpResponseRedirect( - reverse(workout_export_view, + reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1486,7 +1488,7 @@ def workout_csvemail_view(request,id=0): successmessage = "The CSV file was sent to you per email" messages.info(request,successmessage) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1495,7 +1497,7 @@ def workout_csvemail_view(request,id=0): else: message = "You are not allowed to export this workout" messages.error(request,message) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1577,7 +1579,7 @@ def workout_tp_upload_view(request,id=0): message = "You are not allowed to export this workout to TP" messages.error(request,message) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1621,7 +1623,7 @@ def workout_strava_upload_view(request,id=0): os.remove(tcxfile) except WindowsError: pass - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1651,14 +1653,14 @@ def workout_strava_upload_view(request,id=0): messages.error(request,message) w.uploadedtostrava = -1 w.save() - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) response = HttpResponseRedirect(url) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), } @@ -1670,7 +1672,7 @@ def workout_strava_upload_view(request,id=0): w.uploadedtostrava = -1 w.save() os.remove(tcxfile) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1697,10 +1699,11 @@ def workout_c2_upload_view(request,id=0): messages.info(request,message) - url = reverse(workout_export_view, - kwargs = { - 'id':str(w.id), - }) + url = reverse(r.defaultlandingpage, + kwargs = { + 'id':int(id) + }) + response = HttpResponseRedirect(url) @@ -1725,7 +1728,7 @@ def workout_runkeeper_upload_view(request,id=0): if not data: message = "Data error" messages.error(request,message) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1761,7 +1764,7 @@ def workout_runkeeper_upload_view(request,id=0): message = "You are not authorized to upload this workout" messages.error(request,message) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1787,7 +1790,7 @@ def workout_underarmour_upload_view(request,id=0): if not data: message = "Data error" messages.error(request,message) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1824,7 +1827,7 @@ def workout_underarmour_upload_view(request,id=0): message = "You are not authorized to upload this workout" messages.error(request,message) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1851,7 +1854,7 @@ def workout_sporttracks_upload_view(request,id=0): if not data: message = "Data error" messages.error(request,message) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -1889,7 +1892,7 @@ def workout_sporttracks_upload_view(request,id=0): message = "You are not authorized to upload this workout" messages.error(request,message) - url = reverse(workout_export_view, + url = reverse(r.defaultlandingpage, kwargs = { 'id':str(w.id), }) @@ -9543,9 +9546,16 @@ def workout_add_chart_view(request,id,plotnr=1): # The page where you select which Strava workout to import @login_required() -def workout_stravaimport_view(request,message=""): +def workout_stravaimport_view(request,message="",userid=0): res = stravastuff.get_strava_workout_list(request.user) + r = getrequestrower(request,userid=userid) + + if r.user != request.user: + messages.info(request,"You cannot import other people's workouts from Concept2") + + r = getrower(request.user) + if (res.status_code != 200): if (res.status_code == 401): r = getrower(request.user) @@ -9605,10 +9615,25 @@ def workout_stravaimport_view(request,message=""): res = dict(zip(keys,values)) workouts.append(res) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':reverse(workout_stravaimport_view), + 'name':'Strava' + }, + ] + + + r = getrower(request.user) return render(request,'strava_list_import.html', {'workouts':workouts, + 'rower':r, 'active':'nav-workouts', + 'breadcrumbs':breadcrumbs, 'teams':get_my_teams(request.user), }) @@ -9616,7 +9641,7 @@ def workout_stravaimport_view(request,message=""): # The page where you select which RunKeeper workout to import @login_required() -def workout_runkeeperimport_view(request,message=""): +def workout_runkeeperimport_view(request,message="",userid=0): res = runkeeperstuff.get_runkeeper_workout_list(request.user) if (res.status_code != 200): if (res.status_code == 401): @@ -9632,29 +9657,46 @@ def workout_runkeeperimport_view(request,message=""): else: url = reverse(workouts_view) return HttpResponseRedirect(url) - else: - workouts = [] - for item in res.json()['items']: - d = int(float(item['total_distance'])) - i = getidfromuri(item['uri']) - ttot = str(datetime.timedelta(seconds=int(float(item['duration'])))) - s = item['start_time'] - r = item['type'] - keys = ['id','distance','duration','starttime','type'] - values = [i,d,ttot,s,r] - res = dict(zip(keys,values)) - workouts.append(res) - return render(request,'runkeeper_list_import.html', - {'workouts':workouts, - 'teams':get_my_teams(request.user), - }) + workouts = [] + for item in res.json()['items']: + d = int(float(item['total_distance'])) + i = getidfromuri(item['uri']) + ttot = str(datetime.timedelta(seconds=int(float(item['duration'])))) + s = item['start_time'] + r = item['type'] + keys = ['id','distance','duration','starttime','type'] + values = [i,d,ttot,s,r] + + res = dict(zip(keys,values)) + workouts.append(res) + + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':reverse(workout_runkeeperimport_view), + 'name':'Runkeeper' + } + ] + + r = getrower(request.user) + + return render(request,'runkeeper_list_import.html', + {'workouts':workouts, + 'rower':r, + 'active':'nav-workouts', + 'breadcrumbs':breadcrumbs, + 'teams':get_my_teams(request.user), + }) return HttpResponse(res) # The page where you select which RunKeeper workout to import @login_required() -def workout_underarmourimport_view(request,message=""): +def workout_underarmourimport_view(request,message="",userid=0): res = underarmourstuff.get_underarmour_workout_list(request.user) if (res.status_code != 200): if (res.status_code == 401): @@ -9666,38 +9708,53 @@ def workout_underarmourimport_view(request,message=""): messages.error(request,message) url = reverse(workouts_view) return HttpResponseRedirect(url) - else: - workouts = [] - items = res.json()['_embedded']['workouts'] - for item in items: - s = item['start_datetime'] - i,r = underarmourstuff.get_idfromuri(request.user,item['_links']) - n = item['name'] - try: - d = item['aggregates']['distance_total'] - except KeyError: - d = 0 - try: - ttot = item['aggregates']['active_time_total'] - except KeyError: - ttot = 0 - - keys = ['id','distance','duration','starttime','type'] - values = [i,d,ttot,s,r] - thedict = dict(zip(keys,values)) - - workouts.append(thedict) - return render(request,'underarmour_list_import.html', - {'workouts':workouts, - 'teams':get_my_teams(request.user), - }) + workouts = [] + items = res.json()['_embedded']['workouts'] + for item in items: + s = item['start_datetime'] + i,r = underarmourstuff.get_idfromuri(request.user,item['_links']) + n = item['name'] + try: + d = item['aggregates']['distance_total'] + except KeyError: + d = 0 + try: + ttot = item['aggregates']['active_time_total'] + except KeyError: + ttot = 0 + + keys = ['id','distance','duration','starttime','type'] + values = [i,d,ttot,s,r] + thedict = dict(zip(keys,values)) + + workouts.append(thedict) + + rower = getrower(request.user) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':reverse(workout_c2import_view), + 'name':'Concept2' + }, + ] + + return render(request,'underarmour_list_import.html', + {'workouts':workouts, + 'breadcrumbs':breadcrumbs, + 'rower':rower, + 'active':'nav-workouts', + 'teams':get_my_teams(request.user), + }) return HttpResponse(res) # the page where you select which Polar workout to Import @login_required() -def workout_polarimport_view(request): +def workout_polarimport_view(request,userid=0): exercises = polarstuff.get_polar_workouts(request.user) workouts = [] @@ -9727,10 +9784,25 @@ def workout_polarimport_view(request): res = dict(zip(keys,values)) workouts.append(res) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':reverse(workout_polarimport_view), + 'name':'Polar' + }, + ] + + r = getrower(request.user) return render(request, 'polar_list_import.html', { 'workouts':workouts, + 'active':'nav-workouts', + 'rower':r, + 'breadcrumbs':breadcrumbs, 'teams':get_my_teams(request.user), }) @@ -9739,7 +9811,9 @@ def workout_polarimport_view(request): # The page where you select which SportTracks workout to import @login_required() -def workout_sporttracksimport_view(request,message=""): +def workout_sporttracksimport_view(request,message="",userid=0): + + res = sporttracksstuff.get_sporttracks_workout_list(request.user) if (res.status_code != 200): if (res.status_code == 401): @@ -9756,31 +9830,48 @@ def workout_sporttracksimport_view(request,message=""): else: url = reverse(workouts_view) return HttpResponseRedirect(url) - else: - workouts = [] - r = getrower(request.user) - stids = [int(getidfromuri(item['uri'])) for item in res.json()['items']] - knownstids = uniqify([ - w.uploadedtosporttracks for w in Workout.objects.filter(user=r) - ]) - newids = [stid for stid in stids if not stid in knownstids] - for item in res.json()['items']: - d = int(float(item['total_distance'])) - i = int(getidfromuri(item['uri'])) - if i in knownstids: - nnn = '' - else: - nnn = 'NEW' - n = item['name'] - ttot = str(datetime.timedelta(seconds=int(float(item['duration'])))) - s = item['start_time'] - r = item['type'] - keys = ['id','distance','duration','starttime','type','name','new'] - values = [i,d,ttot,s,r,n,nnn] - res = dict(zip(keys,values)) - workouts.append(res) - return render(request,'sporttracks_list_import.html', + + workouts = [] + r = getrower(request.user) + stids = [int(getidfromuri(item['uri'])) for item in res.json()['items']] + knownstids = uniqify([ + w.uploadedtosporttracks for w in Workout.objects.filter(user=r) + ]) + newids = [stid for stid in stids if not stid in knownstids] + for item in res.json()['items']: + d = int(float(item['total_distance'])) + i = int(getidfromuri(item['uri'])) + if i in knownstids: + nnn = '' + else: + nnn = 'NEW' + n = item['name'] + ttot = str(datetime.timedelta(seconds=int(float(item['duration'])))) + s = item['start_time'] + r = item['type'] + keys = ['id','distance','duration','starttime','type','name','new'] + values = [i,d,ttot,s,r,n,nnn] + res = dict(zip(keys,values)) + workouts.append(res) + + r = getrower(request.user) + + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':reverse(workout_sporttracksimport_view), + 'name':'SportTracks' + }, + ] + + return render(request,'sporttracks_list_import.html', {'workouts':workouts, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', + 'rower':r, 'teams':get_my_teams(request.user), }) @@ -9864,7 +9955,15 @@ def workout_getc2workout_all(request,page=1,message=""): # List of workouts available on Concept2 logbook - for import @login_required() -def workout_c2import_view(request,page=1,message=""): +def workout_c2import_view(request,page=1,userid=0,message=""): + + r = getrequestrower(request,userid=userid) + + if r.user != request.user: + messages.info(request,"You cannot import other people's workouts from Concept2") + + r = getrower(request.user) + try: thetoken = c2_open(request.user) except NoTokenError: @@ -9877,38 +9976,57 @@ def workout_c2import_view(request,page=1,message=""): messages.error(request,message) url = reverse(workouts_view) return HttpResponseRedirect(url) - else: - workouts = [] - r = getrower(request.user) - c2ids = [item['id'] for item in res.json()['data']] - knownc2ids = uniqify([ - w.uploadedtoc2 for w in Workout.objects.filter(user=r) - ]) - newids = [c2id for c2id in c2ids if not c2id in knownc2ids] - for item in res.json()['data']: - d = item['distance'] - i = item['id'] - ttot = item['time_formatted'] - s = item['date'] - r = item['type'] - s2 = item['source'] - c = item['comments'] - if i in knownc2ids: - nnn = '' - else: - nnn = 'NEW' - keys = ['id','distance','duration','starttime','rowtype','source','comment','new'] - values = [i,d,ttot,s,r,s2,c,nnn] - res = dict(zip(keys,values)) - workouts.append(res) - - return render(request, - 'c2_list_import2.html', - {'workouts':workouts, - 'teams':get_my_teams(request.user), - 'page':page, - }) + workouts = [] + c2ids = [item['id'] for item in res.json()['data']] + knownc2ids = uniqify([ + w.uploadedtoc2 for w in Workout.objects.filter(user=r) + ]) + newids = [c2id for c2id in c2ids if not c2id in knownc2ids] + for item in res.json()['data']: + d = item['distance'] + i = item['id'] + ttot = item['time_formatted'] + s = item['date'] + r = item['type'] + s2 = item['source'] + c = item['comments'] + if i in knownc2ids: + nnn = '' + else: + nnn = 'NEW' + keys = ['id','distance','duration','starttime','rowtype','source','comment','new'] + values = [i,d,ttot,s,r,s2,c,nnn] + res = dict(zip(keys,values)) + workouts.append(res) + + + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':reverse(workout_c2import_view), + 'name':'Concept2' + }, + { + 'url':reverse(workout_c2import_view,kwargs={'page':page}), + 'name':'Page '+str(page) + } + ] + + r = getrower(request.user) + + return render(request, + 'c2_list_import2.html', + {'workouts':workouts, + 'rower':r, + 'active':'nav-workouts', + 'breadcrumbs':breadcrumbs, + 'teams':get_my_teams(request.user), + 'page':page, + }) importsources = { 'c2':c2stuff, From c693655966a8caf703a6789988c9a29496678750 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 9 Oct 2018 20:35:37 +0200 Subject: [PATCH 62/95] force curve, comments, edit --- rowers/templates/forcecurve_single.html | 60 +-- rowers/templates/list_workouts.html | 7 +- rowers/templates/menu_workout.html | 13 + rowers/templates/workout_comments.html | 192 +++------ rowers/templates/workout_form.html | 322 ++++----------- rowers/templatetags/rowerfilters.py | 10 +- rowers/urls.py | 5 +- rowers/views.py | 215 +++++----- static/css/rowsandall2.css | 509 ++++++++++++------------ 9 files changed, 552 insertions(+), 781 deletions(-) diff --git a/rowers/templates/forcecurve_single.html b/rowers/templates/forcecurve_single.html index c45300c4..e99669c3 100644 --- a/rowers/templates/forcecurve_single.html +++ b/rowers/templates/forcecurve_single.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% load tz %} @@ -6,7 +6,7 @@ {% block title %} Force Curve Plot {% endblock %} {% localtime on %} -{% block content %} +{% block main %} {{ js_res | safe }} {{ css_res| safe }} @@ -19,54 +19,34 @@ {{ the_script |safe }} +

    Empower Force Curve

    - - -
  • +
  • + {{ the_div|safe }} -
  • - - -

     

    - - - -
    - - {{ the_div|safe }} - -
    + + {% endblock %} {% endlocaltime %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/list_workouts.html b/rowers/templates/list_workouts.html index 899e2273..2ba41c61 100644 --- a/rowers/templates/list_workouts.html +++ b/rowers/templates/list_workouts.html @@ -167,7 +167,6 @@ xo Max HR {% if not team %}   -   {% else %} Owner @@ -216,11 +215,7 @@ xo {{ workout.duration |durationprint:"%H:%M:%S.%f" }} {{ workout.averagehr }} {{ workout.maxhr }} - {% if not team %} - - Export - - {% else %} + {% if team %} {{ workout.user.user.first_name }} diff --git a/rowers/templates/menu_workout.html b/rowers/templates/menu_workout.html index c045c36d..b39e243d 100644 --- a/rowers/templates/menu_workout.html +++ b/rowers/templates/menu_workout.html @@ -28,6 +28,14 @@ {% endif %} + {% if user.is_authenticated %} +
  • + +  Comments + ({{ workout|aantalcomments }}) + +
  • + {% endif %}
  •  Statistics @@ -62,6 +70,11 @@  Map
  • +
  • + +  Force Curve + +
  •  OTW Power diff --git a/rowers/templates/workout_comments.html b/rowers/templates/workout_comments.html index 053c7c0d..0a988423 100644 --- a/rowers/templates/workout_comments.html +++ b/rowers/templates/workout_comments.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% load tz %} @@ -7,19 +7,13 @@ {% include "monitorjobs.html" %} {% endblock %} -{% block title %}Change Workout {% endblock %} +{% block title %}Comment Workout {% endblock %} -{% block content %} -
    -
    - {% if form.errors %} -

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

    - {% endif %} - -

    Comments {{ workout.name }}

    +{% block main %} +

    Comments {{ workout.name }}

    +
    - {% for c in comments %} -
    -
    - {{ c.created }} - {{ c.user.first_name }} {{ c.user.last_name }} -
    -
    -
    -
    - {{ c.comment }} -
    +
  • +
  • + {% for c in comments %} + {{ c.created }} + {{ c.user.first_name }} {{ c.user.last_name }} +
    +
    + {{ c.comment }}
    -
  • {% endfor %} - -
    {{ form.as_table }}
    {% csrf_token %} -
    - -
    +
    -
    - - - -
    -

    Images linked to this workout

    - - {% if graphs1 %} - - {% for graph in graphs1 %} - {% if forloop.counter == 1 %} -
    - {% elif forloop.counter == 3 %} -
    - - {{ graph.filename }} -
    - - {% else %} -
    - - {{ graph.filename }} -
    - {% endif %} + {{ graph.filename }} + + {% endfor %} - - {% for graph in graphs2 %} - {% if forloop.counter == 1 %} -
    - - {{ graph.filename }} -
    - {% elif forloop.counter == 3 %} -
    - - {{ graph.filename }} -
    - - {% else %} -
    - - {{ graph.filename }} -
    - {% endif %} - {% endfor %} - - - +
  • +

    Workout Summary

    + +

    +

    +        {{ workout.summary }}
    +      
    +

    + +
  • - {% else %} -

    No graphs found

    - {% endif %} -
    - - - -
    -

    Workout Summary

    +
  • + + -

    -

    -      {{ workout.summary }}
    -    
    -

    + {{ gmscript |safe }} -
  • - - - -
    - - - - {{ gmscript |safe }} - - -
    - {{ gmdiv|safe }} -
    -
    +
    + {{ gmdiv|safe }} +
    + + {% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/workout_form.html b/rowers/templates/workout_form.html index 9cdb027c..c59810a9 100644 --- a/rowers/templates/workout_form.html +++ b/rowers/templates/workout_form.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% load tz %} @@ -42,261 +42,105 @@ $('#id_workouttype').change(); {% endblock %} -{% block content %} -
    +{% block main %} - {% if form.errors %} -

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

    - {% endif %} - -

    Edit Workout {{ workout.name }}

    -
    -
    -

    - Delete -

    -
    -
    -

    - Export - -

    -
    -
    -

    - Advanced -

    - Advanced Functionality (More interactive Charts) - -
    -
    -
    -
    -

    - Workflow View -

    -
    -
    -

    - Map View -

    -
    -
    -

    - Statistics -

    -
    -
    -
    - - - {% localtime on %} - {% if workout.user.user != user %} +

    Edit Workout {{ workout.name }}

    +
    + + {% localtime on %} + {% if workout.user.user != user %} - - {% endif %} - -{% endlocaltime %} - - - - - - - - - - - - - - -
    Rower:{{ rower.user.first_name }} {{ rower.user.last_name }}
    Date/Time:{{ workout.startdatetime|localtime}}
    Distance:{{ workout.distance }}m
    Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
    Public link to this workout - https://rowsandall.com/rowers/workout/{{ workout.id }} -
    Comments - Comment ({{ aantalcomments }}) -
    Public link to interactive chart - https://rowsandall.com/rowers/workout/{{ workout.id }}/interactiveplot - -
    -
    -
    + + {% endif %} + Date/Time:{{ workout.startdatetime|localtime}} + {% endlocaltime %} + + Distance:{{ workout.distance }}m + + Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }} + + Public link to this workout + + https://rowsandall.com/rowers/workout/{{ workout.id }} + + + + +
  • + {% if form.errors %} +

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

    + {% endif %} + +
    - - {{ form.as_table }} -
    - {% csrf_token %} -
    + + {{ form.as_table }} +
    + {% csrf_token %} -
    -
    -
  • -
    -
    -

    Images linked to this workout

    -
    -
    -

    - Add Time Plot -

    -
    - - -
    -
    -

    - Attach Image -

    -
    -
    -

    - Power Pie Chart -

    -
    -
    -

    Generating images takes roughly 1 second per minute - of your workout's duration. Please reload after a minute or so.

    -
    -
    - {% if graphs1 %} + + +
  • - {% for graph in graphs1 %} - {% if forloop.counter == 1 %} -
    - - {{ graph.filename }} -
    - {% elif forloop.counter == 3 %} -
    - - {{ graph.filename }} -
    +

    Workout Summary

    - {% else %} -
    - - {{ graph.filename }} -
    - {% endif %} - {% endfor %} - - - {% for graph in graphs2 %} - {% if forloop.counter == 1 %} -
    - - {{ graph.filename }} -
    - {% elif forloop.counter == 3 %} -
    - - {{ graph.filename }} -
    - - {% else %} -
    - - {{ graph.filename }} -
    - {% endif %} - {% endfor %} - - - - - {% else %} -

    No graphs found

    - {% endif %} -
  • - - - -
    -
    -

    Workout Summary

    - -

    -

    -          {{ workout.summary }}
    -        
    -

    -
    -

    - Update Summary -

    -
    -
    -
    -
    - -
    -
    -

     

    - -
    -
    -
    - - - -
    +

    +

    +        {{ workout.summary }}
    +      
    +

    +

    + Update Summary +

    + + {% if mapdiv %} +
  • - - +
    {{ mapdiv|safe }} - +
    {{ mapscript|safe }} -
  • -
    + + {% endif %} + {% for graph in graphs %} +
  • + + {{ graph.filename }} + +
  • + {% endfor %} + {% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py index 6a8289c0..30a23bc7 100644 --- a/rowers/templatetags/rowerfilters.py +++ b/rowers/templatetags/rowerfilters.py @@ -8,7 +8,7 @@ import datetime import re register = template.Library() from rowers.utils import calculate_age -from rowers.models import course_length +from rowers.models import course_length,WorkoutComment from rowers.plannedsessions import ( race_can_register, race_can_submit,race_rower_status ) @@ -55,6 +55,14 @@ def secondstotimestring(tdelta): return res +@register.filter +def aantalcomments(workout): + comments = WorkoutComment.objects.filter(workout=workout) + + aantalcomments = len(comments) + + return aantalcomments + @register.filter def water(workout): try: diff --git a/rowers/urls.py b/rowers/urls.py index ac80f4a7..e7b87ba1 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -226,7 +226,7 @@ urlpatterns = [ url(r'^workout/(?P\d+)/task$',views.workout_test_task_view), url(r'^workout/(?P\d+)/forcecurve$',views.workout_forcecurve_view), url(r'^workout/(?P\d+)/unsubscribe$',views.workout_unsubscribe_view), - url(r'^workout/(?P\d+)/export$',views.workout_export_view), +# url(r'^workout/(?P\d+)/export$',views.workout_export_view), url(r'^workout/(?P\d+)/comment$',views.workout_comment_view), url(r'^workout/(?P\d+)/emailtcx$',views.workout_tcxemail_view), url(r'^workout/(?P\d+)/emailgpx$',views.workout_gpxemail_view), @@ -328,8 +328,7 @@ urlpatterns = [ url(r'^workout/(?P\d+)/underarmouruploadw/$',views.workout_underarmour_upload_view), url(r'^workout/(?P\d+)/tpuploadw/$',views.workout_tp_upload_view), url(r'^multi-compare$',views.multi_compare_view), - url(r'^user-boxplot/(?P\d+)$',views.boxplot_view), - url(r'^user-boxplot/$',views.boxplot_view), + url(r'^user-boxplot/user/(?P\d+)$',views.boxplot_view), url(r'^user-boxplot$',views.boxplot_view), url(r'^user-boxplot-data$',views.boxplot_view_data), url(r'^user-multiflex/(?P\d+)$',views.multiflex_view), diff --git a/rowers/views.py b/rowers/views.py index e86ec07f..d2179531 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -2830,10 +2830,32 @@ def workout_forcecurve_view(request,id=0,workstrokesonly=False): script,div,js_resources,css_resources = interactive_forcecurve([row], workstrokesonly=workstrokesonly) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(row.id) + }, + { + 'url':reverse(workout_forcecurve_view,kwargs={'id':id}), + 'name': 'Empower Force Curve' + } + + ] + + r = getrower(request.user) + return render(request, 'forcecurve_single.html', { 'the_script':script, + 'rower':r, + 'workout':row, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', 'the_div':div, 'js_res': js_resources, 'css_res':css_resources, @@ -8823,7 +8845,7 @@ def workout_unsubscribe_view(request,id=0): @login_required() def workout_comment_view(request,id=0): w = get_workout(id) - + if w.privacy == 'private' and w.user.user != request.user: return HttpResponseForbidden("Permission error") @@ -8903,26 +8925,36 @@ def workout_comment_view(request,id=0): except: pass + rower = getrower(request.user) - if (len(g)<=3): - return render(request, - 'workout_comments.html', - {'workout':w, - 'teams':get_my_teams(request.user), - 'graphs1':g[0:3], - 'comments':comments, - 'form':form, - }) - else: - return render(request, - 'workout_comments.html', - {'workout':w, - 'teams':get_my_teams(request.user), - 'graphs1':g[0:3], - 'graphs1':g[3:6], - 'comments':comments, - 'form':form, - }) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(w.id) + }, + { + 'url':reverse(workout_comment_view,kwargs={'id':id}), + 'name': 'Comments' + } + + ] + + + return render(request, + 'workout_comments.html', + {'workout':w, + 'rower':rower, + 'breadcrumbs':breadcrumbs, + 'active':'nav-workouts', + 'teams':get_my_teams(request.user), + 'graphs':g, + 'comments':comments, + 'form':form, + }) # for ajax calls def course_map_view(request,id=0): @@ -9083,6 +9115,9 @@ def workout_edit_view(request,id=0,message="",successmessage=""): row = get_workout(id) + if (checkworkoutuser(request.user,row)==False): + raise PermissionDenied("Access denied") + form = WorkoutForm(instance=row) if request.method == 'POST': @@ -9142,50 +9177,43 @@ def workout_edit_view(request,id=0,message="",successmessage=""): thetimezone = 'UTC' - # check if user is owner of this workout - if checkworkoutuser(request.user,row): - row.name = name - row.date = date - row.starttime = starttime - row.startdatetime = startdatetime - row.workouttype = workouttype - row.weightcategory = weightcategory - row.notes = notes - row.duration = duration - row.distance = distance - row.boattype = boattype - row.privacy = privacy - row.rankingpiece = rankingpiece - row.timezone = thetimezone - try: - row.save() - except IntegrityError: - pass - # change data in csv file - r = rdata(row.csvfilename) - if r == 0: - return HttpResponse("Error: CSV Data File Not Found") - r.rowdatetime = startdatetime - r.write_csv(row.csvfilename,gzip=True) - dataprep.update_strokedata(id,r.df) - successmessage = "Changes saved" + row.name = name + row.date = date + row.starttime = starttime + row.startdatetime = startdatetime + row.workouttype = workouttype + row.weightcategory = weightcategory + row.notes = notes + row.duration = duration + row.distance = distance + row.boattype = boattype + row.privacy = privacy + row.rankingpiece = rankingpiece + row.timezone = thetimezone + try: + row.save() + except IntegrityError: + pass + # change data in csv file + + r = rdata(row.csvfilename) + if r == 0: + return HttpResponse("Error: CSV Data File Not Found") + r.rowdatetime = startdatetime + r.write_csv(row.csvfilename,gzip=True) + dataprep.update_strokedata(id,r.df) + successmessage = "Changes saved" - if rankingpiece: - dataprep.runcpupdate(row.user,type=row.workouttype) + if rankingpiece: + dataprep.runcpupdate(row.user,type=row.workouttype) - messages.info(request,successmessage) - url = reverse(workout_edit_view, - kwargs = { - 'id':str(row.id), - }) - response = HttpResponseRedirect(url) - else: - message = "You are not allowed to change this workout" - messages.error(request,message) - url = reverse(workouts_view) - - response = HttpResponseRedirect(url) + messages.info(request,successmessage) + url = reverse(workout_edit_view, + kwargs = { + 'id':str(row.id), + }) + response = HttpResponseRedirect(url) #else: # form not POSTed form = WorkoutForm(instance=row) @@ -9203,15 +9231,6 @@ def workout_edit_view(request,id=0,message="",successmessage=""): pass - # check if user is owner of this workout - - comments = WorkoutComment.objects.filter(workout=row) - - aantalcomments = len(comments) - - if (checkworkoutuser(request.user,row)==False): - raise PermissionDenied("Access denied") - # create interactive plot f1 = row.csvfilename u = row.user.user @@ -9245,34 +9264,40 @@ def workout_edit_view(request,id=0,message="",successmessage=""): except KeyError: pass + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(row.id) + }, + { + 'url':reverse(workout_edit_view,kwargs={'id':id}), + 'name': 'Edit' + } + + ] + + r = getrower(request.user) + # render page - if (len(g)<=3): - return render(request, 'workout_form.html', - {'form':form, - 'workout':row, - 'teams':get_my_teams(request.user), - 'graphs1':g[0:3], - 'mapscript':mapscript, - 'aantalcomments':aantalcomments, - 'mapdiv':mapdiv, - 'rower':r, - }) - - else: - return render(request, 'workout_form.html', - {'form':form, - 'teams':get_my_teams(request.user), - 'workout':row, - 'graphs1':g[0:3], - 'graphs2':g[3:6], - 'mapscript':mapscript, - 'aantalcomments':aantalcomments, - 'mapdiv':mapdiv, - 'rower':r, - }) + return render(request, 'workout_form.html', + {'form':form, + 'workout':row, + 'teams':get_my_teams(request.user), + 'graphs':g, + 'breadcrumbs':breadcrumbs, + 'rower':r, + 'active':'nav-workouts', + 'mapscript':mapscript, + 'mapdiv':mapdiv, + 'rower':r, + }) + - return HttpResponseRedirect(url) @login_required() def workout_map_view(request,id=0): diff --git a/static/css/rowsandall2.css b/static/css/rowsandall2.css index bf6518fa..a55eb415 100644 --- a/static/css/rowsandall2.css +++ b/static/css/rowsandall2.css @@ -332,6 +332,261 @@ th.rotate > div > span { color: #fcd3a5; } +/* CSS talk bubble */ +.talk-bubble { + margin: 40px; + display: inline-block; + position: relative; + width: 200px; + height: auto; + background-color: lightyellow; +} +.border{ + border: 8px solid #666; +} +.round{ + border-radius: 30px; + -webkit-border-radius: 30px; + -moz-border-radius: 30px; + +} + +/* Right triangle placed top left flush. */ +.tri-right.border.left-top:before { + content: ' '; + position: absolute; + width: 0; + height: 0; + left: -40px; + right: auto; + top: -8px; + bottom: auto; + border: 32px solid; + border-color: #666 transparent transparent transparent; +} +.tri-right.left-top:after{ + content: ' '; + position: absolute; + width: 0; + height: 0; + left: -20px; + right: auto; + top: 0px; + bottom: auto; + border: 22px solid; + border-color: lightyellow transparent transparent transparent; +} + +/* Right triangle, left side slightly down */ +.tri-right.border.left-in:before { + content: ' '; + position: absolute; + width: 0; + height: 0; + left: -40px; + right: auto; + top: 30px; + bottom: auto; + border: 20px solid; + border-color: #666 #666 transparent transparent; +} +.tri-right.left-in:after{ + content: ' '; + position: absolute; + width: 0; + height: 0; + left: -20px; + right: auto; + top: 38px; + bottom: auto; + border: 12px solid; + border-color: lightyellow lightyellow transparent transparent; +} + +/*Right triangle, placed bottom left side slightly in*/ +.tri-right.border.btm-left:before { + content: ' '; + position: absolute; + width: 0; + height: 0; + left: -8px; + right: auto; + top: auto; + bottom: -40px; + border: 32px solid; + border-color: transparent transparent transparent #666; +} +.tri-right.btm-left:after{ + content: ' '; + position: absolute; + width: 0; + height: 0; + left: 0px; + right: auto; + top: auto; + bottom: -20px; + border: 22px solid; + border-color: transparent transparent transparent lightyellow; +} + +/*Right triangle, placed bottom left side slightly in*/ +.tri-right.border.btm-left-in:before { + content: ' '; + position: absolute; + width: 0; + height: 0; + left: 30px; + right: auto; + top: auto; + bottom: -40px; + border: 20px solid; + border-color: #666 transparent transparent #666; +} +.tri-right.btm-left-in:after{ + content: ' '; + position: absolute; + width: 0; + height: 0; + left: 38px; + right: auto; + top: auto; + bottom: -20px; + border: 12px solid; + border-color: lightyellow transparent transparent lightyellow; +} + +/*Right triangle, placed bottom right side slightly in*/ +.tri-right.border.btm-right-in:before { + content: ' '; + position: absolute; + width: 0; + height: 0; + left: auto; + right: 30px; + bottom: -40px; + border: 20px solid; + border-color: #666 #666 transparent transparent; +} +.tri-right.btm-right-in:after{ + content: ' '; + position: absolute; + width: 0; + height: 0; + left: auto; + right: 38px; + bottom: -20px; + border: 12px solid; + border-color: lightyellow lightyellow transparent transparent; +} +/* + left: -8px; + right: auto; + top: auto; + bottom: -40px; + border: 32px solid; + border-color: transparent transparent transparent #666; + left: 0px; + right: auto; + top: auto; + bottom: -20px; + border: 22px solid; + border-color: transparent transparent transparent lightyellow; + +/*Right triangle, placed bottom right side slightly in*/ +.tri-right.border.btm-right:before { + content: ' '; + position: absolute; + width: 0; + height: 0; + left: auto; + right: -8px; + bottom: -40px; + border: 20px solid; + border-color: #666 #666 transparent transparent; +} +.tri-right.btm-right:after{ + content: ' '; + position: absolute; + width: 0; + height: 0; + left: auto; + right: 0px; + bottom: -20px; + border: 12px solid; + border-color: lightyellow lightyellow transparent transparent; +} + +/* Right triangle, right side slightly down*/ +.tri-right.border.right-in:before { + content: ' '; + position: absolute; + width: 0; + height: 0; + left: auto; + right: -40px; + top: 30px; + bottom: auto; + border: 20px solid; + border-color: #666 transparent transparent #666; +} +.tri-right.right-in:after{ + content: ' '; + position: absolute; + width: 0; + height: 0; + left: auto; + right: -20px; + top: 38px; + bottom: auto; + border: 12px solid; + border-color: lightyellow transparent transparent lightyellow; +} + +/* Right triangle placed top right flush. */ +.tri-right.border.right-top:before { + content: ' '; + position: absolute; + width: 0; + height: 0; + left: auto; + right: -40px; + top: -8px; + bottom: auto; + border: 32px solid; + border-color: #666 transparent transparent transparent; +} +.tri-right.right-top:after{ + content: ' '; + position: absolute; + width: 0; + height: 0; + left: auto; + right: -20px; + top: 0px; + bottom: auto; + border: 20px solid; + border-color: lightyellow transparent transparent transparent; +} + +/* talk bubble contents */ +.talktext{ + padding: 1em; + text-align: left; + line-height: 1.5em; + word-wrap: break-word; +} +.talktext p{ + /* remove webkit p margins */ + -webkit-margin-before: 0em; + -webkit-margin-after: 0em; +} + +.drop-files { + border: 1px solid #000; + color: #000; + +} + .input { font: 1.1em/1.5em sans-serif; @@ -609,260 +864,6 @@ a.wh:hover { padding: 5% 5%; } -/* CSS talk bubble */ -.talk-bubble { - margin: 40px; - display: inline-block; - position: relative; - width: 200px; - height: auto; - background-color: lightyellow; -} -.border{ - border: 8px solid #666; -} -.round{ - border-radius: 30px; - -webkit-border-radius: 30px; - -moz-border-radius: 30px; - -} - -/* Right triangle placed top left flush. */ -.tri-right.border.left-top:before { - content: ' '; - position: absolute; - width: 0; - height: 0; - left: -40px; - right: auto; - top: -8px; - bottom: auto; - border: 32px solid; - border-color: #666 transparent transparent transparent; -} -.tri-right.left-top:after{ - content: ' '; - position: absolute; - width: 0; - height: 0; - left: -20px; - right: auto; - top: 0px; - bottom: auto; - border: 22px solid; - border-color: lightyellow transparent transparent transparent; -} - -/* Right triangle, left side slightly down */ -.tri-right.border.left-in:before { - content: ' '; - position: absolute; - width: 0; - height: 0; - left: -40px; - right: auto; - top: 30px; - bottom: auto; - border: 20px solid; - border-color: #666 #666 transparent transparent; -} -.tri-right.left-in:after{ - content: ' '; - position: absolute; - width: 0; - height: 0; - left: -20px; - right: auto; - top: 38px; - bottom: auto; - border: 12px solid; - border-color: lightyellow lightyellow transparent transparent; -} - -/*Right triangle, placed bottom left side slightly in*/ -.tri-right.border.btm-left:before { - content: ' '; - position: absolute; - width: 0; - height: 0; - left: -8px; - right: auto; - top: auto; - bottom: -40px; - border: 32px solid; - border-color: transparent transparent transparent #666; -} -.tri-right.btm-left:after{ - content: ' '; - position: absolute; - width: 0; - height: 0; - left: 0px; - right: auto; - top: auto; - bottom: -20px; - border: 22px solid; - border-color: transparent transparent transparent lightyellow; -} - -/*Right triangle, placed bottom left side slightly in*/ -.tri-right.border.btm-left-in:before { - content: ' '; - position: absolute; - width: 0; - height: 0; - left: 30px; - right: auto; - top: auto; - bottom: -40px; - border: 20px solid; - border-color: #666 transparent transparent #666; -} -.tri-right.btm-left-in:after{ - content: ' '; - position: absolute; - width: 0; - height: 0; - left: 38px; - right: auto; - top: auto; - bottom: -20px; - border: 12px solid; - border-color: lightyellow transparent transparent lightyellow; -} - -/*Right triangle, placed bottom right side slightly in*/ -.tri-right.border.btm-right-in:before { - content: ' '; - position: absolute; - width: 0; - height: 0; - left: auto; - right: 30px; - bottom: -40px; - border: 20px solid; - border-color: #666 #666 transparent transparent; -} -.tri-right.btm-right-in:after{ - content: ' '; - position: absolute; - width: 0; - height: 0; - left: auto; - right: 38px; - bottom: -20px; - border: 12px solid; - border-color: lightyellow lightyellow transparent transparent; -} -/* - left: -8px; - right: auto; - top: auto; - bottom: -40px; - border: 32px solid; - border-color: transparent transparent transparent #666; - left: 0px; - right: auto; - top: auto; - bottom: -20px; - border: 22px solid; - border-color: transparent transparent transparent lightyellow; - -/*Right triangle, placed bottom right side slightly in*/ -.tri-right.border.btm-right:before { - content: ' '; - position: absolute; - width: 0; - height: 0; - left: auto; - right: -8px; - bottom: -40px; - border: 20px solid; - border-color: #666 #666 transparent transparent; -} -.tri-right.btm-right:after{ - content: ' '; - position: absolute; - width: 0; - height: 0; - left: auto; - right: 0px; - bottom: -20px; - border: 12px solid; - border-color: lightyellow lightyellow transparent transparent; -} - -/* Right triangle, right side slightly down*/ -.tri-right.border.right-in:before { - content: ' '; - position: absolute; - width: 0; - height: 0; - left: auto; - right: -40px; - top: 30px; - bottom: auto; - border: 20px solid; - border-color: #666 transparent transparent #666; -} -.tri-right.right-in:after{ - content: ' '; - position: absolute; - width: 0; - height: 0; - left: auto; - right: -20px; - top: 38px; - bottom: auto; - border: 12px solid; - border-color: lightyellow transparent transparent lightyellow; -} - -/* Right triangle placed top right flush. */ -.tri-right.border.right-top:before { - content: ' '; - position: absolute; - width: 0; - height: 0; - left: auto; - right: -40px; - top: -8px; - bottom: auto; - border: 32px solid; - border-color: #666 transparent transparent transparent; -} -.tri-right.right-top:after{ - content: ' '; - position: absolute; - width: 0; - height: 0; - left: auto; - right: -20px; - top: 0px; - bottom: auto; - border: 20px solid; - border-color: lightyellow transparent transparent transparent; -} - -/* talk bubble contents */ -.talktext{ - padding: 1em; - text-align: left; - line-height: 1.5em; - word-wrap: break-word; -} -.talktext p{ - /* remove webkit p margins */ - -webkit-margin-before: 0em; - -webkit-margin-after: 0em; -} - -.drop-files { - border: 1px solid #000; - color: #000; - -} .wrapwords{ -ms-word-break: break-all; From 40ec94d5b0f4276490b07476defcc417b72daa33 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 9 Oct 2018 22:03:07 +0200 Subject: [PATCH 63/95] multicompare view line 330 urls.py --- rowers/templates/multicompare.html | 71 +++---- rowers/templates/team_compare_select.html | 106 +++++------ rowers/templates/workout_form.html | 3 - rowers/urls.py | 4 +- rowers/views.py | 217 ++++++++++++++-------- 5 files changed, 209 insertions(+), 192 deletions(-) diff --git a/rowers/templates/multicompare.html b/rowers/templates/multicompare.html index 8901fa8c..2907d8d4 100644 --- a/rowers/templates/multicompare.html +++ b/rowers/templates/multicompare.html @@ -1,10 +1,10 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}View Comparison {% endblock %} -{% block content %} +{% block main %} +

    Interactive Comparison

    +
      +
    • +
      + {% csrf_token %} + + {{ chartform.as_table }} +
      - - - - -
      - {% include "teambuttons.html" with teamid=teamid team=team %} -
      -
      -
      -

      Interactive Comparison

      -
      -
      - - {% csrf_token %} - - {{ chartform.as_table }} -
      -

      + +
    • +
    • + +
      + {{ the_div|safe }}
      - - - -
      - - - -
      - {{ the_div|safe }} -
      -
      +
    • +
    {% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} +{% endblock %} diff --git a/rowers/templates/team_compare_select.html b/rowers/templates/team_compare_select.html index 7aba7ec6..abdde333 100644 --- a/rowers/templates/team_compare_select.html +++ b/rowers/templates/team_compare_select.html @@ -29,7 +29,9 @@ hidden.hide(); - + if (modality.val() == 'water') { + hidden.show(); + } // Setup an event listener for when the state of the // checkbox changes. @@ -68,12 +70,25 @@
      +
    • +

      Select two or more workouts on the left, set your plot settings, + and press submit

      +

      + You can use the date and search forms to search through all + workouts from this team. +

      +

      + TIP: Agree with your team members to put tags (e.g. '8x500m') + in the notes section of + your workouts. That makes it easy to search. +

      +
    • + {% if workouts %}
      - {% if workouts %} Toggle All
      - + {{ form.as_table }}
      @@ -83,54 +98,42 @@ {% endif %}
    • +

      + {% csrf_token %} + + {{ chartform.as_table }} +
      +

      +

      + +

      + +
    • +
    • + {% if team %} +
      + {% else %} + + {% endif %} + + {{ dateform.as_table }} +
      + + {{ modalityform.as_table }} +
      + {% csrf_token %}

      - {% csrf_token %} - - {{ chartform.as_table }} -
      -

      -

      - +

    • -
    • - {% if team %} -
      - {% else %} - - {% endif %} - - {{ dateform.as_table }} -
      -
    • -
    • -

      - {% csrf_token %} - -

      - -
    • -
    • - {% if team %} -
      - {% else %} - - {% endif %} - - {{ modalityform.as_table }} -
      -
    • -
    • - {% csrf_token %} -

      - -

      - -
    • {% if team %} -
      {% else %}
    • -
    • -

      Select two or more workouts on the left, set your plot settings below, - and press submit

      -

      - You can use the date and search forms above to search through all - workouts from this team. -

      -

      - TIP: Agree with your team members to put tags (e.g. '8x500m') - in the notes section of - your workouts. That makes it easy to search. -

      -
    {% endblock %} diff --git a/rowers/templates/workout_form.html b/rowers/templates/workout_form.html index c59810a9..026721ab 100644 --- a/rowers/templates/workout_form.html +++ b/rowers/templates/workout_form.html @@ -107,9 +107,6 @@ $('#id_workouttype').change(); {{ workout.summary }}

    -

    - Update Summary -

    {% if mapdiv %}
  • diff --git a/rowers/urls.py b/rowers/urls.py index e7b87ba1..15545a03 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -240,9 +240,9 @@ urlpatterns = [ url(r'^workout/(?P\d+)/edit$',views.workout_edit_view, name='workout_edit_view'), url(r'^workout/(?P\d+)/map$',views.workout_map_view), - url(r'^workout/(?P\d+)/setprivate$',views.workout_setprivate_view), +# url(r'^workout/(?P\d+)/setprivate$',views.workout_setprivate_view), url(r'^workout/(?P\d+)/updatecp$',views.workout_update_cp_view), - url(r'^workout/(?P\d+)/makepublic$',views.workout_makepublic_view), +# url(r'^workout/(?P\d+)/makepublic$',views.workout_makepublic_view), # url(r'^workout/(?P\d+)/geeky$',views.workout_geeky_view), # url(r'^workout/(?P\d+)/advanced$',views.workout_advanced_view), url(r'^workout/(?P\d+)/instroke/(?P\w+.*)$',views.instroke_chart), diff --git a/rowers/views.py b/rowers/views.py index d2179531..23949385 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -5136,21 +5136,16 @@ def team_comparison_select(request, modalities = [m[0] for m in types.workouttypes] modality = 'all' - if request.method == 'POST' and 'daterange' in request.POST: + if request.method == 'POST': dateform = DateRangeForm(request.POST) if dateform.is_valid(): startdate = dateform.cleaned_data['startdate'] enddate = dateform.cleaned_data['enddate'] startdatestring = startdate.strftime('%Y-%m-%d') enddatestring = enddate.strftime('%Y-%m-%d') - else: - dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) + request.session['startdate'] = startdatestring + request.session['enddate'] = enddatestring - - if request.method == 'POST' and 'modality' in request.POST: modalityform = TrendFlexModalForm(request.POST) if modalityform.is_valid(): modality = modalityform.cleaned_data['modality'] @@ -5171,6 +5166,19 @@ def team_comparison_select(request, request.session['modalities'] = modalities request.session['waterboattype'] = waterboattype + else: + dateform = DateRangeForm(initial={ + 'startdate':startdate, + 'enddate':enddate, + }) + modalityform = TrendFlexModalForm(initial={ + 'modality':modality, + 'waterboattype':waterboattype, + 'rankingonly':rankingonly, + }) + + + negtypes = [] for b in types.boattypes: @@ -5241,16 +5249,23 @@ def team_comparison_select(request, theid = 0 chartform = ChartParamChoiceForm(initial={'teamid':0}) - modalityform = TrendFlexModalForm(initial={ - 'modality':modality, - 'waterboattype':waterboattype, - 'rankingonly':rankingonly, - }) - messages.info(request,successmessage) messages.error(request,message) + r = getrower(request.user) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':reverse(team_comparison_select,kwargs={'teamid':teamid}), + 'name': 'Compare Select' + }, + + ] + return render(request, 'team_compare_select.html', {'workouts': workouts, 'dateform':dateform, @@ -5258,6 +5273,8 @@ def team_comparison_select(request, 'enddate':enddate, 'team':theteam, 'form':form, + 'rower':r, + 'breadcrumbs':breadcrumbs, 'active':'nav-workouts', 'chartform':chartform, 'modalityform':modalityform, @@ -5303,9 +5320,27 @@ def multi_compare_view(request): if errormessage != '': messages.error(request,errormessage) + r = getrower(request.user) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':reverse(team_comparison_select,kwargs={'teamid':teamid}), + 'name': 'Compare Select' + }, + { + 'url':reverse(multi_compare_view), + 'name': 'Comparison Chart' + } + ] return render(request,'multicompare.html', {'interactiveplot':script, 'the_div':div, + 'breadcrumbs':breadcrumbs, + 'rower':r, + 'active':'nav-workouts', 'promember':promember, 'teamid':teamid, 'chartform':chartform, @@ -5335,9 +5370,29 @@ def multi_compare_view(request): script = res[0] div = res[1] + r = getrower(request.user) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':reverse(team_comparison_select,kwargs={'teamid':teamid}), + 'name': 'Compare Select' + }, + { + 'url':reverse(multi_compare_view), + 'name': 'Comparison Chart' + } + ] + + return render(request,'multicompare.html', {'interactiveplot':script, 'the_div':div, + 'breadcrumbs':breadcrumbs, + 'rower':r, + 'active':'nav-workouts', 'promember':promember, 'teamid':teamid, 'chartform':chartform, @@ -6475,74 +6530,76 @@ def workout_comparison_list(request,id=0,message='',successmessage='', try: r = getrower(request.user) - u = User.objects.get(id=r.user.id) - if request.method == 'POST': - dateform = DateRangeForm(request.POST) - if dateform.is_valid(): - startdate = dateform.cleaned_data['startdate'] - enddate = dateform.cleaned_data['enddate'] - else: - dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) - - if startdatestring: - startdate = iso8601.parse_date(startdatestring) - if enddatestring: - enddate = iso8601.parse_date(enddatestring) - - startdate = datetime.datetime.combine(startdate,datetime.time()) - enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) - #enddate = enddate+datetime.timedelta(days=1) - - if enddate < startdate: - s = enddate - enddate = startdate - startdate = s - - workouts = Workout.objects.filter(user=r, - startdatetime__gte=startdate, - startdatetime__lte=enddate).order_by("-date", "-starttime").exclude(id=id) - - query = request.GET.get('q') - if query: - query_list = query.split() - workouts = workouts.filter( - reduce(operator.and_, - (Q(name__icontains=q) for q in query_list)) | - reduce(operator.and_, - (Q(notes__icontains=q) for q in query_list)) - ) - - paginator = Paginator(workouts,15) # show 25 workouts per page - page = request.GET.get('page') - - try: - workouts = paginator.page(page) - except PageNotAnInteger: - workouts = paginator.page(1) - except EmptyPage: - workouts = paginator.page(paginator.num_pages) - - row = get_workout(id) - - messages.error(request,message) - messages.info(request,successmessage) - - return render(request, 'comparison_list.html', - {'id':int(id), - 'workout':row, - 'workouts': workouts, - 'last_name':u.last_name, - 'first_name':u.first_name, - 'dateform':dateform, - 'startdate':startdate, - 'enddate':enddate, - 'teams':get_my_teams(request.user), - }) except Rower.DoesNotExist: raise Http404("User has no rower instance") + u = User.objects.get(id=r.user.id) + + if request.method == 'POST': + dateform = DateRangeForm(request.POST) + if dateform.is_valid(): + startdate = dateform.cleaned_data['startdate'] + enddate = dateform.cleaned_data['enddate'] + else: + dateform = DateRangeForm(initial={ + 'startdate':startdate, + 'enddate':enddate, + }) + + if startdatestring: + startdate = iso8601.parse_date(startdatestring) + if enddatestring: + enddate = iso8601.parse_date(enddatestring) + + startdate = datetime.datetime.combine(startdate,datetime.time()) + enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) + #enddate = enddate+datetime.timedelta(days=1) + + if enddate < startdate: + s = enddate + enddate = startdate + startdate = s + + workouts = Workout.objects.filter(user=r, + startdatetime__gte=startdate, + startdatetime__lte=enddate).order_by("-date", "-starttime").exclude(id=id) + + query = request.GET.get('q') + if query: + query_list = query.split() + workouts = workouts.filter( + reduce(operator.and_, + (Q(name__icontains=q) for q in query_list)) | + reduce(operator.and_, + (Q(notes__icontains=q) for q in query_list)) + ) + + paginator = Paginator(workouts,15) # show 25 workouts per page + page = request.GET.get('page') + + try: + workouts = paginator.page(page) + except PageNotAnInteger: + workouts = paginator.page(1) + except EmptyPage: + workouts = paginator.page(paginator.num_pages) + + row = get_workout(id) + + messages.error(request,message) + messages.info(request,successmessage) + + return render(request, 'comparison_list.html', + {'id':int(id), + 'workout':row, + 'workouts': workouts, + 'last_name':u.last_name, + 'first_name':u.first_name, + 'dateform':dateform, + 'startdate':startdate, + 'enddate':enddate, + 'teams':get_my_teams(request.user), + }) + # List of workouts to compare a selected workout to @user_passes_test(ispromember,login_url="/",redirect_field_name=None) From 6c1c6aac3941a4fb67373f681713b6c8ed011dbf Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 10 Oct 2018 08:08:52 +0200 Subject: [PATCH 64/95] bug fix --- rowers/c2stuff.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rowers/c2stuff.py b/rowers/c2stuff.py index 5693e760..e5646ac9 100644 --- a/rowers/c2stuff.py +++ b/rowers/c2stuff.py @@ -266,7 +266,10 @@ def summaryfromsplitdata(splitdata,data,filename,sep='|'): idist = interval['distance'] itime = interval['time']/10. ipace = 500.*itime/idist - ispm = interval['stroke_rate'] + try: + ispm = interval['stroke_rate'] + except KeyError: + ispm = 0 try: irest_time = interval['rest_time']/10. except KeyError: From 830af9aa3a48e16c21365d3373e2d62abcef7ee5 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 10 Oct 2018 09:17:41 +0200 Subject: [PATCH 65/95] box plot --- rowers/templates/boxplot.html | 62 ++++++------------ rowers/templates/multicompare.html | 14 ++-- rowers/templates/user_boxplot_select.html | 35 ++++------ rowers/urls.py | 4 +- rowers/views.py | 80 +++++++++++++++++------ 5 files changed, 102 insertions(+), 93 deletions(-) diff --git a/rowers/templates/boxplot.html b/rowers/templates/boxplot.html index 346a780e..5d4b313f 100644 --- a/rowers/templates/boxplot.html +++ b/rowers/templates/boxplot.html @@ -1,10 +1,10 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Rowsandall Box Plot {% endblock %} -{% block content %} +{% block main %} - - -
    -

    Box Chart

    -
    -
    +

    Box Chart

    +
      +
    • +
      {{ the_div|safe }}
      -
    -
    -
    -
    +
  • +
  • + {% csrf_token %} {{ chartform.as_table }}
    -
    -

    - -

    -
    +

    + +

    - -
    +
  • +
  • - You can use the form above to change the metric or filter the data. + You can use the form to change the metric or filter the data. Set Min SPM and Max SPM to select only strokes in a certain range of stroke rates. Set Work per Stroke to a minimum value to remove "paddle" strokes or turns.

    - - - +
  • + {% endblock %} @@ -92,3 +68,7 @@ {% endblock %} + +{% block sidebar %} +{% include 'menu_analytics.html' %} +{% endblock %} diff --git a/rowers/templates/multicompare.html b/rowers/templates/multicompare.html index 2907d8d4..e3e32d7b 100644 --- a/rowers/templates/multicompare.html +++ b/rowers/templates/multicompare.html @@ -15,7 +15,13 @@

    Interactive Comparison

      -
    • +
    • + +
      + {{ the_div|safe }} +
      +
    • +
    • {% csrf_token %} @@ -27,12 +33,6 @@

      -
    • - -
      - {{ the_div|safe }} -
      -
    • diff --git a/rowers/templates/user_boxplot_select.html b/rowers/templates/user_boxplot_select.html index 02dddea8..46008816 100644 --- a/rowers/templates/user_boxplot_select.html +++ b/rowers/templates/user_boxplot_select.html @@ -28,6 +28,9 @@ hidden.hide(); + if (modality.val() == 'water') { + hidden.show(); + } // Setup an event listener for when the state of the @@ -69,9 +72,15 @@
        +
      • +

        You can use the date and search forms to search through all + workouts from this team.

        +

        TIP: Agree with your team members to put tags (e.g. '8x500m') in the notes section of + your workouts. That makes it easy to search.

        +
      • -
        + {% if workouts %} @@ -105,23 +114,13 @@
      {{ dateform.as_table }}
      + + {{ optionsform.as_table }} +
      {% csrf_token %} - +
    • -
    • - {% if theuser %} -
      - {% else %} - - {% endif %} - - {{ optionsform.as_table }} -
      - {% csrf_token %} - -
      -
    • @@ -131,12 +130,6 @@
    • -
    • -

      You can use the date and search forms above to search through all - workouts from this team.

      -

      TIP: Agree with your team members to put tags (e.g. '8x500m') in the notes section of - your workouts. That makes it easy to search.

      -
    diff --git a/rowers/urls.py b/rowers/urls.py index 15545a03..25fffff4 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -164,9 +164,7 @@ urlpatterns = [ url(r'^workouts-join-select/team/(?P\d+)/$',views.workouts_join_select), url(r'^workouts-join-select/(?P\w+.*)/(?P\w+.*)$',views.workouts_join_select), url(r'^workouts-join-select/$',views.workouts_join_select), - url(r'^user-boxplot-select/user/(?P\d+)$',views.user_boxplot_select), - url(r'^user-boxplot-select/user/(?P\d+)/(?P\w+.*)/(?P\w+.*)$',views.user_boxplot_select), - url(r'^user-boxplot-select/(?P\w+.*)/(?P\w+.*)$',views.user_boxplot_select), + url(r'^user-boxplot-select/user/(?P\d+)/$',views.user_boxplot_select), url(r'^user-boxplot-select/$',views.user_boxplot_select), url(r'^user-multiflex-select/user/(?P\d+)/(?P\w+.*)/(?P\w+.*)$',views.user_multiflex_select), url(r'^user-multiflex-select/user/(?P\d+)/$',views.user_multiflex_select), diff --git a/rowers/views.py b/rowers/views.py index 23949385..3b9cb626 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -5977,6 +5977,7 @@ def user_boxplot_select(request, r = getrequestrower(request,userid=userid) user = r.user + userid = user.id if 'options' in request.session: options = request.session['options'] @@ -5997,6 +5998,12 @@ def user_boxplot_select(request, except KeyError: includereststrokes = False + if 'startdate' in request.session: + startdate = iso8601.parse_date(request.session['startdate']) + + + if 'enddate' in request.session: + enddate = iso8601.parse_date(request.session['enddate']) workstrokesonly = not includereststrokes @@ -6015,7 +6022,7 @@ def user_boxplot_select(request, startdate = s - if request.method == 'POST' and 'daterange' in request.POST: + if request.method == 'POST': dateform = DateRangeForm(request.POST) if dateform.is_valid(): startdate = dateform.cleaned_data['startdate'] @@ -6024,6 +6031,25 @@ def user_boxplot_select(request, enddatestring = enddate.strftime('%Y-%m-%d') request.session['startdate'] = startdatestring request.session['enddate'] = enddatestring + optionsform = TrendFlexModalForm(request.POST) + if optionsform.is_valid(): + modality = optionsform.cleaned_data['modality'] + waterboattype = optionsform.cleaned_data['waterboattype'] + if modality == 'all': + modalities = [m[0] for m in types.workouttypes] + else: + modalities = [modality] + if modality != 'water': + waterboattype = [b[0] for b in types.boattypes] + + + if 'rankingonly' in optionsform.cleaned_data: + rankingonly = optionsform.cleaned_data['rankingonly'] + else: + rankingonly = False + + request.session['modalities'] = modalities + request.session['waterboattype'] = waterboattype else: dateform = DateRangeForm(initial={ 'startdate':startdate, @@ -6040,27 +6066,8 @@ def user_boxplot_select(request, modalities = [m[0] for m in types.workouttypes] modality = 'all' - if request.method == 'POST' and 'optionsform' in request.POST: - optionsform = TrendFlexModalForm(request.POST) - if optionsform.is_valid(): - modality = optionsform.cleaned_data['modality'] - waterboattype = optionsform.cleaned_data['waterboattype'] - if modality == 'all': - modalities = [m[0] for m in types.workouttypes] - else: - modalities = [modality] - - if modality != 'water': - waterboattype = [b[0] for b in types.boattypes] - if 'rankingonly' in optionsform.cleaned_data: - rankingonly = optionsform.cleaned_data['rankingonly'] - else: - rankingonly = False - - request.session['modalities'] = modalities - request.session['waterboattype'] = waterboattype negtypes = [] for b in types.boattypes: @@ -6128,12 +6135,24 @@ def user_boxplot_select(request, request.session['startdate'] = startdatestring request.session['enddate'] = enddatestring + r = getrower(request.user) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':reverse(user_boxplot_select,kwargs={'userid':userid}), + 'name': 'BoxPlot Select' + }, + ] return render(request, 'user_boxplot_select.html', {'workouts': workouts, 'dateform':dateform, 'startdate':startdate, 'enddate':enddate, 'rower':r, + 'breadcrumbs':breadcrumbs, 'theuser':user, 'form':form, 'active':'nav-analysis', @@ -6305,7 +6324,7 @@ def boxplot_view(request,userid=0, else: return HttpResponse("invalid form") else: - url = reverse(user_boxplot_select) + url = reverse(user_boxplot_select,kwargs={'userid':userid}) return HttpResponseRedirect(url) div = get_call() @@ -6321,9 +6340,28 @@ def boxplot_view(request,userid=0, request.session['options'] = options + r = getrequestrower(request,userid=userid) + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':reverse(user_boxplot_select,kwargs={'userid':userid}), + 'name': 'BoxPlot Select' + }, + { + 'url':reverse(boxplot_view,kwargs={'userid':userid}), + 'name': 'BoxPlot Select' + }, + ] + return render(request,'boxplot.html', {'interactiveplot':'', 'the_div':div, + 'rower':r, + 'breadcrumbs':breadcrumbs, + 'active':'nav-analysis', 'chartform':chartform, 'userid':userid, 'teams':get_my_teams(request.user), From 401fc7c82afbd72ba4d277bb170e61c7c9f9518e Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 10 Oct 2018 13:59:53 +0200 Subject: [PATCH 66/95] gdpr-optin --- rowers/interactiveplots.py | 4 +- rowers/templates/gdpr_optin.html | 75 +++++++++---------- rowers/templates/multiflex.html | 61 ++++++--------- rowers/templates/user_multiflex_select.html | 28 +++---- rowers/templates/userprofile_deactivate.html | 11 ++- rowers/templates/userprofile_delete.html | 12 +-- rowers/urls.py | 3 +- rowers/views.py | 79 +++++++++++++++----- 8 files changed, 145 insertions(+), 128 deletions(-) diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 72f70a09..f2f5004b 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -181,7 +181,7 @@ def interactive_boxchart(datadf,fieldname,extratitle=''): tools=TOOLS, toolbar_location="above", toolbar_sticky=False, - x_mapper_type='datetime') + x_mapper_type='datetime',plot_width=920) yrange1 = Range1d(start=yaxminima[fieldname],end=yaxmaxima[fieldname]) plot.y_range = yrange1 @@ -2365,7 +2365,7 @@ def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type, tools=TOOLS, toolbar_location="above", - toolbar_sticky=False) #,plot_width=500,plot_height=500) + toolbar_sticky=False,plot_width=920) # add watermark plot.extra_y_ranges = {"watermark": watermarkrange} diff --git a/rowers/templates/gdpr_optin.html b/rowers/templates/gdpr_optin.html index f340bd93..bc2bb898 100644 --- a/rowers/templates/gdpr_optin.html +++ b/rowers/templates/gdpr_optin.html @@ -1,53 +1,52 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}GDPR Opt-In{% endblock %} -{% block content %} -
    -

    GDPR Opt-In

    -

    - - To comply with the European Union General Data Protection Regulation, - we need to record your consent to use personal data on this website. - Please take some time to review our data policies. If you agree and - opt in, click the green button at the bottom to be taken to the site. - If you do not agree, please use the red button to delete your - account. This will irreversibly delete all your data on rowsandall.com - and remove your account. - -

    -
    +{% block main %} +

    GDPR Opt-In

    +

    + + To comply with the European Union General Data Protection Regulation, + we need to record your consent to use personal data on this website. + Please take some time to review our data policies. If you agree and + opt in, click the green button at the bottom to be taken to the site. + If you do not agree, please use the red button to delete your + account. This will irreversibly delete all your data on rowsandall.com + and remove your account. + +

    +
    - {% include "privacypolicy.html" %} +{% include "privacypolicy.html" %} -
    +
    -

    - To start or continue using the site, please give your consent by clicking on the green Opt In button below. -

    +

    + To start or continue using the site, please give your consent by clicking on the green Opt In button below. +

    -

    -

    -

    - - +

    + Download your data +

    + +

    + Opt in and continue +

    + +

    {% csrf_token %} -
    - - -
    + +
    - -
    +

    {% endblock %} + +{% block sidebar %} +{% include 'menu_profile.html' %} +{% endblock %} + diff --git a/rowers/templates/multiflex.html b/rowers/templates/multiflex.html index 2ab83e20..7f432a71 100644 --- a/rowers/templates/multiflex.html +++ b/rowers/templates/multiflex.html @@ -1,10 +1,10 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}View Comparison {% endblock %} -{% block content %} +{% block main %} - -
    -

    Trend Flex Chart

    -
    +

    Trend Flex Chart

    + +
      +
    • +
      {{ the_div|safe }}
      -
    -
    -
    -
    + +
  • + {% csrf_token %} {{ chartform.as_table }}
    -
    -

    - -

    -
    +

    + +

  • -
    -
    + +
  • - You can use the form above to change the metric or filter the data. + You can use the form to change the metric or filter the data. Set Min SPM and Max SPM to select only strokes in a certain range of stroke rates. Set Work per Stroke to a minimum value to remove "paddle" strokes or turns.

    -
  • -
    -
    + + {% endblock %} @@ -93,3 +72,7 @@ {% endblock %} + +{% block sidebar %} +{% include 'menu_analytics.html' %} +{% endblock %} diff --git a/rowers/templates/user_multiflex_select.html b/rowers/templates/user_multiflex_select.html index 4eb69f21..2e0ede5c 100644 --- a/rowers/templates/user_multiflex_select.html +++ b/rowers/templates/user_multiflex_select.html @@ -28,7 +28,9 @@ hidden.hide(); - + if (modality.val() == 'water') { + hidden.show(); + } // Setup an event listener for when the state of the // checkbox changes. @@ -70,8 +72,14 @@ {% endif %}
      +
    • +

      You can use the date and search forms to search through all + workouts from this team.

      +

      TIP: Agree with your team members to put tags (e.g. '8x500m') in the notes section of + your workouts. That makes it easy to search.

      +
    • -
      + {% if workouts %} Toggle All
      @@ -102,16 +110,6 @@ {{ dateform.as_table }}
      - {% csrf_token %} - -
      -
    • -
    • - {% if theuser %} -
      - {% else %} - - {% endif %} {{ modalityform.as_table }}
      @@ -129,12 +127,6 @@
    • -
    • -

      You can use the date and search forms above to search through all - workouts from this team.

      -

      TIP: Agree with your team members to put tags (e.g. '8x500m') in the notes section of - your workouts. That makes it easy to search.

      -
    diff --git a/rowers/templates/userprofile_deactivate.html b/rowers/templates/userprofile_deactivate.html index 7f4b2c15..de600442 100644 --- a/rowers/templates/userprofile_deactivate.html +++ b/rowers/templates/userprofile_deactivate.html @@ -1,11 +1,10 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Deactivate your account{% endblock %} -{% block content %} -
    +{% block main %}

    Deactivate your account


    Account deactivation is reversible. After you logout, you will not be @@ -17,6 +16,10 @@ -

    + +{% endblock %} + +{% block sidebar %} +{% include 'menu_profile.html' %} {% endblock %} diff --git a/rowers/templates/userprofile_delete.html b/rowers/templates/userprofile_delete.html index 04331c3f..d5099477 100644 --- a/rowers/templates/userprofile_delete.html +++ b/rowers/templates/userprofile_delete.html @@ -1,12 +1,11 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Delete your account{% endblock %} -{% block content %} -
    -

    Delete your account

    +{% block main %} +

    Delete your account


    Warning: This will remove your account and all your data. You will not be able to recover from this action. We cannot restore @@ -17,6 +16,9 @@ -

    {% endblock %} + +{% block sidebar %} +{% include 'menu_profile.html' %} +{% endblock %} diff --git a/rowers/urls.py b/rowers/urls.py index 25fffff4..46cf766d 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -329,9 +329,8 @@ urlpatterns = [ url(r'^user-boxplot/user/(?P\d+)$',views.boxplot_view), url(r'^user-boxplot$',views.boxplot_view), url(r'^user-boxplot-data$',views.boxplot_view_data), - url(r'^user-multiflex/(?P\d+)$',views.multiflex_view), + url(r'^user-multiflex/user/(?P\d+)$',views.multiflex_view), url(r'^user-multiflex/$',views.multiflex_view), - url(r'^user-multiflex$',views.multiflex_view), url(r'^user-multiflex-data$',views.multiflex_data), url(r'^me/deactivate$',views.deactivate_user), url(r'^me/delete$',views.remove_user), diff --git a/rowers/views.py b/rowers/views.py index 3b9cb626..247cf04d 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -5437,17 +5437,14 @@ def user_multiflex_select(request, except: ploterrorbars = False - - -# if 'startdate' in request.session: -# startdate = iso8601.parse_date(request.session['startdate']) + if 'startdate' in request.session: + startdate = iso8601.parse_date(request.session['startdate']) -# if 'enddate' in request.session: -# enddate = iso8601.parse_date(request.session['enddate']) + if 'enddate' in request.session: + enddate = iso8601.parse_date(request.session['enddate']) - if 'waterboattype' in request.session: waterboattype = request.session['waterboattype'] else: waterboattype = types.waterboattype @@ -5468,7 +5465,7 @@ def user_multiflex_select(request, modalities = [m[0] for m in types.workouttypes] modality = 'all' - if request.method == 'POST' and 'daterange' in request.POST: + if request.method == 'POST': dateform = DateRangeForm(request.POST) if dateform.is_valid(): startdate = dateform.cleaned_data['startdate'] @@ -5477,13 +5474,6 @@ def user_multiflex_select(request, enddatestring = enddate.strftime('%Y-%m-%d') request.session['startdate'] = startdatestring request.session['enddate'] = enddatestring - else: - dateform = DateRangeForm(initial={ - 'startdate':startdate, - 'enddate':enddate, - }) - - if request.method == 'POST' and 'modality' in request.POST: modalityform = TrendFlexModalForm(request.POST) if modalityform.is_valid(): modality = modalityform.cleaned_data['modality'] @@ -5501,6 +5491,12 @@ def user_multiflex_select(request, request.session['modalities'] = modalities request.session['waterboattype'] = waterboattype request.session['rankingonly'] = rankingonly + else: + dateform = DateRangeForm(initial={ + 'startdate':startdate, + 'enddate':enddate, + }) + startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) @@ -5577,9 +5573,25 @@ def user_multiflex_select(request, request.session['modalities'] = modalities + breadcrumbs = [ + { + 'url':'/rowers/analysis', + 'name':'Analysis' + }, + { + 'url':reverse(user_multiflex_select,kwargs={'userid':userid}), + 'name': 'Compare Select' + }, + { + 'url':reverse(multi_compare_view), + 'name': 'Comparison Chart' + } + ] + return render(request, 'user_multiflex_select.html', {'workouts': workouts, 'dateform':dateform, + 'breadcrumbs':breadcrumbs, 'startdate':startdate, 'enddate':enddate, 'theuser':user, @@ -5846,6 +5858,13 @@ def multiflex_view(request,userid=0, except KeyError: palette = 'monochrome_blue' + if 'startdate' in request.session: + startdate = iso8601.parse_date(request.session['startdate']) + + + if 'enddate' in request.session: + enddate = iso8601.parse_date(request.session['enddate']) + workstrokesonly = not includereststrokes if userid==0: @@ -5946,10 +5965,30 @@ def multiflex_view(request,userid=0, request.session['options'] = options + + r = getrequestrower(request,userid=userid) + + breadcrumbs = [ + { + 'url':'/rowers/analysis', + 'name':'Analysis' + }, + { + 'url':reverse(user_multiflex_select,kwargs={'userid':userid}), + 'name': 'Trend Flex Select' + }, + { + 'url':reverse(multiflex_view), + 'name': 'Trend Flex Chart' + } + ] return render(request,'multiflex.html', {'interactiveplot':'', + 'active':'nav-analysis', + 'rower':r, + 'breadcrumbs':breadcrumbs, 'the_div':div, 'active':'nav-analysis', 'chartform':chartform, @@ -6135,11 +6174,11 @@ def user_boxplot_select(request, request.session['startdate'] = startdatestring request.session['enddate'] = enddatestring - r = getrower(request.user) + breadcrumbs = [ { - 'url':'/rowers/list-workouts', - 'name':'Workouts' + 'url':'/rowers/analysis', + 'name':'Analysis' }, { 'url':reverse(user_boxplot_select,kwargs={'userid':userid}), @@ -6343,8 +6382,8 @@ def boxplot_view(request,userid=0, r = getrequestrower(request,userid=userid) breadcrumbs = [ { - 'url':'/rowers/list-workouts', - 'name':'Workouts' + 'url':'/rowers/Analysis', + 'name':'Analysis' }, { 'url':reverse(user_boxplot_select,kwargs={'userid':userid}), From 58cdc1edc21c168b961d0229b70daad6193ca689 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 10 Oct 2018 15:54:32 +0200 Subject: [PATCH 67/95] profile menu and export settings --- rowers/templates/menu_profile.html | 25 +++++ rowers/templates/menu_teams.html | 107 ++++++++++++++++++--- rowers/templates/rower_exportsettings.html | 43 ++++----- rowers/templates/teams.html | 106 ++++++++------------ rowers/urls.py | 3 +- rowers/views.py | 35 ++++++- static/css/styles2.css | 14 ++- templates/newbase.html | 2 +- 8 files changed, 225 insertions(+), 110 deletions(-) diff --git a/rowers/templates/menu_profile.html b/rowers/templates/menu_profile.html index 11c21fe0..1315e599 100644 --- a/rowers/templates/menu_profile.html +++ b/rowers/templates/menu_profile.html @@ -1,3 +1,5 @@ +{% load staticfiles %} +{% load rowerfilters %}

    Profile

    • @@ -17,3 +19,26 @@
    +{% if user.is_authenticated and user|is_manager %} +

     

    + + +{% endif %} diff --git a/rowers/templates/menu_teams.html b/rowers/templates/menu_teams.html index d6c62fc8..b98bc6c2 100644 --- a/rowers/templates/menu_teams.html +++ b/rowers/templates/menu_teams.html @@ -1,24 +1,101 @@ +{% load staticfiles %} +{% load rowerfilters %}

    Teams

    + +

     

    + +{% if teams %} +

    Managing

    + +{% endif %} + +

     

    + +{% if memberteams %} +

    Member

    + +{% endif %} + diff --git a/rowers/templates/rower_exportsettings.html b/rowers/templates/rower_exportsettings.html index fbaa80e2..980e8d73 100644 --- a/rowers/templates/rower_exportsettings.html +++ b/rowers/templates/rower_exportsettings.html @@ -1,31 +1,28 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% block title %}Change Rower Export Settings{% endblock %} -{% block content %} -
    -
    -

    -

    Export Settings

    - {% if form.errors %} -

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

    - {% endif %} +{% block main %} +

    Import and Export Settings for {{ rower.user.first_name }} {{ rower.user.last_name }}

    +{% if form.errors %} +

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

    +{% endif %} -
    - - {{ form.as_table }} -
    - {% csrf_token %} -
    - - -

    -
    -
    -
    +
    + + {{ form.as_table }} +
    + {% csrf_token %} + +
    + +{% endblock %} + +{% block sidebar %} +{% include 'menu_profile.html' %} {% endblock %} diff --git a/rowers/templates/teams.html b/rowers/templates/teams.html index af2bc40e..280ac749 100644 --- a/rowers/templates/teams.html +++ b/rowers/templates/teams.html @@ -1,46 +1,39 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% block title %}Teams {% endblock %} -{% block content %} -
    -
    -

    -

    My Teams

    - {% if teams %} - - - - - - - - +{% block main %} +
      + {% if teams %} +
    • +

      My Teams

      +
    Name 
    + + + + + + + {% for team in teams %} {% endfor %} -
    Name 
    - {{ team.name }} + {{ team.name }} -
    - Leave -
    + Leave
    - {% else %} -

    You are not a member of any team.

    - {% endif %} -

    - + + + {% endif %} -
    -
    - {% if otherteams %} + {% if otherteams %} +
  • Other Teams

    - +
    @@ -60,21 +53,16 @@ {% endfor %}
    Name
    - - {% else %} -

     

    - {% endif %} -
  • -
    + + {% endif %} -
    -
    - {% if user.rower.rowerplan == 'coach' %} + {% if user.rower.rowerplan == 'coach' %} +
  • Teams I manage

    Number of members: {{ clubsize }}

    Maximum club size: {{ max_clubsize }}

    {% if myteams %} - +
    @@ -88,31 +76,25 @@ {{ team.name }} {% endfor %}
    Name -
    - Delete -
    + Delete
    {% endif %} -
    - New Team -
    - {% else %} -

     

    - {% endif %} -
  • -
    - {% if invites or requests or myrequests or myinvites %} + New Team + + {% endif %} + {% if invites or requests or myrequests or myinvites %} +
  • Invitations and Requests

    This section lists open invites to join a team. By accepting a team invite, you are agreeing with the sharing of personal data between team members and coaches according to our privacy policy.

    - +

    As a team manager, by accepting a team invite, you are agreeing with privacy policy regarding teams and personal data owned by team members.

    @@ -177,8 +159,7 @@ {% endfor %} -

    Manual code redeem

    -
    +

    Manual code redeem

    {% if form.errors %}

    @@ -187,20 +168,17 @@ {% endif %} {% csrf_token %} - {{ form.as_table }} + {{ form.as_table }}
    -

    - -
    -
    -

    - {% else %} -

     

    {% endif %} -
  • -
    + + {% endblock %} + +{% block sidebar %} +{% include 'menu_teams.html' %} +{% endblock %} diff --git a/rowers/urls.py b/rowers/urls.py index 46cf766d..e88eb97e 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -339,6 +339,7 @@ urlpatterns = [ url(r'^me/teams/$',views.rower_teams_view), url(r'^me/calcdps/$',views.rower_calcdps_view), url(r'^me/exportsettings/$',views.rower_exportsettings_view), + url(r'^me/exportsettings/user/(?P\d+)$',views.rower_exportsettings_view), url(r'^team/(?P\d+)/$',views.team_view), url(r'^team/(?P\d+)/memberstats$',views.team_members_stats_view), url(r'^team/(?P\d+)/edit$',views.team_edit_view), @@ -359,7 +360,7 @@ urlpatterns = [ url(r'^me/request/(\w+.*)/$',views.manager_requests_view), url(r'^me/request/$',views.manager_requests_view), url(r'^me/edit/$',views.rower_edit_view), - url(r'^rower/edit/(?P\d+)$',views.rower_edit_view), + url(r'^me/edit/user/(?P\d+)$',views.rower_edit_view), url(r'^me/edit/(.+.*)/$',views.rower_edit_view), url(r'^me/c2authorize/$',views.rower_c2_authorize), url(r'^me/polarauthorize/$',views.rower_polar_authorize), diff --git a/rowers/views.py b/rowers/views.py index 247cf04d..73aad27b 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -11887,8 +11887,8 @@ def rower_favoritecharts_view(request): # page where user sets his export settings @login_required() -def rower_exportsettings_view(request): - r = getrower(request.user) +def rower_exportsettings_view(request,userid=0): + r = getrequestrower(request,userid=userid) if request.method == 'POST': form = RowerExportForm(request.POST) if form.is_valid(): @@ -11901,17 +11901,30 @@ def rower_exportsettings_view(request): messages.info(request,'Settings saved') else: form = RowerExportForm(instance=r) + + breadcrumbs = [ + { + 'url':'/rowers/me', + 'name': 'Profile' + }, + { + 'url': reverse(rower_exportsettings_view), + 'name': 'Export Settings' + } + ] + return render(request, 'rower_exportsettings.html', {'form':form, - 'rower':r, + 'rower':r, + 'breadcrumbs': breadcrumbs, }) # Page where user can set his details # Add email address to form so user can change his email address @login_required() -def rower_edit_view(request,rowerid=0,message=""): - r = getrequestrower(request,rowerid=rowerid,notpermanent=True) +def rower_edit_view(request,rowerid=0,userid=0,message=""): + r = getrequestrower(request,rowerid=rowerid,userid=userid,notpermanent=True) rowerid = r.id @@ -12628,6 +12641,7 @@ def rower_teams_view(request,message='',successmessage=''): r = getrower(request.user) ts = Team.objects.filter(rower=r) myteams = Team.objects.filter(manager=request.user) + memberteams = Team.objects.filter(rower=r).exclude(manager=request.user).order_by('name') otherteams = Team.objects.filter(private='open').exclude(rower=r).exclude(manager=request.user).order_by('name') teams.remove_expired_invites() @@ -12640,12 +12654,23 @@ def rower_teams_view(request,message='',successmessage=''): messages.info(request,successmessage) messages.error(request,message) + + breadcrumbs = [ + { + 'url':reverse(rower_teams_view), + 'name': 'Teams' + } + ] + return render(request, 'teams.html', { 'teams':ts, + 'active':'nav-teams', + 'breadcrumbs':breadcrumbs, 'clubsize':clubsize, 'max_clubsize':max_clubsize, 'myteams':myteams, + 'memberteams':memberteams, 'invites':invites, 'otherteams':otherteams, 'requests':requests, diff --git a/static/css/styles2.css b/static/css/styles2.css index 81c8d8a8..f1ecf61c 100644 --- a/static/css/styles2.css +++ b/static/css/styles2.css @@ -245,7 +245,7 @@ nav a { margin: 1em; } - + nav a, nav a i { color: white; @@ -328,10 +328,22 @@ .sideheader { grid-area: side-header; + color: #dddddd; background: #35383d; padding: 0; } +.side h2 { + color: #dddddd; + font-weight: bold; + text-align: left; + font-size: 1.0em; + padding: 5px; + margin: 0; + margin-left: 30px; + padding-bottom: 0; +} + .sideheader h1 { font-weight: bold; text-align: left; diff --git a/templates/newbase.html b/templates/newbase.html index 398a855b..524b4263 100644 --- a/templates/newbase.html +++ b/templates/newbase.html @@ -209,7 +209,7 @@ From 77d72ea60d44bc9ff982ac51180514e079aace3c Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 10 Oct 2018 21:12:57 +0200 Subject: [PATCH 68/95] team delete - line 349 urls.py --- rowers/templates/team.html | 125 +++++++++-------------- rowers/templates/teamdeleteconfirm.html | 53 +++++----- rowers/templates/teamedit.html | 63 ++++++------ rowers/templates/teamleaveconfirm.html | 50 +++++---- rowers/templates/teamstats.html | 55 +++++----- rowers/views.py | 128 ++++++++++++++++++++++-- 6 files changed, 278 insertions(+), 196 deletions(-) diff --git a/rowers/templates/team.html b/rowers/templates/team.html index 2d992033..22a363fd 100644 --- a/rowers/templates/team.html +++ b/rowers/templates/team.html @@ -1,81 +1,56 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% block title %}Team {% endblock %} -{% block content %} -
    - {% include "teambuttons.html" with teamid=team.id %} -
    -
    -
    -

    {{ team.name }}

    +{% block main %} +

    {{ team.name }}

    -
    +
      +
    • {{ team.notes }}

      Manager: {{ team.manager.first_name }} {{ team.manager.last_name }}

      -
    -
    -
    - {% if ismember %} - - {% if team.manager == user %} -
    - Edit Team -
    - {% else %} -
    -

      -

    - {% endif %} - {% elif hasrequested %} -

    You have requested access to this team

    - {% else %} -

    You can request access to this team. By requesting access, you - agree to the Privacy Policy regarding - team functionality. You agree to share your workout data (except - workouts marked as "private") to all team members and the team manager. - You also grant the team manager access to your heart rate and power - zone settings, as well as your functional threshold information. You - are granting the team manager permission to edit your workouts.

    -
    + +
  • + + {% if hasrequested %} +

    You have requested access to this team

    + {% elif team not in myteams %} +

    You can request access to this team. By requesting access, you + agree to the Privacy Policy regarding + team functionality. You agree to share your workout data (except + workouts marked as "private") to all team members and the team manager. + You also grant the team manager access to your heart rate and power + zone settings, as well as your functional threshold information. You + are granting the team manager permission to edit your workouts.

    Join A request will be sent to the team manager - -
  • - {% endif %} -
    - -
    -
    -

    -

    Members

    - - - - - - - - - {% for member in members %} - - - {% if team.manager == user %} - - {% else %} - - {% endif %} - - {% endfor %} + {% endif %} + +
  • +

    Members

    +
  • Name 
    {{ member.user.first_name }} {{ member.user.last_name }}Drop 
    + + + + + + + + {% for member in members %} + + + {% if team.manager == user %} + + {% else %} + + {% endif %} + + {% endfor %} -
    Name 
    {{ member.user.first_name }} {{ member.user.last_name }}Drop 
    -

    + + +
  • - -
  • -
    {% if team.manager == user %}

    Use the form to add a new user. You can either select a user from the list of your existing club members who are not on this team yet, or you can type the user's email address, which also works for users who have not registered to the site yet.

    {% if inviteform.errors %} @@ -88,19 +63,19 @@ {{ inviteform.as_table }} {% csrf_token %} -
    - -
    + {% else %}

     

    {% endif %} -
    -
    -
    - + + {% endblock %} + +{% block sidebar %} +{% include 'menu_teams.html' %} +{% endblock %} diff --git a/rowers/templates/teamdeleteconfirm.html b/rowers/templates/teamdeleteconfirm.html index 56088353..20120bc3 100644 --- a/rowers/templates/teamdeleteconfirm.html +++ b/rowers/templates/teamdeleteconfirm.html @@ -1,43 +1,38 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} -{% block title %}Leave Team {% endblock %} +{% block title %}Delete Team {% endblock %} -{% block content %} -
    +{% block main %} +

    Confirm Deleting the team {{ team.name }}

    +
      +
    • {% if form.errors %} -

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

      +

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

      {% endif %} - -

      Confirm Deleting the team {{ team.name }}

      +

      This will remove the team. Your team members and their workouts will not be deleted.

      + + +

      + Cancel +

      - -
      -

      - Cancel -

      - -
      -

      - Delete -

      -
      - -
    - -
    -

    -  -

    - -
    +

    + Delete +

    + + {% endblock %} + +{% block sidebar %} +{% include 'menu_teams.html' %} +{% endblock %} diff --git a/rowers/templates/teamedit.html b/rowers/templates/teamedit.html index b8ea6366..0f5b065a 100644 --- a/rowers/templates/teamedit.html +++ b/rowers/templates/teamedit.html @@ -1,37 +1,42 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} -{% block title %}New Team{% endblock %} +{% block title %}Edit Team{% endblock %} -{% block content %} -
    -

    Edit Team {{ team.name }}

    -
    -
    -
    - {% if form.errors %} -

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

    - {% endif %} +{% block main %} - - {{ form.as_table }} -
    - {% csrf_token %} -
    - -
    -
    - +

    Edit Team {{ team.name }}

    + +
      +
    • + + {% if form.errors %} +

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

      + {% endif %} + + + {{ form.as_table }} +
      + {% csrf_token %} + + +
    • +
    • +
        +
      • Team Type: A private team is invisible on the Teams Management page, except for its members. The only way to add members is for the manager to send an invitation. An open team is visible for all rowsandall.com users. In addition to the invitation mechanism, any user can request to be added to this team. The team manager will always have to approve membership.
      • +
      • Sharing Behavior: When set to "All Members", all members of a team will see each other's workouts. This is the recommended setting. If te sharing bhavior is set to "Coach Only", team members only see their own workouts. The coach sees all team members' workouts.
      • +
      • These settings can be changed at any point in time through the Team Edit page +
      • +
      +
    • +
    - +{% endblock %} + +{% block sidebar %} +{% include 'menu_teams.html' %} {% endblock %} diff --git a/rowers/templates/teamleaveconfirm.html b/rowers/templates/teamleaveconfirm.html index 4210a404..cae64c93 100644 --- a/rowers/templates/teamleaveconfirm.html +++ b/rowers/templates/teamleaveconfirm.html @@ -1,45 +1,41 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Leave Team {% endblock %} -{% block content %} -
    +{% block main %} +

    Confirm Leaving the team {{ team.name }}

    +
      +
    • {% if form.errors %} -

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

      +

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

      {% endif %} - -

      Confirm Leaving the team {{ team.name }}

      +

      This will remove you and all your workouts from the team. If this is a closed team, you can only return when the team manager reinvites you. If this is an open team, you can return by applying for team membership.

      + + +

      + Cancel +

      +

      + Leave +

      +
    • +
    - -
    -

    - Cancel -

    -
    -

    - Leave -

    -
    - -
    - -
    -

    -  -

    - -
    +{% endblock %} + +{% block sidebar %} +{% include 'menu_teams.html' %} {% endblock %} diff --git a/rowers/templates/teamstats.html b/rowers/templates/teamstats.html index 83de0907..5dfa6a1b 100644 --- a/rowers/templates/teamstats.html +++ b/rowers/templates/teamstats.html @@ -1,35 +1,32 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% block title %}Team {% endblock %} -{% block content %} -
    - {% include "teambuttons.html" with teamid=team.id team=team %} -
    -
    -

    {{ team.name }} Stats

    -

    - Links to the cumulative statistics pages for your team's members -

    - - - {% for u in theusers %} - - - - - - - - - - - {% endfor %} - -
    {{ u.first_name }} {{ u.last_name }}Ranking PiecesStroke AnalysisPower HistogramStatsBox ChartOTW Ranking PiecesTrend Flex
    -
    - - +{% block main %} +

    {{ team.name }} Stats

    +

    + Links to the cumulative statistics pages for your team's members +

    + + + {% for u in theusers %} + + + + + + + + + + + {% endfor %} + +
    {{ u.first_name }} {{ u.last_name }}Ranking PiecesStroke AnalysisPower HistogramStatsBox ChartOTW Ranking PiecesTrend Flex
    {% endblock %} + +{% block sidebar %} +{% include 'menu_teams.html' %} +{% endblock %} diff --git a/rowers/views.py b/rowers/views.py index 73aad27b..5af7d474 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -12423,11 +12423,14 @@ def strokedatajson(request,id): import teams @login_required() -def team_view(request,id=0): +def team_view(request,id=0,userid=0): ismember = 0 hasrequested = 0 - r = getrower(request.user) - myteams = Team.objects.filter(manager=request.user) + r = getrequestrower(request,userid=userid) + + myteams, memberteams, otherteams = get_teams(request) + teams.remove_expired_invites() + try: t = Team.objects.get(id=id) @@ -12467,13 +12470,27 @@ def team_view(request,id=0): if r in members: ismember = 1 + breadcrumbs = [ + { + 'url':reverse(rower_teams_view), + 'name': 'Teams' + }, + { + 'url':reverse(team_view,kwargs={'id':id}), + 'name': t.name + } + ] return render(request, 'team.html', { 'team':t, 'teams':get_my_teams(request.user), + 'myteams':myteams, + 'memberteams':memberteams, 'members':members, + 'breadcrumbs':breadcrumbs, + 'active':'nav-teams', 'inviteform':inviteform, 'ismember':ismember, 'hasrequested':hasrequested, @@ -12486,10 +12503,31 @@ def team_leaveconfirm_view(request,id=0): except Team.DoesNotExist: raise Http404("Team doesn't exist") + myteams, memberteams, otherteams = get_teams(request) + + breadcrumbs = [ + { + 'url':reverse(rower_teams_view), + 'name': 'Teams' + }, + { + 'url':reverse(team_view,kwargs={'id':id}), + 'name': t.name + }, + { + 'url':reverse(team_leaveconfirm_view,kwargs={'id':id}), + 'name': 'Leave' + } + ] return render(request,'teamleaveconfirm.html', { 'team':t, 'teams':get_my_teams(request.user), + 'myteams':myteams, + 'memberteams':memberteams, + 'otherteams':otherteams, + 'active':'nav-teams', + 'breadcrumbs':breadcrumbs, }) @login_required() @@ -12624,6 +12662,19 @@ def team_leave_view(request,id=0): from rowers.forms import TeamInviteCodeForm +def get_teams(request): + r = Rower.objects.get(user=request.user) + + myteams = Team.objects.filter( + manager=request.user).order_by('name') + memberteams = Team.objects.filter( + rower=r).exclude(manager=request.user).order_by('name') + otherteams = Team.objects.filter( + private='open').exclude( + rower=r).exclude(manager=request.user).order_by('name') + + return myteams, memberteams, otherteams + @login_required() def rower_teams_view(request,message='',successmessage=''): if request.method == 'POST': @@ -12640,10 +12691,11 @@ def rower_teams_view(request,message='',successmessage=''): r = getrower(request.user) ts = Team.objects.filter(rower=r) - myteams = Team.objects.filter(manager=request.user) - memberteams = Team.objects.filter(rower=r).exclude(manager=request.user).order_by('name') - otherteams = Team.objects.filter(private='open').exclude(rower=r).exclude(manager=request.user).order_by('name') + + + myteams, memberteams, otherteams = get_teams(request) teams.remove_expired_invites() + invites = TeamInvite.objects.filter(user=request.user) requests = TeamRequest.objects.filter(user=request.user) @@ -12843,10 +12895,32 @@ def team_edit_view(request,id=0): else: teamcreateform = TeamForm(instance=t) + myteams, memberteams, otherteams = get_teams(request) + + breadcrumbs = [ + { + 'url':reverse(rower_teams_view), + 'name': 'Teams' + }, + { + 'url':reverse(team_view,kwargs={'id':id}), + 'name': t.name + }, + { + 'url':reverse(team_edit_view,kwargs={'id':id}), + 'name': 'Edit' + } + ] + return render(request,'teamedit.html', { 'form':teamcreateform, 'teams':get_my_teams(request.user), + 'myteams':myteams, + 'memberteams':memberteams, + 'otherteams':otherteams, + 'active':'nav-teams', + 'breadcrumbs':breadcrumbs, 'team':t, }) @@ -12886,10 +12960,30 @@ def team_deleteconfirm_view(request,id): if t.manager != request.user: raise PermissionDenied("You are not allowed to delete this team") + myteams, memberteams, otherteams = get_teams(request) + + breadcrumbs = [ + { + 'url':reverse(rower_teams_view), + 'name': 'Teams' + }, + { + 'url':reverse(team_view,kwargs={'id':id}), + 'name': t.name + }, + { + 'url':reverse(team_deleteconfirm_view,kwargs={'id':id}), + 'name': 'Leave' + } + ] return render(request,'teamdeleteconfirm.html', { 'teams':get_my_teams(request.user), - 'team':t + 'team':t, + 'myteams':myteams, + 'memberteams':memberteams, + 'otherteams':otherteams, + 'active':'nav-teams', }) @user_passes_test(iscoachmember,login_url="/",redirect_field_name=None) @@ -12922,11 +13016,31 @@ def team_members_stats_view(request,id): theusers = [member.user for member in members] + myteams, memberteams, otherteams = get_teams(request) + breadcrumbs = [ + { + 'url':reverse(rower_teams_view), + 'name': 'Teams' + }, + { + 'url':reverse(team_view,kwargs={'id':id}), + 'name': t.name + }, + { + 'url':reverse(team_members_stats_view,kwargs={'id':id}), + 'name': 'Members Stats' + } + ] response = render(request,'teamstats.html', { 'teams':get_my_teams(request.user), + 'myteams':myteams, + 'memberteams':memberteams, + 'otherteams':otherteams, + 'active':'nav-teams', + 'breadcrumbs':breadcrumbs, 'team':t, 'theusers':theusers, }) From 841b9b3b5b08b2454e2ac38032370d82db1eebdd Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 10 Oct 2018 21:41:27 +0200 Subject: [PATCH 69/95] team create --- rowers/tasks.py | 20 +++++------ rowers/templates/menu_teams.html | 9 +---- rowers/templates/team.html | 2 +- rowers/templates/teamcreate.html | 59 +++++++++++++++++--------------- rowers/templates/teams.html | 4 +-- rowers/views.py | 17 +++++++++ 6 files changed, 62 insertions(+), 49 deletions(-) diff --git a/rowers/tasks.py b/rowers/tasks.py index 72f278e8..653c5513 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -1382,7 +1382,7 @@ def handle_makeplot(f1, f2, t, hrdata, plotnr, imagename, @app.task def handle_sendemail_invite(email, name, code, teamname, manager, debug=False,**kwargs): - fullemail = name + ' <' + email + '>' + fullemail = email subject = 'Invitation to join team ' + teamname siteurl = SITE_URL @@ -1414,7 +1414,7 @@ def handle_sendemailnewresponse(first_name, last_name, comment, workoutname, workoutid, commentid, debug=False,**kwargs): - fullemail = first_name + ' ' + last_name + ' <' + email + '>' + fullemail = email from_email = 'Rowsandall ' subject = 'New comment on workout ' + workoutname @@ -1451,7 +1451,7 @@ def handle_sendemailnewcomment(first_name, - fullemail = first_name + ' ' + last_name + ' <' + email + '>' + fullemail = email from_email = 'Rowsandall ' subject = 'New comment on workout ' + workoutname @@ -1480,7 +1480,7 @@ def handle_sendemailnewcomment(first_name, @app.task def handle_sendemail_request(email, name, code, teamname, requestor, id, debug=False,**kwargs): - fullemail = name + ' <' + email + '>' + fullemail = email subject = 'Request to join team ' + teamname from_email = 'Rowsandall ' @@ -1506,7 +1506,7 @@ def handle_sendemail_request(email, name, code, teamname, requestor, id, @app.task def handle_sendemail_request_accept(email, name, teamname, managername, debug=False,**kwargs): - fullemail = name + ' <' + email + '>' + fullemail = email subject = 'Welcome to ' + teamname from_email = 'Rowsandall ' @@ -1530,7 +1530,7 @@ def handle_sendemail_request_accept(email, name, teamname, managername, @app.task def handle_sendemail_request_reject(email, name, teamname, managername, debug=False,**kwargs): - fullemail = name + ' <' + email + '>' + fullemail = email subject = 'Your application to ' + teamname + ' was rejected' from_email = 'Rowsandall ' @@ -1553,7 +1553,7 @@ def handle_sendemail_request_reject(email, name, teamname, managername, @app.task def handle_sendemail_member_dropped(email, name, teamname, managername, debug=False,**kwargs): - fullemail = name + ' <' + email + '>' + fullemail = email subject = 'You were removed from ' + teamname from_email = 'Rowsandall ' @@ -1578,7 +1578,7 @@ def handle_sendemail_member_dropped(email, name, teamname, managername, def handle_sendemail_team_removed(email, name, teamname, managername, debug=False,**kwargs): - fullemail = name + ' <' + email + '>' + fullemail = email subject = 'You were removed from ' + teamname from_email = 'Rowsandall ' @@ -1602,7 +1602,7 @@ def handle_sendemail_team_removed(email, name, teamname, managername, @app.task def handle_sendemail_invite_reject(email, name, teamname, managername, debug=False,**kwargs): - fullemail = managername + ' <' + email + '>' + fullemail = email subject = 'Your invitation to ' + name + ' was rejected' from_email = 'Rowsandall ' @@ -1626,7 +1626,7 @@ def handle_sendemail_invite_reject(email, name, teamname, managername, @app.task def handle_sendemail_invite_accept(email, name, teamname, managername, debug=False,**kwargs): - fullemail = managername + ' <' + email + '>' + fullemail = email subject = 'Your invitation to ' + name + ' was accepted' from_email = 'Rowsandall ' diff --git a/rowers/templates/menu_teams.html b/rowers/templates/menu_teams.html index b98bc6c2..e0696a4e 100644 --- a/rowers/templates/menu_teams.html +++ b/rowers/templates/menu_teams.html @@ -17,9 +17,8 @@ {% endif %} -

     

    -{% if teams %} +{% if myteams %}

    Managing

      {% for team in myteams %} @@ -63,7 +62,6 @@
    {% endif %} -

     

    {% if memberteams %}

    Member

    @@ -78,11 +76,6 @@  View -
  • - -  Member Stats - -
  •  Member Workouts diff --git a/rowers/templates/team.html b/rowers/templates/team.html index 22a363fd..35d82bc5 100644 --- a/rowers/templates/team.html +++ b/rowers/templates/team.html @@ -14,7 +14,7 @@ {% if hasrequested %}

    You have requested access to this team

    - {% elif team not in myteams %} + {% elif team not in myteams and team not in memberteams %}

    You can request access to this team. By requesting access, you agree to the Privacy Policy regarding team functionality. You agree to share your workout data (except diff --git a/rowers/templates/teamcreate.html b/rowers/templates/teamcreate.html index ebc4ccae..7014ef5c 100644 --- a/rowers/templates/teamcreate.html +++ b/rowers/templates/teamcreate.html @@ -1,38 +1,41 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% block title %}New Team{% endblock %} -{% block content %} -

    -

    Create a new Team

    -
    -
    -
    - {% if form.errors %} -

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

    - {% endif %} +{% block main %} +

    Create a new Team

    - - {{ form.as_table }} -
    - {% csrf_token %} -
    - -
    -
    - +
      +
    • + + {% if form.errors %} +

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

      + {% endif %} + + + {{ form.as_table }} +
      + {% csrf_token %} + +
    • +
    • +
        +
      • Team Type: A private team is invisible on the Teams Management page, except for its members. The only way to add members is for the manager to send an invitation. An open team is visible for all rowsandall.com users. In addition to the invitation mechanism, any user can request to be added to this team. The team manager will always have to approve membership.
      • +
      • Sharing Behavior: When set to "All Members", all members of a team will see each other's workouts. This is the recommended setting. + If the sharing behavior is set to "Coach Only", team members only see their own workouts. The coach sees all team members' workouts.
      • +
      • These settings can be changed at any point in time through the Team Edit page
      • +
      +
    • +
    {% endblock %} + +{% block sidebar %} +{% include 'menu_teams.html' %} +{% endblock %} diff --git a/rowers/templates/teams.html b/rowers/templates/teams.html index 280ac749..e3094b70 100644 --- a/rowers/templates/teams.html +++ b/rowers/templates/teams.html @@ -87,7 +87,7 @@
  • {% endif %} {% if invites or requests or myrequests or myinvites %} -
  • +
  • Invitations and Requests

    This section lists open invites to join a team. By accepting a team invite, you are agreeing with the sharing @@ -96,7 +96,7 @@

    As a team manager, by accepting a team invite, you are agreeing - with privacy policy regarding teams and + with our privacy policy regarding teams and personal data owned by team members.

    diff --git a/rowers/views.py b/rowers/views.py index 5af7d474..5db4a4bd 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -12944,10 +12944,27 @@ def team_create_view(request): else: teamcreateform = TeamForm() + myteams, memberteams, otherteams = get_teams(request) + + breadcrumbs = [ + { + 'url':reverse(rower_teams_view), + 'name': 'Teams' + }, + { + 'url':reverse(team_create_view), + 'name': "New Team" + }, + ] return render(request,'teamcreate.html', { 'teams':get_my_teams(request.user), 'form':teamcreateform, + 'myteams':myteams, + 'memberteams':memberteams, + 'otherteams':otherteams, + 'active':'nav-teams', + 'breadcrumbs':breadcrumbs, }) @user_passes_test(iscoachmember,login_url="/",redirect_field_name=None) From 921e3b1070227fb09758f612b9bd83cb398641a8 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 10 Oct 2018 22:19:46 +0200 Subject: [PATCH 70/95] user prefernces - continue urls.py line 381 --- rowers/templates/menu_profile.html | 14 +- rowers/templates/rower_form.html | 282 +++++----------------- rowers/templates/rower_preferences.html | 108 +++++++++ rowers/urls.py | 4 +- rowers/views.py | 295 +++++++++++++++++++++++- 5 files changed, 465 insertions(+), 238 deletions(-) create mode 100644 rowers/templates/rower_preferences.html diff --git a/rowers/templates/menu_profile.html b/rowers/templates/menu_profile.html index 1315e599..f4535b50 100644 --- a/rowers/templates/menu_profile.html +++ b/rowers/templates/menu_profile.html @@ -13,8 +13,18 @@
  • - -  Preferences + +  Zones + +
  • +
  • + +  Favorite Charts + +
  • +
  • + +  Manage Workflow
  • diff --git a/rowers/templates/rower_form.html b/rowers/templates/rower_form.html index 8333ccef..c5b82b61 100644 --- a/rowers/templates/rower_form.html +++ b/rowers/templates/rower_form.html @@ -1,117 +1,25 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Change Rower {% endblock %} -{% block content %} -
    -
    -

    User Settings for {{ rower.user.first_name }} {{ rower.user.last_name }}

    -

    Need help? Click to read the tutorial

    -
    - -
    +{% block main %} +

    User Settings for {{ rower.user.first_name }} {{ rower.user.last_name }}

    + +

    Need help? Click to read the tutorial

    + +
      +
    • +

      Account Information

      -

      Heart Rate Zones

      -

      Set your heart rate zones with this form.

      - {% if form.errors %} -

      - Please correct the error{{ form.errors|pluralize }} below. + {% if rower.user == user %} + Password Change + {% else %} +   + {% endif %}

      - {% endif %} - -
      -
    - {{ form.as_table }} -
    - {% csrf_token %} -
    - - -

    -
    - -
    -

    -

    Power Zones

    -

    The power zones are defined relative to power as measured by the - indoor rower.

    -
    - {% if powerzonesform.errors %} -

    - Please correct the error{{ powerzonesform.errors|pluralize }} below. - {{ powerzonesform.non_field_errors }} - -

    - {% endif %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    IDZone NameLower Boundary (Watt)
    1{{ powerzonesform.ut3name }}
    2{{ powerzonesform.ut2name }}{{ powerzonesform.pw_ut2 }}
    3{{ powerzonesform.ut1name }}{{ powerzonesform.pw_ut1 }}
    4{{ powerzonesform.atname }}{{ powerzonesform.pw_at }}
    5{{ powerzonesform.trname }}{{ powerzonesform.pw_tr }}
    6{{ powerzonesform.anname }}{{ powerzonesform.pw_an }}
    - {% csrf_token %} -
    - -
    -
    -

    -
    - -
    -
    -

    -

    Account Information

    -
    -
    -

    - {% if rower.user == user %} - Password Change - {% else %} -   - {% endif %} -

    -
    -
    -

    - {% if userform.errors %} + {% if userform.errors %}

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

    @@ -133,137 +41,59 @@ {% csrf_token %} -
    - {% if rower.rowerplan == 'basic' and rower.user == user %} - Upgrade - {% else %} -   - {% endif %} -
    -
    - + {% if rower.rowerplan == 'basic' and rower.user == user %} + Upgrade + {% else %} +   + {% endif %} + -
    - - -

    -
    -
    +
  • + {% if rower.user == user %} +
  • +

    GDPR - Data Protection

    -

    Functional Threshold Power and OTW Slack

    -

    Use this form to quickly change your zones based on the power of a - recent - full out 60 minutes effort on the ergometer. - It will update all zones defined above.

    -

    The OTW Power Slack is the percentage drop of your On-the-water - rowing power - vs the erg power. Typical values are around 15%. This will lower - the power zones for your OTW workouts.

    -
    - - {{ powerform.as_table }} -
    - {% csrf_token %} -
    - - -
    - - -{% if rower.user == user %} -
    -
    -

    -

    Teams

    - + Download your data

    -
    - -

    -

    Favorite Charts

    - + Deactivate Account

    -
    -
    -
    -

    -

    Export Settings

    - + Delete Account

    -
    -
    -

    -

    Configure Workflow Layout

    - -

    -
    - - -
    - -
    -
    -

    -

    GDPR - Data Protection

    - - -
    -

    - Delete Account -

    -
    -

    -
    -
    +
  • +
  • {% if grants %} -

    -

    Applications

    - - - - - - - - - - {% for grant in grants %} - - - - + {% endfor %} + +
    ApplicationScopeRevoke
    {{ grant.application }}{{ grant.scope }} - Revoke +

    Applications

    + + + + + + + + + + {% for grant in grants %} + + + + - - {% endfor %} - -
    ApplicationScopeRevoke
    {{ grant.application }}{{ grant.scope }} + Revoke
    -

    - {% else %} -

     

    +
    {% endif %} - - +
  • + {% endif %} +{% endblock %} + +{% block sidebar %} +{% include 'menu_profile.html' %} {% endblock %} diff --git a/rowers/templates/rower_preferences.html b/rowers/templates/rower_preferences.html new file mode 100644 index 00000000..74c8da4c --- /dev/null +++ b/rowers/templates/rower_preferences.html @@ -0,0 +1,108 @@ +{% extends "newbase.html" %} +{% load staticfiles %} +{% load rowerfilters %} + +{% block title %}Change Rower Preferences{% endblock %} + +{% block main %} +

    User Preferences for {{ rower.user.first_name }} {{ rower.user.last_name }}

    + +

    Need help? Click to read the tutorial

    + +
      +
    • +

      +

      Heart Rate Zones

      +

      Set your heart rate zones with this form.

      + {% if form.errors %} +

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

      + {% endif %} + +
      + + {{ form.as_table }} +
      + {% csrf_token %} + +
      +
    • +
    • +

      Power Zones

      +

      The power zones are defined relative to power as measured by the + indoor rower.

      +
      + {% if powerzonesform.errors %} +

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

      + {% endif %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      IDZone NameLower Boundary (Watt)
      1{{ powerzonesform.ut3name }}
      2{{ powerzonesform.ut2name }}{{ powerzonesform.pw_ut2 }}
      3{{ powerzonesform.ut1name }}{{ powerzonesform.pw_ut1 }}
      4{{ powerzonesform.atname }}{{ powerzonesform.pw_at }}
      5{{ powerzonesform.trname }}{{ powerzonesform.pw_tr }}
      6{{ powerzonesform.anname }}{{ powerzonesform.pw_an }}
      + {% csrf_token %} +
      + +
      +
      +
    • +
    • +

      Functional Threshold Power and OTW Slack

      +

      Use this form to quickly change your zones based on the power of a + recent + full out 60 minutes effort on the ergometer. + It will update all zones defined above.

      +

      The OTW Power Slack is the percentage drop of your On-the-water + rowing power + vs the erg power. Typical values are around 15%. This will lower + the power zones for your OTW workouts.

      +
      + + {{ powerform.as_table }} +
      + {% csrf_token %} + +
      +
    • +
    + + + +{% endblock %} + +{% block sidebar %} +{% include 'menu_profile.html' %} +{% endblock %} diff --git a/rowers/urls.py b/rowers/urls.py index e88eb97e..c15018fb 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -361,6 +361,8 @@ urlpatterns = [ url(r'^me/request/$',views.manager_requests_view), url(r'^me/edit/$',views.rower_edit_view), url(r'^me/edit/user/(?P\d+)$',views.rower_edit_view), + url(r'^me/preferences/$',views.rower_prefs_view), + url(r'^me/preferences/user/(?P\d+)$',views.rower_prefs_view), url(r'^me/edit/(.+.*)/$',views.rower_edit_view), url(r'^me/c2authorize/$',views.rower_c2_authorize), url(r'^me/polarauthorize/$',views.rower_polar_authorize), @@ -375,7 +377,7 @@ urlpatterns = [ url(r'^me/tprefresh/$',views.rower_tp_token_refresh), url(r'^me/c2refresh/$',views.rower_c2_token_refresh), url(r'^me/favoritecharts/$',views.rower_favoritecharts_view), - url(r'^me/workflowconfig$',views.workout_workflow_config_view), +# url(r'^me/workflowconfig$',views.workout_workflow_config_view), url(r'^me/workflowconfig2$',views.workout_workflow_config2_view), url(r'^me/workflowdefault$',views.workflow_default_view), url(r'^email/send/$', views.sendmail), diff --git a/rowers/views.py b/rowers/views.py index 5db4a4bd..65d5860a 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -11927,7 +11927,7 @@ def rower_edit_view(request,rowerid=0,userid=0,message=""): r = getrequestrower(request,rowerid=rowerid,userid=userid,notpermanent=True) rowerid = r.id - + if request.method == 'POST' and "ut2" in request.POST: form = RowerForm(request.POST) if form.is_valid(): @@ -12018,7 +12018,7 @@ def rower_edit_view(request,rowerid=0,userid=0,message=""): messages.info(request,message) url = reverse(rower_edit_view, kwargs = { - 'rowerid':r.id, + 'userid':r.user.id, }) response = HttpResponseRedirect(url) except Rower.DoesNotExist: @@ -12202,6 +12202,288 @@ def rower_edit_view(request,rowerid=0,userid=0,message=""): except Rower.DoesNotExist: raise Http404("This user doesn't exist") +# Page where user can set his details +# Add email address to form so user can change his email address +@login_required() +def rower_prefs_view(request,rowerid=0,userid=0,message=""): + r = getrequestrower(request,rowerid=rowerid,userid=userid,notpermanent=True) + + rowerid = r.id + + if request.method == 'POST' and "ut2" in request.POST: + form = RowerForm(request.POST) + if form.is_valid(): + # something + cd = form.cleaned_data + hrmax = cd['max'] + ut2 = cd['ut2'] + ut1 = cd['ut1'] + at = cd['at'] + tr = cd['tr'] + an = cd['an'] + rest = cd['rest'] + try: + r.max = max(min(hrmax,250),10) + r.ut2 = max(min(ut2,250),10) + r.ut1 = max(min(ut1,250),10) + r.at = max(min(at,250),10) + r.tr = max(min(tr,250),10) + r.an = max(min(an,250),10) + r.rest = max(min(rest,250),10) + r.save() + successmessage = "Your Heart Rate data were changed" + messages.info(request,successmessage) + form = RowerForm(instance=r) + powerform = RowerPowerForm(instance=r) + powerzonesform = RowerPowerZonesForm(instance=r) + accountform = AccountRowerForm(instance=r) + userform = UserForm(instance=r.user) + return render(request, 'rower_preferences.html', + {'form':form, + 'powerzonesform':powerzonesform, + 'teams':get_my_teams(request.user), + 'powerform':powerform, + 'rower':r, + 'accountform':accountform, + 'userform':userform, + }) + except Rower.DoesNotExist: + message = "Funny. This user doesn't exist." + messages.error(request,message) + url = reverse(workouts_view) + response = HttpResponseRedirect(url) + else: + message = HttpResponse("invalid form") + #form = RowerForm(instance=r) + powerform = RowerPowerForm(instance=r) + powerzonesform = RowerPowerZonesForm(instance=r) + userform = UserForm(instance=r.user) + accountform = AccountRowerForm(instance=r) + return render(request, 'rower_preferences.html', + {'form':form, + 'teams':get_my_teams(request.user), + 'powerzonesform':powerzonesform, + 'userform':userform, + 'accountform':accountform, + 'powerform':powerform, + 'rower':r, + }) + + + + return response + elif request.method == 'POST' and "ftp" in request.POST: + powerform = RowerPowerForm(request.POST) + if powerform.is_valid(): + cd = powerform.cleaned_data + hrftp = cd['hrftp'] + if hrftp == 0: + hrftp = int((r.an+r.tr)/2.) + ftp = cd['ftp'] + otwslack = cd['otwslack'] + try: + powerfrac = 100*np.array([r.pw_ut2, + r.pw_ut1, + r.pw_at, + r.pw_tr,r.pw_an])/r.ftp + r.ftp = max(min(ftp,650),50) + r.otwslack = max(min(otwslack,50),0) + ut2,ut1,at,tr,an = (r.ftp*powerfrac/100.).astype(int) + r.pw_ut2 = ut2 + r.pw_ut1 = ut1 + r.pw_at = at + r.pw_tr = tr + r.pw_an = an + r.hrftp = hrftp + r.save() + message = "FTP and/or OTW slack values changed." + messages.info(request,message) + url = reverse(rower_prefs_view, + kwargs = { + 'userid':r.user.id, + }) + response = HttpResponseRedirect(url) + except Rower.DoesNotExist: + message = "Funny. This user doesn't exist." + messages.error(request,message) + url = reverse(rower_edit_view) + response = HttpResponseRedirect(url) + else: + message = HttpResponse("invalid form") + form = RowerForm(instance=r) + #powerform = RowerPowerForm(instance=r) + powerzonesform = RowerPowerZonesForm(instance=r) + userform = UserForm(instance=r.user) + accountform = AccountRowerForm(instance=r) + return render(request, 'rower_preferences.html', + {'form':form, + 'teams':get_my_teams(request.user), + 'powerform':powerform, + 'rower':r, + 'userform':userform, + 'accountform':accountform, + }) + + + return response + elif request.method == 'POST' and "ut3name" in request.POST: + powerzonesform = RowerPowerZonesForm(request.POST) + if powerzonesform.is_valid(): + cd = powerzonesform.cleaned_data + pw_ut2 = cd['pw_ut2'] + pw_ut1 = cd['pw_ut1'] + pw_at = cd['pw_at'] + pw_tr = cd['pw_tr'] + pw_an = cd['pw_an'] + ut3name = cd['ut3name'] + ut2name = cd['ut2name'] + ut1name = cd['ut1name'] + atname = cd['atname'] + trname = cd['trname'] + anname = cd['anname'] + powerzones = [ut3name,ut2name,ut1name,atname,trname,anname] + try: + r.pw_ut2 = pw_ut2 + r.pw_ut1 = pw_ut1 + r.pw_at = pw_at + r.pw_tr = pw_tr + r.pw_an = pw_an + r.powerzones = powerzones + r.save() + successmessage = "Your Power Zone data were changed" + messages.info(request,successmessage) + form = RowerForm(instance=r) + accountform = AccountRowerForm(instance=r) + userform = UserForm(instance=r.user) + powerform = RowerPowerForm(instance=r) + powerzonesform = RowerPowerZonesForm(instance=r) + return render(request, 'rower_preferences.html', + {'form':form, + 'teams':get_my_teams(request.user), + 'powerzonesform':powerzonesform, + 'powerform':powerform, + 'userform':userform, + 'accountform':accountform, + 'rower':r, + }) + except Rower.DoesNotExist: + message = "Funny. This user doesn't exist." + messages.error(request,message) + url = reverse(workouts_view) + response = HttpResponseRedirect(url) + return response + else: + form = RowerForm(instance=r) + powerform = RowerPowerForm(instance=r) + accountform = AccountRowerForm(instance=r) + userform = UserForm(instance=r.user) + #powerzonesform = RowerPowerZonesForm(instance=r) + message = HttpResponse("invalid form") + return render(request, 'rower_preferences.html', + {'form':form, + 'teams':get_my_teams(request.user), + 'powerform':powerform, + 'powerzonesform':powerzonesform, + 'accountform':accountform, + 'userform':userform, + 'rower':r, + }) + elif request.method == 'POST' and "weightcategory" in request.POST: + accountform = AccountRowerForm(request.POST) + userform = UserForm(request.POST,instance=r.user) + if accountform.is_valid() and userform.is_valid(): + # process + cd = accountform.cleaned_data + ucd = userform.cleaned_data + first_name = ucd['first_name'] + last_name = ucd['last_name'] + email = ucd['email'] + sex = cd['sex'] + defaultlandingpage = cd['defaultlandingpage'] + weightcategory = cd['weightcategory'] + birthdate = cd['birthdate'] + showfavoritechartnotes = cd['showfavoritechartnotes'] + getemailnotifications = cd['getemailnotifications'] + getimportantemails = cd['getimportantemails'] + defaulttimezone=cd['defaulttimezone'] + u = r.user + if u.email != email and len(email): + resetbounce = True + else: + resetbounce = False + if len(first_name): + u.first_name = first_name + u.last_name = last_name + if len(email): ## and check_email_freeforuse(u,email): + u.email = email + resetbounce = True + + + u.save() + r.defaulttimezone=defaulttimezone + r.weightcategory = weightcategory + r.getemailnotifications = getemailnotifications + r.getimportantemails = getimportantemails + r.defaultlandingpage = defaultlandingpage + r.showfavoritechartnotes = showfavoritechartnotes + r.sex = sex + r.birthdate = birthdate + if resetbounce and r.emailbounced: + r.emailbounced = False + r.save() + form = RowerForm(instance=r) + powerform = RowerPowerForm(instance=r) + powerzonesform = RowerPowerZonesForm(instance=r) + accountform = AccountRowerForm(instance=r) + userform = UserForm(instance=u) + successmessage = 'Account Information changed' + messages.info(request,successmessage) + return render(request, 'rower_preferences.html', + {'form':form, + 'teams':get_my_teams(request.user), + 'powerzonesform':powerzonesform, + 'powerform':powerform, + 'accountform':accountform, + 'userform':userform, + 'rower':r, + }) + else: + form = RowerForm(instance=r) + powerform = RowerPowerForm(instance=r) + powerzonesform = RowerPowerZonesForm(instance=r) + return render(request, 'rower_preferences.html', + {'form':form, + 'teams':get_my_teams(request.user), + 'powerzonesform':powerzonesform, + 'powerform':powerform, + 'accountform':accountform, + 'userform':userform, + 'rower':r, + }) + + + else: + try: + form = RowerForm(instance=r) + powerform = RowerPowerForm(instance=r) + powerzonesform = RowerPowerZonesForm(instance=r) + accountform = AccountRowerForm(instance=r) + userform = UserForm(instance=r.user) + grants = AccessToken.objects.filter(user=request.user) + return render(request, 'rower_preferences.html', + { + 'form':form, + 'teams':get_my_teams(request.user), + 'powerform':powerform, + 'powerzonesform':powerzonesform, + 'userform':userform, + 'accountform':accountform, + 'grants':grants, + 'rower':r, + }) + except Rower.DoesNotExist: + raise Http404("This user doesn't exist") + # Revoke an app that you granted access through the API. # this views is called when you press a button on the User edit page # the button is only there when you have granted access to an app @@ -12219,13 +12501,8 @@ def rower_revokeapp_view(request,id=0): form = RowerForm(instance=r) powerform = RowerPowerForm(instance=r) grants = AccessToken.objects.filter(user=request.user) - return render(request, 'rower_form.html', - { - 'form':form, - 'teams':get_my_teams(request.user), - 'powerform':powerform, - 'grants':grants, - }) + url = reverse(rower_edit_view) + return HttpResponseRedirect(url) except AccessToken.DoesNotExist: raise Http404("Access token doesn't exist") From 7401fc2e66bf656386d1b4743191d48e0ab44b48 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 11 Oct 2018 08:39:14 +0200 Subject: [PATCH 71/95] fav charts --- rowers/templates/favoritecharts.html | 41 ++++++++++++++++------------ rowers/templates/menu_profile.html | 12 ++++---- rowers/views.py | 5 ++-- static/css/rowsandall2.css | 11 +++++++- 4 files changed, 42 insertions(+), 27 deletions(-) diff --git a/rowers/templates/favoritecharts.html b/rowers/templates/favoritecharts.html index d8c52704..b9ffd6eb 100644 --- a/rowers/templates/favoritecharts.html +++ b/rowers/templates/favoritecharts.html @@ -1,41 +1,46 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% block title %}Change Favorite Charts{% endblock %} -{% block content %} +{% block main %} +

    Change Your Favorite Charts

    +
    +
      {% csrf_token %} -
      -
      +
    • +

      -
    • {{ favorites_formset.management_form }} + {% for favorites_form in favorites_formset %} -
      -

      Chart {{ forloop.counter }}

      - - {{ favorites_form.as_table }} -
      -
      +
    • +
      +

      Chart {{ forloop.counter }}

      + + {{ favorites_form.as_table }} +
      +
      +
    • {% endfor %} -
      -

       

      -
      +
    - - {% endblock %} + +{% block sidebar %} +{% include 'menu_profile.html' %} +{% endblock %} diff --git a/rowers/templates/menu_profile.html b/rowers/templates/menu_profile.html index f4535b50..7afa79d9 100644 --- a/rowers/templates/menu_profile.html +++ b/rowers/templates/menu_profile.html @@ -2,9 +2,9 @@ {% load rowerfilters %}

    Profile

    diff --git a/rowers/templates/promembership.html b/rowers/templates/promembership.html index c3422175..201d3044 100644 --- a/rowers/templates/promembership.html +++ b/rowers/templates/promembership.html @@ -1,211 +1,245 @@ - {% extends "base.html" %} - {% block title %}Rowsandall Pro Membership{% endblock title %} - {% block content %} +{% extends "newbase.html" %} +{% block title %}Rowsandall Pro Membership{% endblock title %} +{% block main %} {% load rowerfilters %} -
    -

    Pro Membership

    +

    Paid Membership Plans

    -

    Donations are welcome to keep this web site going. To help cover the hosting - costs, I have created several paid plans offering advanced functionality. - Once I process your - donation, I will give you access to some special features on this - website.

    +
      +
    • +

      Donations are welcome to keep this web site going. To help cover the hosting + costs, I have created several paid plans offering advanced functionality. + Once I process your + donation, I will give you access to some special features on this + website.

      -

      The following table gives an overview of the different plans. As we are - constantly developing new functionality, the table might be slightly outdated. Don't - hesitate to contact us.

      +

      The following table gives an overview of the different plans. As we are + constantly developing new functionality, the table might be slightly outdated. Don't + hesitate to contact us.

      -

      The Pro membership is open for a free 14 day trial

      +

      The Pro membership is open for a free 14 day trial

      +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
       BASICPROSELF-COACHCOACH
      Basic rowing metrics (spm, time, distance, heart rate, power)
      Manual Import, Export, Synchronization and download of all your data
      Automatic Synchronization with other fitness sites 
      Heart rate and power zones
      Ranking Pieces, Stroke Analysis
      Advanced Analysis (Critical Power, Stats, Box Chart, Trend Flex) 
      Compare Workouts 
      Empower Stroke Profile 
      Sensor Fusion, Split Workout, In-stroke metrics 
      Create Training plans, tests and challenges for yourself. Track your performance + against plan.  
      Create Training plans, tests and challenges for your athletes. Track their performance + against plan.    
      Create and manage teams.   
      Manage your athlete's workouts   
      +

      +

      Coach and Self-Coach Membership

      + +

      The Coach plan functionality listed is available to the coach only. Individual athletes + can purchase upgrades to "Pro" and "Self-Coach" plans. +

      -

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
       BASICPROSELF-COACHCOACH
      Basic rowing metrics (spm, time, distance, heart rate, power)
      Manual Import, Export, Synchronization and download of all your data
      Automatic Synchronization with other fitness sites 
      Heart rate and power zones
      Ranking Pieces, Stroke Analysis
      Advanced Analysis (Critical Power, Stats, Box Chart, Trend Flex) 
      Compare Workouts 
      Empower Stroke Profile 
      Sensor Fusion, Split Workout, In-stroke metrics 
      Create Training plans, tests and challenges for yourself. Track your performance - against plan.  
      Create Training plans, tests and challenges for your athletes. Track their performance - against plan.    
      Create and manage teams. -    
      Manage your athlete's workouts -    
      -

      - -

      The Coach plan functionality listed is available to the coach only. Individual athletes - can purchase upgrades to Pro membership. -

      -

      Click on the PayPal button to pay for your Pro membership. Before you pay, please register for the free Basic membership and add your user name to the form. -Your payment will be valid for one year with automatic renewal which you can stop at any time. -You will be taken to the secure PayPal payment site. -

      -
    - -
    - {% if user.rower.rowerplan == 'basic' and user.rower.protrialexpires|date_dif == 1 %} -

    Free Trial

    -

    - You qualify for a 14 day free trial. No credit card needed. - Try out Pro or Self-Coach membership for two weeks. Click the button below to - sign up for the trial. After your trial period expires, you will be - automatically reset to the Basic plan, unless you upgrade to Pro. -

    - -
     
    - -{% endif %} - -

    Recurring Payment

    -

    You need a Paypal account for this

    -

    -

    - - - - - -
    Plans
    Your User Name
    - - - -
    -

    - -

    One Year Subscription

    -

    Only a credit card needed. Will not automatically renew

    - -

    -

    - - - - - -
    Plans
    Your User Name
    - - - -
    -

    - - -

    Payment Processing

    -

    After you do the payment, we will manually change your membership to - "Pro". Depending on our availability, this may take some time - (typically one working day). Don't hesitate to contact us - if you have any questions at this stage.

    - -

    If, for any reason, you are not happy with your Pro membership, please let me know through the contact form. I will contact you as soon as possible to discuss how we can make things better.

    - -
    +

    Rowsandall.com's Training Planning functionality + is part of the paid "Self-Coach" and "Coach" plans.

    + +

    On the "Self-Coach" plan, you can plan your own sessions.

    + +

    On the "Coach" plan, you can establish teams, see workouts done by + athletes on your team, and plan individual and group sessions for your + athletes. +

    + +

    If you would like to find a coach who helps you plan your training + through rowsandall.com, contact me throught the contact form.

    + + {% if user.rower.rowerplan == 'basic' and user.rower.protrialexpires|date_dif == 1 %} +

    Free Trial

    +

    + You qualify for a 14 day free trial. No credit card needed. + Try out Pro or Self-Coach membership for two weeks. Click the button below to + sign up for the trial. After your trial period expires, you will be + automatically reset to the Basic plan, unless you upgrade to Pro. +

    +

    Yes, I want to try Pro membership for 14 days for free. No strings attached.

    +

    Yes, I want to try Self-Coach membership for 14 days for free. No strings attached.

    + {% endif %} + +
  • +

    Click on the PayPal button to pay for your Pro membership. Before you pay, please register for the free Basic membership and add your user name to the form. + Your payment will be valid for one year. + You will be taken to the secure PayPal payment site. +

    +

    Recurring Payment

    +

    You need a Paypal account for this. This is plan will automatically renew each year.

    +

    +

    + + + + + + + + + + + + + + + +
    + Plans +
    + +
    + Your User Name +
    + +
    + + + +
    +

    +

    One Year Subscription

    +

    Only a credit card needed. Will not automatically renew

    + +

    +

    + + + + + +
    Plans
    Your User Name
    + + + +
    +

    +

    Payment Processing

    +

    After you do the payment, we will manually change your membership to + "Pro". Depending on our availability, this may take some time + (typically one working day). Don't hesitate to contact us + if you have any questions at this stage.

    + +

    If, for any reason, you are not happy with your Pro membership, please let me know through the contact form. I will contact you as soon as possible to discuss how we can make things better.

    + +
  • + - {% endblock content %} +{% endblock %} + +{% block sidebar %} +{% include 'menu_help.html' %} +{% endblock %} + diff --git a/rowers/templates/registerthankyou.html b/rowers/templates/registerthankyou.html index 73ae6509..1c0914b1 100644 --- a/rowers/templates/registerthankyou.html +++ b/rowers/templates/registerthankyou.html @@ -1,27 +1,18 @@ - {% extends "base.html" %} - {% block title %}Contact Us - Thank You{% endblock title %} - {% block content %} -

    Thank you.

    -

    Thank you for registering. You can now login using the credential you provided. You can also view some of the videos below to get you started.

    +{% extends "newbase.html" %} +{% block title %}Contact Us - Thank You{% endblock title %} +{% block main %} +

    Thank you for registering

    +

    Thank you for registering. You can now login using the credentials you provided.

    Return home

    -

    Basic Navigation

    - - -

    Upload Page

    - - -

    Integration with Strava, SportTracks or Concept2 logbook

    - -

    - -

    - -

    - -

    - {% endblock content %} +{% endblock main %} + + +{% block sidebar %} +{% include 'menu_help.html' %} +{% endblock %} + diff --git a/rowers/templates/registration_form.html b/rowers/templates/registration_form.html index 0e337e78..bd75d942 100644 --- a/rowers/templates/registration_form.html +++ b/rowers/templates/registration_form.html @@ -1,45 +1,50 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Contact Us{% endblock title %} {% block meta %} {% endblock %} - {% block content %} -
    - - {% if form.errors %} -

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

    - {% endif %} - -
    - {% csrf_token %} - - {{ form.as_table }} +{% block main %} +

    New User Registration

    +
      +
    • +
      + + {% if form.errors %} +

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

      + {% endif %} + + + {% csrf_token %} +
    + {{ form.as_table }}
    - - -
    - -
    -
    -

    To use rowsandall, you need to register and agree with the Terms of Service.

    -

    Registration is free.

    - -

    Some of our advanced services only work if you give us your - (approximate) birth date, sex and weight category, with 72.5 kg the - bounday between heavies and lighties for men, and 59 kg for women. -

    - -

    Also, we are restricting access to the site to 16 years and older - because of EU data protection regulations.

    - -
    - {% endblock content %} + + + +
  • + +

    To use rowsandall, you need to register and agree with the Terms of Service.

    +

    Registration is free.

    + +

    Some of our advanced services only work if you give us your + (approximate) birth date, sex and weight category, with 72.5 kg the + bounday between heavies and lighties for men, and 59 kg for women. +

    + +

    Also, we are restricting access to the site to 16 years and older + because of EU data protection regulations.

    +
  • + + +{% endblock main %} + +{% block sidebar %} +{% include 'menu_help.html' %} +{% endblock %} diff --git a/rowers/urls.py b/rowers/urls.py index 688f0242..d163c11a 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -391,13 +391,15 @@ urlpatterns = [ url(r'^developers', TemplateView.as_view(template_name='developers.html'),name='about'), # url(r'^compatibility', TemplateView.as_view(template_name='compatibility.html'),name='about'), # url(r'^videos', TemplateView.as_view(template_name='videos.html'),name='videos'), + url(r'^analysis/user/(?P\d+)', views.analysis_view,name='analysis'), + url(r'^laboratory/user/(?P\d+)', views.laboratory_view,name='laboratory'), url(r'^analysis', views.analysis_view,name='analysis'), - url(r'^laboratory', TemplateView.as_view(template_name='laboratory.html'),name='laboratory'), + url(r'^laboratory', views.laboratory_view,name='laboratory'), url(r'^promembership', TemplateView.as_view(template_name='promembership.html'),name='promembership'), url(r'^starttrial$',views.start_trial_view), url(r'^startplantrial$',views.start_plantrial_view), - url(r'^planmembership', TemplateView.as_view(template_name='planmembership.html'),name='planmembership'), - url(r'^paypaltest', TemplateView.as_view(template_name='paypaltest.html'),name='paypaltest'), +# url(r'^planmembership', TemplateView.as_view(template_name='planmembership.html'),name='planmembership'), + # url(r'^paypaltest', TemplateView.as_view(template_name='paypaltest.html'),name='paypaltest'), url(r'^legal', TemplateView.as_view(template_name='legal.html'),name='legal'), url(r'^register$',views.rower_register_view), url(r'^register/thankyou/$', TemplateView.as_view(template_name='registerthankyou.html'), name='registerthankyou'), diff --git a/rowers/views.py b/rowers/views.py index c7bf1f74..6692cd82 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -1032,6 +1032,17 @@ def analysis_view(request): } ) +# Shows laboratory page +def laboratory_view(request): + r = getrequestrower(request) + return render(request, + "laboratory.html", + { + 'active':'nav-analysis', + 'rower':r, + } + ) + # Shows email form and sends it if submitted def sendmail(request): if request.method == 'POST': diff --git a/static/css/styles2.css b/static/css/styles2.css index 5dd4dcad..32bb9c12 100644 --- a/static/css/styles2.css +++ b/static/css/styles2.css @@ -564,4 +564,8 @@ aside .cd-accordion-menu.animated label::before { float: left !important; padding: 0 !important; } - + +.paypalpix { + width: auto !important; + max-width: 1px; +} diff --git a/templates/newbase.html b/templates/newbase.html index a8ef01cc..0967858d 100644 --- a/templates/newbase.html +++ b/templates/newbase.html @@ -199,12 +199,12 @@ @@ -219,13 +219,14 @@
      {% if user.rower.protrialexpires and user.rower.protrialexpires|is_future_date %} - {% if user.rower.plantrialexpires %} + {% if user.rower.plantrialexpires and user.rower.rowerplan != 'plan' %}
    • {{ user.rower.protrialexpires|date_dif|ddays }} days left of your Self-Coach trial - Would you like to upgrade now?

    • {% else %} + {% if user.rower.rowerplan == 'basic' %}
    • {{ user.rower.protrialexpires|date_dif|ddays }} days left of your Pro trial - Would you like to upgrade now? @@ -233,6 +234,7 @@

    • {% endif %} {% endif %} + {% endif %} {% if user.rower.emailbounced %}
    • From 3b8c252cd84ecdaf78de837870926db1b9c5bc56 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 11 Oct 2018 16:36:29 +0200 Subject: [PATCH 75/95] workflow view --- rowers/interactiveplots.py | 3 +- rowers/templates/flexthumbnails.html | 7 +-- rowers/templates/panel_comments.html | 59 ++++++++++--------- rowers/templates/panel_map.html | 16 +++-- rowers/templates/panel_middlesocial.html | 11 ++-- rowers/templates/panel_shortcomment.html | 6 +- rowers/templates/panel_statcharts.html | 21 ++++--- rowers/templates/panel_stats.html | 8 +-- rowers/templates/workflow.html | 75 ++++++++---------------- rowers/urls.py | 3 +- rowers/views.py | 18 ++++++ 11 files changed, 114 insertions(+), 113 deletions(-) diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index f2f5004b..e74bd2b6 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -3509,7 +3509,8 @@ def thumbnail_flex_chart(rowdata,id=0,promember=0, - plot.sizing_mode = 'scale_width' +# plot.sizing_mode = 'scale_width' + plot.sizing_mode = 'fixed' plot.toolbar.logo = None plot.toolbar_location = None #plot.yaxis.visible = False diff --git a/rowers/templates/flexthumbnails.html b/rowers/templates/flexthumbnails.html index 16de6af0..9e6c2179 100644 --- a/rowers/templates/flexthumbnails.html +++ b/rowers/templates/flexthumbnails.html @@ -1,9 +1,8 @@ -

      Flex Charts

      -
      - {{ charts | safe }} -
      +
        + {{ charts| safe }} +
      diff --git a/rowers/templates/panel_comments.html b/rowers/templates/panel_comments.html index 99de3bbc..db1bd207 100644 --- a/rowers/templates/panel_comments.html +++ b/rowers/templates/panel_comments.html @@ -1,31 +1,34 @@ {% load rowerfilters %} {% load tz %} -
      - - -{% localtime on %} - -{% endlocaltime %} - - - - - - - - - - + + + + +
      Date/Time:{{ workout.startdatetime|localtime}}
      Distance:{{ workout.distance }}m
      Duration:{{ workout.duration |durationprint:"%H:%M:%S.%f" }}
      Public link to this workout - https://rowsandall.com/rowers/workout/{{ workout.id }} -
      Comments +
      Public link to interactive chart + https://rowsandall.com/rowers/workout/{{ workout.id }}/interactiveplot + +
      +
    • +
    + diff --git a/rowers/templates/panel_map.html b/rowers/templates/panel_map.html index 9fd40d8a..77cfda5a 100644 --- a/rowers/templates/panel_map.html +++ b/rowers/templates/panel_map.html @@ -1,9 +1,13 @@ -
    - {{ mapdiv|safe }} - - - {{ mapscript|safe }} -
    +
      +
    • +
      + {{ mapdiv|safe }} + + + {{ mapscript|safe }} +
      +
    • +
    diff --git a/rowers/templates/panel_middlesocial.html b/rowers/templates/panel_middlesocial.html index b1c917d2..bf8e07a2 100644 --- a/rowers/templates/panel_middlesocial.html +++ b/rowers/templates/panel_middlesocial.html @@ -1,14 +1,13 @@ -
    -
    +

    Share -
    - +

    diff --git a/rowers/templates/panel_shortcomment.html b/rowers/templates/panel_shortcomment.html index 6774494b..800d8706 100644 --- a/rowers/templates/panel_shortcomment.html +++ b/rowers/templates/panel_shortcomment.html @@ -1,6 +1,7 @@ {% load rowerfilters %} {% load tz %} -
    +
      +
    • @@ -10,4 +11,5 @@
      Comments
      -
    + + diff --git a/rowers/templates/panel_statcharts.html b/rowers/templates/panel_statcharts.html index 39b6de59..ec79eea0 100644 --- a/rowers/templates/panel_statcharts.html +++ b/rowers/templates/panel_statcharts.html @@ -1,11 +1,14 @@ {% if statcharts %}

    Static Charts

    -{% for graph in statcharts %} -
    - - {{ graph.filename }} -
    -{% endfor %} -{% endif %} +
      + {% for graph in statcharts %} +
    • + + {{ graph.filename }} + +
    • + {% endfor %} + {% endif %} +
    diff --git a/rowers/templates/panel_stats.html b/rowers/templates/panel_stats.html index 248127f8..68543efc 100644 --- a/rowers/templates/panel_stats.html +++ b/rowers/templates/panel_stats.html @@ -1,5 +1,3 @@ -
    -

    - Workout Stats -

    -
    +

    + Workout Stats +

    diff --git a/rowers/templates/workflow.html b/rowers/templates/workflow.html index 7beed9d4..1a71dab0 100644 --- a/rowers/templates/workflow.html +++ b/rowers/templates/workflow.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% load tz %} @@ -37,69 +37,44 @@ var counter=0; $("#id_sitready").remove(); $("#id_thumbs").append( - "

    Click on the thumbnails to view the full chart.

    "); + "
  • Click on the thumbnails to view the full chart.

  • "); $.each(json, function(index, element) { console.log('adding thumbnail'); var counter2 = counter+1; - if (shownotes) { $("#id_thumbs").append( - "
    "+ + "
  • "+ ""+counter2+""+ - "
    "+ ""+element.div+""+ - ""+element.notes+""+ - "
  • "); - } else { - $("#id_thumbs").append( - "
    "+ - ""+counter2+""+ - "
    "+ - ""+element.div+""+ - "
    "); + ""); - } $("#id_thumbscripts").append(element.script); counter += 1; + }); }); - }); + + }); {% endblock %} -{% block content %} -
    -
    -

    {{ workout.name }}

    - {% if workout.user.user != user %} -

    {{ workout.user.user.first_name }} {{ workout.user.user.last_name }} - {% endif %} -

    -
    - {% block left_panel %} - - {% for templateName in leftTemplates %} - {% include templateName %} - {% endfor %} - {% endblock %} -
    -
    - {% block middle_panel %} - {% for templateName in middleTemplates %} -
    - {% include templateName %} -
    - {% endfor %} - {% endblock %} -
    -
    - {% block right_panel %} -

     

    - {% endblock %} -
    +{% block main %} +

    {{ workout.name }}

    +{% if workout.user.user != user %} +

    {{ workout.user.user.first_name }} {{ workout.user.user.last_name }}

    +{% endif %} + +{% block middle_panel %} +{% for templateName in middleTemplates %} +{% include templateName %} +{% endfor %} +{% endblock %} +{% block right_panel %} +{% endblock %} + +{% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} {% endblock %} diff --git a/rowers/urls.py b/rowers/urls.py index d163c11a..9a99a301 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -525,8 +525,7 @@ urlpatterns = [ url(r'^courses/(?P\d+)$',views.course_view), url(r'^courses/(?P\d+)/map$',views.course_map_view), # URLS to be created -# url(r'^me/preferences$',views.user_preferences_view), -# url(r'^help$',views.user_preferences_view),TemplateView.as_view(template_name='help.html'), name='help'), + url(r'^help$',TemplateView.as_view(template_name='help.html'), name='help'), ] diff --git a/rowers/views.py b/rowers/views.py index 6692cd82..124c5414 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -8543,11 +8543,29 @@ def workout_workflow_view(request,id): pass + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(row.id) + }, + { + 'url':reverse(workout_workflow_view,kwargs={'id':id}), + 'name': 'View' + } + + ] + return render(request, 'workflow.html', { 'middleTemplates':middleTemplates, 'leftTemplates':leftTemplates, + 'active':'nav-workouts', + 'breadcrumbs':breadcrumbs, 'charts':charts, 'workout':row, 'mapscript':mapscript, From cc724afcbbc076bb4d8fad5666a0c06d8beeb4be Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 11 Oct 2018 20:26:23 +0200 Subject: [PATCH 76/95] flex chart --- rowers/forms.py | 11 ++ rowers/templates/flexchart3otw.html | 258 ++++++--------------------- rowers/templates/panel_comments.html | 5 - rowers/views.py | 63 ++++++- 4 files changed, 124 insertions(+), 213 deletions(-) diff --git a/rowers/forms.py b/rowers/forms.py index 8b9ff819..f4877106 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -923,6 +923,17 @@ class VirtualRaceSelectForm(forms.Form): choices = get_countries(),initial='All' ) +class FlexOptionsForm(forms.Form): + includereststrokes = forms.BooleanField(initial=True, required = False, + label='Include Rest Strokes') + plotchoices = ( + ('line','Line Plot'), + ('scatter','Scatter Plot'), + ) + plottype = forms.ChoiceField(choices=plotchoices,initial='scatter', + label='Chart Type') + + class FlexAxesForm(forms.Form): axchoices = ( (ax[0],ax[1]) for ax in axes if ax[0] not in ['cumdist','None'] diff --git a/rowers/templates/flexchart3otw.html b/rowers/templates/flexchart3otw.html index 7c6180c7..be59e81f 100644 --- a/rowers/templates/flexchart3otw.html +++ b/rowers/templates/flexchart3otw.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% load tz %} @@ -6,7 +6,7 @@ {% block title %} Flexible Plot {% endblock %} {% localtime on %} -{% block content %} +{% block main %} {{ js_res | safe }} {{ css_res| safe }} @@ -20,197 +20,45 @@ {{ the_script |safe }} - - - - - -

     

    - -
    - - -
    - - - +

    Flexible Chart

    - - -
    -
    -
    - {% csrf_token %} - {% if workstrokesonly %} - - - {% else %} - - - {% endif %} -
    - If your data source allows, this will show or hide strokes taken during rest intervals. -
    -
    - {% if plottype == 'scatter' %} - Line - {% else %} - Scatter - {% endif %} -
    -
    - -
    - -
    - - - {{ the_div|safe }} - -
    - -
    -
    - {% if maxfav >= 0 %} - Manage Favorites - {% else %} -   - {% endif %} -
    -
    - {% if favoritenr > 0 %} - < - {% else %} - < - {% endif %} -
    -
    +
  • + {% if favoritenr > 0 %} + + + + {% else %} + + + + {% endif %} {% csrf_token %} {% if workstrokesonly %} @@ -218,22 +66,28 @@ {% else %} {% endif %} - -
    -
  • -
    - {% if favoritenr < maxfav %} - > + + {% if favoritenr < maxfav %} + + + {% else %} - > + + + {% endif %} -
    - {% if favoritechartnotes %} -
    + + {% if favoritechartnotes %}

    Chart {{ favoritenr|add:1 }}:{{ favoritechartnotes }}

    -
    - {% endif %} -
    + {% endif %} + + {% endblock %} {% endlocaltime %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/panel_comments.html b/rowers/templates/panel_comments.html index db1bd207..3250e4fd 100644 --- a/rowers/templates/panel_comments.html +++ b/rowers/templates/panel_comments.html @@ -22,11 +22,6 @@ Comment ({{ aantalcomments }}) - - Public link to interactive chart - - https://rowsandall.com/rowers/workout/{{ workout.id }}/interactiveplot - diff --git a/rowers/views.py b/rowers/views.py index 124c5414..b9b544ae 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -44,6 +44,7 @@ from rowers.forms import ( PlannedSessionTeamForm,PlannedSessionTeamMemberForm, VirtualRaceSelectForm,WorkoutRaceSelectForm,CourseSelectForm, RaceResultFilterForm,PowerIntervalUpdateForm,FlexAxesForm, + FlexOptionsForm ) from django.core.urlresolvers import reverse, reverse_lazy @@ -8705,12 +8706,23 @@ def workout_flexchart3_view(request,*args,**kwargs): except KeyError: messages.error(request,'We cannot save the ad hoc metrics in a favorite chart') - if request.method == 'POST' and 'workstrokesonly' in request.POST: - workstrokesonly = request.POST['workstrokesonly'] - if workstrokesonly == 'True': - workstrokesonly = True - else: - workstrokesonly = False + if request.method == 'POST' and 'xaxis' in request.POST: + flexoptionsform = FlexOptionsForm(request.POST) + if flexoptionsform.is_valid(): + cd = flexoptionsform.cleaned_data + includereststrokes = cd['includereststrokes'] + plottype = cd['plottype'] + + workstrokesonly = not includereststrokes + + flexaxesform = FlexAxesForm(request,request.POST) + + + if flexaxesform.is_valid(): + cd = flexaxesform.cleaned_data + xparam = cd['xaxis'] + yparam1 = cd['yaxis1'] + yparam2 = cd['yaxis2'] if not promember: for name,d in rowingmetrics: @@ -8803,10 +8815,49 @@ def workout_flexchart3_view(request,*args,**kwargs): except KeyError: pass + initial = { + 'xaxis':xparam, + 'yaxis1':yparam1, + 'yaxis2':yparam2 + } + flexaxesform = FlexAxesForm(request,initial=initial) + + initial = { + 'includereststrokes': not workstrokesonly, + 'plottype':plottype + } + + flexoptionsform = FlexOptionsForm(initial=initial) + + row = Workout.objects.get(id=id) + + breadcrumbs = [ + { + 'url':'/rowers/list-workouts', + 'name':'Workouts' + }, + { + 'url':get_workout_default_page(request,id), + 'name': str(row.id) + }, + { + 'url':reverse(workout_flexchart3_view,kwargs=kwargs), + 'name': 'Flex Chart' + } + + ] + + return render(request, 'flexchart3otw.html', {'the_script':script, 'the_div':div, + 'breadcrumbs':breadcrumbs, + 'rower':r, + 'active':'nav-workouts', + 'workout':row, + 'chartform':flexaxesform, + 'optionsform':flexoptionsform, 'js_res': js_resources, 'css_res':css_resources, 'teams':get_my_teams(request.user), From 9d69fa15ea91609de294fa79149e56e11ebd3d37 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 11 Oct 2018 20:35:38 +0200 Subject: [PATCH 77/95] flex chart --- rowers/interactiveplots.py | 6 +++++- rowers/templates/flexchart3otw.html | 2 +- rowers/templates/multiflex.html | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index e74bd2b6..c06a7415 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -629,6 +629,8 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False): ), plot]) + layout.sizing_mode = 'scale_width' + script, div = components(layout) js_resources = INLINE.render_js() css_resources = INLINE.render_css() @@ -3342,7 +3344,9 @@ def interactive_flex_chart2(id=0,promember=0, slider_work_max, ], ), - plot]) + plot]) + + layout.sizing_mode = 'scale_width' script, div = components(layout) js_resources = INLINE.render_js() diff --git a/rowers/templates/flexchart3otw.html b/rowers/templates/flexchart3otw.html index be59e81f..6dc19c95 100644 --- a/rowers/templates/flexchart3otw.html +++ b/rowers/templates/flexchart3otw.html @@ -24,7 +24,7 @@
    • -
      +
      {{ the_div|safe }}
    • diff --git a/rowers/templates/multiflex.html b/rowers/templates/multiflex.html index 7f432a71..92d4f140 100644 --- a/rowers/templates/multiflex.html +++ b/rowers/templates/multiflex.html @@ -22,7 +22,7 @@
      • -
        +
        {{ the_div|safe }}
      • From f72335c49d3d5f2382306c16b07922593d4c7fad Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 11 Oct 2018 22:03:04 +0200 Subject: [PATCH 78/95] line 422 and started with plan_menu --- rowers/templates/menu_plan.html | 39 +++++ .../templates/plannedsession_multicreate.html | 159 ++++++------------ rowers/templates/trainingplan_create.html | 104 ++++-------- rowers/templates/trainingplan_delete.html | 28 ++- rowers/urls.py | 48 +++--- rowers/views.py | 21 +-- 6 files changed, 175 insertions(+), 224 deletions(-) diff --git a/rowers/templates/menu_plan.html b/rowers/templates/menu_plan.html index d70323af..70cd7191 100644 --- a/rowers/templates/menu_plan.html +++ b/rowers/templates/menu_plan.html @@ -1,5 +1,20 @@ +{% load staticfiles %} +{% load rowerfilters %}

        Plan

      + +{% if user.is_authenticated and user|is_manager %} +

       

      + + +{% endif %} diff --git a/rowers/templates/plannedsession_multicreate.html b/rowers/templates/plannedsession_multicreate.html index 2bf4c0b4..0a3ede15 100644 --- a/rowers/templates/plannedsession_multicreate.html +++ b/rowers/templates/plannedsession_multicreate.html @@ -1,118 +1,65 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Plan entire microcycle{% endblock %} -{% block content %} -
      - {% include "planningbuttons.html" %} -
      +{% block main %} +

      Create Sessions for {{ rower.user.first_name }} {{ rower.user.last_name }}

      -
      -
      -

      Create Sessions for {{ rower.user.first_name }} {{ rower.user.last_name }}

      -
      - -{% if user.is_authenticated and user|is_manager %} - -{% endif %} -
      - -
      -

      - On this page, you can create and edit sessions for an entire time - period. You see a list of the current sessions planned for the - selected time period. Each row in the table is a session. You can - remove a session by clicking "remove" at the end of a row. - You can edit the date in the forms. If you need to add a new session, - click the "Add More" button to add a new session. Use the "Submit" - button to commit any changes you made. +

        +
      • +

        + On this page, you can create and edit sessions for an entire time + period. You see a list of the current sessions planned for the + selected time period. Each row in the table is a session. You can + remove a session by clicking "remove" at the end of a row. + You can edit the date in the forms. If you need to add a new session, + click the "Add More" button to add a new session. Use the "Submit" + button to commit any changes you made.

        -
        - {% csrf_token %} - {{ ps_formset.management_form }} - - - - - {% for field in ps_formset.0.visible_fields %} - - {% endfor %} - - - - {% for form in ps_formset %} - - +
         {{ field.label_tag }}
        {{ forloop.counter }} - {% if form.instance.pk %}{{ form.DELETE }}{% endif %} - {{ form.id }} - {% for field in form.hidden_fields %} - {{ field }} + + {% csrf_token %} + {{ ps_formset.management_form }} + + + + + {% for field in ps_formset.0.visible_fields %} + {% endfor %} - {% for field in form.visible_fields %} - + + + + {% for form in ps_formset %} + + + {% endfor %} + {% endfor %} - - {% endfor %} - -
         {{ field.label_tag }} - {{ field }} -
        {{ forloop.counter }} + {% if form.instance.pk %}{{ form.DELETE }}{% endif %} + {{ form.id }} + {% for field in form.hidden_fields %} + {{ field }} + {% endfor %} + {% for field in form.visible_fields %} + + {{ field }} +
        - Add More - - Clone multiple sessions +
        + + Add More + + or + + Clone multiple sessions + +
        +
      • +
      -
      - -
      - -
      -{% endblock %} - -{% block scripts %} @@ -200,3 +147,7 @@ {% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} diff --git a/rowers/templates/trainingplan_create.html b/rowers/templates/trainingplan_create.html index 350e2119..cbf7ffb6 100644 --- a/rowers/templates/trainingplan_create.html +++ b/rowers/templates/trainingplan_create.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -8,32 +8,11 @@ {% endblock %} -{% block content %} - - -
      - {% if user.is_authenticated and user|is_manager %} - - {% endif %} -
      - - -
      -
      -

      Training Targets

      +{% block main %} +

      Manage Training Targets and Plan for {{ rower.user.first_name }} {{ rower.user.last_name }}

      +
        +
      • +

        Training Targets

        {% if targets %} @@ -59,31 +38,22 @@ {% else %} No training targets found {% endif %} - -
        -
        -

        Add a target

        + +
      • +

        Add a target

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

    Plans

    + +
  • +

    Plans

    {% if plans %} @@ -111,28 +81,24 @@

    No plans found

    {% endif %} - - - - -
    -
    -

    Add a plan

    - -
    -
    - {{ form.as_table }} -
    - {% csrf_token %} -
    +
  • +
  • +

    Add a plan

    + + + + {{ form.as_table }} +
    + {% csrf_token %} -
  • - -
    -
    + + -
    {% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} diff --git a/rowers/templates/trainingplan_delete.html b/rowers/templates/trainingplan_delete.html index ffb9362c..d39d971f 100644 --- a/rowers/templates/trainingplan_delete.html +++ b/rowers/templates/trainingplan_delete.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -8,23 +8,17 @@ {% endblock %} -{% block content %} - +{% block main %} -
    - -
    - {% csrf_token %} -

    Are you sure you want to delete {{ object }}?

    -
    - -
    -
    +
    + {% csrf_token %} +

    Are you sure you want to delete {{ object }}?

    + +
    -
    {% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} diff --git a/rowers/urls.py b/rowers/urls.py index 9a99a301..34607d96 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -409,11 +409,11 @@ urlpatterns = [ url(r'^workout/(?P\d+)/flexchart/(?P\w+.*)/(?P[\w\ ]+.*)/(?P[\w\ ]+.*)/(?P\w+.*)$',views.workout_flexchart3_view), url(r'^workout/(?P\d+)/flexchart/(?P\w+.*)/(?P[\w\ ]+.*)/(?P[\w\ ]+.*)$',views.workout_flexchart3_view), url(r'^workout/(?P\d+)/flexchart$',views.workout_flexchart3_view), - 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'^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'^createplan/$',views.rower_create_trainingplan), + url(r'^createplan/user/(?P\d+)$',views.rower_create_trainingplan), url(r'^deleteplan/(?P\d+)$',views.TrainingPlanDelete.as_view()), url(r'^deletemicrocycle/(?P\d+)$',views.MicroCycleDelete.as_view()), url(r'^deletemesocycle/(?P\d+)$',views.MesoCycleDelete.as_view()), @@ -440,57 +440,57 @@ urlpatterns = [ url(r'^sessions/teamedit/(?P\d+)/(?P[\w\ ]+.*)$',views.plannedsession_teamedit_view), url(r'^sessions/create$',views.plannedsession_create_view), - url(r'^sessions/create/rower/(?P\d+)$', + url(r'^sessions/create/user/(?P\d+)$', views.plannedsession_create_view), url( - r'^sessions/create/(?P[\w\ ]+.*)/rower/(?P\d+)$', + r'^sessions/create/(?P[\w\ ]+.*)/user/(?P\d+)$', views.plannedsession_create_view), url(r'^sessions/create/(?P[\w\ ]+.*)$', views.plannedsession_create_view), url(r'^sessions/multiclone$',views.plannedsession_multiclone_view), - url(r'^sessions/multiclone/(?P[\w\ ]+.*)/rower/(?P\d+)/extra/(?P\d+)$', + url(r'^sessions/multiclone/(?P[\w\ ]+.*)/user/(?P\d+)/extra/(?P\d+)$', views.plannedsession_multiclone_view), - url(r'^sessions/multiclone/rower/(?P\d+)$', + url(r'^sessions/multiclone/user/(?P\d+)$', views.plannedsession_multiclone_view), url( - r'^sessions/multiclone/(?P[\w\ ]+.*)/rower/(?P\d+)$', + r'^sessions/multiclone/(?P[\w\ ]+.*)/user/(?P\d+)$', views.plannedsession_multiclone_view), url(r'^sessions/multiclone/(?P[\w\ ]+.*)$', views.plannedsession_multiclone_view), url(r'^sessions/multicreate$',views.plannedsession_multicreate_view), - url(r'^sessions/multicreate/(?P[\w\ ]+.*)/rower/(?P\d+)/extra/(?P\d+)$', + url(r'^sessions/multicreate/(?P[\w\ ]+.*)/user/(?P\d+)/extra/(?P\d+)$', views.plannedsession_multicreate_view), - url(r'^sessions/multicreate/rower/(?P\d+)$', + url(r'^sessions/multicreate/user/(?P\d+)$', views.plannedsession_multicreate_view), url( - r'^sessions/multicreate/(?P[\w\ ]+.*)/rower/(?P\d+)$', + r'^sessions/multicreate/(?P[\w\ ]+.*)/user/(?P\d+)$', views.plannedsession_multicreate_view), url(r'^sessions/multicreate/(?P[\w\ ]+.*)$', views.plannedsession_multicreate_view), - url(r'^sessions/(?P\d+)/edit/(?P[\w\ ]+.*)/rower/(?P\d+)$',views.plannedsession_edit_view), + url(r'^sessions/(?P\d+)/edit/(?P[\w\ ]+.*)/user/(?P\d+)$',views.plannedsession_edit_view), url(r'^sessions/(?P\d+)/edit/(?P[\w\ ]+.*)$',views.plannedsession_edit_view), url(r'^sessions/(?P\d+)/edit$',views.plannedsession_edit_view), url(r'^sessions/(?P\d+)/clone$',views.plannedsession_clone_view), - url(r'^sessions/(?P\d+)/clone/(?P[\w\ ]+.*)/rower/(?P\d+)$',views.plannedsession_clone_view), + url(r'^sessions/(?P\d+)/clone/(?P[\w\ ]+.*)/user/(?P\d+)$',views.plannedsession_clone_view), url(r'^sessions/(?P\d+)/clone/(?P[\w\ ]+.*)$',views.plannedsession_clone_view), url(r'^sessions/(?P\d+)$',views.plannedsession_view, name='plannedsession_view'), - url(r'^sessions/(?P\d+)/(?P[\w\ ]+.*)/rower/(?P\d+)$',views.plannedsession_view, + url(r'^sessions/(?P\d+)/(?P[\w\ ]+.*)/user/(?P\d+)$',views.plannedsession_view, name='plannedsession_view'), - url(r'^sessions/(?P\d+)/rower/(?P\d+)$',views.plannedsession_view, + url(r'^sessions/(?P\d+)/user/(?P\d+)$',views.plannedsession_view, name='plannedsession_view'), url(r'^sessions/(?P\d+)/deleteconfirm$',views.plannedsession_deleteconfirm_view), url(r'^sessions/(?P\d+)/delete$',views.plannedsession_delete_view), url(r'^sessions/manage/session/(?P\d+)$', views.plannedsessions_manage_view), - url(r'^sessions/manage/rower/(?P\d+)/session/(?P\d+)$', + url(r'^sessions/manage/user/(?P\d+)/session/(?P\d+)$', views.plannedsessions_manage_view), - url(r'^sessions/manage/(?P[\w\ ]+.*)/rower/(?P\d+)/session/(?P\d+)$', + url(r'^sessions/manage/(?P[\w\ ]+.*)/user/(?P\d+)/session/(?P\d+)$', views.plannedsessions_manage_view), url(r'^sessions/manage/(?P[\w\ ]+.*)/session/(?P\d+)$', views.plannedsessions_manage_view), @@ -498,9 +498,9 @@ urlpatterns = [ url(r'^sessions/manage/?$', views.plannedsessions_manage_view), - url(r'^sessions/manage/rower/(?P\d+)$', + url(r'^sessions/manage/user/(?P\d+)$', views.plannedsessions_manage_view), - url(r'^sessions/manage/(?P[\w\ ]+.*)/rower/(?P\d+)$', + url(r'^sessions/manage/(?P[\w\ ]+.*)/user/(?P\d+)$', views.plannedsessions_manage_view), url(r'^sessions/manage/(?P[\w\ ]+.*)$', views.plannedsessions_manage_view), @@ -510,12 +510,12 @@ urlpatterns = [ url(r'^sessions/coach/(?P[\w\ ]+.*)$', views.plannedsessions_coach_view), url(r'^sessions/print/?$',views.plannedsessions_print_view), - url(r'^sessions/print/rower/(?P\d+)$',views.plannedsessions_print_view), - url(r'^sessions/print/(?P[\w\ ]+.*)/rower/(?P\d+)$',views.plannedsessions_print_view), + url(r'^sessions/print/user/(?P\d+)$',views.plannedsessions_print_view), + url(r'^sessions/print/(?P[\w\ ]+.*)/user/(?P\d+)$',views.plannedsessions_print_view), url(r'^sessions/print/(?P[\w\ ]+.*)$',views.plannedsessions_print_view), url(r'^sessions/?$',views.plannedsessions_view), - url(r'^sessions/rower/(?P\d+)$',views.plannedsessions_view), - url(r'^sessions/(?P[\w\ ]+.*)/rower/(?P\d+)$',views.plannedsessions_view), + url(r'^sessions/user/(?P\d+)$',views.plannedsessions_view), + url(r'^sessions/(?P[\w\ ]+.*)/user/(?P\d+)$',views.plannedsessions_view), url(r'^sessions/(?P[\w\ ]+.*)$',views.plannedsessions_view), url(r'^courses/(?P\d+)/edit$',views.course_edit_view, name='course_edit_view'), diff --git a/rowers/views.py b/rowers/views.py index b9b544ae..64b0711c 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -13527,7 +13527,7 @@ def agegrouprecordview(request,sex='male',weightcategory='hwt', redirect_field_name=None) def plannedsession_multiclone_view( request,timeperiod='none', - rowerid=0, + rowerid=0,userid=0, startdate=timezone.now()-datetime.timedelta(days=30), enddate=timezone.now()): @@ -13630,7 +13630,7 @@ def plannedsession_multiclone_view( # Individual user creates training for himself @user_passes_test(hasplannedsessions,login_url="/rowers/planmembership/", redirect_field_name=None) -def plannedsession_create_view(request,timeperiod='thisweek',rowerid=0): +def plannedsession_create_view(request,timeperiod='thisweek',rowerid=0,userid=0): r = getrequestrower(request,rowerid=rowerid) startdate,enddate = get_dates_timeperiod(timeperiod) @@ -13754,7 +13754,7 @@ def plannedsession_create_view(request,timeperiod='thisweek',rowerid=0): @user_passes_test(hasplannedsessions,login_url="/rowers/planmembership/", redirect_field_name=None) def plannedsession_multicreate_view(request,timeperiod='thisweek', - teamid=0,rowerid=0,extrasessions=0): + teamid=0,rowerid=0,userid=0,extrasessions=0): extrasessions=int(extrasessions) @@ -13845,6 +13845,7 @@ def plannedsession_multicreate_view(request,timeperiod='thisweek', context = { 'ps_formset':ps_formset, 'rower':r, + 'active':'nav-plan', 'plan':trainingplan, 'timeperiod':timeperiod, 'teams':get_my_teams(request.user), @@ -14145,7 +14146,7 @@ def plannedsessions_coach_view(request,timeperiod='thisweek', ) @login_required() -def plannedsessions_view(request,timeperiod='thisweek',rowerid=0): +def plannedsessions_view(request,timeperiod='thisweek',rowerid=0,userid=0): r = getrequestrower(request,rowerid=rowerid) @@ -14192,7 +14193,7 @@ def plannedsessions_view(request,timeperiod='thisweek',rowerid=0): }) @login_required() -def plannedsessions_print_view(request,timeperiod='thisweek',rowerid=0): +def plannedsessions_print_view(request,timeperiod='thisweek',rowerid=0,userid=0): r = getrequestrower(request,rowerid=rowerid) @@ -14225,7 +14226,7 @@ def plannedsessions_print_view(request,timeperiod='thisweek',rowerid=0): @login_required() -def plannedsessions_manage_view(request,timeperiod='thisweek',rowerid=0, +def plannedsessions_manage_view(request,timeperiod='thisweek',rowerid=0,userid=0, initialsession=0): is_ajax = False @@ -14358,7 +14359,7 @@ def plannedsessions_manage_view(request,timeperiod='thisweek',rowerid=0, # need clarity on cloning behavior time shift @user_passes_test(hasplannedsessions,login_url="/rowers/planmembership/", redirect_field_name=None) -def plannedsession_clone_view(request,id=0,rowerid=0, +def plannedsession_clone_view(request,id=0,rowerid=0,userid=0, timeperiod='thisweek'): r = getrequestrower(request,rowerid=rowerid) @@ -14414,7 +14415,7 @@ def plannedsession_clone_view(request,id=0,rowerid=0, # Edit an existing planned session @user_passes_test(hasplannedsessions,login_url="/rowers/planmembership/", redirect_field_name=None) -def plannedsession_edit_view(request,id=0,timeperiod='thisweek',rowerid=0): +def plannedsession_edit_view(request,id=0,timeperiod='thisweek',rowerid=0,userid=0): r = getrequestrower(request,rowerid=rowerid) @@ -14489,7 +14490,7 @@ def plannedsession_edit_view(request,id=0,timeperiod='thisweek',rowerid=0): @login_required() -def plannedsession_view(request,id=0,rowerid=0, +def plannedsession_view(request,id=0,rowerid=0,userid=0, timeperiod='thisweek'): m = getrower(request.user) @@ -15524,7 +15525,7 @@ def rower_delete_trainingtarget(request,id=0): if checkaccessuser(request.user,target.rower): target.delete() - messages.info(request,"We have deleted the training plan") + messages.info(request,"We have deleted the training target") else: raise PermissionDenied("Access denied") From 3cfc6209063a730adf23079c48454a65ab1960ae Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Fri, 12 Oct 2018 15:40:49 +0200 Subject: [PATCH 79/95] restore --- rowers/plannedsessions.py | 28 ++ rowers/templates/menu_analytics.html | 2 + rowers/templates/menu_plan.html | 69 +++ rowers/templates/menu_profile.html | 3 +- rowers/templates/menu_workouts.html | 3 +- rowers/templates/trainingplan.html | 619 ++++++++++++++++----------- rowers/templatetags/rowerfilters.py | 22 +- rowers/urls.py | 11 +- rowers/views.py | 85 +++- static/css/rowsandall2.css | 77 ++-- static/css/styles2.css | 79 +++- 11 files changed, 700 insertions(+), 298 deletions(-) diff --git a/rowers/plannedsessions.py b/rowers/plannedsessions.py index da035b17..6428378e 100644 --- a/rowers/plannedsessions.py +++ b/rowers/plannedsessions.py @@ -32,6 +32,34 @@ import courses from rowers.tasks import handle_check_race_course +def get_todays_micro(plan): + thismicro = None + + thismacro = TrainingMacroCycle.objects.filter( + plan=plan, + startdate__lte = date.today(), + enddate__gte = date.today() + ) + + if thismacro: + thismeso = TrainingMesoCycle.objects.filter( + plan=thismacro[0], + startdate__lte = date.today(), + enddate__gte = date.today() + ) + + if thismeso: + thismicro = TrainingMicroCycle.objects.filter( + plan=thismeso[0], + startdate__lte = date.today(), + enddate__gte = date.today() + ) + + if thismicro: + thismicro = thismicro[0] + + return thismicro + # Low Level functions - to be called by higher level methods def add_workouts_plannedsession(ws,ps,r): result = 0 diff --git a/rowers/templates/menu_analytics.html b/rowers/templates/menu_analytics.html index 7c3557c6..ba495712 100644 --- a/rowers/templates/menu_analytics.html +++ b/rowers/templates/menu_analytics.html @@ -71,6 +71,7 @@ {% if user.is_authenticated and user|is_manager %}

     

    +{% if user|team_members %}
    • @@ -91,3 +92,4 @@
    {% endif %} +{% endif %} diff --git a/rowers/templates/menu_plan.html b/rowers/templates/menu_plan.html index 70cd7191..018fd7d7 100644 --- a/rowers/templates/menu_plan.html +++ b/rowers/templates/menu_plan.html @@ -13,6 +13,13 @@  Manage Plans + {% for plan in rower|trainingplans %} +
  • + +  {{ plan.name }} + +
  • + {% endfor %}
  • @@ -63,9 +70,70 @@
  • +

     

    + + + + {% if user.is_authenticated and user|is_manager %}

     

    +{% if user|team_members %}
    • @@ -86,3 +154,4 @@
    {% endif %} +{% endif %} diff --git a/rowers/templates/menu_profile.html b/rowers/templates/menu_profile.html index 88c78261..8bcdfc7c 100644 --- a/rowers/templates/menu_profile.html +++ b/rowers/templates/menu_profile.html @@ -31,7 +31,7 @@ {% if user.is_authenticated and user|is_manager %}

     

    - +{% if user|team_members %}
    • @@ -52,3 +52,4 @@
    {% endif %} +{% endif %} diff --git a/rowers/templates/menu_workouts.html b/rowers/templates/menu_workouts.html index 0370e37a..54f1189c 100644 --- a/rowers/templates/menu_workouts.html +++ b/rowers/templates/menu_workouts.html @@ -40,7 +40,7 @@ {% if user.is_authenticated and user|is_manager %}

     

    - +{% if user|team_members %|
    • @@ -61,3 +61,4 @@
    {% endif %} +{% endif %} diff --git a/rowers/templates/trainingplan.html b/rowers/templates/trainingplan.html index cc17460f..f62ab943 100644 --- a/rowers/templates/trainingplan.html +++ b/rowers/templates/trainingplan.html @@ -1,197 +1,72 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Rowsandall Training Plans{% endblock %} -{% block scripts %} -{% endblock %} +{% block main %} +

    Training Plan - {{ plan.name }}

    +

    This plan starts on {{ plan.startdate }} and ends on {{ plan.enddate }}. + {% if plan.target %} + The training plan target is: {{ plan.target.name }} on {{ plan.target.date }}. + {% endif %} +

    +

    Edit the plan

    -{% block content %} -
    -
    -

    Training Plan - {{ plan.name }}

    -

    This plan starts on {{ plan.startdate }} and ends on {{ plan.enddate }}. The training plan target is: {{ plan.target.name }} on {{ plan.target.date }}.

    -

    Edit the plan

    -
    -
    -

    Macro Cycles

    -
    -
    -

    Meso Cycles

    -
    -
    -

    Micro Cycles

    -
    -
    +

    Plan Macro, Meso and Micro Cycles

    - -
    - {% now "Y-m-d" as todays_date %} - - {% for key,macrocycle in cycles.items %} - -
    - - {% if macrocycle.0.type == 'filler' %} -
    - {% else %} -
    - {% endif %} - -
    - - - - - {% if macrocycle.0.type == 'userdefined' %} - - - - - - - - - - - - - - - - - - - - - - {% endif %} - {% if todays_date <= macrocycle.0.enddate|date:"Y-m-d" %} - - - - - - - {% else %} - - - - - - - {% endif %} -
    - {{ macrocycle.0.name }} ({{ macrocycle.0.startdate }} - {{ macrocycle.0.enddate }}) -
    dist (m)t (min)rScoreTRIMP
    plan{{ macrocycle.0.plandistance }}{{ macrocycle.0.plantime }}{{ macrocycle.0.planrscore }}{{ macrocycle.0.plantrimp }}
    actual{{ macrocycle.0.actualdistance }}{{ macrocycle.0.actualtime }}{{ macrocycle.0.actualrscore }}{{ macrocycle.0.actualtrimp }}
     
    - edit - / - delete - / - sessions -
     
    - sessions -
    -
    - -
    - - -
    - - {% for key, mesocycle in macrocycle.1.items %} - -
    - - {% if mesocycle.0.type == 'filler' %} -
    - {% else %} -
    - {% endif %} - -
    - - - - - {% if mesocycle.0.type == 'userdefined' %} - - - - - - - - - - - - - - - - - - - - - - {% endif %} - {% if todays_date <= mesocycle.0.enddate|date:"Y-m-d" %} - {% if mesocycle.0.plan.type == 'userdefined' %} - - - - - - - {% endif %} - {% else %} - {% if mesocycle.0.plan.type == 'userdefined' %} - - - - - - - {% endif %} - {% endif %} -
    - {{ mesocycle.0.name }} ({{ mesocycle.0.startdate }} - {{ mesocycle.0.enddate }}) -
    dist (m)t (min)rScoreTRIMP
    plan{{ mesocycle.0.plandistance }}{{ mesocycle.0.plantime }}{{ mesocycle.0.planrscore }}{{ mesocycle.0.plantrimp }}
    actual{{ mesocycle.0.actualdistance }}{{ mesocycle.0.actualtime }}{{ mesocycle.0.actualrscore }}{{ mesocycle.0.actualtrimp }}
     
    - edit - / - delete - / - sessions -
     
    - sessions -
    -
    - -
    - - -
    - - {% for microcycle in mesocycle.1 %} - - {% if microcycle.type == 'filler' %} -
    - {% else %} -
    - {% endif %} - +
      + + {% for key, macrocycle in cycles.items %} +
    • + + +
        +
      • + {% if macrocycle.0.type == 'filler' %} +
        - {% if microcycle.type == 'userdefined' %} + {% if todays_date <= macrocycle.0.enddate|date:"Y-m-d" %} + +   + + + + + {% else %} + +   + + + sessions + + {% endif %} +
        - {{ microcycle.name }} ({{ microcycle.startdate }} - {{ microcycle.enddate }}) + {{ macrocycle.0.name }} ({{ macrocycle.0.startdate }} - {{ macrocycle.0.enddate }})
        + edit + / + delete + / + sessions +
        +
        +
        + {% else %} +
        +
        + + + + + {% if macrocycle.0.type == 'userdefined' %} @@ -201,90 +76,340 @@ - - - - + + + + - - - - + + + + {% endif %} - {% if todays_date <= microcycle.enddate|date:"Y-m-d" %} - {% if microcycle.plan.type == 'userdefined' %} + {% if todays_date <= macrocycle.0.enddate|date:"Y-m-d" %} + + {% else %} + + + + + {% endif %} - {% else %} - {% if microcycle.plan.type == 'userdefined' %} - - - - - - - {% endif %} - {% endif %}
        + {{ macrocycle.0.name }} ({{ macrocycle.0.startdate }} - {{ macrocycle.0.enddate }}) +
        dist (m)
        plan{{ microcycle.plandistance }}{{ microcycle.plantime }}{{ microcycle.planrscore }}{{ microcycle.plantrimp }}{{ macrocycle.0.plandistance }}{{ macrocycle.0.plantime }}{{ macrocycle.0.planrscore }}{{ macrocycle.0.plantrimp }}
        actual{{ microcycle.actualdistance }}{{ microcycle.actualtime }}{{ microcycle.actualrscore }}{{ microcycle.actualtrimp }}{{ macrocycle.0.actualdistance }}{{ macrocycle.0.actualtime }}{{ macrocycle.0.actualrscore }}{{ macrocycle.0.actualtrimp }}
         
        - edit + edit / - delete + delete / - sessions + sessions +
         
        + sessions
         
        - sessions -
        -
        - - {% endfor %} - -
    - -
    - - {% endfor %} - -
    - -
    - + {% endif %} + +
  • + + +
      + + {% for key, mesocycle in macrocycle.1.items %} +
    • + {% if mesocycle.0.type == 'filler' %} +
      +
      + + + + + {% if todays_date <= mesocycle.0.enddate|date:"Y-m-d" %} + {% if mesocycle.0.plan.type == 'userdefined' %} + +   + + + + + {% endif %} + {% else %} + +   + + + sessions + + {% endif %} +
      + Meso {{ mesocycle.0.name }} ({{ mesocycle.0.startdate }} - {{ mesocycle.0.enddate }}) +
      + edit + / + delete + / + sessions +
      +
      +
      + {% else %} +
      +
      + + + + + {% if mesocycle.0.type == 'userdefined' %} + + + + + + + + + + + + + + + + + + + + + + {% endif %} + {% if todays_date <= mesocycle.0.enddate|date:"Y-m-d" %} + + + + + + + {% else %} + + + + + + + {% endif %} +
      + Meso {{ mesocycle.0.name }} ({{ mesocycle.0.startdate }} - {{ mesocycle.0.enddate }}) +
      dist (m)t (min)rScoreTRIMP
      plan{{ mesocycle.0.plandistance }}{{ mesocycle.0.plantime }}{{ mesocycle.0.planrscore }}{{ mesocycle.0.plantrimp }}
      actual{{ mesocycle.0.actualdistance }}{{ mesocycle.0.actualtime }}{{ mesocycle.0.actualrscore }}{{ mesocycle.0.actualtrimp }}
       
      + edit + / + delete + / + sessions +
       
      + sessions +
      +
      +
      + {% endif %} +
    • +
    • + + +
        + + {% for microcycle in mesocycle.1 %} +
      • + {% if microcycle.type == 'filler' %} +
        +
        + + + + + {% if todays_date <= microcycle.enddate|date:"Y-m-d" %} + {% if microcycle.plan.type == 'userdefined' %} + + + + + + + {% endif %} + {% else %} + {% if microcycle.plan.type == 'userdefined' %} + + + + + + + {% endif %} + {% endif %} +
        + Micro {{ microcycle.name }} ({{ microcycle.startdate }} - {{ microcycle.enddate }}) +
         
        + edit + / + delete + / + sessions +
         
        + sessions +
        +
        +
        + {% else %} +
        +
        + + + + + + + + + + + + + + + + + + + + + + + + + + {% if todays_date <= microcycle.enddate|date:"Y-m-d" %} + + + + + + + {% else %} + + + + + + + {% endif %} +
        + Micro {{ microcycle.name }} ({{ microcycle.startdate }} - {{ microcycle.enddate }}) +
        dist (m)t (min)rScoreTRIMP
        plan{{ microcycle.plandistance }}{{ microcycle.plantime }}{{ microcycle.planrscore }}{{ microcycle.plantrimp }}
        actual{{ microcycle.actualdistance }}{{ microcycle.actualtime }}{{ microcycle.actualrscore }}{{ microcycle.actualtrimp }}
         
        + edit + / + delete + / + sessions +
         
        + sessions +
        +
        +
        + {% endif %} +
      • + {% endfor %} + +
      +
    • + {% endfor %} + +
    +
  • + + {% endfor %} - -
    - -
    -

    Click on the plan cycles to edit their names, start and end dates. - The gray "filler" - cycles are generated, adjusted and deleted automatically to - ensure the entire plan - duration is covered with non-overlapping cycles. - Once you edit a filler cycle, it become a user-defined - cycle, which cannot be deleted - by the system.

    -

    Filler cycles which have a filler cycle as a parent cannot be edited - or deleted. You have to edit the parent cycle first. The reason is - that children of filler cycles are not safe. They are deleted when - their parent is deleted by the system.

    -

    Click on "Sessions" in the cycle of your interest to see details - of the individual training sessions planned for this period.

    -

    A good way to organize the plan is to think of micro - cycles as training weeks. Macro cycles - are typically used to address specific phases of preparation - and to indicate the racing - season and may span several months. - Meso cycles can be used to group sequences of three to five - light, medium and - hard weeks. It is recommended to work from left to right, - starting with the macro cycles.

    -
    + + + +

    Click on the cycle to fold out its contents.

    + +

    Click on the plan cycles to edit their names, start and end dates. + The gray "filler" + cycles are generated, adjusted and deleted automatically to + ensure the entire plan + duration is covered with non-overlapping cycles. + Once you edit a filler cycle, it become a user-defined + cycle, which cannot be deleted + by the system.

    +

    Filler cycles which have a filler cycle as a parent cannot be edited + or deleted. You have to edit the parent cycle first. The reason is + that children of filler cycles are not safe. They are deleted when + their parent is deleted by the system.

    +

    Click on "Sessions" in the cycle of your interest to see details + of the individual training sessions planned for this period.

    +

    A good way to organize the plan is to think of micro + cycles as training weeks. Macro cycles + are typically used to address specific phases of preparation + and to indicate the racing + season and may span several months. + Meso cycles can be used to group sequences of three to five + light, medium and + hard weeks. It is recommended to work from left to right, + starting with the macro cycles.

    +{% endblock %} + +{% block scripts %} +{% if thismicro %} + + +{% elif thismeso %} + + +{% endif %} +{% if thismacro %} + + +{% endif %} +{% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} {% endblock %} diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py index 30a23bc7..223736a2 100644 --- a/rowers/templatetags/rowerfilters.py +++ b/rowers/templatetags/rowerfilters.py @@ -185,7 +185,7 @@ def get_field_id(id,s,form): field_name = s+str(id) return form.__getitem__(field_name) -from rowers.models import Rower,Team +from rowers.models import Rower,Team,TrainingPlan,TrainingTarget from rowers.views import ispromember @register.filter @@ -350,3 +350,23 @@ def userurl(path,member): replaced = path+userstring return replaced + +@register.filter +def timeurl(path,timestring): + pattern = re.compile('\?when=w.*') + timeurl = '?when=%s' % timestring + replaced = '' + + if pattern.search(path) is not None: + replaced = pattern.sub(timeurl,path) + + if not replaced: + replaced = path+timeurl + + return replaced + +@register.filter +def trainingplans(rower): + plans = TrainingPlan.objects.filter(rower=rower).order_by("-startdate") + + return plans diff --git a/rowers/urls.py b/rowers/urls.py index 34607d96..510e2008 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -413,13 +413,20 @@ urlpatterns = [ # 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/user/(?P\d+)$',views.rower_create_trainingplan), + url(r'^createplan/user/(?P\d+)/$',views.rower_create_trainingplan), url(r'^deleteplan/(?P\d+)$',views.TrainingPlanDelete.as_view()), url(r'^deletemicrocycle/(?P\d+)$',views.MicroCycleDelete.as_view()), url(r'^deletemesocycle/(?P\d+)$',views.MesoCycleDelete.as_view()), url(r'^deletemacrocycle/(?P\d+)$',views.MacroCycleDelete.as_view()), # url(r'^deleteplan/(?P\d+)$',views.rower_delete_trainingplan), - url(r'^plan/(?P\d+)$',views.rower_trainingplan_view), + url(r'^plan/(?P\d+)/$',views.rower_trainingplan_view), + url(r'^plan/(?P\d+)/user/(?P\d+)/$',views.rower_trainingplan_view), + url(r'^plan/(?P\d+)/micro/(?P\d+)/$',views.rower_trainingplan_view), + url(r'^plan/(?P\d+)/micro/(?P\d+)/user/(?P\d+)/$',views.rower_trainingplan_view), + url(r'^plan/(?P\d+)/meso/(?P\d+)/$',views.rower_trainingplan_view), + url(r'^plan/(?P\d+)/meso/(?P\d+)/user/(?P\d+)/$',views.rower_trainingplan_view), + url(r'^plan/(?P\d+)/macro/(?P\d+)/$',views.rower_trainingplan_view), + url(r'^plan/(?P\d+)/macro/(?P\d+)/user/(?P\d+)/$',views.rower_trainingplan_view), url(r'^macrocycle/(?P\d+)$',views.TrainingMacroCycleUpdate.as_view()), url(r'^mesocycle/(?P\d+)$',views.TrainingMesoCycleUpdate.as_view()), url(r'^microcycle/(?P\d+)$',views.TrainingMicroCycleUpdate.as_view()), diff --git a/rowers/views.py b/rowers/views.py index 64b0711c..1289cec3 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -209,6 +209,8 @@ class JSONResponse(HttpResponse): def getrequestrower(request,rowerid=0,userid=0,notpermanent=False): + userid = int(userid) + rowerid = int(rowerid) if notpermanent == False: if rowerid == 0 and 'rowerid' in request.session: @@ -15506,12 +15508,27 @@ def rower_create_trainingplan(request,userid=0): plans = TrainingPlan.objects.filter(rower=therower).order_by("-startdate") form = TrainingPlanForm(targets=targets) - + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url':reverse(rower_create_trainingplan, + kwargs={'userid':userid}), + 'name': 'Manage Plans and Targets' + } + ] + + return render(request,'trainingplan_create.html', { 'form':form, 'rower':therower, + 'breadcrumbs':breadcrumbs, 'plans':plans, + 'active':'nav-plan', 'targets':targets, 'targetform':targetform, }) @@ -15570,9 +15587,11 @@ class MicroCycleDelete(DeleteView): def get_success_url(self): plan = self.object.plan.plan.plan createmacrofillers(plan) + thismesoid = self.object.plan.pk return reverse(rower_trainingplan_view, kwargs = { - 'id':plan.id + 'id':plan.id, + 'thismesoid':thismesoid }) def get_object(self, *args, **kwargs): @@ -15588,10 +15607,12 @@ class MesoCycleDelete(DeleteView): def get_success_url(self): plan = self.object.plan.plan + thismacroid = self.object.plan.pk createmacrofillers(plan) return reverse(rower_trainingplan_view, kwargs = { - 'id':plan.id + 'id':plan.id, + 'thismacroid':thismacroid, }) def get_object(self, *args, **kwargs): @@ -15723,11 +15744,18 @@ class MacroCycleDelete(DeleteView): @user_passes_test(hasplannedsessions,login_url="/", redirect_field_name=None) -def rower_trainingplan_view(request,id=0): +def rower_trainingplan_view(request, + id=0, + userid=0, + thismicroid=0, + thismacroid=0, + thismesoid=0): try: plan = TrainingPlan.objects.get(id=id) except TrainingPlan.DoesNotExist: raise Http404("Training Plan Does Not Exist") + + r = getrequestrower(request,userid=userid) if not checkaccessuser(request.user,plan.rower): raise PermissionDenied("Access denied") @@ -15854,11 +15882,51 @@ def rower_trainingplan_view(request,id=0): cycles[count] = (m,mesos) count += 1 + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url':reverse(rower_trainingplan_view, + kwargs={'userid':userid, + 'id':id}), + 'name': plan.name + } + ] + + if thismicroid: + thismicro = TrainingMicroCycle.objects.get(id=int(thismicroid)) + else: + if not thismacroid and not thismesoid: + thismicro = get_todays_micro(plan) + else: + thismicro = None + + if thismacroid: + thismacro = TrainingMacroCycle.objects.get(id=int(thismacroid)) + else: + thismacro = None + + if thismesoid: + thismeso = TrainingMesoCycle.objects.get(id=int(thismesoid)) + else: + thismeso = None + + print thismacro,thismeso,thismicro,'aap' + return render(request,'trainingplan.html', { 'plan':plan, + 'active':'nav-plan', + 'breadcrumbs':breadcrumbs, + 'rower':r, 'cycles':cycles, + 'thismicro':thismicro, + 'thismacro':thismacro, + 'thismeso':thismeso, } ) @@ -15873,7 +15941,8 @@ class TrainingMacroCycleUpdate(UpdateView): createmacrofillers(plan) return reverse(rower_trainingplan_view, kwargs = { - 'id':plan.id + 'id':plan.id, + 'thismacro':self.object.id, } ) @@ -15904,7 +15973,8 @@ class TrainingMesoCycleUpdate(UpdateView): createmesofillers(plan) return reverse(rower_trainingplan_view, kwargs = { - 'id':plan.plan.id + 'id':plan.plan.id, + 'thismesoid':self.object.id, } ) @@ -15938,7 +16008,8 @@ class TrainingMicroCycleUpdate(UpdateView): createmicrofillers(plan) return reverse(rower_trainingplan_view, kwargs = { - 'id':plan.plan.plan.id + 'id':plan.plan.plan.id, + 'thismicroid':self.object.pk } ) def form_valid(self, form): diff --git a/static/css/rowsandall2.css b/static/css/rowsandall2.css index b4147b22..60a14cc3 100644 --- a/static/css/rowsandall2.css +++ b/static/css/rowsandall2.css @@ -626,6 +626,45 @@ th.rotate > div > span { } +/* palegreen */ +.palegreen { + background: palegreen; + box-shadow:inset 0px 0px 0px 6px #fff; + -moz-box-shadow:inset 0px 0px 0px 6px #fff; + box-shadow:inset 0px 0px 0px 6px #fff; +} + +/* paleblue */ +.paleblue { +# padding: 8px; + background: aliceblue; + box-shadow:inset 0px 0px 0px 6px #fff; + -moz-box-shadow:inset 0px 0px 0px 6px #fff; + box-shadow:inset 0px 0px 0px 6px #fff; +} + +/* lightsalmon */ +.lightsalmon { +# padding: 4px; + background: lightsalmon; + box-shadow:inset 0px 0px 0px 6px #fff; + -moz-box-shadow:inset 0px 0px 0px 6px #fff; + box-shadow:inset 0px 0px 0px 6px #fff; +} + +/* filler */ +.filler { + background: darkgray; + box-shadow:inset 0px 0px 0px 6px #fff; + -moz-box-shadow:inset 0px 0px 0px 6px #fff; + box-shadow:inset 0px 0px 0px 6px #fff; +} + +.padded { + padding: 10px; +} + + .input { font: 1.1em/1.5em sans-serif; text-decoration: none; @@ -753,44 +792,6 @@ th.rotate > div > span { } -/* palegreen */ -.palegreen { - background: palegreen; - box-shadow:inset 0px 0px 0px 6px #fff; - -moz-box-shadow:inset 0px 0px 0px 6px #fff; - box-shadow:inset 0px 0px 0px 6px #fff; -} - -/* paleblue */ -.paleblue { -# padding: 8px; - background: aliceblue; - box-shadow:inset 0px 0px 0px 6px #fff; - -moz-box-shadow:inset 0px 0px 0px 6px #fff; - box-shadow:inset 0px 0px 0px 6px #fff; -} - -/* lightsalmon */ -.lightsalmon { -# padding: 4px; - background: lightsalmon; - box-shadow:inset 0px 0px 0px 6px #fff; - -moz-box-shadow:inset 0px 0px 0px 6px #fff; - box-shadow:inset 0px 0px 0px 6px #fff; -} - -/* filler */ -.filler { - background: darkgray; - box-shadow:inset 0px 0px 0px 6px #fff; - -moz-box-shadow:inset 0px 0px 0px 6px #fff; - box-shadow:inset 0px 0px 0px 6px #fff; -} - -.padded { - padding: 10px; -} - /* pink */ .pink { color: #feeef5; diff --git a/static/css/styles2.css b/static/css/styles2.css index 32bb9c12..c2a3040a 100644 --- a/static/css/styles2.css +++ b/static/css/styles2.css @@ -201,7 +201,6 @@ aside .cd-accordion-menu label { cursor: pointer; - background: #35383d; } @@ -223,6 +222,84 @@ display: block; } + + main .cd-accordion-menu { + width: 100%; + max-width: 600px; + } + + main .cd-accordion-menu ul { + /* by default hide all sub menus */ + display: none; + } + + main .cd-accordion-menu li { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } + + main .cd-accordion-menu input[type=checkbox] { + /* hide native checkbox */ + position: absolute; + opacity: 0; + } + + main .cd-accordion-menu label { + position: relative; + display: block; + font-size: 1.0em; + } + + + + main .cd-accordion-menu ul, + main .cd-accordion-menu li { + list-style: none; + } + + main .cd-accordion-menu label::before + { + /* icons */ + font: normal normal normal 1.0em/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + content: '\f0da'; + display: inline-block; + width: 16px; + height: 16px; + + -webkit-transform: translateY(0%); + -moz-transform: translateY(0%); + -ms-transform: translateY(0%); + -o-transform: translateY(0%); + transform: translateY(0%); + } + + main .cd-accordion-menu label { + cursor: pointer; + } + + + main .cd-accordion-menu input[type=checkbox]:checked + label::before { + /* rotate arrow */ + -webkit-transform: translateY(20%) rotate(90deg); + -moz-transform: translateY(20%) rotate(90deg); + -ms-transform: translateY(20%) rotate(90deg); + -o-transform: translateY(20%) rotate(90deg); + transform: translateY(20%) rotate(90deg); + + } + + + main .cd-accordion-menu input[type=checkbox]:checked + label + ul, + main .cd-accordion-menu input[type=checkbox]:checked + label:nth-of-type(n) + ul { + /* use label:nth-of-type(n) to fix a bug on safari (<= 8.0.8) with multiple adjacent-sibling selectors*/ + /* show children when item is checked */ + display: block; + } + user ul { From a2c1777c745aa28f7de1a63472a713aa3fabcb29 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Fri, 12 Oct 2018 16:55:22 +0200 Subject: [PATCH 80/95] teamcreate done --- rowers/plannedsessions.py | 14 +- rowers/templates/menu_plan.html | 16 +- rowers/templates/menu_workouts.html | 1 - .../templates/plannedsessionteamcreate.html | 196 +++++------- rowers/templates/strokedata_form.html | 38 +-- rowers/templates/trainingplan.html | 20 +- rowers/templates/trainingplan_edit.html | 35 +-- rowers/templatetags/rowerfilters.py | 5 +- rowers/urls.py | 34 ++- rowers/views.py | 281 +++++++++++++++++- 10 files changed, 434 insertions(+), 206 deletions(-) diff --git a/rowers/plannedsessions.py b/rowers/plannedsessions.py index 6428378e..73f3a364 100644 --- a/rowers/plannedsessions.py +++ b/rowers/plannedsessions.py @@ -32,27 +32,27 @@ import courses from rowers.tasks import handle_check_race_course -def get_todays_micro(plan): +def get_todays_micro(plan,thedate=date.today()): thismicro = None thismacro = TrainingMacroCycle.objects.filter( plan=plan, - startdate__lte = date.today(), - enddate__gte = date.today() + startdate__lte = thedate, + enddate__gte = thedate ) if thismacro: thismeso = TrainingMesoCycle.objects.filter( plan=thismacro[0], - startdate__lte = date.today(), - enddate__gte = date.today() + startdate__lte = thedate, + enddate__gte = thedate ) if thismeso: thismicro = TrainingMicroCycle.objects.filter( plan=thismeso[0], - startdate__lte = date.today(), - enddate__gte = date.today() + startdate__lte = thedate, + enddate__gte = thedate ) if thismicro: diff --git a/rowers/templates/menu_plan.html b/rowers/templates/menu_plan.html index 018fd7d7..a9065fd0 100644 --- a/rowers/templates/menu_plan.html +++ b/rowers/templates/menu_plan.html @@ -15,7 +15,7 @@ {% for plan in rower|trainingplans %}
  • - +  {{ plan.name }}
  • @@ -29,17 +29,17 @@
    • - +  Sessions
    • -
    • - +
    • +  Coach View
    • @@ -52,17 +52,17 @@ {% endif %} -{% endif %} diff --git a/rowers/templates/plannedsessionteamcreate.html b/rowers/templates/plannedsessionteamcreate.html index b84bad43..8d1e17df 100644 --- a/rowers/templates/plannedsessionteamcreate.html +++ b/rowers/templates/plannedsessionteamcreate.html @@ -1,134 +1,90 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}New Planned Session{% endblock %} -{% block content %} -
      - {% include "planningbuttons.html" %} -
      +{% block main %} +

      Create Team Session

      -
      -
      -

      Create Team Session

      -
      - -
      - -
      -
      - {% if form.errors %} -

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

      - {% endif %} + + {% if form.errors %} +

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

      + {% endif %} + +
        +
      • {% csrf_token %} - - - - -
        -

        New Session

        +

        Select Team

        +

        + + {{ teamform.as_table }} +

        + {% if plannedsessions %} +

        Team Plan Sessions

        +

        + + + + + + + + + + + + + + + {% for ps in plannedsessions %} + + + + + + + + + + + + {% endfor %} + +
        AfterBeforeNameValue EditCloneDelete
        {{ ps.startdate|date:"Y-m-d" }} {{ ps.enddate|date:"Y-m-d" }} + {% if ps.name != '' %} + {{ ps.name }} + {% else %} + Unnamed Session + {% endif %} + {{ ps.sessionvalue }} {{ ps.sessionunit }} + Edit + + Clone + + Delete +
        +

        + {% endif %} +
      • +
      • +

        New Session

        {{ form.as_table }}
        -
        - + +
        +
        - -
        +
      • +
      + -
      -
    -
    {% endblock %} {% block scripts %} @@ -234,3 +190,7 @@ {% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} diff --git a/rowers/templates/strokedata_form.html b/rowers/templates/strokedata_form.html index df0d5388..21e39da9 100644 --- a/rowers/templates/strokedata_form.html +++ b/rowers/templates/strokedata_form.html @@ -1,23 +1,23 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} -{% block content %} - {% if form.errors %} -

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

    - {% endif %} -
    -

    Stroke Data for workout {{ id }}

    +{% block main %} +{% if form.errors %} +

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

    +{% endif %} +

    Stroke Data for workout

    -
    - - {{ form.as_table }} -
    - {% csrf_token %} -
    - - -
    -
    +
    + + {{ form.as_table }} +
    + {% csrf_token %} + +
    {% endblock %} + +{% block sidebar %} +{% include 'menu_workout.html' %} +{% endblock %} diff --git a/rowers/templates/trainingplan.html b/rowers/templates/trainingplan.html index f62ab943..1c52c06a 100644 --- a/rowers/templates/trainingplan.html +++ b/rowers/templates/trainingplan.html @@ -39,9 +39,9 @@ - edit + edit / - delete + delete / sessions @@ -95,9 +95,9 @@ - edit + edit / - delete + delete / sessions @@ -140,9 +140,9 @@ - edit + edit / - delete + delete / sessions @@ -197,9 +197,9 @@ - edit + edit / - delete + delete / sessions @@ -247,9 +247,9 @@ - edit + edit / - delete + delete / sessions diff --git a/rowers/templates/trainingplan_edit.html b/rowers/templates/trainingplan_edit.html index 1dc67326..3f24d8d7 100644 --- a/rowers/templates/trainingplan_edit.html +++ b/rowers/templates/trainingplan_edit.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -8,25 +8,20 @@ {% endblock %} -{% block content %} - +{% block main %} -
    +
    + {% csrf_token %} + + {{ form.as_table }} +
    + +
    -
    - {% csrf_token %} - - {{ form.as_table }} -
    -
    - -
    -
    - -
    - {% endblock %} +{% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} + diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py index 223736a2..66fde5a2 100644 --- a/rowers/templatetags/rowerfilters.py +++ b/rowers/templatetags/rowerfilters.py @@ -57,7 +57,10 @@ def secondstotimestring(tdelta): @register.filter def aantalcomments(workout): - comments = WorkoutComment.objects.filter(workout=workout) + try: + comments = WorkoutComment.objects.filter(workout=workout) + except: + return 0 aantalcomments = len(comments) diff --git a/rowers/urls.py b/rowers/urls.py index 510e2008..a88f39ad 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -415,9 +415,9 @@ urlpatterns = [ url(r'^createplan/$',views.rower_create_trainingplan), url(r'^createplan/user/(?P\d+)/$',views.rower_create_trainingplan), url(r'^deleteplan/(?P\d+)$',views.TrainingPlanDelete.as_view()), - url(r'^deletemicrocycle/(?P\d+)$',views.MicroCycleDelete.as_view()), - url(r'^deletemesocycle/(?P\d+)$',views.MesoCycleDelete.as_view()), - url(r'^deletemacrocycle/(?P\d+)$',views.MacroCycleDelete.as_view()), + url(r'^deletemicrocycle/(?P\d+)/$',views.MicroCycleDelete.as_view()), + url(r'^deletemesocycle/(?P\d+)/$',views.MesoCycleDelete.as_view()), + url(r'^deletemacrocycle/(?P\d+)/$',views.MacroCycleDelete.as_view()), # url(r'^deleteplan/(?P\d+)$',views.rower_delete_trainingplan), url(r'^plan/(?P\d+)/$',views.rower_trainingplan_view), url(r'^plan/(?P\d+)/user/(?P\d+)/$',views.rower_trainingplan_view), @@ -427,22 +427,24 @@ urlpatterns = [ url(r'^plan/(?P\d+)/meso/(?P\d+)/user/(?P\d+)/$',views.rower_trainingplan_view), url(r'^plan/(?P\d+)/macro/(?P\d+)/$',views.rower_trainingplan_view), url(r'^plan/(?P\d+)/macro/(?P\d+)/user/(?P\d+)/$',views.rower_trainingplan_view), - url(r'^macrocycle/(?P\d+)$',views.TrainingMacroCycleUpdate.as_view()), - url(r'^mesocycle/(?P\d+)$',views.TrainingMesoCycleUpdate.as_view()), - url(r'^microcycle/(?P\d+)$',views.TrainingMicroCycleUpdate.as_view()), - url(r'^deletetarget/(?P\d+)$',views.rower_delete_trainingtarget), - url(r'^editplan/(?P\d+)$',views.TrainingPlanUpdate.as_view()), - url(r'^edittarget/(?P\d+)$',views.TrainingTargetUpdate.as_view()), + url(r'^macrocycle/(?P\d+)/$',views.TrainingMacroCycleUpdate.as_view(), + name='macrocycle_update_view'), + url(r'^mesocycle/(?P\d+)/$',views.TrainingMesoCycleUpdate.as_view(), + name='mesocycle_update_view'), + url(r'^microcycle/(?P\d+)/$',views.TrainingMicroCycleUpdate.as_view(), + name='microcycle_update_view'), + url(r'^deletetarget/(?P\d+)/$',views.rower_delete_trainingtarget), + url(r'^editplan/(?P\d+)$',views.TrainingPlanUpdate.as_view(), + name='trainingplan_update_view'), + url(r'^edittarget/(?P\d+)/$',views.TrainingTargetUpdate.as_view(), + name='trainingtarget_update_view'), url(r'^workout/(?P\d+)/test\_strokedata$',views.strokedataform), - - url(r'^sessions/teamcreate$',views.plannedsession_teamcreate_view), - url(r'^sessions/teamcreate/(?P[\w\ ]+.*)$', + url(r'^sessions/teamcreate/user/(?P\d+)/$',views.plannedsession_teamcreate_view), + url(r'^sessions/teamcreate/team/(?P\d+)/user/(?P\d+)/$', views.plannedsession_teamcreate_view), - url(r'^sessions/teamcreate/(?P[\w\ ]+.*)/team/(?P\d+)$', + url(r'^sessions/teamcreate/$',views.plannedsession_teamcreate_view), + url(r'^sessions/teamcreate/team/$', views.plannedsession_teamcreate_view), - url(r'^sessions/teamcreate/team/(?P\d+)$', - views.plannedsession_teamcreate_view), - url(r'^sessions/teamedit/(?P\d+)$',views.plannedsession_teamedit_view), url(r'^sessions/teamedit/(?P\d+)/(?P[\w\ ]+.*)$',views.plannedsession_teamedit_view), diff --git a/rowers/views.py b/rowers/views.py index 1289cec3..fbd9ba5c 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -12663,13 +12663,25 @@ def trydf(df,aantal,column): # Stroke data form to test API upload @login_required() def strokedataform(request,id=0): + + try: + id=int(id) + except ValueError: + id = 0 + + try: + w = Workout.objects.get(id=id) + except Workout.DoesNotExist: + raise Http404("Workout doesn't exist") + if request.method == 'GET': form = StrokeDataForm() return render(request, 'strokedata_form.html', { 'form':form, 'teams':get_my_teams(request.user), - 'id':int(id), + 'id':id, + 'workout':w, }) elif request.method == 'POST': form = StrokeDataForm() @@ -12678,7 +12690,8 @@ def strokedataform(request,id=0): { 'form':form, 'teams':get_my_teams(request.user), - 'id':int(id), + 'id':id, + 'workout':w, }) # Process the POSTed stroke data according to the API definition @@ -13860,9 +13873,16 @@ def plannedsession_multicreate_view(request,timeperiod='thisweek', @user_passes_test(iscoachmember,login_url="/rowers/planmembership/", redirect_field_name=None) def plannedsession_teamcreate_view(request,timeperiod='thisweek', - teamid=0): + teamid=0,userid=0): + therower = getrequestrower(request,userid=userid) + when = request.GET.get('when') + if when: + timeperiod = when + else: + timeperiod = 'thisweek' + teams = Team.objects.filter(manager=request.user) if len(teams)>0: teamchoices = [(team.id, team.name) for team in teams] @@ -13940,7 +13960,13 @@ def plannedsession_teamcreate_view(request,timeperiod='thisweek', }) return HttpResponseRedirect(url) else: - sessioncreateform = PlannedSessionForm() + initial = { + 'startdate':startdate, + 'enddate':enddate, + 'preferreddate':startdate, + } + + sessioncreateform = PlannedSessionForm(initial=initial) sessionteamselectform = PlannedSessionTeamForm( request.user ) @@ -13953,7 +13979,8 @@ def plannedsession_teamcreate_view(request,timeperiod='thisweek', 'teamform':sessionteamselectform, 'timeperiod':timeperiod, 'plannedsessions':sps, - 'rower':getrower(request.user), + 'rower':therower, + 'active':'nav-plan' }) # Manager edits sessions for entire team @@ -15584,6 +15611,51 @@ class MicroCycleDelete(DeleteView): model = TrainingMicroCycle template_name = 'trainingplan_delete.html' + # extra parameters + def get_context_data(self, **kwargs): + context = super(MicroCycleDelete, self).get_context_data(**kwargs) + + if 'userid' in kwargs: + userid = kwargs['userid'] + else: + userid=0 + + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url':reverse(rower_trainingplan_view, + kwargs={'userid':userid, + 'id':self.object.plan.plan.plan.id}), + 'name': self.object.plan.plan.plan.name + }, + { + 'url':reverse('macrocycle_update_view', + kwargs={'pk':self.object.plan.plan.pk}), + 'name': self.object.plan.plan.name + }, + { + 'url':reverse('mesocycle_update_view', + kwargs={'pk':self.object.plan.pk}), + 'name': self.object.plan.name + }, + { + 'url':reverse('microcycle_update_view', + kwargs={'pk':self.object.pk}), + 'name': self.object.name + } + + ] + + context['active'] = 'nav-plan' + context['breadcrumbs'] = breadcrumbs + context['rower'] = getrequestrower(self.request,userid=userid) + + return context + def get_success_url(self): plan = self.object.plan.plan.plan createmacrofillers(plan) @@ -15605,6 +15677,46 @@ class MesoCycleDelete(DeleteView): model = TrainingMesoCycle template_name = 'trainingplan_delete.html' + # extra parameters + def get_context_data(self, **kwargs): + context = super(MesoCycleDelete, self).get_context_data(**kwargs) + + if 'userid' in kwargs: + userid = kwargs['userid'] + else: + userid=0 + + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url':reverse(rower_trainingplan_view, + kwargs={'userid':userid, + 'id':self.object.plan.plan.id}), + 'name': self.object.plan.plan.name + }, + { + 'url':reverse('macrocycle_update_view', + kwargs={'pk':self.object.plan.pk}), + 'name': self.object.plan.name + }, + { + 'url':reverse('mesocycle_update_view', + kwargs={'pk':self.object.pk}), + 'name': self.object.name + } + + ] + + context['active'] = 'nav-plan' + context['breadcrumbs'] = breadcrumbs + context['rower'] = getrequestrower(self.request,userid=userid) + + return context + def get_success_url(self): plan = self.object.plan.plan thismacroid = self.object.plan.pk @@ -15725,6 +15837,40 @@ class MacroCycleDelete(DeleteView): model = TrainingMacroCycle template_name = 'trainingplan_delete.html' + # extra parameters + def get_context_data(self, **kwargs): + context = super(MacroCycleDelete, self).get_context_data(**kwargs) + + if 'userid' in kwargs: + userid = kwargs['userid'] + else: + userid=0 + + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url':reverse(rower_trainingplan_view, + kwargs={'userid':userid, + 'id':self.object.plan.id}), + 'name': self.object.plan.name + }, + { + 'url':reverse('macrocycle_update_view', + kwargs={'pk':self.object.pk}), + 'name': self.object.name + } + ] + + context['active'] = 'nav-plan' + context['breadcrumbs'] = breadcrumbs + context['rower'] = getrequestrower(self.request,userid=userid) + + return context + def get_success_url(self): plan = self.object.plan createmacrofillers(plan) @@ -15750,6 +15896,13 @@ def rower_trainingplan_view(request, thismicroid=0, thismacroid=0, thismesoid=0): + + when = request.GET.get('when') + if when: + startdate,enddate = get_dates_timeperiod(when) + else: + startdate = datetime.date.today() + try: plan = TrainingPlan.objects.get(id=id) except TrainingPlan.DoesNotExist: @@ -15901,7 +16054,7 @@ def rower_trainingplan_view(request, thismicro = TrainingMicroCycle.objects.get(id=int(thismicroid)) else: if not thismacroid and not thismesoid: - thismicro = get_todays_micro(plan) + thismicro = get_todays_micro(plan,thedate=startdate) else: thismicro = None @@ -15935,6 +16088,39 @@ class TrainingMacroCycleUpdate(UpdateView): template_name = 'trainingplan_edit.html' form_class = TrainingMacroCycleForm + # extra parameters + def get_context_data(self, **kwargs): + context = super(TrainingMacroCycleUpdate, self).get_context_data(**kwargs) + + if 'userid' in kwargs: + userid = kwargs['userid'] + else: + userid=0 + + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url':reverse(rower_trainingplan_view, + kwargs={'userid':userid, + 'id':self.object.plan.id}), + 'name': self.object.plan.name + }, + { + 'url':reverse('macrocycle_update_view', + kwargs={'pk':self.object.pk}), + 'name': self.object.name + } + ] + + context['active'] = 'nav-plan' + context['breadcrumbs'] = breadcrumbs + context['rower'] = getrequestrower(self.request,userid=userid) + + return context def get_success_url(self): plan = self.object.plan @@ -15967,6 +16153,45 @@ class TrainingMesoCycleUpdate(UpdateView): template_name = 'trainingplan_edit.html' form_class = TrainingMesoCycleForm + # extra parameters + def get_context_data(self, **kwargs): + context = super(TrainingMesoCycleUpdate, self).get_context_data(**kwargs) + + if 'userid' in kwargs: + userid = kwargs['userid'] + else: + userid=0 + + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url':reverse(rower_trainingplan_view, + kwargs={'userid':userid, + 'id':self.object.plan.plan.id}), + 'name': self.object.plan.plan.name + }, + { + 'url':reverse('macrocycle_update_view', + kwargs={'pk':self.object.plan.pk}), + 'name': self.object.plan.name + }, + { + 'url':reverse('mesocycle_update_view', + kwargs={'pk':self.object.pk}), + 'name': self.object.name + } + + ] + + context['active'] = 'nav-plan' + context['breadcrumbs'] = breadcrumbs + context['rower'] = getrequestrower(self.request,userid=userid) + + return context def get_success_url(self): plan = self.object.plan @@ -16002,6 +16227,50 @@ class TrainingMicroCycleUpdate(UpdateView): template_name = 'trainingplan_edit.html' form_class = TrainingMicroCycleForm + # extra parameters + def get_context_data(self, **kwargs): + context = super(TrainingMicroCycleUpdate, self).get_context_data(**kwargs) + + if 'userid' in kwargs: + userid = kwargs['userid'] + else: + userid=0 + + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url':reverse(rower_trainingplan_view, + kwargs={'userid':userid, + 'id':self.object.plan.plan.plan.id}), + 'name': self.object.plan.plan.plan.name + }, + { + 'url':reverse('macrocycle_update_view', + kwargs={'pk':self.object.plan.plan.pk}), + 'name': self.object.plan.plan.name + }, + { + 'url':reverse('mesocycle_update_view', + kwargs={'pk':self.object.plan.pk}), + 'name': self.object.plan.name + }, + { + 'url':reverse('microcycle_update_view', + kwargs={'pk':self.object.pk}), + 'name': self.object.name + } + + ] + + context['active'] = 'nav-plan' + context['breadcrumbs'] = breadcrumbs + context['rower'] = getrequestrower(self.request,userid=userid) + + return context def get_success_url(self): plan = self.object.plan From ab1cba4ccb2adf386a3833e3ba467f88949351da Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Fri, 12 Oct 2018 17:06:37 +0200 Subject: [PATCH 81/95] plannedsessionteamedit --- .../templates/plannedsessionteamcreate.html | 2 +- rowers/templates/plannedsessionteamedit.html | 225 +++++++----------- rowers/views.py | 4 + 3 files changed, 97 insertions(+), 134 deletions(-) diff --git a/rowers/templates/plannedsessionteamcreate.html b/rowers/templates/plannedsessionteamcreate.html index 8d1e17df..b35ee0ac 100644 --- a/rowers/templates/plannedsessionteamcreate.html +++ b/rowers/templates/plannedsessionteamcreate.html @@ -7,7 +7,7 @@ {% block main %}

    Create Team Session

    -
    + {% if form.errors %}

    Please correct the error{{ form.errors|pluralize }} below. diff --git a/rowers/templates/plannedsessionteamedit.html b/rowers/templates/plannedsessionteamedit.html index 016a13c5..3e36dfe5 100644 --- a/rowers/templates/plannedsessionteamedit.html +++ b/rowers/templates/plannedsessionteamedit.html @@ -1,151 +1,106 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}New Planned Session{% endblock %} -{% block content %} -

    - {% include "planningbuttons.html" %} -
    +{% block main %} +

    Edit Team Session

    + + {% if form.errors %} +

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

    + {% endif %} + {% csrf_token %} -
    -
    -

    Edit Team Session

    -
    - -
    - -
    - - {% if form.errors %} -

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

    - {% endif %} - {% csrf_token %} - - - - - -
    -

    Session {{ plannedsession.name }}

    +

    Session {{ plannedsession.name }}

    {{ form.as_table }}
    -
    - Delete -
    -
    - Clone -
    -
    +

    + Delete +

    +

    + Clone +

    +

    -

    - -
    +

    +
    -
    - -
    + + + {% endblock %} {% block scripts %} @@ -258,3 +213,7 @@ {% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} diff --git a/rowers/views.py b/rowers/views.py index fbd9ba5c..22cdbcd7 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -13989,6 +13989,8 @@ def plannedsession_teamcreate_view(request,timeperiod='thisweek', def plannedsession_teamedit_view(request,timeperiod='thisweek', sessionid=0): + r = getrequestrower(request) + try: ps = PlannedSession.objects.get(id=sessionid) except PlannedSession.DoesNotExist: @@ -14098,6 +14100,8 @@ def plannedsession_teamedit_view(request,timeperiod='thisweek', { 'plannedsession':ps, 'plan':trainingplan, + 'rower':r, + 'active':'nav-plan', 'teams':get_my_teams(request.user), 'form':sessioncreateform, 'teamform':sessionteamselectform, From 37147dc4d461ddffef3115f3392164b0b7e1a88e Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Fri, 12 Oct 2018 17:26:44 +0200 Subject: [PATCH 82/95] plannedsessioncreate --- rowers/templates/plannedsessioncreate.html | 91 ++++------------------ rowers/urls.py | 7 +- rowers/views.py | 13 +++- 3 files changed, 28 insertions(+), 83 deletions(-) diff --git a/rowers/templates/plannedsessioncreate.html b/rowers/templates/plannedsessioncreate.html index d803e32c..162646f2 100644 --- a/rowers/templates/plannedsessioncreate.html +++ b/rowers/templates/plannedsessioncreate.html @@ -1,71 +1,14 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}New Planned Session{% endblock %} -{% block content %} -
    - {% include "planningbuttons.html" %} -
    +{% block main %} +

    Create Sessions for {{ rower.user.first_name }} {{ rower.user.last_name }}

    -
    -
    -

    Create Sessions for {{ rower.user.first_name }} {{ rower.user.last_name }}

    -
    - -{% if user.is_authenticated and user|is_manager %} - - -{% endif %} -
    - Courses -
    -
    - -
    - - - - -
    + +
  • New Session

    {% if form.errors %} @@ -130,15 +70,12 @@ {{ form.as_table }} {% csrf_token %} -
    - -
    -
    -
  • -
    -
    + + +
    + + -
    {% endblock %} {% block scripts %} @@ -240,3 +177,7 @@ {% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} diff --git a/rowers/urls.py b/rowers/urls.py index a88f39ad..e2833eb2 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -448,14 +448,9 @@ urlpatterns = [ url(r'^sessions/teamedit/(?P\d+)$',views.plannedsession_teamedit_view), url(r'^sessions/teamedit/(?P\d+)/(?P[\w\ ]+.*)$',views.plannedsession_teamedit_view), - url(r'^sessions/create$',views.plannedsession_create_view), + url(r'^sessions/create/$',views.plannedsession_create_view), url(r'^sessions/create/user/(?P\d+)$', views.plannedsession_create_view), - url( - r'^sessions/create/(?P[\w\ ]+.*)/user/(?P\d+)$', - views.plannedsession_create_view), - url(r'^sessions/create/(?P[\w\ ]+.*)$', - views.plannedsession_create_view), url(r'^sessions/multiclone$',views.plannedsession_multiclone_view), url(r'^sessions/multiclone/(?P[\w\ ]+.*)/user/(?P\d+)/extra/(?P\d+)$', diff --git a/rowers/views.py b/rowers/views.py index 22cdbcd7..0d59dc38 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -13645,9 +13645,17 @@ def plannedsession_multiclone_view( # Individual user creates training for himself @user_passes_test(hasplannedsessions,login_url="/rowers/planmembership/", redirect_field_name=None) -def plannedsession_create_view(request,timeperiod='thisweek',rowerid=0,userid=0): +def plannedsession_create_view(request, + userid=0): - r = getrequestrower(request,rowerid=rowerid) + r = getrequestrower(request,userid=userid) + + when = request.GET.get('when') + if when: + timeperiod = when + else: + timeperiod = 'thisweek' + startdate,enddate = get_dates_timeperiod(timeperiod) if request.method == 'POST': @@ -13761,6 +13769,7 @@ def plannedsession_create_view(request,timeperiod='thisweek',rowerid=0,userid=0) 'teams':get_my_teams(request.user), 'plan':trainingplan, 'form':sessioncreateform, + 'active':'nav-plan', 'plannedsessions':sps, 'rower':r, 'timeperiod':timeperiod, From d40dd47731aedc795b812c1f9b4d371538b72f10 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Fri, 12 Oct 2018 17:57:58 +0200 Subject: [PATCH 83/95] session create --- .../templates/plannedsession_multicreate.html | 4 +- rowers/templates/plannedsessioncreate.html | 4 +- .../plannedsessions_multiclone_select.html | 145 ++++-------------- rowers/urls.py | 61 ++------ rowers/views.py | 35 +++-- 5 files changed, 73 insertions(+), 176 deletions(-) diff --git a/rowers/templates/plannedsession_multicreate.html b/rowers/templates/plannedsession_multicreate.html index 0a3ede15..3f8f8ffa 100644 --- a/rowers/templates/plannedsession_multicreate.html +++ b/rowers/templates/plannedsession_multicreate.html @@ -48,11 +48,11 @@ {% endfor %} - + Add More or - + Clone multiple sessions diff --git a/rowers/templates/plannedsessioncreate.html b/rowers/templates/plannedsessioncreate.html index 162646f2..e66d61d0 100644 --- a/rowers/templates/plannedsessioncreate.html +++ b/rowers/templates/plannedsessioncreate.html @@ -43,10 +43,10 @@ {{ ps.sessionvalue }} {{ ps.sessionunit }} - Edit + Edit - Clone + Clone diff --git a/rowers/templates/plannedsessions_multiclone_select.html b/rowers/templates/plannedsessions_multiclone_select.html index d17e200a..bbe7a459 100644 --- a/rowers/templates/plannedsessions_multiclone_select.html +++ b/rowers/templates/plannedsessions_multiclone_select.html @@ -1,10 +1,10 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Workouts{% endblock %} -{% block content %} +{% block main %} - -
    - {% include "planningbuttons.html" %} -
    -
    -

    Clone Multiple Sessions

    -
    - -{% if user.is_authenticated and user|is_manager %} - -
    -
    -
    - {% endif %} -
    - - {{ dateform.as_table }} -
    - {% csrf_token %} -
    -
    - -
    -
    -
    -
    -
    -
    - -
    -
    - -
    -
    -
    - -
    +

    Clone Multiple Sessions

    -
    +
      +
    • - {% if plannedsessions %} - - Toggle All
      - - - {{ form.as_table }} -
      - -{% else %} -

      No sessions found

      -{% endif %} -
    -
    -

    Select one or more planned sessions on the left, - select the date when the new cycle starts below - and press submit

    - {% csrf_token %} - - {{ dateshiftform.as_table }} -
    -
    -

    - + {% if plannedsessions %} + + Toggle All
    + + + {{ form.as_table }} +
    + + {% else %} +

    No sessions found

    + {% endif %} + +
  • +

    Select one or more planned sessions on the left, + select the date when the new cycle starts below + and press submit

    + {% csrf_token %} + + {{ dateshiftform.as_table }} +
    +

    +

    -
  • -
    -

    You can use the date and search forms above to search through all +

    You can use the date and search forms above to search through all sessions.

    -
    -
    + +
    {% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} diff --git a/rowers/urls.py b/rowers/urls.py index e2833eb2..53e87a49 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -446,46 +446,25 @@ urlpatterns = [ url(r'^sessions/teamcreate/team/$', views.plannedsession_teamcreate_view), url(r'^sessions/teamedit/(?P\d+)$',views.plannedsession_teamedit_view), - url(r'^sessions/teamedit/(?P\d+)/(?P[\w\ ]+.*)$',views.plannedsession_teamedit_view), - url(r'^sessions/create/$',views.plannedsession_create_view), url(r'^sessions/create/user/(?P\d+)$', views.plannedsession_create_view), - - url(r'^sessions/multiclone$',views.plannedsession_multiclone_view), - url(r'^sessions/multiclone/(?P[\w\ ]+.*)/user/(?P\d+)/extra/(?P\d+)$', + url(r'^sessions/multiclone/$',views.plannedsession_multiclone_view), + url(r'^sessions/multiclone/user/(?P\d+)/extra/(?P\d+)$/', views.plannedsession_multiclone_view), - url(r'^sessions/multiclone/user/(?P\d+)$', + url(r'^sessions/multiclone/user/(?P\d+)/$', views.plannedsession_multiclone_view), - url( - r'^sessions/multiclone/(?P[\w\ ]+.*)/user/(?P\d+)$', - views.plannedsession_multiclone_view), - url(r'^sessions/multiclone/(?P[\w\ ]+.*)$', - views.plannedsession_multiclone_view), - - url(r'^sessions/multicreate$',views.plannedsession_multicreate_view), - url(r'^sessions/multicreate/(?P[\w\ ]+.*)/user/(?P\d+)/extra/(?P\d+)$', + url(r'^sessions/multicreate/$',views.plannedsession_multicreate_view), + url(r'^sessions/multicreate/user/(?P\d+)/extra/(?P\d+)/$', views.plannedsession_multicreate_view), - url(r'^sessions/multicreate/user/(?P\d+)$', + url(r'^sessions/multicreate/user/(?P\d+)/$', views.plannedsession_multicreate_view), - url( - r'^sessions/multicreate/(?P[\w\ ]+.*)/user/(?P\d+)$', - views.plannedsession_multicreate_view), - url(r'^sessions/multicreate/(?P[\w\ ]+.*)$', - views.plannedsession_multicreate_view), - - url(r'^sessions/(?P\d+)/edit/(?P[\w\ ]+.*)/user/(?P\d+)$',views.plannedsession_edit_view), - url(r'^sessions/(?P\d+)/edit/(?P[\w\ ]+.*)$',views.plannedsession_edit_view), - url(r'^sessions/(?P\d+)/edit$',views.plannedsession_edit_view), - - url(r'^sessions/(?P\d+)/clone$',views.plannedsession_clone_view), - url(r'^sessions/(?P\d+)/clone/(?P[\w\ ]+.*)/user/(?P\d+)$',views.plannedsession_clone_view), - url(r'^sessions/(?P\d+)/clone/(?P[\w\ ]+.*)$',views.plannedsession_clone_view), - + url(r'^sessions/(?P\d+)/edit/$',views.plannedsession_edit_view), + url(r'^sessions/(?P\d+)/edit/user/(?P/$',views.plannedsession_edit_view), + url(r'^sessions/(?P\d+)/clone/user/(?P/$',views.plannedsession_clone_view), + url(r'^sessions/(?P\d+)/clone/$',views.plannedsession_clone_view), url(r'^sessions/(?P\d+)$',views.plannedsession_view, name='plannedsession_view'), - url(r'^sessions/(?P\d+)/(?P[\w\ ]+.*)/user/(?P\d+)$',views.plannedsession_view, - name='plannedsession_view'), url(r'^sessions/(?P\d+)/user/(?P\d+)$',views.plannedsession_view, name='plannedsession_view'), url(r'^sessions/(?P\d+)/deleteconfirm$',views.plannedsession_deleteconfirm_view), @@ -494,33 +473,15 @@ urlpatterns = [ views.plannedsessions_manage_view), url(r'^sessions/manage/user/(?P\d+)/session/(?P\d+)$', views.plannedsessions_manage_view), - url(r'^sessions/manage/(?P[\w\ ]+.*)/user/(?P\d+)/session/(?P\d+)$', - views.plannedsessions_manage_view), - url(r'^sessions/manage/(?P[\w\ ]+.*)/session/(?P\d+)$', - views.plannedsessions_manage_view), - - url(r'^sessions/manage/?$', views.plannedsessions_manage_view), url(r'^sessions/manage/user/(?P\d+)$', views.plannedsessions_manage_view), - url(r'^sessions/manage/(?P[\w\ ]+.*)/user/(?P\d+)$', - views.plannedsessions_manage_view), - url(r'^sessions/manage/(?P[\w\ ]+.*)$', - views.plannedsessions_manage_view), url(r'^sessions/coach$',views.plannedsessions_coach_view), - url(r'^sessions/coach/(?P[\w\ ]+.*)/team/(?P\d+)$', - views.plannedsessions_coach_view), - url(r'^sessions/coach/(?P[\w\ ]+.*)$', - views.plannedsessions_coach_view), url(r'^sessions/print/?$',views.plannedsessions_print_view), url(r'^sessions/print/user/(?P\d+)$',views.plannedsessions_print_view), - url(r'^sessions/print/(?P[\w\ ]+.*)/user/(?P\d+)$',views.plannedsessions_print_view), - url(r'^sessions/print/(?P[\w\ ]+.*)$',views.plannedsessions_print_view), - url(r'^sessions/?$',views.plannedsessions_view), + url(r'^sessions/$',views.plannedsessions_view), url(r'^sessions/user/(?P\d+)$',views.plannedsessions_view), - url(r'^sessions/(?P[\w\ ]+.*)/user/(?P\d+)$',views.plannedsessions_view), - url(r'^sessions/(?P[\w\ ]+.*)$',views.plannedsessions_view), url(r'^courses/(?P\d+)/edit$',views.course_edit_view, name='course_edit_view'), url(r'^courses/(?P\d+)/delete$',views.course_delete_view), diff --git a/rowers/views.py b/rowers/views.py index 0d59dc38..c604b8e6 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -13541,15 +13541,20 @@ def agegrouprecordview(request,sex='male',weightcategory='hwt', @user_passes_test(hasplannedsessions,login_url="/rowers/planmembership/", redirect_field_name=None) def plannedsession_multiclone_view( - request,timeperiod='none', - rowerid=0,userid=0, + request, + userid=0, startdate=timezone.now()-datetime.timedelta(days=30), enddate=timezone.now()): - r = getrequestrower(request,rowerid=rowerid) + r = getrequestrower(request,userid=userid) - if timeperiod != 'none': - startdate,enddate = get_dates_timeperiod(timeperiod) + when = request.GET.get('when') + if when: + timeperiod = when + else: + timeperiod = 'thisweek' + + startdate,enddate = get_dates_timeperiod(timeperiod) if request.method == 'POST' and 'daterange' in request.POST: @@ -13591,10 +13596,11 @@ def plannedsession_multiclone_view( url = reverse(plannedsession_multicreate_view, kwargs = { - 'rowerid':r.id, - 'timeperiod':timeperiod, + 'userid':r.user.id, }) + url += '?when='+when + return HttpResponseRedirect(url) sps = PlannedSession.objects.filter( @@ -13638,6 +13644,7 @@ def plannedsession_multiclone_view( 'form':form, 'dateshiftform':dateshiftform, 'rower':r, + 'active':'nav-plan', 'timeperiod':timeperiod, } ) @@ -13777,12 +13784,18 @@ def plannedsession_create_view(request, @user_passes_test(hasplannedsessions,login_url="/rowers/planmembership/", redirect_field_name=None) -def plannedsession_multicreate_view(request,timeperiod='thisweek', - teamid=0,rowerid=0,userid=0,extrasessions=0): +def plannedsession_multicreate_view(request, + teamid=0,userid=0,extrasessions=0): extrasessions=int(extrasessions) - r = getrequestrower(request,rowerid=rowerid) + r = getrequestrower(request,userid=userid) + + when = request.GET.get('when') + if when: + timeperiod = when + else: + timeperiod = 'thisweek' startdate,enddate = get_dates_timeperiod(timeperiod) try: @@ -13834,6 +13847,7 @@ def plannedsession_multicreate_view(request,timeperiod='thisweek', initials = [initial for i in range(extrasessions)] + PlannedSessionFormSet = modelformset_factory( PlannedSession, form=PlannedSessionFormSmall, @@ -16081,7 +16095,6 @@ def rower_trainingplan_view(request, else: thismeso = None - print thismacro,thismeso,thismicro,'aap' return render(request,'trainingplan.html', { From e479a45941f0c815f61d3f088be8c3a51ed324d0 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sun, 14 Oct 2018 14:30:42 +0200 Subject: [PATCH 84/95] bug fix --- rowers/dataprepnodjango.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rowers/dataprepnodjango.py b/rowers/dataprepnodjango.py index 486a4336..8ac8692f 100644 --- a/rowers/dataprepnodjango.py +++ b/rowers/dataprepnodjango.py @@ -1242,7 +1242,7 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True, with engine.connect() as conn, conn.begin(): try: data.to_sql('strokedata',engine,if_exists='append',index=False) - except OperationalError: + except: data.drop(columns=['rhythm'],inplace=True) data.to_sql('strokedata',engine,if_exists='append',index=False) From 296f8af6d06df6a3db7e57c22d925448681ef9a6 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 15 Oct 2018 10:27:30 +0200 Subject: [PATCH 85/95] plannedsession_view --- rowers/templates/plannedsessionedit.html | 109 +++----- .../plannedsessions_multiclone_select.html | 2 +- rowers/templates/plannedsessionview.html | 253 +++++++++--------- rowers/urls.py | 9 +- rowers/views.py | 169 +++++++++--- 5 files changed, 289 insertions(+), 253 deletions(-) diff --git a/rowers/templates/plannedsessionedit.html b/rowers/templates/plannedsessionedit.html index deb11173..a3adbdfe 100644 --- a/rowers/templates/plannedsessionedit.html +++ b/rowers/templates/plannedsessionedit.html @@ -1,65 +1,21 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} {% block title %}Update Planned Session{% endblock %} -{% block content %} -
    - {% include "planningbuttons.html" %} - -
    -
    -
    -

    Edit Session

    -
    - +{% block main %} +

    Edit Session

    {% if user.is_authenticated and user|is_manager %} - +

    {% endif %} -
    - Courses -
    -
    -
    - - -
    -

    {{ thesession.name }}

    + +
  • +

    {{ thesession.name }}

    {% if form.errors %}

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

    {% endif %} - +

    {{ form.as_table }}
    +

    {% csrf_token %} -
    - Delete +
    +
    -
    - Clone -
    -
    - -
    -
    +

    + Delete + Clone +

    + + -
    +
  • + -
    - - - -
    {% endblock %} +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} + + {% block scripts %} -

    Clone Multiple Sessions

    +

    Clone Multiple Sessions for {{ rower.user.first_name }} {{ rower.user.last_name }}

    diff --git a/rowers/templates/plannedsessionview.html b/rowers/templates/plannedsessionview.html index 34df6972..fb967acd 100644 --- a/rowers/templates/plannedsessionview.html +++ b/rowers/templates/plannedsessionview.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% load rowerfilters %} @@ -7,158 +7,145 @@ {% block title %}Planned Session{% endblock %} -{% block content %} +{% block main %} -
    - {% include "planningbuttons.html" %} - -
    -
    -
    - {% if user.is_authenticated and psdict.id.1|is_session_manager:user %} - +{% if user.is_authenticated and psdict.id.1|is_session_manager:user %} +

    + Edit Session - {% else %} -   - {% endif %} -

    -
    - {% if plannedsession.sessiontype == 'coursetest' %} - Courses - {% else %} -   - {% endif %} -
    -
    -
    -
    -

    Session {{ psdict.name.1 }}

    - - {% for attr in attrs %} - {% for key,value in psdict.items %} - {% if key == attr %} - - {% if key == 'comment' %} - - {% else %} - - {% endif %} - - {% endif %} - {% endfor %} - {% endfor %} -
    {{ value.0 }}{{ value.1|linebreaks }}{{ value.0 }}{{ value.1 }}
    -
    - -
    -
    -
    -

    {{ rower.user.first_name }} {{ rower.user.last_name }}

    -

    Status: {{ status }}

    -

    Percentage complete: {{ ratio }}

    -
    - -
    -
    - {% if coursescript %} -

    Course

    + + {% endfor %} + + + +
  • +

    {{ rower.user.first_name }} {{ rower.user.last_name }}

    +

    Status: {{ status }}

    +

    Percentage complete: {{ ratio }}

    +
  • +
  • +

    Stats

    + + + + + + + + + + + + + + {% for id, value in results.items %} + + + + + + + + + + {% endfor %} + +
    NameMinutesMetersrScoreTRIMPComplete DateStatus
    {{ value|lookup:'first_name' }} {{ value|lookup:'last_name' }}{{ value|lookup:'duration' }}{{ value|lookup:'distance' }}{{ value|lookup:'rscore' }}{{ value|lookup:'trimp' }}{{ value|lookup:'completedate' }}{{ value|lookup:'status' }}
    +
  • + {% if coursescript %} +
  • +

    Course

    {{ coursediv|safe }} - + {{ coursescript|safe }} - {% endif %} -
  • - -
    + + {% endif %} + + {% endblock %} +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} + {% block scripts %} diff --git a/rowers/urls.py b/rowers/urls.py index 53e87a49..080a7b61 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -445,13 +445,12 @@ urlpatterns = [ url(r'^sessions/teamcreate/$',views.plannedsession_teamcreate_view), url(r'^sessions/teamcreate/team/$', views.plannedsession_teamcreate_view), - url(r'^sessions/teamedit/(?P\d+)$',views.plannedsession_teamedit_view), + url(r'^sessions/teamedit/(?P\d+)/$',views.plannedsession_teamedit_view), + url(r'^sessions/teamedit/(?P\d+)/user/(?P\d+)/$',views.plannedsession_teamedit_view), url(r'^sessions/create/$',views.plannedsession_create_view), url(r'^sessions/create/user/(?P\d+)$', views.plannedsession_create_view), url(r'^sessions/multiclone/$',views.plannedsession_multiclone_view), - url(r'^sessions/multiclone/user/(?P\d+)/extra/(?P\d+)$/', - views.plannedsession_multiclone_view), url(r'^sessions/multiclone/user/(?P\d+)/$', views.plannedsession_multiclone_view), url(r'^sessions/multicreate/$',views.plannedsession_multicreate_view), @@ -460,8 +459,8 @@ urlpatterns = [ url(r'^sessions/multicreate/user/(?P\d+)/$', views.plannedsession_multicreate_view), url(r'^sessions/(?P\d+)/edit/$',views.plannedsession_edit_view), - url(r'^sessions/(?P\d+)/edit/user/(?P/$',views.plannedsession_edit_view), - url(r'^sessions/(?P\d+)/clone/user/(?P/$',views.plannedsession_clone_view), + url(r'^sessions/(?P\d+)/edit/user/(?P\d+)/$',views.plannedsession_edit_view), + url(r'^sessions/(?P\d+)/clone/user/(?P\d+)/$',views.plannedsession_clone_view), url(r'^sessions/(?P\d+)/clone/$',views.plannedsession_clone_view), url(r'^sessions/(?P\d+)$',views.plannedsession_view, name='plannedsession_view'), diff --git a/rowers/views.py b/rowers/views.py index c604b8e6..0c33fa4b 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -12318,7 +12318,7 @@ def rower_edit_view(request,rowerid=0,userid=0,message=""): # Page where user can set his details # Add email address to form so user can change his email address @login_required() -def rower_prefs_view(request,rowerid=0,userid=0,message=""): +def rower_prefs_view(request,userid=0,message=""): r = getrequestrower(request,rowerid=rowerid,userid=userid,notpermanent=True) rowerid = r.id @@ -13598,8 +13598,8 @@ def plannedsession_multiclone_view( kwargs = { 'userid':r.user.id, }) - - url += '?when='+when + if when: + url += '?when='+when return HttpResponseRedirect(url) @@ -13657,6 +13657,7 @@ def plannedsession_create_view(request, r = getrequestrower(request,userid=userid) + when = request.GET.get('when') if when: timeperiod = when @@ -13710,7 +13711,7 @@ def plannedsession_create_view(request, url = reverse(plannedsession_create_view, kwargs = { - 'rowerid':r.id, + 'userid':r.user.id, 'timeperiod':timeperiod, }) return HttpResponseRedirect(url) @@ -13739,6 +13740,7 @@ def plannedsession_create_view(request, if fprefdate > enddate: fprefdate = enddate + forminitial = { 'startdate':fstartdate, 'enddate':fenddate, @@ -13871,10 +13873,14 @@ def plannedsession_multicreate_view(request, url = reverse(plannedsession_multicreate_view, kwargs = { - 'rowerid':r.id, - 'timeperiod':timeperiod + 'userid':r.user.id, } ) + + + if when: + url += '?when='+when + return HttpResponseRedirect(url) ps_formset = PlannedSessionFormSet(queryset = qset, @@ -14009,10 +14015,17 @@ def plannedsession_teamcreate_view(request,timeperiod='thisweek', # Manager edits sessions for entire team @user_passes_test(iscoachmember,login_url="/rowers/planmembership/", redirect_field_name=None) -def plannedsession_teamedit_view(request,timeperiod='thisweek', - sessionid=0): +def plannedsession_teamedit_view(request, + sessionid=0,userid=0): - r = getrequestrower(request) + r = getrequestrower(request,userid=userid) + + when = request.GET.get('when') + if when: + timeperiod = when + else: + timeperiod = 'thisweek' + try: ps = PlannedSession.objects.get(id=sessionid) @@ -14103,8 +14116,11 @@ def plannedsession_teamedit_view(request,timeperiod='thisweek', url = reverse(plannedsession_teamedit_view, kwargs = { 'sessionid':sessionid, - 'timeperiod':timeperiod, }) + + if when: + url += '?when='+when + return HttpResponseRedirect(url) else: sessioncreateform = PlannedSessionForm(instance=ps) @@ -14202,9 +14218,9 @@ def plannedsessions_coach_view(request,timeperiod='thisweek', ) @login_required() -def plannedsessions_view(request,timeperiod='thisweek',rowerid=0,userid=0): +def plannedsessions_view(request,timeperiod='thisweek',userid=0): - r = getrequestrower(request,rowerid=rowerid) + r = getrequestrower(request,userid=userid) startdate,enddate = get_dates_timeperiod(timeperiod) @@ -14249,9 +14265,9 @@ def plannedsessions_view(request,timeperiod='thisweek',rowerid=0,userid=0): }) @login_required() -def plannedsessions_print_view(request,timeperiod='thisweek',rowerid=0,userid=0): +def plannedsessions_print_view(request,timeperiod='thisweek',userid=0): - r = getrequestrower(request,rowerid=rowerid) + r = getrequestrower(request,userid=userid) startdate,enddate = get_dates_timeperiod(timeperiod) @@ -14282,14 +14298,14 @@ def plannedsessions_print_view(request,timeperiod='thisweek',rowerid=0,userid=0) @login_required() -def plannedsessions_manage_view(request,timeperiod='thisweek',rowerid=0,userid=0, +def plannedsessions_manage_view(request,timeperiod='thisweek',userid=0, initialsession=0): is_ajax = False if request.is_ajax(): is_ajax = True - r = getrequestrower(request,rowerid=rowerid) + r = getrequestrower(request,userid=userid) startdate,enddate = get_dates_timeperiod(timeperiod) @@ -14415,10 +14431,15 @@ def plannedsessions_manage_view(request,timeperiod='thisweek',rowerid=0,userid=0 # need clarity on cloning behavior time shift @user_passes_test(hasplannedsessions,login_url="/rowers/planmembership/", redirect_field_name=None) -def plannedsession_clone_view(request,id=0,rowerid=0,userid=0, - timeperiod='thisweek'): +def plannedsession_clone_view(request,id=0,userid=0): - r = getrequestrower(request,rowerid=rowerid) + r = getrequestrower(request,userid=userid) + + when = request.GET.get('when') + if when: + timeperiod = when + else: + timeperiod = 'thisweek' startdate,enddate = get_dates_timeperiod(timeperiod) @@ -14460,10 +14481,12 @@ def plannedsession_clone_view(request,id=0,rowerid=0,userid=0, url = reverse(plannedsession_edit_view, kwargs = { 'id':ps.id, - 'timeperiod':timeperiod, - 'rowerid':r.id, + 'userid':r.user.id, } - ) + ) + + if when: + url += '?when='+when return HttpResponseRedirect(url) @@ -14471,9 +14494,15 @@ def plannedsession_clone_view(request,id=0,rowerid=0,userid=0, # Edit an existing planned session @user_passes_test(hasplannedsessions,login_url="/rowers/planmembership/", redirect_field_name=None) -def plannedsession_edit_view(request,id=0,timeperiod='thisweek',rowerid=0,userid=0): +def plannedsession_edit_view(request,id=0,userid=0): - r = getrequestrower(request,rowerid=rowerid) + r = getrequestrower(request,userid=userid) + + when = request.GET.get('when') + if when: + timeperiod = when + else: + timeperiod = 'thisweek' startdate,enddate = get_dates_timeperiod(timeperiod) @@ -14493,11 +14522,10 @@ def plannedsession_edit_view(request,id=0,timeperiod='thisweek',rowerid=0,userid if ps.manager != request.user: raise PermissionDenied("You are not allowed to edit this planned session") - + if ps.team.all() or len(ps.rower.all())>1: url = reverse(plannedsession_teamedit_view, kwargs={ - 'timeperiod':timeperiod, 'sessionid':id, }) return HttpResponseRedirect(url) @@ -14523,20 +14551,57 @@ def plannedsession_edit_view(request,id=0,timeperiod='thisweek',rowerid=0,userid url = reverse(plannedsession_edit_view, kwargs={ 'id':int(ps.id), - 'timeperiod':timeperiod, - 'rowerid':r.id, + 'userid':r.user.id, }) + + if when: + url += '?when='+when + return HttpResponseRedirect(url) else: sessioncreateform = PlannedSessionForm(instance=ps) sps = get_sessions(r,startdate=startdate,enddate=enddate) - + + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url': reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Sessions' + }, + { + 'url':reverse(plannedsession_view, + kwargs={ + 'userid':userid, + 'id':id, + } + ), + 'name': ps.id + }, + { + 'url':reverse(plannedsession_edit_view, + kwargs={ + 'userid':userid, + 'id':id, + } + ), + 'name': 'Edit' + } + ] + + return render(request,'plannedsessionedit.html', { 'teams':get_my_teams(request.user), 'plan':trainingplan, + 'breadcrumbs':breadcrumbs, 'form':sessioncreateform, + 'active':'nav-plan', 'plannedsessions':sps, 'thesession':ps, 'rower':r, @@ -14546,16 +14611,16 @@ def plannedsession_edit_view(request,id=0,timeperiod='thisweek',rowerid=0,userid @login_required() -def plannedsession_view(request,id=0,rowerid=0,userid=0, - timeperiod='thisweek'): +def plannedsession_view(request,id=0,userid=0): - m = getrower(request.user) + r = getrequestrower(request,userid=userid) - - if not rowerid: - r = m + when = request.GET.get('when') + if when: + timeperiod = when else: - r = Rower.objects.get(id=rowerid) + timeperiod = 'thisweek' + try: ps = PlannedSession.objects.get(id=id) @@ -14569,6 +14634,8 @@ def plannedsession_view(request,id=0,rowerid=0,userid=0, coursescript = '' coursediv = '' + m = ps.manager + if ps.manager != request.user: if r.rowerplan == 'coach': teams = Team.objects.filter(manager=request.user) @@ -14668,6 +14735,30 @@ def plannedsession_view(request,id=0,rowerid=0,userid=0, enddate__gte = enddate)[0] except IndexError: trainingplan = None + + + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url': reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Sessions' + }, + { + 'url':reverse(plannedsession_view, + kwargs={ + 'userid':userid, + 'id':id, + } + ), + 'name': ps.id + } + ] + return render(request,'plannedsessionview.html', { @@ -14679,6 +14770,8 @@ def plannedsession_view(request,id=0,rowerid=0,userid=0, 'sessionvalue','sessionunit','comment', ], 'workouts': ws, + 'active':'nav-plan', + 'breadcrumbs':breadcrumbs, 'manager':m, 'rower':r, 'ratio':ratio, @@ -15106,7 +15199,7 @@ def virtualevent_addboat_view(request,id=0): { 'form':form, 'race':race, - 'rowerid':r.id, + 'userid':r.user.id, 'active': 'nav-racing', }) @@ -15199,7 +15292,7 @@ def virtualevent_register_view(request,id=0): { 'form':form, 'race':race, - 'rowerid':r.id, + 'userid':r.user.id, }) From e1cd70f0163f89b213a6fc7904f062b5dcaa4ed2 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 15 Oct 2018 12:47:36 +0200 Subject: [PATCH 86/95] plannedsession_delete_view line 470 --- .../plannedsessiondeleteconfirm.html | 64 ++++++++--------- rowers/urls.py | 5 +- rowers/views.py | 72 +++++++++++++++++++ 3 files changed, 106 insertions(+), 35 deletions(-) diff --git a/rowers/templates/plannedsessiondeleteconfirm.html b/rowers/templates/plannedsessiondeleteconfirm.html index 0a6f3d7f..a299e6b0 100644 --- a/rowers/templates/plannedsessiondeleteconfirm.html +++ b/rowers/templates/plannedsessiondeleteconfirm.html @@ -1,45 +1,43 @@ -{% extends "base.html" %} +{% extends "newbase.html" %} {% load staticfiles %} {% block title %}Planned Session{% endblock %} -{% block content %} -
    - {% include "planningbuttons.html" %} - -
    -
    -

    Confirm Delete

    -

    This will permanently delete the planned session

    +{% block main %} +

    Confirm Delete

    +

    This will permanently delete the planned session

    - -
    +
    - -
    -

    - Delete + + {% csrf_token %} +

    Are you sure you want to delete {{ object }}?

    + +

    -
    + -
    - +
  • +

    Session {{ psdict.name.1 }}

    + + {% for attr in attrs %} + {% for key,value in psdict.items %} + {% if key == attr %} + + + + {% endif %} + {% endfor %} + {% endfor %} +
    {{ value.0 }}{{ value.1 }}
    +
  • + +{% endblock %} + +{% block sidebar %} +{% include 'menu_plan.html' %} {% endblock %} diff --git a/rowers/urls.py b/rowers/urls.py index 080a7b61..c6184a05 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -466,8 +466,9 @@ urlpatterns = [ name='plannedsession_view'), url(r'^sessions/(?P\d+)/user/(?P\d+)$',views.plannedsession_view, name='plannedsession_view'), - url(r'^sessions/(?P\d+)/deleteconfirm$',views.plannedsession_deleteconfirm_view), - url(r'^sessions/(?P\d+)/delete$',views.plannedsession_delete_view), + url(r'^sessions/(?P\d+)/deleteconfirm$',views.PlannedSessionDelete.as_view()), + url(r'^sessions/(?P\d+)/delete$',views.PlannedSessionDelete.as_view(), + name='plannedsession_delete_view'), url(r'^sessions/manage/session/(?P\d+)$', views.plannedsessions_manage_view), url(r'^sessions/manage/user/(?P\d+)/session/(?P\d+)$', diff --git a/rowers/views.py b/rowers/views.py index 0c33fa4b..f2216340 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -14786,6 +14786,78 @@ def plannedsession_view(request,id=0,userid=0): } ) +class PlannedSessionDelete(DeleteView): + model = PlannedSession + template_name = 'plannedsessiondeleteconfirm.html' + + # extra parameters + def get_context_data(self, **kwargs): + context = super(PlannedSessionDelete,self).get_context_data(**kwargs) + + if 'userid' in kwargs: + userid = kwargs['userid'] + else: + userid = 0 + + context['active']= 'nav-plan' + context['rower'] = getrequestrower(self.request,userid=userid) + context['ps'] = self.object + + psdict = my_dict_from_instance(self.object,PlannedSession) + + context['psdict'] = psdict + + context['attrs'] = ['name','startdate','enddate','sessiontype'] + + breadcrumbs = [ + { + 'url':reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Plan' + }, + { + 'url': reverse(plannedsessions_view, + kwargs={'userid':userid}), + 'name': 'Sessions' + }, + { + 'url':reverse(plannedsession_view, + kwargs={ + 'userid':userid, + 'id':self.object.pk, + } + ), + 'name': self.object.pk + }, + { + 'url':reverse('plannedsession_delete_view', + kwargs={'pk':self.object.pk}), + 'name': 'Delete' + } + ] + + context['breadcrumbs'] = breadcrumbs + + return context + + def get_success_url(self): + ws = Workout.objects.filter(plannedsession=self.object) + for w in ws: + w.plannedsession = None + w.save() + + url = reverse(plannedsessions_view) + + return url + + def get_object(self, *args, **kwargs): + obj = super(PlannedSessionDelete, self).get_object(*args, **kwargs) + m = Rower.objects.get(user=obj.manager) + if not checkaccessuser(self.request.user,m): + raise PermissionDenied('You are not allowed to delete this planned session') + + return obj + @login_required() def plannedsession_delete_view(request,id=0): r = getrower(request.user) From bd757c7bb4ebe8e6e01928eefb93abd2ab582d5c Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 15 Oct 2018 16:56:45 +0200 Subject: [PATCH 87/95] sessions manage --- rowers/templates/menu_plan.html | 2 +- rowers/templates/plannedsessionsmanage.html | 166 +++++++------------- rowers/urls.py | 6 +- rowers/views.py | 81 ++++------ 4 files changed, 90 insertions(+), 165 deletions(-) diff --git a/rowers/templates/menu_plan.html b/rowers/templates/menu_plan.html index a9065fd0..3545b52e 100644 --- a/rowers/templates/menu_plan.html +++ b/rowers/templates/menu_plan.html @@ -140,7 +140,7 @@ {% endblock %} +{% block sidebar %} +{% include 'menu_plan.html' %} +{% endblock %} + + {% block scripts %}