From 6afde11b7c5c13477bf800b29d17ac72a76d1e13 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Sat, 16 Feb 2019 10:31:47 +0100 Subject: [PATCH] athlete edit permissions in place --- rowers/braintreestuff.py | 5 ++ rowers/models.py | 20 ++++---- rowers/teams.py | 7 +++ rowers/templatetags/rowerfilters.py | 9 ++++ rowers/tests/test_permissions.py | 80 ++++++++++++++++++++++++++++- rowers/utils.py | 6 +++ rowers/views/teamviews.py | 1 + rowers/views/workoutviews.py | 6 ++- 8 files changed, 121 insertions(+), 13 deletions(-) diff --git a/rowers/braintreestuff.py b/rowers/braintreestuff.py index bfe978bc..1dd572a0 100644 --- a/rowers/braintreestuff.py +++ b/rowers/braintreestuff.py @@ -167,6 +167,11 @@ def update_subscription(rower,data,method='up'): l = rower.user.last_name, ) + if rower.paidplan != 'coach': + coachees = Rower.objects.filter(coach__in=[rower]).distinct() + for coachee in coachees: + coachee.coaches.remove(rower) + if method == 'up': transactions = result.subscription.transactions diff --git a/rowers/models.py b/rowers/models.py index b261d323..ca7f3869 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -340,6 +340,7 @@ class Team(models.Model): viewing = models.CharField(max_length=30,choices=viewchoices,default='allmembers',verbose_name='Sharing Behavior') + def __unicode__(self): return self.name @@ -831,6 +832,7 @@ class Rower(models.Model): # Friends/Team friends = models.ManyToManyField("self",blank=True) + coaches = models.ManyToManyField("self",blank=True) privacy = models.CharField(default='visible',max_length=30, choices=privacychoices) @@ -986,12 +988,12 @@ def checkworkoutuser(user,workout): return False try: r = Rower.objects.get(user=user) - teams = workout.team.all() + coaches = user.rower.coaches.filter(rowerplan='coach') if workout.user == r: return True - elif teams: - for team in teams: - if user == team.manager and workout.privacy == 'visible': + elif coaches: + for coach in coaches: + if user.rower == coach and workout.privacy == 'visible': return True else: return False @@ -1003,17 +1005,17 @@ def checkworkoutuser(user,workout): def checkaccessuser(user,rower): try: r = Rower.objects.get(user=user) - teams = Team.objects.filter(manager=user) if rower == r: return True - elif teams: - for team in teams: - if team in rower.team.all(): + coaches = rower.coaches.filter(rowerplan='coach') + if coaches: + for coach in coaches: + if user.rower == coach: return True else: return False except Rower.DoesNotExist: - return False + return False timezones = ( (x,x) for x in pytz.common_timezones diff --git a/rowers/teams.py b/rowers/teams.py index a8fdad1a..e6604f98 100644 --- a/rowers/teams.py +++ b/rowers/teams.py @@ -102,6 +102,11 @@ def remove_team(id): return (1,'Updated rower team expiry') +def add_coach(manager,rower): + rower.coaches.add(m) + + return (1,"Added Coach") + def add_member(id,rower): t= Team.objects.get(id=id) try: @@ -414,6 +419,7 @@ def process_request_code(manager,code): result = add_member(t.id,r) if not result: return (result,"The member couldn't be added") + send_request_accept_email(rekwest) @@ -439,6 +445,7 @@ def process_invite_code(user,code): if not result: return (result,"The member couldn't be added") + send_invite_accept_email(invitation) invitation.delete() return result diff --git a/rowers/templatetags/rowerfilters.py b/rowers/templatetags/rowerfilters.py index 11758611..be4413fb 100644 --- a/rowers/templatetags/rowerfilters.py +++ b/rowers/templatetags/rowerfilters.py @@ -376,6 +376,15 @@ def team_rowers(user): return [] +@register.filter +def coach_rowers(user): + try: + members = Rower.objects.filter(coaches__in=user.rower).distinct().order_by( + "user__last_name","user__last_name" + ) + return members + except TypeError: + return [] @register.filter def verbosetimeperiod(timeperiod): diff --git a/rowers/tests/test_permissions.py b/rowers/tests/test_permissions.py index 408cf6ff..7b30f9e9 100644 --- a/rowers/tests/test_permissions.py +++ b/rowers/tests/test_permissions.py @@ -455,9 +455,10 @@ class PermissionsViewTests(TestCase): - ## Coach can edit on behalf of athlete + ## Coach can edit on behalf of athlete if permitted def test_coach_edit_athlete_settings(self): self.rbasic.team.add(self.teamcoach) + self.rbasic.coaches.add(self.rcoach) login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) self.assertTrue(login) @@ -467,10 +468,22 @@ class PermissionsViewTests(TestCase): response = self.c.get(url) self.assertEqual(response.status_code,200) + def test_coach_edit_athlete_settings_not(self): + self.rbasic.team.add(self.teamcoach) + + login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) + self.assertTrue(login) + + url = reverse('rower_prefs_view',kwargs={'userid':self.ubasic.id}) + + response = self.c.get(url) + self.assertEqual(response.status_code,403) + ## Coach can run analytics for athlete @patch('rowers.dataprep.read_cols_df_sql', side_effect = mocked_read_df_cols_sql_multistats) def test_coach_edit_athlete_analysis(self,mocked_df): self.rbasic.team.add(self.teamcoach) + self.rbasic.coaches.add(self.rcoach) login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) self.assertTrue(login) @@ -486,12 +499,31 @@ class PermissionsViewTests(TestCase): self.assertEqual(response.status_code,200) + @patch('rowers.dataprep.read_cols_df_sql', side_effect = mocked_read_df_cols_sql_multistats) + def test_coach_edit_athlete_analysis_not(self,mocked_df): + self.rbasic.team.add(self.teamcoach) - ## Coach can upload on behalf of athlete + login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) + self.assertTrue(login) + + + url = reverse('cumstats', + kwargs={ + 'theuser':self.ubasic.id, + } + ) + + response = self.c.get(url) + + self.assertEqual(response.status_code,403) + + + ## Coach can upload on behalf of athlete - if team allows @patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db) def test_coach_edit_athlete_upload(self,mocked_sqlalchemy,mocked_getsmallrowdata_db): self.rbasic.team.add(self.teamcoach) + self.rbasic.coaches.add(self.rcoach) login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) self.assertTrue(login) @@ -531,6 +563,50 @@ class PermissionsViewTests(TestCase): self.assertEqual(aantal2,aantal+1) + ## Coach can upload on behalf of athlete - if team allows + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db) + def test_coach_edit_athlete_uploadnot(self,mocked_sqlalchemy,mocked_getsmallrowdata_db): + self.rbasic.team.add(self.teamcoach) + + login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) + self.assertTrue(login) + + url = reverse('team_workout_upload_view') + + aantal = len(Workout.objects.filter(user=self.rbasic)) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + filename = 'rowers/tests/testdata/testdata.csv' + f = open(filename,'rb') + file_data = {'file': f} + form_data = { + 'title':'test', + 'workouttype':'rower', + 'boattype':'1x', + 'notes':'aap noot mies', + 'make_plot':False, + 'upload_to_c2':False, + 'plottype':'timeplot', + 'file': f, + 'user': self.ubasic.id + } + + response = self.c.post(url, form_data, follow=True) + f.close() + + self.assertEqual(response.status_code,200) + + self.assertRedirects(response, + expected_url = url, + status_code=302,target_status_code=200) + + aantal2 = len(Workout.objects.filter(user=self.rbasic)) + + self.assertEqual(aantal2,aantal) + ## Coach can edit athlete's workout def test_coach_edit_athlete_workout(self): self.rbasic.team.add(self.teamcoach) diff --git a/rowers/utils.py b/rowers/utils.py index 53194091..3cc90707 100644 --- a/rowers/utils.py +++ b/rowers/utils.py @@ -404,6 +404,12 @@ def isprorower(r): result = r.rowerplan == 'basic' and r.protrialexpires >= datetime.date.today() return result + +def iscoach(m,r): + result = False + result = m in r.coaches + + return result # Exponentially weighted moving average # Used for data smoothing of the jagged data obtained by Strava diff --git a/rowers/views/teamviews.py b/rowers/views/teamviews.py index f947b1b4..0bce82e3 100644 --- a/rowers/views/teamviews.py +++ b/rowers/views/teamviews.py @@ -267,6 +267,7 @@ def team_requestmembership_view(request,teamid,userid): messages.info(request,text) else: messages.error(request,text) + url = reverse('team_view',kwargs={ 'id':int(teamid), diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 546405a3..e6528084 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -4235,16 +4235,18 @@ def team_workout_upload_view(request,message="", rowerform = TeamInviteForm(request.POST) rowerform.fields.pop('email') - rowerform.fields['user'].queryset = User.objects.filter(rower__isnull=False,rower__team__in=myteams).distinct() + rowers = Rower.objects.filter(coaches__in=[request.user.rower]).distinct() + rowerform.fields['user'].queryset = User.objects.filter(rower__in=rowers).distinct() if form.is_valid(): f = request.FILES['file'] res = handle_uploaded_file(f) t = form.cleaned_data['title'] offline = form.cleaned_data['offline'] boattype = form.cleaned_data['boattype'] + workouttype = form.cleaned_data['workouttype'] if rowerform.is_valid(): u = rowerform.cleaned_data['user'] - if u: + if u and request.user.rower in u.rower.coaches.all(): r = getrower(u) else: message = 'Please select a rower'