Private
Public Access
1
0

Merge branch 'feature/freecoach' into develop

This commit is contained in:
Sander Roosendaal
2019-05-18 09:55:07 +02:00
25 changed files with 573 additions and 62 deletions
+13 -9
View File
@@ -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
+1 -1
View File
@@ -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
+6 -3
View File
@@ -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:
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:
+3 -3
View File
@@ -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'
+1 -1
View File
@@ -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.'
+12 -4
View File
@@ -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:
@@ -2576,6 +2577,13 @@ class Workout(models.Model):
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):
date = self.date
@@ -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')
+2 -2
View File
@@ -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()
+6 -6
View File
@@ -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)<coach.clubsize:
if 'coach' in coach.rowerplan and get_coach_club_size(coach)<coach.clubsize:
rekwest = CoachOffer(coach=coach,user=user,code=code)
rekwest.save()
@@ -597,7 +597,7 @@ def process_request_code(manager,code):
if rekwest.team.manager != manager:
return (0,'You are not the manager of this team')
result = add_member(t.id,r)
result,comment = add_member(t.id,r)
if not result:
return (result,"The member couldn't be added")
@@ -622,7 +622,7 @@ def process_invite_code(user,code):
return (0,'The invitation has expired')
t = invitation.team
result = add_member(t.id,r)
result, comment = add_member(t.id,r)
if not result:
return (result,"The member couldn't be added")
+42
View File
@@ -0,0 +1,42 @@
{% extends "emailbase.html" %}
{% block body %}
<p>Dear <strong>{{ first_name }}</strong>,</p>
<p>
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
<a href="https://rowsandall.com/rowers/me/edit/">Settings page</a>
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.
</p>
<p>
As a new member on the free coach plan, you can start with your first training
group and invite athletes to join it.
For more information about training groups and managing athletes, please read
our explanation at
<a href="https://analytics.rowsandall.com/2019/02/24/managing-a-rowing-training-group-coaching-and-remote-coaching-on-rowsandall-com/">https://analytics.rowsandall.com/2019/02/24/managing-a-rowing-training-group-coaching-and-remote-coaching-on-rowsandall-com/</a>.
</p>
<p>
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:
<a href="https://rowsandall.com/rowers/paidplans">https://rowsandall.com/rowers/paidplans</a>
</p>
<p>
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.
</p>
<p>
Best Regards, the Rowsandall Team
</p>
{% endblock %}
@@ -0,0 +1,79 @@
{% extends "newbase.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}New Coach Registration{% endblock title %}
{% block meta %}
{% endblock %}
{% block main %}
<ul class="main-content">
<li class="grid_4">
<h1>New Coach Registration (free Coach Plan)</h1>
</li>
<li class="rounder grid_2">
<a href="/rowers/coachregister">
<h1>Register as a coach (free)</h1>
<p>Click here and fill out the form to start with a free Coach plan. This allows you
to manage up to 10 athletes (who have to be on the Pro plan). You cannot upload your own
workouts.
</p>
<p>
From this free plan, you can upgrade to any of the paid plans, including coach plans for larger
athlete groups or rower plans for managing your own training plan and workouts.
</p>
</a>
</li>
<li class="rounder grid_2">
<a href="/rowers/register">
<h1>Register as an athlete (free)</h1>
<p>Click here and fill out the form to start with a free Athlete plan. This allows you
to store and analyze your own workouts.
</p>
<p>
From this free plan, you can upgrade to any of the paid plans, including
coach plans for managing larger athlete groups or rower plans for expanded
analytics.
</p>
</a>
</li>
<li class="grid_2">
<div id="registrationform">
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
</p>
{% endif %}
<form enctype="multipart/form-data" action="" method="post">
{% csrf_token %}
<table width=100%>
{{ form.as_table }}
</table>
<a href="/rowers/legal/">Terms of Service</a>
<input type="hidden" name="next" value="{{ next }}"/>
<input type="submit" value="Submit">
</form>
</div>
</li>
<li class="grid_2">
<p> To use rowsandall, you need to register and agree with the Terms of Service. </p>
<p> Registration is free. </p>
<p>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.
</p>
<p>Also, we are restricting access to the site to 16 years and older
because of EU data protection regulations.</p>
</li>
</ul>
{% endblock main %}
{% block sidebar %}
{% include 'menu_help.html' %}
{% endblock %}
+2 -2
View File
@@ -120,8 +120,8 @@
<thead>
<tr>
<th> Duration</th>
<th> Power (upper)</th>
<th> Power</th>
<th> Power Estimate 1</th>
<th> Power Estimate 2</th>
<tr>
</thead>
<tbody>
+20
View File
@@ -178,6 +178,8 @@
<td>
{% if rower.rowerplan == 'coach' %}
<h3>COACH ({{ rower.paymenttype }})</h3>
{% elif rower.rowerplan == 'freecoach' %}
<h3>Free COACH</h3>
{% else %}
&nbsp;
{% endif %}
@@ -248,6 +250,16 @@
{% endif %}
</button>
</td>
{% elif rower and rower.rowerplan == 'freecoach' %}
<td colspan="3">
<button style="width:100%">
{% if user|existing_customer %}
<a href="/rowers/upgrade/">UPGRADE NOW</a>
{% else %}
<a href="/rowers/billing/">BUY NOW</a>
{% endif %}
</button>
</td>
{% elif rower and rower.rowerplan == 'pro' %}
<td>&nbsp;</td>
<td colspan="2">
@@ -298,6 +310,14 @@
can purchase upgrades to "Pro" and "Self-Coach" plans.
</p>
<p>
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.
</p>
<p>If you would like to find a coach who helps you plan your training
through rowsandall.com, contact me throught the contact form.</p>
+2 -2
View File
@@ -10,8 +10,8 @@
<a href="https://rowsandall.com/rowers/me/edit/">Settings page</a>
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.
</p>
<p>
+29 -1
View File
@@ -6,8 +6,36 @@
{% endblock %}
{% block main %}
<h1>New User Registration</h1>
<ul class="main-content">
<li class="grid_4">
<h1>New User Registration</h1>
</li>
<li class="rounder grid_2">
<a href="/rowers/coachregister">
<h1>Register as a coach (free)</h1>
<p>Click here and fill out the form to start with a free Coach plan. This allows you
to manage up to 10 athletes (who have to be on the Pro plan). You cannot upload your own
workouts.
</p>
<p>
From this free plan, you can upgrade to any of the paid plans, including coach plans for larger
athlete groups or rower plans for managing your own training plan and workouts.
</p>
</a>
</li>
<li class="rounder grid_2">
<a href="/rowers/register">
<h1>Register as an athlete (free)</h1>
<p>Click here and fill out the form to start with a free Athlete plan. This allows you
to store and analyze your own workouts.
</p>
<p>
From this free plan, you can upgrade to any of the paid plans, including
coach plans for managing larger athlete groups or rower plans for expanded
analytics.
</p>
</a>
</li>
<li class="grid_2">
<div id="registrationform">
+27 -4
View File
@@ -11,7 +11,7 @@ from rowers.utils import calculate_age
from rowers.models import (
course_length,WorkoutComment,
TrainingMacroCycle,TrainingMesoCycle, TrainingMicroCycle,
Rower,Workout,SiteAnnouncement
Rower,Workout,SiteAnnouncement, TeamInvite, TeamRequest, CoachOffer,CoachRequest
)
from rowers.plannedsessions import (
race_can_register, race_can_submit,race_rower_status
@@ -51,6 +51,10 @@ def nogoals(user):
date__gte=datetime.date.today())
return len(targets)==0
@register.filter
def notfree(rower):
return rower.rowerplan not in ['basic','freecoach']
def strfdelta(tdelta):
minutes,seconds = divmod(tdelta.seconds,60)
tenths = int(tdelta.microseconds/1e5)
@@ -327,7 +331,7 @@ def is_promember(user):
@register.filter
def is_manager(user):
r = Rower.objects.get(user=user)
return r.rowerplan == 'coach'
return 'coach' in r.rowerplan
from rowers.views import hasplannedsessions
@register.filter
@@ -385,13 +389,29 @@ def team_members(user):
team__in=teams
).distinct().order_by(
"user__last_name","user__first_name"
)
).exclude(rowerplan='freecoach')
return [rower.user for rower in members]
except TypeError:
return []
return []
@register.filter
def openactions(user):
myteams = Team.objects.filter(manager=user)
invites = TeamInvite.objects.filter(user=user).count()
requests = TeamRequest.objects.filter(user=user).count()
myrequests = TeamRequest.objects.filter(team__in=myteams).count()
myinvites = TeamInvite.objects.filter(team__in=myteams).count()
mycoachoffers = CoachOffer.objects.filter(coach=user.rower).count()
coachoffers = CoachOffer.objects.filter(user=user).count()
mycoachrequests = CoachRequest.objects.filter(user=user).count()
coachrequests = CoachRequest.objects.filter(coach=user.rower).count()
return invites+requests+myrequests+myinvites+mycoachoffers+coachoffers+mycoachrequests+coachrequests
@register.filter
def team_rowers(user):
try:
@@ -401,7 +421,7 @@ def team_rowers(user):
teams = Team.objects.filter(manager=user)
members = Rower.objects.filter(team__in=teams).distinct().order_by(
"user__last_name","user__last_name"
)
).exclude(rowerplan='freecoach')
return members
except TypeError:
return []
@@ -412,7 +432,10 @@ from rowers.teams import coach_getcoachees
@register.filter
def coach_rowers(user):
if user.rower.rowerplan != 'freecoach':
thelist = [user.rower]+[c for c in coach_getcoachees(user.rower)]
else:
thelist = [c for c in coach_getcoachees(user.rower)]
return thelist
+177
View File
@@ -14,6 +14,183 @@ from django.db import transaction
import rowers.teams as teams
import rowers.plannedsessions as plannedsessions
@override_settings(TESTING=True)
class PermissionsFreeCoach(TestCase):
def setUp(self):
self.c = Client()
## Users - Pro, Basic, Free Coach & Self Coach
self.ufreecoach = UserFactory(username='coachuser')
self.rfreecoach = Rower.objects.create(user=self.ufreecoach,
birthdate=faker.profile()['birthdate'],
gdproptin=True,gdproptindate=timezone.now(),
rowerplan='freecoach',clubsize=100)
self.coachinggroup = CoachingGroup.objects.create()
self.rfreecoach.mycoachgroup = self.coachinggroup
self.rfreecoach.save()
self.factory = RequestFactory()
self.password = faker.word()
self.ufreecoach.set_password(self.password)
self.ufreecoach.save()
self.uplan = UserFactory(username='planuser')
self.rplan = Rower.objects.create(user=self.uplan,
birthdate=faker.profile()['birthdate'],
gdproptin=True,gdproptindate=timezone.now(),
rowerplan='plan')
self.uplan_workouts = WorkoutFactory.create_batch(5, user=self.rplan)
self.factory = RequestFactory()
self.password = faker.word()
self.uplan.set_password(self.password)
self.uplan.save()
self.upro = UserFactory(username='prouser')
self.rpro = Rower.objects.create(user=self.upro,
birthdate=faker.profile()['birthdate'],
gdproptin=True,gdproptindate=timezone.now(),
rowerplan='pro')
self.upro_workouts = WorkoutFactory.create_batch(5, user=self.rpro)
self.factory = RequestFactory()
self.password = faker.word()
self.upro.set_password(self.password)
self.upro.save()
self.uplan2 = UserFactory(username='planuser2')
self.rplan2 = Rower.objects.create(user=self.uplan2,
birthdate=faker.profile()['birthdate'],
gdproptin=True,gdproptindate=timezone.now(),
rowerplan='plan')
self.uplan2_workouts = WorkoutFactory.create_batch(5, user=self.rplan2)
self.factory = RequestFactory()
self.password = faker.word()
self.uplan2.set_password(self.password)
self.uplan2.save()
self.upro2 = UserFactory(username='prouser2')
self.rpro2 = Rower.objects.create(user=self.upro2,
birthdate=faker.profile()['birthdate'],
gdproptin=True,gdproptindate=timezone.now(),
rowerplan='pro')
self.upro2_workouts = WorkoutFactory.create_batch(5, user=self.rpro2)
self.factory = RequestFactory()
self.password = faker.word()
self.upro2.set_password(self.password)
self.upro2.save()
self.ubasic = UserFactory(username='basicuser')
self.rbasic = Rower.objects.create(user=self.ubasic,
birthdate=faker.profile()['birthdate'],
gdproptin=True,gdproptindate=timezone.now(),
rowerplan='basic')
self.ubasic_workouts = WorkoutFactory.create_batch(5, user=self.rbasic)
self.factory = RequestFactory()
self.ubasicpassword = faker.word()
self.ubasic.set_password(self.ubasicpassword)
self.ubasic.save()
## TeamFreeCoach
self.teamfreecoach = Team.objects.create(
name=faker.word()+'3',
notes=faker.text(),
manager=self.ufreecoach)
## Coach can have any number of groups
def test_coach_groupmanager(self):
team1 = Team.objects.create(
name = 'FirstTeam',
notes = faker.text(),
manager = self.ufreecoach,
)
self.assertEqual(team1.manager,self.ufreecoach)
team2 = Team.objects.create(
name = 'SecondTeam',
notes = faker.text(),
manager = self.ufreecoach,
)
self.assertEqual(team2.manager,self.ufreecoach)
team3 = Team.objects.create(
name = 'ThirdTeam',
notes = faker.text(),
manager = self.ufreecoach,
)
self.assertEqual(team3.manager,self.ufreecoach)
## Free coach can create more than one group
def test_plan_groupmanager(self):
team1 = Team.objects.create(
name = 'FirstTeam',
notes = faker.text(),
manager = self.ufreecoach,
)
self.assertEqual(team1.manager,self.ufreecoach)
team2 = Team.objects.create(
name = 'SecondTeam',
notes = faker.text(),
manager = self.ufreecoach,
)
self.assertEqual(team2.manager,self.ufreecoach)
## Free Coach can create planned sessions and team planned sessions
## Self Coach and higher can create planned sessions and team planned sessions
def test_plan_create_session(self):
ps = PlannedSession.objects.create(
manager=self.ufreecoach,
name=faker.word(),
comment=faker.text()
)
self.assertEqual(ps.manager,self.ufreecoach)
def test_coach_create_session(self):
ps = PlannedSession.objects.create(
manager=self.ufreecoach,
name=faker.word(),
comment=faker.text()
)
self.assertEqual(ps.manager,self.ufreecoach)
## Basic cannot join groups led by Free Coach
def test_add_basic_pro_or_plan(self):
with transaction.atomic():
with self.assertRaises(ValidationError):
self.rbasic.team.add(self.teamfreecoach)
## Free Coach user cannot have workouts
def test_add_workout_freecoach(self):
result = get_random_file(filename='rowers/tests/testdata/erg1.csv')
with transaction.atomic():
with self.assertRaises(ValidationError):
self.werg1 = WorkoutFactory(user=self.rfreecoach,
csvfilename=result['filename'],
starttime=result['starttime'],
startdatetime=result['startdatetime'],
duration=result['duration'],
distance=result['totaldist'],
workouttype = 'rower',
)
@override_settings(TESTING=True)
class PermissionsBasicsTests(TestCase):
Binary file not shown.
+1
View File
@@ -533,6 +533,7 @@ urlpatterns = [
# re_path(r'^paypaltest', TemplateView.as_view(template_name='paypaltest.html'),name='paypaltest'),
re_path(r'^legal', TemplateView.as_view(template_name='legal.html'),name='legal'),
re_path(r'^register/$',views.rower_register_view,name='rower_register_view'),
re_path(r'^coachregister/$',views.freecoach_register_view,name='freecoach_register_view'),
re_path(r'^register/thankyou/$', TemplateView.as_view(template_name='registerthankyou.html'), name='registerthankyou'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/workflow/$',views.workout_workflow_view,
name='workout_workflow_view'),
+80
View File
@@ -566,6 +566,86 @@ def rower_register_view(request):
{'form':form,
'next':nextpage,})
# User registration
def freecoach_register_view(request):
nextpage = request.GET.get('next','/rowers/me/teams/')
if nextpage == '':
nextpage = '/rowers/me/teams/'
if request.method == 'POST':
#form = RegistrationFormUniqueEmail(request.POST)
form = RegistrationFormSex(request.POST)
if form.is_valid():
first_name = form.cleaned_data['first_name']
last_name = form.cleaned_data['last_name']
email = form.cleaned_data['email']
password = form.cleaned_data['password1']
username = form.cleaned_data['username']
sex = form.cleaned_data['sex']
birthdate = form.cleaned_data['birthdate']
weightcategory = form.cleaned_data['weightcategory']
adaptiveclass = form.cleaned_data['adaptiveclass']
nextpage = request.POST['next']
theuser = User.objects.create_user(username,password=password)
theuser.first_name = first_name
theuser.last_name = last_name
theuser.email = email
theuser.save()
birthdate = birthdate.replace(tzinfo=None)
therower = Rower(user=theuser,sex=sex,birthdate=birthdate,
weightcategory=weightcategory,
adaptiveclass=adaptiveclass,
rowerplan='freecoach',clubsize=10)
therower.save()
# create default favorite charts
add_defaultfavorites(therower)
# Create and send email
fullemail = first_name + " " + last_name + " " + "<" + email + ">"
subject = "Thank you for registering on rowsandall.com"
from_address = 'Sander Roosendaal <info@rowsandall.com>'
d = {'first_name':theuser.first_name}
send_template_email(from_address,[fullemail],
subject,'coachregisteremail.html',d)
subject2 = "New Free Coach"
message2 = "New Free Coach registered.\n"
message2 += fullemail + "\n"
message2 += "User name: "+username
send_mail(subject2, message2,
'Rowsandall Server <info@rowsandall.com>',
['roosendaalsander@gmail.com'])
theuser = authenticate(username=username,password=password)
login(request,theuser)
return HttpResponseRedirect(nextpage)
else:
return render(request,
"freecoach_registration_form.html",
{'form':form,
'next':nextpage,})
else:
form = RegistrationFormSex()
form.fields.pop('sex')
form.fields.pop('weightcategory')
form.fields.pop('adaptiveclass')
return render(request,
"freecoach_registration_form.html",
{'form':form,
'next':nextpage,})
@login_required()
def transactions_view(request):
if not request.user.is_staff:
+18 -9
View File
@@ -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)
@@ -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)
@@ -912,12 +912,16 @@ def plannedsessions_coach_view(request,
rteams = therower.team.filter(viewing='allmembers')
sps = get_sessions(therower,startdate=startdate,enddate=enddate)
if therower.rowerplan != 'freecoach':
rowers = [therower]
for ps in sps:
if request.user.rower.rowerplan == 'coach':
rowers += ps.rower.all()
else:
rowers += ps.rower.filter(team__in=rteams)
rowers = []
for ps in sps:
if 'coach' in request.user.rower.rowerplan:
rowers += ps.rower.all().exclude(rowerplan='freecoach')
else:
rowers += ps.rower.filter(team__in=rteams).exclude(rowerplan='freecoach')
rowers = list(set(rowers))
@@ -1569,7 +1573,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 +1812,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 +1825,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 +2598,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
+3 -3
View File
@@ -1009,7 +1009,7 @@ def iscoachmember(user):
r = Rower(user=user)
r.save()
result = user.is_authenticated and (r.rowerplan=='coach')
result = user.is_authenticated and ('coach' in r.rowerplan)
else:
result = False
@@ -1025,7 +1025,7 @@ def cancreateteam(user):
r = Rower(user=user)
r.save()
if user.is_authenticated and (r.rowerplan=='coach'):
if user.is_authenticated and ('coach' in r.rowerplan):
return True
elif user.is_athenticated() and r.rowerplan in ['plan','pro']:
ts = Team.objects.filter(manager=user)
@@ -1041,7 +1041,7 @@ def hasplannedsessions(user):
r = Rower(user=user)
r.save()
result = user.is_authenticated and (r.rowerplan=='coach' or r.rowerplan=='plan')
result = user.is_authenticated and (r.rowerplan=='coach' or r.rowerplan=='freecoach' or r.rowerplan=='plan')
if not result and r.plantrialexpires:
result = user.is_authenticated and r.plantrialexpires >= datetime.date.today()
else:
+13 -3
View File
@@ -200,10 +200,10 @@ def rower_teams_view(request,message='',successmessage=''):
coaches = teams.rower_get_coaches(r)
potentialcoaches = [t.manager for t in memberteams if t.manager not in coaches and t.manager.rower.rowerplan == 'coach']
potentialcoaches = [t.manager for t in memberteams if t.manager not in coaches and 'coach' in t.manager.rower.rowerplan]
offercoaches = [
r.user for r in Rower.objects.filter(
offercoaching=True,rowerplan='coach').exclude(user=request.user)
offercoaching=True,rowerplan__in='coach').exclude(user=request.user)
]
potentialcoaches = list(set(potentialcoaches+offercoaches))
potentialcoaches = [c for c in potentialcoaches if c.rower not in invitedcoaches+coaches]
@@ -218,6 +218,16 @@ def rower_teams_view(request,message='',successmessage=''):
user__in=invitedathletes).exclude(
user=request.user
).exclude(coachinggroups__in=[request.user.rower.mycoachgroup])
elif request.user.rower.rowerplan == 'freecoach':
potentialathletes = Rower.objects.filter(
team__in=myteams).exclude(
user__in=invitedathletes).exclude(
user=request.user
).exclude(
coachinggroups__in=[request.user.rower.mycoachgroup]
).exclude(
rowerplan__in=['basic','freecoach']
)
else:
potentialathletes = []
@@ -437,7 +447,7 @@ def request_coaching_view(request,coachid):
coach = User.objects.get(id=coachid).rower
if coach.rowerplan == 'coach':
if 'coach' in coach.rowerplan:
res,text = teams.create_coaching_request(coach,request.user)
if res:
messages.info(request,text)
+23 -2
View File
@@ -3842,6 +3842,9 @@ def workout_upload_view(request,
is_ajax = True
r = getrower(request.user)
if r.rowerplan == 'freecoach':
url = reverse('team_workout_upload_view')
return HttpResponseRedirect(url)
breadcrumbs = [
{
@@ -4295,7 +4298,14 @@ def team_workout_upload_view(request,message="",
rowerform = TeamInviteForm(request.POST)
rowerform.fields.pop('email')
rowers = Rower.objects.filter(coachinggroups__in=[r.mycoachgroup]).distinct()
rowers = Rower.objects.filter(
coachinggroups__in=[r.mycoachgroup]
).exclude(
rowerplan='freecoach'
).distinct()
if r.rowerplan == 'freecoach':
rowers = rowers.exclude(rowerplan='basic')
rowerform.fields['user'].queryset = User.objects.filter(rower__in=rowers).distinct()
if form.is_valid():
f = request.FILES['file']
@@ -4420,7 +4430,18 @@ def team_workout_upload_view(request,message="",
optionsform = TeamUploadOptionsForm(initial=uploadoptions)
rowerform = TeamInviteForm()
rowerform.fields.pop('email')
rowerform.fields['user'].queryset = User.objects.filter(rower__isnull=False,rower__team__in=myteams).distinct()
rowers = Rower.objects.filter(
coachinggroups__in=[r.mycoachgroup]
).exclude(
rowerplan='freecoach'
).distinct()
if r.rowerplan == 'freecoach':
rowers = rowers.exclude(rowerplan='basic')
rowerform.fields['user'].queryset = User.objects.filter(rower__in=rowers).distinct()
return render(request, 'team_document_form.html',
{'form':form,
# 'teams':get_my_teams(request.user),
+5 -2
View File
@@ -161,7 +161,7 @@
<a href="/rowers/me/preferences/" title="Profile">
{% if user.rower.rowerplan == 'pro' %}
<i class="fas fa-user-ninja "></i>
{% elif user.rower.rowerplan == 'coach' %}
{% elif 'coach' in user.rower.rowerplan %}
<i class="fas fa-users "></i>
{% elif user.rower.rowerplan == 'plan' %}
<i class="fas fa-user-chart "></i>
@@ -220,6 +220,9 @@
<li id="nav-teams">
<a href="/rowers/me/teams/">
<i class="fas fa-bullhorn"></i>&nbsp;Groups
{% if user.is_authenticated and user|openactions %}
({{ user|openactions }})
{% endif %}
</a>
</li>
</ul>
@@ -234,7 +237,7 @@
</p>
</li>
{% 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 %}
<li class="grid_4">
+4 -1
View File
@@ -169,7 +169,7 @@
<a href="/rowers/me/preferences/" title="Profile">
{% if user.rower.rowerplan == 'pro' %}
<i class="fas fa-user-ninja "></i>
{% elif user.rower.rowerplan == 'coach' %}
{% elif 'coach' in user.rower.rowerplan %}
<i class="fas fa-users "></i>
{% elif user.rower.rowerplan == 'plan' %}
<i class="fas fa-user-tie "></i>
@@ -229,6 +229,9 @@
<li id="nav-teams">
<a href="/rowers/me/teams/">
<i class="fas fa-bullhorn"></i>&nbsp;Groups
{% if user.is_authenticated and user|openactions %}
({{ user|openactions }})
{% endif %}
</a>
</li>
</ul>