diff --git a/requirements.txt b/requirements.txt
index d7f1e8b0..4b13966d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,13 +8,13 @@ atomicwrites==1.3.0
attrs==19.1.0
backcall==0.1.0
beautifulsoup4==4.7.1
-billiard==3.5.0.5
+billiard==3.6.0.0
bleach==3.1.0
bokeh==1.0.4
boto==2.49.0
braintree==3.51.0
cairocffi==1.0.2
-celery==4.2.2
+celery==4.3.0
certifi==2019.3.9
cffi==1.12.2
chardet==3.0.4
@@ -46,9 +46,10 @@ django-leaflet==0.24.0
django-mailbox==4.7.1
django-oauth-toolkit==1.2.0
django-oauth2-provider==0.2.6.1
+django-redis==4.10.0
django-rest-framework==0.1.0
django-rest-swagger==2.2.0
-django-rq==2.0
+django-rq==1.3.1
django-rq-dashboard==0.3.3
django-ses==0.8.10
django-shell-plus==1.1.7
@@ -66,7 +67,9 @@ Faker==1.0.4
fitparse==1.1.0
Flask==1.0.2
future==0.17.1
+GDAL==2.3.3
geocoder==1.38.1
+geos==0.2.1
holoviews==1.11.3
html5lib==1.0.1
htmlmin==0.1.12
@@ -96,7 +99,7 @@ jupyterlab==0.35.4
jupyterlab-server==0.3.0
keyring==18.0.0
kiwisolver==1.0.1
-kombu==4.3.0
+kombu==4.5.0
lxml==4.3.2
Markdown==3.0.1
MarkupSafe==1.1.1
@@ -129,9 +132,9 @@ pip-upgrader==1.4.6
pluggy==0.9.0
prometheus-client==0.6.0
prompt-toolkit==2.0.9
+psycopg2==2.8.1
ptyprocess==0.6.0
py==1.8.0
-pycairo==1.18.0
pycparser==2.19
Pygments==2.3.1
pyparsing==2.3.1
@@ -157,10 +160,9 @@ ratelim==0.1.6
redis==3.2.1
requests==2.21.0
requests-oauthlib==1.2.0
-rowingdata==2.3.1
+rowingdata==2.3.8
rowingphysics==0.5.0
-rq==1.0
-rq-dashboard==0.4.0
+rq==0.13.0
scipy==1.2.1
SecretStorage==3.1.1
Send2Trash==1.5.0
@@ -188,8 +190,10 @@ VerbalExpressions==0.0.2
vine==1.3.0
wcwidth==0.1.7
webencodings==0.5.1
-Werkzeug==0.15.2
+Werkzeug==0.15.1
widgetsnbextension==3.4.2
+winkerberos==0.7.0
+xlrd==1.2.0
xmltodict==0.12.0
yamjam==0.1.7
yamllint==1.15.0
diff --git a/rowers/braintreestuff.py b/rowers/braintreestuff.py
index c7238887..184cc58e 100644
--- a/rowers/braintreestuff.py
+++ b/rowers/braintreestuff.py
@@ -241,7 +241,6 @@ def create_subscription(rower,data):
"payment_method_nonce": nonce_from_the_client
})
-
if result.is_success:
payment_method_token = result.payment_method.token
else:
@@ -252,6 +251,7 @@ def create_subscription(rower,data):
"plan_id": plan.external_id
})
+
if result.is_success:
rower.paidplan = plan
rower.planexpires = result.subscription.billing_period_end_date
diff --git a/rowers/dataprep.py b/rowers/dataprep.py
index f4a20ba2..89266aba 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)
@@ -1189,7 +1195,6 @@ parsers = {
}
def parsenonpainsled(fileformat,f2,summary):
-
try:
row = parsers[fileformat](f2)
hasrecognized = True
@@ -1197,7 +1202,6 @@ def parsenonpainsled(fileformat,f2,summary):
hasrecognized = False
return None, hasrecognized, '', 'unknown'
-
# handle speed coach GPS 2
if (fileformat == 'speedcoach2'):
oarlength, inboard = get_empower_rigging(f2)
@@ -1211,7 +1215,6 @@ def parsenonpainsled(fileformat,f2,summary):
except ZeroDivisionError:
summary = ''
-
# handle FIT
if (fileformat == 'fit'):
try:
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/middleware.py b/rowers/middleware.py
index d2fbd425..c1b968f2 100644
--- a/rowers/middleware.py
+++ b/rowers/middleware.py
@@ -124,7 +124,7 @@ class RowerPlanMiddleWare(object):
self.get_response = get_response
def __call__(self, request):
- if request.user.is_authenticated and request.user.rower.rowerplan != 'basic':
+ if request.user.is_authenticated and request.user.rower.rowerplan not in ['basic','freecoach']:
if request.user.rower.paymenttype == 'single':
if request.user.rower.planexpires < timezone.now().date():
messg = 'Your paid plan has expired. We have reset you to a free basic plan.'
diff --git a/rowers/models.py b/rowers/models.py
index f124ac36..7f50af9f 100644
--- a/rowers/models.py
+++ b/rowers/models.py
@@ -601,7 +601,8 @@ plans = (
('basic','basic'),
('pro','pro'),
('plan','plan'),
- ('coach','coach')
+ ('coach','coach'),
+ ('freecoach','freecoach'),
)
paymenttypes = (
@@ -913,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)
@@ -1074,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:
@@ -2575,6 +2576,13 @@ class Workout(models.Model):
choices=privacychoices)
rankingpiece = models.BooleanField(default=False,verbose_name='Ranking Piece')
duplicate = models.BooleanField(default=False,verbose_name='Duplicate Workout')
+
+ def save(self, *args, **kwargs):
+ user = self.user
+ if self.user.rowerplan == 'freecoach':
+ raise forms.ValidationError("Free Coach User cannot have any workouts")
+
+ super(Workout, self).save(*args, **kwargs)
def __str__(self):
@@ -3263,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/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/teams.py b/rowers/teams.py
index 3dc3f310..7be8d03a 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)Dear {{ first_name }},
+
+
+ Thank you for registering on rowsandall.com.
+ You can now login using the credentials you provided.
+ The first thing you should do is go to the
+ 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 user icon
+ in the upper right corner of the opening page.
+
+ 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.
+
+
+
+ Best Regards, the Rowsandall Team
+
+{% endblock %}
diff --git a/rowers/templates/freecoach_registration_form.html b/rowers/templates/freecoach_registration_form.html
new file mode 100644
index 00000000..0c1f6a5d
--- /dev/null
+++ b/rowers/templates/freecoach_registration_form.html
@@ -0,0 +1,79 @@
+{% extends "newbase.html" %}
+{% load staticfiles %}
+{% load rowerfilters %}
+{% block title %}New Coach Registration{% endblock title %}
+{% block meta %}
+
+{% endblock %}
+{% block main %}
+
+ 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.
+ {% elif rower and rower.rowerplan == 'freecoach' %}
+
+
+
{% elif rower and rower.rowerplan == 'pro' %}
@@ -298,6 +310,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.
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.
@@ -234,7 +237,7 @@
{% 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 %}