+ Please correct the error{{ form.errors|pluralize }} below.
+
+ {% endif %}
+
+
+
+
+
+
+
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.
{% endif %}
- {% if user.rower.planexpires and user.rower.rowerplan != 'basic' and user.rower.paymenttype == 'single'%}
+ {% if user.rower.planexpires and user.rower|notfree and user.rower.paymenttype == 'single'%}
{% if user.rower.planexpires|is_future_date %}
{% if user.rower.planexpires|date_dif|ddays < 4 %}
From 2baa9b6ba9df9164e9f0f35d49ee77a76561b84f Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Wed, 8 May 2019 15:52:34 +0200
Subject: [PATCH 04/18] debugging
---
rowers/dataprep.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/rowers/dataprep.py b/rowers/dataprep.py
index 8c00e384..cae4057f 100644
--- a/rowers/dataprep.py
+++ b/rowers/dataprep.py
@@ -1189,14 +1189,12 @@ parsers = {
}
def parsenonpainsled(fileformat,f2,summary):
-
try:
row = parsers[fileformat](f2)
hasrecognized = True
except KeyError:
hasrecognized = False
-
# handle speed coach GPS 2
if (fileformat == 'speedcoach2'):
oarlength, inboard = get_empower_rigging(f2)
@@ -1207,6 +1205,8 @@ def parsenonpainsled(fileformat,f2,summary):
fileformat = 'speedcoach2v0'
summary = row.allstats()
+ print(summary)
+ print('aap',hasrecognized)
# handle FIT
if (fileformat == 'fit'):
From ba9a1a542fcb149ab617b25fe42e6f83b20900c7 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Thu, 9 May 2019 20:37:46 +0200
Subject: [PATCH 05/18] requirements
---
requirements.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/requirements.txt b/requirements.txt
index d7f1e8b0..13e095cc 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -157,7 +157,7 @@ ratelim==0.1.6
redis==3.2.1
requests==2.21.0
requests-oauthlib==1.2.0
-rowingdata==2.3.1
+rowingdata==2.3.5
rowingphysics==0.5.0
rq==1.0
rq-dashboard==0.4.0
From 0f8a9f285b828ba3f1dfdc312bdd4e19e011d52b Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Wed, 15 May 2019 15:40:51 +0200
Subject: [PATCH 06/18] added freecoach to coach priviliges
---
rowers/dataprep.py | 10 ++++++++--
rowers/forms.py | 6 +++---
rowers/models.py | 2 +-
rowers/stravastuff.py | 5 ++++-
rowers/teams.py | 8 ++++----
.../templates/freecoach_registration_form.html | 2 +-
rowers/templates/paidplans.html | 10 ++++++++++
rowers/views/paymentviews.py | 3 +++
rowers/views/statements.py | 2 +-
rowers/views/teamviews.py | 16 +++++++++++++---
templates/newbase.html | 2 +-
templates/newbasefront.html | 2 +-
12 files changed, 50 insertions(+), 18 deletions(-)
diff --git a/rowers/dataprep.py b/rowers/dataprep.py
index b8a197f6..a04e0e76 100644
--- a/rowers/dataprep.py
+++ b/rowers/dataprep.py
@@ -3,6 +3,8 @@ from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
+
+
# All the data preparation, data cleaning and data mangling should
# be defined here
from __future__ import unicode_literals, absolute_import
@@ -28,6 +30,7 @@ from pandas import DataFrame, Series
from django.utils import timezone
from django.utils.timezone import get_current_timezone
from django_mailbox.models import Message,Mailbox,MessageAttachment
+from django.core.exceptions import ValidationError
from time import strftime
import arrow
@@ -1100,8 +1103,11 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
try:
w.save()
except ValidationError:
- w.startdatetime = timezone.now()
- w.save()
+ try:
+ w.startdatetime = timezone.now()
+ w.save()
+ except ValidationError:
+ return (0,'Unable to create your workout')
if privacy == 'visible':
ts = Team.objects.filter(rower=r)
diff --git a/rowers/forms.py b/rowers/forms.py
index fcc54568..3c3cef2d 100644
--- a/rowers/forms.py
+++ b/rowers/forms.py
@@ -632,16 +632,16 @@ class RegistrationFormSex(RegistrationFormUniqueEmail):
raise forms.ValidationError('Must be at least 16 years old to register')
return self.cleaned_data['birthdate']
- sex = forms.ChoiceField(required=True,
+ sex = forms.ChoiceField(required=False,
choices=sexcategories,
initial='not specified',
label='Sex')
weightcategory = forms.ChoiceField(label='Weight Category',
- choices=weightcategories)
+ choices=weightcategories,initial='hwt',required=False)
adaptiveclass = forms.ChoiceField(label='Adaptive Classification',
- choices=adaptivecategories)
+ choices=adaptivecategories,initial='None',required=False)
# def __init__(self, *args, **kwargs):
# self.fields['sex'].initial = 'not specified'
diff --git a/rowers/models.py b/rowers/models.py
index b44fc6c7..21f3ae10 100644
--- a/rowers/models.py
+++ b/rowers/models.py
@@ -2580,7 +2580,7 @@ class Workout(models.Model):
def save(self, *args, **kwargs):
user = self.user
if self.user.rowerplan == 'freecoach':
- raise ValidationError("Free Coach User cannot have any workouts")
+ raise forms.ValidationError("Free Coach User cannot have any workouts")
super(Workout, self).save(*args, **kwargs)
diff --git a/rowers/stravastuff.py b/rowers/stravastuff.py
index 156e14a3..4a4c8c75 100644
--- a/rowers/stravastuff.py
+++ b/rowers/stravastuff.py
@@ -701,7 +701,10 @@ def handle_strava_import_stroke_data(title,
coords = get_strava_stream(r,'latlng',stravaid)
power = get_strava_stream(r,'power',stravaid)
- nr_rows = len(t)
+ if nr_rows is not None:
+ nr_rows = len(t)
+ else:
+ return 0
if nr_rows == 0:
return 0
diff --git a/rowers/teams.py b/rowers/teams.py
index 3dc3f310..95073998 100644
--- a/rowers/teams.py
+++ b/rowers/teams.py
@@ -77,7 +77,7 @@ def create_team(name,manager,private='open',notes='',viewing='allmembers'):
if manager.rower.rowerplan == 'basic':
if manager.rower.protrialexpires < timezone.now().date() and manager.rower.plantrialexpires < timezone.now().date():
return (0,'You need to upgrade to a paid plan to establish a team')
- if manager.rower.rowerplan != 'coach':
+ if manager.rower.rowerplan not in ('coach','freecoach'):
ts = Team.objects.filter(manager=manager)
if len(ts)>=1:
return (0,'You need to upgrade to the Coach plan to have more than one team')
@@ -180,7 +180,7 @@ def rower_get_coaches(rower):
def coach_getcoachees(coach):
- if coach.mycoachgroup and coach.rowerplan == 'coach':
+ if coach.mycoachgroup and coach.rowerplan in ('coach','freecoach'):
return Rower.objects.filter(
coachinggroups__in=[coach.mycoachgroup]
).distinct().order_by("user__last_name","user__first_name")
@@ -237,7 +237,7 @@ def create_coaching_request(coach,user):
while code in codes:
code = uuid.uuid4().hex[:10].upper()
- if coach.rowerplan == 'coach':
+ if 'coach' in coach.rowerplan:
rekwest = CoachRequest(coach=coach,user=user,code=code)
rekwest.save()
@@ -316,7 +316,7 @@ def create_coaching_offer(coach,user):
while code in codes:
code = uuid.uuid4().hex[:10].upper()
- if coach.rowerplan == 'coach' and get_coach_club_size(coach)
{% if rower.rowerplan == 'coach' %}
COACH ({{ rower.paymenttype }})
+ {% elif rower.rowerplan == 'freecoach' %}
+
Free COACH
{% else %}
{% endif %}
@@ -298,6 +300,14 @@
can purchase upgrades to "Pro" and "Self-Coach" plans.
+
+ The Coach plans come in two versions: free and paid. On the free coach plan,
+ you can only have athletes who are on the PRO paid plans or higher, and you
+ cannot upload any workouts to your own account. On the paid
+ plans, your athletes can be on the free plan, and you have full access to the
+ site functionality for your own workouts and those of 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.
+ As long as you are on the free coach plan, you can only managing athletes who are
+ on one of the paid plans. To remove this limitation, upgrade to one of the paid
+ coaching plans:
+ https://rowsandall.com/rowers/paidplans
+
+
+
+ Oh, one more thing. The site is
+ developing fast. Bear with us. Don't hesitate to
+ contact me at info@rowsandall.com if anything is broken
+ or doesn't seem to work as advertised.
+
diff --git a/rowers/templates/registeremail.html b/rowers/templates/registeremail.html
index 20c81907..267358b3 100644
--- a/rowers/templates/registeremail.html
+++ b/rowers/templates/registeremail.html
@@ -10,8 +10,8 @@
Settings page
and make yourself familiar with the various options
available to personalize your experience on the website.
- To get there, use the link in the email or click the button
- with your first name on it in the upper right corner of the opening page.
+ To get there, use the link in the email or click the user icon
+ in the upper right corner of the opening page.
From 304b2559c76fd2b3b8e5cabadc7045a3f6de1bb6 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Thu, 16 May 2019 16:02:50 +0200
Subject: [PATCH 14/18] corrected training plan permissions and form
---
rowers/models.py | 6 +++---
rowers/views/planviews.py | 15 ++++++++++-----
2 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/rowers/models.py b/rowers/models.py
index 21f3ae10..7f50af9f 100644
--- a/rowers/models.py
+++ b/rowers/models.py
@@ -914,7 +914,7 @@ def check_teams_on_change(sender, **kwargs):
team = Team.objects.get(id=id)
if team.manager.rower.rowerplan not in ['coach']:
raise ValidationError(
- "You cannot join a team led by a Pro or Self-Coach user"
+ "You cannot join a team led by a Pro, Free Coach Plan or Self-Coach user"
)
m2m_changed.connect(check_teams_on_change, sender=Rower.team.through)
@@ -1075,7 +1075,7 @@ def checkaccessplanuser(user,rower):
r = Rower.objects.get(user=user)
if rower == r:
return True
- team_managers = [t.manager for t in rower.team.all() if t.manager.rower.rowerplan in ['plan','coach']]
+ team_managers = [t.manager for t in rower.team.all() if t.manager.rower.rowerplan in ['plan','coach','freecoach']]
if user.rower.rowerplan != 'basic':
return user in team_managers
else:
@@ -3271,7 +3271,7 @@ class AccountRowerForm(ModelForm):
def __init__(self, *args, **kwargs):
super(AccountRowerForm, self).__init__(*args, **kwargs)
- if self.instance.rowerplan != 'coach':
+ if 'coach' not in self.instance.rowerplan:
self.fields.pop('offercoaching')
diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py
index a455213c..e397e498 100644
--- a/rowers/views/planviews.py
+++ b/rowers/views/planviews.py
@@ -904,7 +904,7 @@ def plannedsessions_coach_view(request,
else:
theteam = False
- if request.user.rower.rowerplan == 'coach':
+ if 'coach' in request.user.rower.rowerplan:
sps = get_sessions_manager(request.user,teamid=teamid,
enddate=enddate,
startdate=startdate)
@@ -914,7 +914,7 @@ def plannedsessions_coach_view(request,
rowers = [therower]
for ps in sps:
- if request.user.rower.rowerplan == 'coach':
+ if 'coach' in request.user.rower.rowerplan:
rowers += ps.rower.all()
else:
rowers += ps.rower.filter(team__in=rteams)
@@ -1569,7 +1569,7 @@ def plannedsession_view(request,id=0,userid=0):
mm = Rower.objects.get(user=m)
if ps.manager != request.user:
- if r.rowerplan == 'coach' and r not in ps.rower.all():
+ if 'coach' in r.rowerplan and r not in ps.rower.all():
teams = Team.objects.filter(manager=request.user)
members = Rower.objects.filter(team__in=teams).distinct()
teamusers = [m.user for m in members]
@@ -1808,6 +1808,10 @@ def rower_create_trainingplan(request,userid=0):
name = targetform.cleaned_data['name']
date = targetform.cleaned_data['date']
notes = targetform.cleaned_data['notes']
+ try:
+ rowers = targetform.cleaned_data['rowers']
+ except KeyError:
+ rowers = [therower]
t = TrainingTarget(
name=name,
@@ -1817,7 +1821,8 @@ def rower_create_trainingplan(request,userid=0):
t.save()
- t.rowers.add(therower)
+ for athlete in rowers:
+ t.rowers.add(athlete)
t.save()
elif request.method == 'POST' and 'startdate' in request.POST:
@@ -2589,7 +2594,7 @@ class TrainingPlanUpdate(UpdateView):
obj = super(TrainingPlanUpdate, self).get_object(*args, **kwargs)
if obj.manager is not None and self.request.user.rower != obj.manager:
raise PermissionDenied('You are not allowed to edit this training plan cycle')
- if obj.manager.rowerplan not in ['coach','plan']:
+ if obj.manager.rowerplan not in ['coach','freecoach','plan']:
raise PermissionDenied('You are not allowed to edit this training plan')
return obj
From f45aea0aa3a73dbd8c2653a457d204891e57628d Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Thu, 16 May 2019 16:18:22 +0200
Subject: [PATCH 15/18] tested
---
rowers/tests/testdata/testdata.csv.gz | Bin 12534 -> 12534 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
diff --git a/rowers/tests/testdata/testdata.csv.gz b/rowers/tests/testdata/testdata.csv.gz
index 1fabe752573d943c09f4eda5d4782c33d4b3e885..56afbcb2ed58c4161c8f9d871aae8bc4ca1814a8 100644
GIT binary patch
delta 15
WcmeyC_$`r5zMF&NQ^7{I4+a1@W(KGL
delta 15
WcmeyC_$`r5zMF%CL1QD^2Lk{xQUz`R
From cb424ead67b0125c6b76795b1c6bb2c56f729889 Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Thu, 16 May 2019 16:43:32 +0200
Subject: [PATCH 16/18] prevent free coach from showing up in plan
---
rowers/plannedsessions.py | 4 ++--
rowers/views/planviews.py | 12 ++++++++----
2 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/rowers/plannedsessions.py b/rowers/plannedsessions.py
index d62b50eb..34642a46 100644
--- a/rowers/plannedsessions.py
+++ b/rowers/plannedsessions.py
@@ -465,12 +465,12 @@ def add_team_session(t,ps):
def add_rower_session(r,ps):
teams = Team.objects.filter(manager=ps.manager)
members = Rower.objects.filter(team__in=teams).distinct()
- if r in members:
+ if r in members and r.rowerplan != 'freecoach':
ps.rower.add(r)
ps.save()
return 1
- elif ps.manager.rower == r:
+ elif ps.manager.rower == r and r.rowerplan != 'freecoach':
ps.rower.add(r)
ps.save()
diff --git a/rowers/views/planviews.py b/rowers/views/planviews.py
index e397e498..82e6e065 100644
--- a/rowers/views/planviews.py
+++ b/rowers/views/planviews.py
@@ -632,7 +632,7 @@ def plannedsession_teamcreate_view(request,
request.session['teams'] = [team.id for team in teams]
for team in teams:
add_team_session(team,ps)
- rs = Rower.objects.filter(team__in=[team])
+ rs = Rower.objects.filter(team__in=[team]).exclude(rowerplan='freecoach')
for r in rs:
add_rower_session(r,ps)
@@ -912,12 +912,16 @@ def plannedsessions_coach_view(request,
rteams = therower.team.filter(viewing='allmembers')
sps = get_sessions(therower,startdate=startdate,enddate=enddate)
- rowers = [therower]
+ if therower.rowerplan != 'freecoach':
+ rowers = [therower]
+ else:
+ rowers = []
+
for ps in sps:
if 'coach' in request.user.rower.rowerplan:
- rowers += ps.rower.all()
+ rowers += ps.rower.all().exclude(rowerplan='freecoach')
else:
- rowers += ps.rower.filter(team__in=rteams)
+ rowers += ps.rower.filter(team__in=rteams).exclude(rowerplan='freecoach')
rowers = list(set(rowers))
From 597c66a80ffd54605188715481af9108e765f01b Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Thu, 16 May 2019 21:25:14 +0200
Subject: [PATCH 17/18] bla
---
rowers/templates/otwrankings.html | 4 ++--
rowers/tests/testdata/testdata.csv.gz | Bin 12534 -> 12523 bytes
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/rowers/templates/otwrankings.html b/rowers/templates/otwrankings.html
index a4b2cdd6..a627b362 100644
--- a/rowers/templates/otwrankings.html
+++ b/rowers/templates/otwrankings.html
@@ -120,8 +120,8 @@