diff --git a/rowers/models.py b/rowers/models.py index 7f7d5300..b261d323 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -346,8 +346,9 @@ class Team(models.Model): def save(self, *args, **kwargs): manager = self.manager if manager.rower.rowerplan == 'basic': - raise ValidationError( - "Basic user cannot be team manager" + if manager.rower.protrialexpires < datetime.date.today() and manager.rower.plantrialexpires < datetime.date.today(): + raise ValidationError( + "Basic user cannot be team manager" ) if manager.rower.rowerplan in ['plan','pro']: @@ -695,8 +696,8 @@ class Rower(models.Model): planexpires = models.DateField(default=current_day) teamplanexpires = models.DateField(default=current_day) clubsize = models.IntegerField(default=0) - protrialexpires = models.DateField(blank=True,null=True) - plantrialexpires = models.DateField(blank=True,null=True) + protrialexpires = models.DateField(default=datetime.date(1970,1,1)) + plantrialexpires = models.DateField(default=datetime.date(1970,1,1)) # Privacy Data @@ -875,11 +876,12 @@ def check_teams_on_change(sender, **kwargs): action = kwargs.pop('action', None) pk_set = kwargs.pop('pk_set',None) if action == 'pre_add' and instance.rowerplan=='basic': - for id in pk_set: - 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" + if instance.protrialexpires < datetime.date.today() and instance.plantrialexpires < datetime.date.today(): + for id in pk_set: + 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" ) m2m_changed.connect(check_teams_on_change, sender=Rower.team.through) @@ -1905,9 +1907,10 @@ class PlannedSession(models.Model): manager = self.manager if self.sessiontype not in ['race','indoorrace']: if manager.rower.rowerplan in ['basic','pro']: - raise ValidationError( - "Basic user cannot be team manager" - ) + if manager.rower.plantrialexpires < timezone.now().date(): + raise ValidationError( + "You must be a Self-Coach user or higher to create a planned session" + ) # sort units diff --git a/rowers/teams.py b/rowers/teams.py index 6bdbf566..b6a7bfc2 100644 --- a/rowers/teams.py +++ b/rowers/teams.py @@ -61,6 +61,14 @@ def update_team(t,name,manager,private,notes,viewing): def create_team(name,manager,private='open',notes='',viewing='allmembers'): # needs some error testing + 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') + elif manager.rower.rowerplan != 'coach': + 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') + try: t = Team(name=name,manager=manager,notes=notes, private=private,viewing=viewing) @@ -77,17 +85,18 @@ def remove_team(id): send_team_delete_mail(t,r) return t.delete() -def set_teamplanexpires(rower): - ts = Team.objects.filter(rower=rower) +#def set_teamplanexpires(rower): +# ts = Team.objects.filter(rower=rower) - texp = datetime.date(timezone.now()) +# texp = datetime.date(timezone.now()) - for t in ts: - mr = Rower.objects.get(user=t.manager) - if mr.teamplanexpires > texp: - rower.teamplanexpires = mr.teamplanexpires +# for t in ts: +# print t.name +# mr = Rower.objects.get(user=t.manager) +# if mr.teamplanexpires > texp: +# rower.teamplanexpires = mr.teamplanexpires - t.save() +# t.save() return (1,'Updated rower team expiry') @@ -99,7 +108,7 @@ def add_member(id,rower): res = handle_add_workouts_team(ws,t) - set_teamplanexpires(rower) +# set_teamplanexpires(rower) return (id,'Member added') @@ -111,7 +120,7 @@ def remove_member(id,rower): res = handle_remove_workouts_team(ws,t) - set_teamplanexpires(rower) +# set_teamplanexpires(rower) return (id,'Member removed') def mgr_remove_member(id,manager,rower): diff --git a/rowers/templates/teams.html b/rowers/templates/teams.html index 7db3247f..2f5a4396 100644 --- a/rowers/templates/teams.html +++ b/rowers/templates/teams.html @@ -56,7 +56,6 @@ {% endif %} - {% if user.rower.rowerplan == 'coach' %}
  • Teams I manage

    Number of members: {{ clubsize }}

    @@ -85,7 +84,6 @@ {% endif %} New Team
  • - {% endif %} {% if invites or requests or myrequests or myinvites %}
  • Invitations and Requests

    diff --git a/rowers/tests/test_permissions.py b/rowers/tests/test_permissions.py index 8c6e2618..408cf6ff 100644 --- a/rowers/tests/test_permissions.py +++ b/rowers/tests/test_permissions.py @@ -112,7 +112,7 @@ class PermissionsBasicsTests(TestCase): ## Low level ## Coach can have any number of groups - def test_plan_groupmanager(self): + def test_coach_groupmanager(self): team1 = Team.objects.create( name = 'FirstTeam', notes = faker.text(), @@ -131,7 +131,7 @@ class PermissionsBasicsTests(TestCase): team3 = Team.objects.create( - name = 'SecondTeam', + name = 'ThirdTeam', notes = faker.text(), manager = self.ucoach, ) @@ -447,8 +447,6 @@ class PermissionsViewTests(TestCase): 'name': faker.word(), } - print 'posting to sessions/create' - form = PlannedSessionForm(post_data) self.assertTrue(form.is_valid()) @@ -545,7 +543,7 @@ class PermissionsViewTests(TestCase): ) response = self.c.get(url) - self.assertEqual(response.status_code,200) + self.assertEqual(response.status_code,403) ## Self coach can create one group ## Self coach cannot create more than one group @@ -629,7 +627,6 @@ class PermissionsViewTests(TestCase): 'name': faker.word(), } - print 'posting to sessions/create' form = PlannedSessionForm(post_data) self.assertTrue(form.is_valid()) @@ -641,7 +638,7 @@ class PermissionsViewTests(TestCase): ## Self Coach cannot edit on behalf of athlete def test_plan_edit_athlete_settings(self): - self.rbasic.team.add(self.teamplan) + self.rpro.team.add(self.teamplan) login = self.c.login(username=self.uplan2.username, password=self.uplan2password) self.assertTrue(login) @@ -649,12 +646,12 @@ class PermissionsViewTests(TestCase): url = reverse('rower_prefs_view',kwargs={'userid':self.ubasic.id}) response = self.c.get(url) - self.assertEqual(response.status_code,404) + self.assertEqual(response.status_code,403) ## Self Coach cannot run analytics on behalf of athlete @patch('rowers.dataprep.read_cols_df_sql', side_effect = mocked_read_df_cols_sql_multistats) def test_plan_edit_athlete_analysis(self,mocked_df): - self.rbasic.team.add(self.teamplan) + self.rpro.team.add(self.teamplan) login = self.c.login(username=self.uplan2.username, password=self.uplan2password) self.assertTrue(login) @@ -668,13 +665,13 @@ class PermissionsViewTests(TestCase): response = self.c.get(url) - self.assertEqual(response.status_code,404) + self.assertEqual(response.status_code,403) ## Self Coach cannot upload on behalf of athlete @patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db) def test_plan_edit_athlete_upload(self,mocked_sqlalchemy,mocked_getsmallrowdata_db): - self.rbasic.team.add(self.teamplan) + self.rpro.team.add(self.teamplan) login = self.c.login(username=self.uplan2.username, password=self.uplan2password) self.assertTrue(login) @@ -682,7 +679,7 @@ class PermissionsViewTests(TestCase): url = reverse('team_workout_upload_view') response = self.c.get(url) - self.assertEqual(response.status_code,404) + self.assertEqual(response.status_code,403) ## Pro can have one group @@ -763,7 +760,7 @@ class PermissionsViewTests(TestCase): ## Pro cannot edit on behalf of athlete def test_pro_edit_athlete_settings(self): - self.rbasic.team.add(self.teampro) + self.rpro.team.add(self.teampro) login = self.c.login(username=self.upro2.username, password=self.upro2password) self.assertTrue(login) @@ -771,12 +768,12 @@ class PermissionsViewTests(TestCase): url = reverse('rower_prefs_view',kwargs={'userid':self.ubasic.id}) response = self.c.get(url) - self.assertEqual(response.status_code,404) + self.assertEqual(response.status_code,403) ## Pro cannot run analytics on behalf of athlete @patch('rowers.dataprep.read_cols_df_sql', side_effect = mocked_read_df_cols_sql_multistats) def test_pro_edit_athlete_analysis(self,mocked_df): - self.rbasic.team.add(self.teampro) + self.rpro.team.add(self.teampro) login = self.c.login(username=self.upro2.username, password=self.upro2password) self.assertTrue(login) @@ -790,40 +787,45 @@ class PermissionsViewTests(TestCase): response = self.c.get(url) - self.assertEqual(response.status_code,404) + self.assertEqual(response.status_code,403) ## Self Coach cannot upload on behalf of athlete @patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db) def test_plan_edit_athlete_upload(self,mocked_sqlalchemy,mocked_getsmallrowdata_db): - self.rbasic.team.add(self.teamplan) + self.rpro.team.add(self.teamplan) login = self.c.login(username=self.uplan2.username, password=self.uplan2password) self.assertTrue(login) url = reverse('team_workout_upload_view') - response = self.c.get(url) - self.assertEqual(response.status_code,404) + response = self.c.get(url,follow=True) + self.assertEqual(response.status_code,200) + + expected_url = reverse('paidplans') + self.assertRedirects(response, + expected_url = expected_url, + status_code=302,target_status_code=200) ## Pro users can see team members' workout, but not edit - def test_coach_edit_athlete_workout(self,mocked_sqlalchemy,mocked_getsmallrowdata_db): - self.rbasic.team.add(self.proplan) - self.rpro2.team.add(self.proplan) + def test_pro_edit_athlete_workout(self): + self.rpro.team.add(self.teampro) + self.rpro2.team.add(self.teampro) login = self.c.login(username=self.upro2.username, password=self.upro2password) self.assertTrue(login) url = reverse('workout_edit_view', - kwargs={'id':encoder.encode_hex(self.ubasic_workouts[0].id)} + kwargs={'id':encoder.encode_hex(self.upro_workouts[0].id)} ) response = self.c.get(url) - self.assertEqual(response.status_code,404) + self.assertEqual(response.status_code,403) url = reverse('workout_view', - kwargs={'id':encoder.encode_hex(self.ubasic_workouts[0].id)} + kwargs={'id':encoder.encode_hex(self.upro_workouts[0].id)} ) response = self.c.get(url) @@ -831,8 +833,8 @@ class PermissionsViewTests(TestCase): ## Self Coach users can see team members' workout, but not edit - def test_coach_edit_athlete_workout(self,mocked_sqlalchemy,mocked_getsmallrowdata_db): - self.rbasic.team.add(self.teamplan) + def test_plan_edit_athlete_workout(self): + self.rpro.team.add(self.teamplan) login = self.c.login(username=self.uplan2.username, password=self.uplan2password) self.assertTrue(login) @@ -842,7 +844,7 @@ class PermissionsViewTests(TestCase): ) response = self.c.get(url) - self.assertEqual(response.status_code,200) + self.assertEqual(response.status_code,403) url = reverse('workout_view', kwargs={'id':encoder.encode_hex(self.ubasic_workouts[0].id)} @@ -853,9 +855,9 @@ class PermissionsViewTests(TestCase): ## Basic users can see team members' workout, but not edit - def test_basic_edit_athlete_workout(self,mocked_sqlalchemy,mocked_getsmallrowdata_db): - self.rbasic.team.add(self.teamplan) - self.rplan2.team.add(self.teamplan) + def test_basic_edit_athlete_workout(self): + self.rbasic.team.add(self.teamcoach) + self.rplan2.team.add(self.teamcoach) login = self.c.login(username=self.ubasic.username, password=self.ubasicpassword) self.assertTrue(login) @@ -865,7 +867,7 @@ class PermissionsViewTests(TestCase): ) response = self.c.get(url) - self.assertEqual(response.status_code,404) + self.assertEqual(response.status_code,403) url = reverse('workout_view', kwargs={'id':encoder.encode_hex(self.uplan2_workouts[0].id)} @@ -902,7 +904,7 @@ class PermissionsViewTests(TestCase): url = reverse('team_requestmembership_view', kwargs = { 'teamid':self.teampro.id, - 'userid':self.upro.id + 'userid':self.ubasic.id }) response = self.c.get(url,follow=True) @@ -913,3 +915,8 @@ class PermissionsViewTests(TestCase): expected_url = expected_url, status_code=302,target_status_code=200) +# Race related + +## Basic and Pro users can create races + +## Basic users can subscribe to any race diff --git a/rowers/tests/testdata/testdata.csv.gz b/rowers/tests/testdata/testdata.csv.gz index 3c6cc8df..a3190522 100644 Binary files a/rowers/tests/testdata/testdata.csv.gz and b/rowers/tests/testdata/testdata.csv.gz differ diff --git a/rowers/views/statements.py b/rowers/views/statements.py index 69a5c1a2..7dff9589 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -928,6 +928,23 @@ def iscoachmember(user): return result +def cancreateteam(user): + if user.is_anonymous(): + return False + + try: + r = Rower.objects.get(user=user) + except Rower.DoesNotExist: + r = Rower(user=user) + r.save() + + if user.is_authenticated() and (r.rowerplan=='coach'): + return True + elif user.is_athenticated() and r.rowerplan in ['plan','pro']: + ts = Team.objects.filter(manager=user) + if len(otherteams) >= 1: + return False + # Check if a user can create planned sessions def hasplannedsessions(user): if not user.is_anonymous(): diff --git a/rowers/views/teamviews.py b/rowers/views/teamviews.py index a248c24b..54c8736e 100644 --- a/rowers/views/teamviews.py +++ b/rowers/views/teamviews.py @@ -254,6 +254,15 @@ def team_requestmembership_view(request,teamid,userid): t = Team.objects.get(id=teamid) except Team.DoesNotExist: raise Http404("Team doesn't exist") + + r = getrequestrower(request,userid=userid) + + if t.manager.rower.rowerplan in ['plan','pro'] and r.rowerplan == 'basic': + messages.error(request, + "You have to be on a paid plan (Pro or higher) to join this team. As a basic user you can only join teams managed by users on the Coach plan.") + + url = reverse('paidplans') + return HttpResponseRedirect(url) res,text = teams.create_request(t,userid) if res: @@ -395,8 +404,19 @@ def team_edit_view(request,id=0): 'team':t, }) -@user_passes_test(iscoachmember,login_url="/rowers/paidplans",redirect_field_name=None) +#@user_passes_test(cancreateteam,login_url="/rowers/paidplans",redirect_field_name=None) +@login_required() def team_create_view(request): + r = getrequestrower(request) + + if r.rowerplan == 'basic': + if r.protrialexpires < timezone.now().date() and r.plantrialexpires < timezone.now().date(): + messages.error(request,"You must upgrade to Pro or higher to create teams/training groups") + url = reverse('paidplans') + return HttpResponseRedirect(url) + + + if request.method == 'POST': teamcreateform = TeamForm(request.POST) if teamcreateform.is_valid(): @@ -408,6 +428,12 @@ def team_create_view(request): viewing = cd['viewing'] res,message=teams.create_team(name,manager,private,notes, viewing) + + if not res: + messages.error(request,message) + url = reverse('paidplans') + return HttpResponseRedirect(url) + url = reverse('rower_teams_view') response = HttpResponseRedirect(url) return response @@ -438,7 +464,7 @@ def team_create_view(request): 'breadcrumbs':breadcrumbs, }) -@user_passes_test(iscoachmember,login_url="/rowers/paidplans",redirect_field_name=None) +@login_required() def team_deleteconfirm_view(request,id): r = getrower(request.user) try: @@ -474,7 +500,7 @@ def team_deleteconfirm_view(request,id): 'active':'nav-teams', }) -@user_passes_test(iscoachmember,login_url="/rowers/paidplans",redirect_field_name=None) +@login_required() def team_delete_view(request,id): r = getrower(request.user) try: