From 355d97edc76891c0870c4eb0a28503f1065c3fbf Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 9 Apr 2018 20:00:49 +0200 Subject: [PATCH 01/27] expanded explanation text on HRTSS --- rowers/templates/workoutstats.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rowers/templates/workoutstats.html b/rowers/templates/workoutstats.html index 965a54f8..3b1fbc5b 100644 --- a/rowers/templates/workoutstats.html +++ b/rowers/templates/workoutstats.html @@ -86,7 +86,9 @@

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

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

+

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

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

+

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

Other Stats

From b6411893e56f0a5a3a47f1666f6f658954725997 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 9 Apr 2018 22:02:14 +0200 Subject: [PATCH 02/27] added plan trial --- rowers/templates/promembership.html | 4 +++- rowers/urls.py | 1 + rowers/views.py | 31 +++++++++++++++++++++++++++++ templates/basebase.html | 4 +++- 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/rowers/templates/promembership.html b/rowers/templates/promembership.html index 1b9abe94..f1094c22 100644 --- a/rowers/templates/promembership.html +++ b/rowers/templates/promembership.html @@ -135,11 +135,13 @@ You will be taken to the secure PayPal payment site.

Free Trial

You qualify for a 14 day free trial. No credit card needed. - Try out Pro membership for two weeks. Click the button below to + 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

diff --git a/rowers/urls.py b/rowers/urls.py index 6d04aaa6..4f0e818f 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -395,6 +395,7 @@ urlpatterns = [ url(r'^analysis', TemplateView.as_view(template_name='analysis.html'),name='analysis'), 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'^legal', TemplateView.as_view(template_name='legal.html'),name='legal'), diff --git a/rowers/views.py b/rowers/views.py index 560166d2..489ade0a 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -876,6 +876,8 @@ def hasplannedsessions(user): r.save() result = user.is_authenticated() and (r.rowerplan=='coach' or r.rowerplan=='plan') + if not result and r.plantrialexpires: + result = user.is_authenticaded() and r.rowerplan=='basic' and r.plantrialexpires >= datetime.date.today() else: result = False @@ -1039,7 +1041,36 @@ def start_trial_view(request): ['roosendaalsander@gmail.com']) return HttpResponseRedirect(url) + +@login_required() +def start_plantrial_view(request): + r = getrower(request.user) + + if r.plantrialexpires is not None: + messages.error(request,'You do not qualify for a trial') + url = '/rowers/promembership' + return HttpResponseRedirect(url) + r.plantrialexpires = datetime.date.today()+datetime.timedelta(13) + r.protrialexpires = datetime.date.today()+datetime.timedelta(13) + r.save() + + url = reverse(workouts_view) + + messages.info(request,'We have started your 14 day trial period') + + subject2 = "User started Pro Trial" + message2 = "User Started Pro Trial.\n" + message2 += request.user.email + "\n" + message2 += "User name: "+request.user.username + + send_mail(subject2, message2, + 'Rowsandall Server ', + ['roosendaalsander@gmail.com']) + + return HttpResponseRedirect(url) + + # Create workout data from Strava or Concept2 # data and create the associated Workout object and save it def add_workout_from_strokedata(user,importid,data,strokedata, diff --git a/templates/basebase.html b/templates/basebase.html index f88878fe..4a7c44a2 100644 --- a/templates/basebase.html +++ b/templates/basebase.html @@ -162,6 +162,8 @@ {% endif %} {% if user.rower.rowerplan == 'basic' and user.rower.protrialexpires|date_dif == 1 %} + {% elif user.rower.rowerplan == 'basic' and user.rower.plantrialexpires|date_dif == 1 %} + {% endif %} @@ -244,7 +246,7 @@ {% endif %} {% if user.rower.protrialexpires and user.rower.protrialexpires|is_future_date %}

- {{ user.rower.protrialexpires|date_dif|ddays }} days left in Pro trial + {{ user.rower.protrialexpires|date_dif|ddays }} days left of your trial

{% endif %} {% if user.rower.emailbounced %} From c05dac188b8412cb1d826369030d6fca3e7ce71a Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 10 Apr 2018 08:41:20 +0200 Subject: [PATCH 03/27] fine tuning trial messages and planmembership page --- rowers/templates/planmembership.html | 15 ++++++++------- templates/basebase.html | 12 +++++++++--- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/rowers/templates/planmembership.html b/rowers/templates/planmembership.html index 5a9415d3..5d9c9c3a 100644 --- a/rowers/templates/planmembership.html +++ b/rowers/templates/planmembership.html @@ -6,11 +6,8 @@

Coach and Self-Coach Membership

-

You have arrived at this page, because you tried to create a - training plan for yourself.

- -

This option is restricted to rowers on our "Self-Coach" plan or - coaches on our "Coach" plan.

+

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.

@@ -32,7 +29,8 @@

What training planning functionality do we offer?

-

Over the spring of 2018, we will gradually expand this functionality. +

Over the spring of 2018, we are rolling out the first phases + of Training Planning functionality. Our current roadmap is to deploy the following and more:

@@ -45,12 +43,15 @@

  • Implemented:Track your teams performance against plan. See how well each of your team members adhere to their (team or personalized) plan.
  • Implemented:See test outcomes ranked by performance.
  • -
  • Implemented:Attach courses to your OTW tests. This advanced functionality +
  • Implemented:Attach courses to your OTW tests. + This advanced functionality allows you, for example, to assign "Row the 6km from bridge A to bridge B on Saturday" to your team members. The resulting workout tracks will be evaluated against the course, and you will receive a results table for the net time spent between the start and finish points on the course. It's like a mini head race. +
  • Define your own macro, meso and microcycle start and end dates
  • +
  • More to come ...
  • diff --git a/templates/basebase.html b/templates/basebase.html index 4a7c44a2..47d5bf19 100644 --- a/templates/basebase.html +++ b/templates/basebase.html @@ -158,12 +158,12 @@ {% elif user.rower.rowerplan == 'plan' %}
    Self-Coach
    {% else %} - + {% endif %} {% if user.rower.rowerplan == 'basic' and user.rower.protrialexpires|date_dif == 1 %} {% elif user.rower.rowerplan == 'basic' and user.rower.plantrialexpires|date_dif == 1 %} - + {% endif %}
    @@ -245,8 +245,14 @@

    {% endif %} {% if user.rower.protrialexpires and user.rower.protrialexpires|is_future_date %} + {% if user.rower.plantrialexpires %}

    - {{ user.rower.protrialexpires|date_dif|ddays }} days left of your trial + {{ user.rower.protrialexpires|date_dif|ddays }} days left of your Self-Coach trial - Would you like to upgrade now? + + {% else %} +

    + {{ user.rower.protrialexpires|date_dif|ddays }} days left of your Pro trial - Would you like to upgrade now? + {% endif %}

    {% endif %} {% if user.rower.emailbounced %} From c3448ebf2c11141f27ca8f3166233b8c0195fbdf Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 10 Apr 2018 15:42:38 +0200 Subject: [PATCH 04/27] added test to travel all links --- rowers/dataprep.py | 4 +- rowers/templates/about_us.html | 2 +- rowers/templates/compatibility.html | 7 +- rowers/templates/workout_form.html | 2 +- rowers/tests.py | 6 +- rowers/traverselinktest.py | 186 ++++++++++++++++++++++++++++ rowers/views.py | 55 ++++---- 7 files changed, 228 insertions(+), 34 deletions(-) create mode 100644 rowers/traverselinktest.py diff --git a/rowers/dataprep.py b/rowers/dataprep.py index 7233b84e..e9a077cc 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -309,6 +309,8 @@ def clean_df_stats(datadf, workstrokesonly=True, ignorehr=True, # clean data remove zeros and negative values # bring metrics which have negative values to positive domain + if datadf.empty: + return datadf try: datadf['catch'] = -datadf['catch'] except KeyError: @@ -1670,7 +1672,7 @@ def getsmallrowdata_db(columns, ids=[], doclean=True, workstrokesonly=True): try: f = row.df['TimeStamp (sec)'].diff().mean() - except AttributeError: + except (AttributeError,KeyError) as e: f = 0 if f != 0 and not np.isnan(f): diff --git a/rowers/templates/about_us.html b/rowers/templates/about_us.html index 0cf52bcf..b2b2d03c 100644 --- a/rowers/templates/about_us.html +++ b/rowers/templates/about_us.html @@ -75,7 +75,7 @@

    Rowsandall.com tries to be compatible with the most important tools that rowers use to capture the data (both indoor and OTW). For a full list of currently supported devides/apps, click - here. + here. diff --git a/rowers/templates/compatibility.html b/rowers/templates/compatibility.html index 6dc792cb..c23ce1f2 100644 --- a/rowers/templates/compatibility.html +++ b/rowers/templates/compatibility.html @@ -7,14 +7,15 @@

    Import Compatibility

    Rowsandall.com tries to be compatible with the most important tools - that rowers use to capture the data (both indoor and OTW). For a full - list of currently supported devides/apps, click - here. + that rowers use to capture the data (both indoor and OTW).

    On The Water

    • CrewNerd (TCX)
    • Rowing In Motion (TCX)
    • +
    • BoatCoach
    • +
    • RowingCoach
    • +
    • Quiske RowP
    • Speedcoach XL (CSV)
    • Speedcoach GPS (FIT and CSV)

    diff --git a/rowers/templates/workout_form.html b/rowers/templates/workout_form.html index 03a04eff..a2692ca7 100644 --- a/rowers/templates/workout_form.html +++ b/rowers/templates/workout_form.html @@ -235,7 +235,7 @@

    diff --git a/rowers/tests.py b/rowers/tests.py index 5fd5288b..c465447d 100644 --- a/rowers/tests.py +++ b/rowers/tests.py @@ -1,3 +1,6 @@ +from __future__ import print_function +from bs4 import BeautifulSoup +import re from django.test import TestCase, Client,override_settings from django.core.management import call_command from django.utils.six import StringIO @@ -34,8 +37,7 @@ from dataprep import delete_strokedata from redis import StrictRedis redis_connection = StrictRedis() - - + class DjangoTestCase(TestCase, MockTestCase): def _pre_setup(self): MockTestCase.setUp(self) diff --git a/rowers/traverselinktest.py b/rowers/traverselinktest.py new file mode 100644 index 00000000..bb9826a2 --- /dev/null +++ b/rowers/traverselinktest.py @@ -0,0 +1,186 @@ +from __future__ import print_function +from bs4 import BeautifulSoup +import re +from django.test import TestCase, Client,override_settings +from django.core.management import call_command +from django.utils.six import StringIO +from django.test.client import RequestFactory +from .views import checkworkoutuser,c2_open +from rowers.models import Workout, User, Rower, WorkoutForm,RowerForm,GraphImage +from rowers.forms import DocumentsForm,CNsummaryForm,RegistrationFormUniqueEmail +import rowers.plots as plots +import rowers.interactiveplots as iplots +import datetime +from rowingdata import rowingdata as rdata +from rowingdata import rower as rrower +from django.utils import timezone +from rowers.rows import handle_uploaded_file +from django.core.files.uploadedfile import SimpleUploadedFile +from time import strftime,strptime,mktime,time,daylight +import os +from rowers.tasks import handle_makeplot +from rowers.utils import serialize_list,deserialize_list +from rowers.c2stuff import C2NoTokenError +from shutil import copyfile + +from minimocktest import MockTestCase +import pandas as pd + +import json +import numpy as np + +from rowers import urls +from rowers.views import error500_view,error404_view,error400_view,error403_view + +from dataprep import delete_strokedata + +from redis import StrictRedis +redis_connection = StrictRedis() + +VERBOSE = True + +class TraverseLinksTest(TestCase): + def setUp(self): + self.u = User.objects.create_superuser( + 'superuser1', + 'superuser1@example.com','pwd') + self.r = Rower.objects.create(user=self.u,gdproptin=True,gdproptindate=timezone.now()) + nu = datetime.datetime.now() + + self.w = Workout.objects.create( + name='testworkout',workouttype='On-water', + user=self.r,date=nu.strftime('%Y-%m-%d'), + starttime=nu.strftime('%H:%M:%S'), + duration="0:55:00",distance=8000) + self.w2 = Workout.objects.create( + name='testworkout 2',workouttype='On-water', + user=self.r,date=nu.strftime('%Y-%m-%d'), + starttime=nu.strftime('%H:%M:%S'), + duration="0:55:00",distance=8000) + if self.client.login( + username="superuser1", password="pwd"): + if VERBOSE: + print('\nLogin as superuser OK') + else: + raise BaseException('Login failed') + + @classmethod + def setUpTestData(cls): + # Initialise your database here as needed + pass + + + def test_traverse_urls(self): + # Fill these lists as needed with your site specific URLs to check and to avoid + to_traverse_list = ['/rowers/list-workouts'] + to_avoid_list = ['^/$', '^$', 'javascript:history\.back()', + 'javascript:history\.go\(-1\)', '^mailto:.*', + '.*github\.io.*', 'javascript:.*', + '.*biorow\.com.*','.*facebook.*', + '.*wordpress.*','.*analytics.*','.*freenet.*', + '.*twitter.*','^blog.*', + '.*\d+-\d+-\d+.*', + '.*flexchart/.*', + '.*heroku.*', + '.*oauth.*', + '.*rowingdata.*', + '.*thisisant.*', + '.*garmin.*', + '.*sub7.*', + '.*bitbucket.*', + '.*rathburn.*', + '.*team.*', + '.*concept2.*', + '.*static.*', + '.*authorize.*', + '.*youtu.*', + '.*earth.*', + '.*underarmour.*', + '.*runkeeper.*', + '.*c2list.*', + '.*stravaimport.*', + '.*performancephones.*', + '.*sporttracks.*', + '.*join-select.*', + ] + + done_list = [] + error_list = [] + source_of_link = dict() + for link in to_traverse_list: + source_of_link[link] = 'initial' + + (to_traverse_list, to_avoid_list, done_list, error_list, source_of_link) = \ + self.recurse_into_path(to_traverse_list, to_avoid_list, done_list, error_list, source_of_link) + + print('END REACHED\nStats:') + if VERBOSE: print('\nto_traverse_list = ' + str(to_traverse_list)) + if VERBOSE: print('\nto_avoid_list = ' + str(to_avoid_list)) + if VERBOSE: print('\nsource_of_link = ' + str(source_of_link)) + if VERBOSE: print('\ndone_list = ' + str(done_list)) + print('Followed ' + str(len(done_list)) + ' links successfully') + print('Avoided ' + str(len(to_avoid_list)) + ' links') + + if error_list: + print('!! ' + str(len(error_list)) + ' error(s) : ') + for error in error_list: + print(str(error) + ' found in page ' + source_of_link[error[0]]) + + print('Errors found traversing links') + assert False + else: + print('No errors') + + def recurse_into_path(self, to_traverse_list, to_avoid_list, done_list, error_list, source_of_link): + """ Dives into first item of to_traverse_list + Returns: (to_traverse_list, to_avoid_list, done_list, source_of_link) + """ + + if to_traverse_list: + url = to_traverse_list.pop() + + if not match_any(url, to_avoid_list): + print('Surfing to ' + str(url) + ', discovered in ' + str(source_of_link[url])) + response = self.client.get(url, follow=True) + + if response.status_code == 200: + soup = BeautifulSoup(response.content, 'html.parser') + + text = soup.get_text() + + for link in soup.find_all('a'): + new_link = link.get('href') + if VERBOSE: print(' Found link: ' + str(new_link)) + if match_any(new_link, to_avoid_list): + if VERBOSE: print(' Avoiding it') + elif new_link in done_list: + if VERBOSE: print(' Already done, ignoring') + elif new_link in to_traverse_list: + if VERBOSE: print(' Already in to traverse list, ignoring') + else: + if VERBOSE: print(' New, unknown link: Storing it to traverse later') + source_of_link[new_link] = url + to_traverse_list.append(new_link) + + done_list.append(url) + if VERBOSE: print('Done') + else: + error_list.append((url, response.status_code)) + to_avoid_list.append(url) + + if VERBOSE: print('Diving into next level') + return self.recurse_into_path(to_traverse_list, to_avoid_list, done_list, error_list, source_of_link) + + else: + # Nothing to traverse + if VERBOSE: print('Returning to upper level') + return to_traverse_list, to_avoid_list, done_list, error_list, source_of_link + + +def match_any(my_string, regexp_list): + if my_string: + combined = "(" + ")|(".join(regexp_list) + ")" + return re.match(combined, my_string) + else: + # 'None' as string always matches + return True diff --git a/rowers/views.py b/rowers/views.py index 489ade0a..a4d51b0d 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -2712,7 +2712,7 @@ def rower_process_testcallback(request): @login_required() def histo_all(request,theuser=0, startdate=timezone.now()-datetime.timedelta(days=10), - enddate=timezone.now()+datetime.timedelta(days=1), + enddate=timezone.now(), deltadays=-1, startdatestring="", enddatestring="", @@ -2894,7 +2894,7 @@ def cum_flex(request,theuser=0, yparam1='power', yparam2='None', startdate=timezone.now()-datetime.timedelta(days=10), - enddate=timezone.now()+datetime.timedelta(days=1), + enddate=timezone.now(), deltadays=-1, startdatestring="", enddatestring="", @@ -3623,7 +3623,7 @@ def rankings_view(request,theuser=0, # test to fix bug startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) - enddate = enddate+datetime.timedelta(days=1) + #enddate = enddate+datetime.timedelta(days=1) thedistances = [] theworkouts = [] @@ -4029,7 +4029,7 @@ def rankings_view2(request,theuser=0, # test to fix bug startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) - enddate = enddate+datetime.timedelta(days=1) + #enddate = enddate+datetime.timedelta(days=1) thedistances = [] @@ -4407,7 +4407,7 @@ def otwrankings_view(request,theuser=0, # test to fix bug startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) - enddate = enddate+datetime.timedelta(days=1) + #enddate = enddate+datetime.timedelta(days=1) @@ -4683,7 +4683,7 @@ def oterankings_view(request,theuser=0, # test to fix bug startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) - enddate = enddate+datetime.timedelta(days=1) + #enddate = enddate+datetime.timedelta(days=1) @@ -5080,7 +5080,7 @@ def workouts_join_select(request, message='', successmessage='', startdate=timezone.now()-datetime.timedelta(days=30), - enddate=timezone.now()+datetime.timedelta(days=1), + enddate=timezone.now(), teamid=0): try: @@ -5152,7 +5152,7 @@ def workouts_join_select(request, startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) - enddate = enddate+datetime.timedelta(days=1) + #enddate = enddate+datetime.timedelta(days=1) if startdatestring: startdate = iso8601.parse_date(startdatestring) @@ -5239,7 +5239,7 @@ def team_comparison_select(request, message='', successmessage='', startdate=timezone.now()-datetime.timedelta(days=30), - enddate=timezone.now()+datetime.timedelta(days=1), + enddate=timezone.now(), teamid=0): try: @@ -5311,7 +5311,7 @@ def team_comparison_select(request, startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) - enddate = enddate+datetime.timedelta(days=1) + #enddate = enddate+datetime.timedelta(days=1) if startdatestring: startdate = iso8601.parse_date(startdatestring) @@ -5478,13 +5478,13 @@ def multi_compare_view(request): # Multi Flex Chart with Grouping @user_passes_test(ispromember,login_url="/",redirect_field_name=None) def user_multiflex_select(request, - startdatestring="", - enddatestring="", - message='', - successmessage='', - startdate=timezone.now()-datetime.timedelta(days=30), - enddate=timezone.now()+datetime.timedelta(days=1), - userid=0): + startdatestring="", + enddatestring="", + message='', + successmessage='', + startdate=timezone.now()-datetime.timedelta(days=30), + enddate=timezone.now(), + userid=0): if userid == 0: user = request.user @@ -5575,7 +5575,7 @@ def user_multiflex_select(request, startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) - enddate = enddate+datetime.timedelta(days=1) + #enddate = enddate+datetime.timedelta(days=1) if startdatestring: startdate = iso8601.parse_date(startdatestring) @@ -6017,7 +6017,7 @@ def user_boxplot_select(request, message='', successmessage='', startdate=timezone.now()-datetime.timedelta(days=30), - enddate=timezone.now()+datetime.timedelta(days=1), + enddate=timezone.now(), options={ 'includereststrokes':False, 'workouttypes':['rower','dynamic','slides'], @@ -6113,7 +6113,7 @@ def user_boxplot_select(request, startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) - enddate = enddate+datetime.timedelta(days=1) + #enddate = enddate+datetime.timedelta(days=1) if startdatestring: startdate = iso8601.parse_date(startdatestring) @@ -6413,7 +6413,7 @@ def courses_view(request): def workouts_view(request,message='',successmessage='', startdatestring="",enddatestring="", startdate=timezone.now()-datetime.timedelta(days=365), - enddate=timezone.now()+datetime.timedelta(days=1), + enddate=timezone.now(), teamid=0,rankingonly=False,rowerid=0,userid=0): request.session['referer'] = absolute(request)['PATH'] @@ -6448,7 +6448,7 @@ def workouts_view(request,message='',successmessage='', startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) - enddate = enddate+datetime.timedelta(days=1) + #enddate = enddate+datetime.timedelta(days=1) if startdatestring: startdate = iso8601.parse_date(startdatestring) @@ -6590,7 +6590,7 @@ def workout_comparison_list(request,id=0,message='',successmessage='', startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) - enddate = enddate+datetime.timedelta(days=1) + #enddate = enddate+datetime.timedelta(days=1) if enddate < startdate: s = enddate @@ -6668,7 +6668,7 @@ def workout_fusion_list(request,id=0,message='',successmessage='', startdate = datetime.datetime.combine(startdate,datetime.time()) enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59)) - enddate = enddate+datetime.timedelta(days=1) + #enddate = enddate+datetime.timedelta(days=1) if enddate < startdate: s = enddate @@ -10988,7 +10988,10 @@ def workout_summary_edit_view(request,id,message="",successmessage="" if rowdata == 0: return HttpResponse("Error: CSV Data File Not Found") intervalstats = rowdata.allstats() - itime,idist,itype = rowdata.intervalstats_values() + try: + itime,idist,itype = rowdata.intervalstats_values() + except TypeError: + return HttpResponse("Error: CSV Data File Not Found") nrintervals = len(idist) # create interactive plot @@ -12209,7 +12212,7 @@ def plannedsession_multiclone_view( request,timeperiod='none', rowerid=0, startdate=timezone.now()-datetime.timedelta(days=30), - enddate=timezone.now()+datetime.timedelta(days=1)): + enddate=timezone.now()): if rowerid==0: r = getrower(request.user) From b6dd38ab9e8b4a0c52ce02b6cb14b62e9cb0314c Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 10 Apr 2018 16:40:48 +0200 Subject: [PATCH 05/27] moved fitness-trend to new lab page --- rowers/templates/analysis.html | 4 ++-- rowers/templates/laboratory.html | 41 ++++++++++++++++++++++++++++++++ rowers/urls.py | 1 + 3 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 rowers/templates/laboratory.html diff --git a/rowers/templates/analysis.html b/rowers/templates/analysis.html index f64f716b..e18454fc 100644 --- a/rowers/templates/analysis.html +++ b/rowers/templates/analysis.html @@ -141,9 +141,9 @@

    {% if user|is_planmember %} - Lab + The Labs {% else %} - Lab + The Labs {% endif %}

    diff --git a/rowers/templates/laboratory.html b/rowers/templates/laboratory.html new file mode 100644 index 00000000..9b5b2964 --- /dev/null +++ b/rowers/templates/laboratory.html @@ -0,0 +1,41 @@ +{% extends "base.html" %} +{% load staticfiles %} +{% load rowerfilters %} + +{% block title %}Rowsandall - Analysis {% endblock %} + +{% block content %} + +

    The Labs

    +

    This is where whacky new ideas are tested

    + + +
    +
    + +
    +
    +

    Basic

    + +
    + +
    +

    Pro

    +
    +

    + {% if user|is_planmember %} + Power Progress + {% else %} + Power Progress + {% endif %} +

    +

    + Monitoring power duration evidence from all your workouts. Feel free to explore. +

    +
    +
    +
    + + + +{% endblock %} diff --git a/rowers/urls.py b/rowers/urls.py index 4f0e818f..ab554f47 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -393,6 +393,7 @@ urlpatterns = [ 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', TemplateView.as_view(template_name='analysis.html'),name='analysis'), + url(r'^laboratory', TemplateView.as_view(template_name='laboratory.html'),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), From 0c496fa31587a77bf631579baf9f62e574464eec Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 10 Apr 2018 16:48:44 +0200 Subject: [PATCH 06/27] small bug fix --- .gpx | 3 +++ .tcx | 14 ++++++++++++++ rowers/interactiveplots.py | 5 ++++- 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 .gpx create mode 100644 .tcx diff --git a/.gpx b/.gpx new file mode 100644 index 00000000..c2d010f0 --- /dev/null +++ b/.gpx @@ -0,0 +1,3 @@ + +Garmin InternationalExport by rowingdata + diff --git a/.tcx b/.tcx new file mode 100644 index 00000000..f3a5c6af --- /dev/null +++ b/.tcx @@ -0,0 +1,14 @@ + + + + + + 2015-03-28T20:45:15.000Z + + Empty File + 0 + 0 + + + + diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 9e652900..90d0ee2a 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -774,7 +774,10 @@ def interactive_histoall(theworkouts): if rowdata.empty: return "","No Valid Data Available","","" - histopwr = rowdata['power'].values + try: + histopwr = rowdata['power'].values + except KeyError: + return "","No power data","","" if len(histopwr) == 0: return "","No valid data available","","" From 02a65e69656132e339a5c49053954480754b8987 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 10 Apr 2018 17:00:37 +0200 Subject: [PATCH 07/27] ordered workouts in planned session manage by startdatetime --- rowers/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rowers/views.py b/rowers/views.py index a4d51b0d..d8d97e28 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -12902,7 +12902,7 @@ def plannedsessions_manage_view(request,timeperiod='thisweek',rowerid=0, user=r,date__gte=startdate, date__lte=enddate ).order_by( - "date","id" + "date","startdatetime","id" ) From ea526d1ae027ab6be224e11d9b2632929570e722 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 11 Apr 2018 11:12:04 +0200 Subject: [PATCH 08/27] intervals --- rowers/views.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/rowers/views.py b/rowers/views.py index d8d97e28..69a9a60f 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -10811,8 +10811,6 @@ def workout_summary_restore_view(request,id,message="",successmessage=""): intervalstats = rowdata.allstats() row.summary = intervalstats row.save() - itime,idist,itype = rowdata.intervalstats_values() - nrintervals = len(idist) # create interactive plot try: From 97cce15d6881349077525a860f9c6029bbe59476 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 11 Apr 2018 17:16:32 +0200 Subject: [PATCH 09/27] updated model --- rowers/models.py | 9 + rowers/templates/list_workouts.html | 4 +- rowers/templates/plannedsessionview.html | 12 +- static/admin/js/jquery.tablesorter.min.js | 1 + static/admin/js/jquery.tablesorter.widgets.js | 3192 +++++++++++++++++ static/js/jquery.tablesorter.min.js | 1 + static/js/jquery.tablesorter.widgets.js | 3192 +++++++++++++++++ templates/base.html | 4 + 8 files changed, 6412 insertions(+), 3 deletions(-) create mode 100644 static/admin/js/jquery.tablesorter.min.js create mode 100644 static/admin/js/jquery.tablesorter.widgets.js create mode 100644 static/js/jquery.tablesorter.min.js create mode 100644 static/js/jquery.tablesorter.widgets.js diff --git a/rowers/models.py b/rowers/models.py index c35331e1..68279d6f 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -1056,6 +1056,15 @@ class PlannedSession(models.Model): super(PlannedSession,self).save(*args, **kwargs) +class PopUpRegatta(PlannedSession): + has_registration = models.BooleanField(default=False) + registration_closure = models.DateTimeField(blank=True,null=True) + evaluation_closure = models.DateTimeField(blank=True,null=True) + start_time = models.TimeField(blank=True,null=True) + end_time = models.TimeField(blank=True,null=True) + + + # Date input utility class DateInput(forms.DateInput): input_type = 'date' diff --git a/rowers/templates/list_workouts.html b/rowers/templates/list_workouts.html index d4904466..11e7cef6 100644 --- a/rowers/templates/list_workouts.html +++ b/rowers/templates/list_workouts.html @@ -63,11 +63,11 @@
    {% csrf_token %} -
    +
    {% if user.is_authenticated and user|is_manager %} -