diff --git a/rowers/models.py b/rowers/models.py index 687bcaa6..7f7d5300 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -349,6 +349,15 @@ class Team(models.Model): raise ValidationError( "Basic user cannot be team manager" ) + + if manager.rower.rowerplan in ['plan','pro']: + otherteams = Team.objects.filter(manager=manager) + if len(otherteams) >= 1: + raise ValidationError( + "Pro and Self-Coach users cannot have more than one team" + ) + + super(Team, self).save(*args,**kwargs) class TeamForm(ModelForm): @@ -844,6 +853,7 @@ class Rower(models.Model): def clean_email(self): return self.user.email.lower() + class DeactivateUserForm(forms.ModelForm): class Meta: model = User @@ -856,13 +866,31 @@ class DeleteUserForm(forms.ModelForm): class Meta: model = User fields = [] + + +from django.db.models.signals import m2m_changed -@receiver(models.signals.post_save,sender=Rower) -def auto_delete_teams_on_change(sender, instance, **kwargs): - if instance.rowerplan != 'coach': - teams = Team.objects.filter(manager=instance.user) - for team in teams: - team.delete() +def check_teams_on_change(sender, **kwargs): + instance = kwargs.pop('instance', None) + 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" + ) + +m2m_changed.connect(check_teams_on_change, sender=Rower.team.through) + + +#@receiver(models.signals.post_save,sender=Rower) +#def auto_delete_teams_on_change(sender, instance, **kwargs): +# if instance.rowerplan != 'coach': +# teams = Team.objects.filter(manager=instance.user) +# for team in teams: +# team.delete() from rowers.metrics import axlabels favchartlabelsx = axlabels.copy() @@ -1149,6 +1177,12 @@ class TrainingPlan(models.Model): return stri def save(self, *args, **kwargs): + manager = self.manager + if manager.rowerplan in ['basic','pro']: + raise ValidationError( + "Basic user cannot have a training plan" + ) + if self.enddate < self.startdate: startdate = self.startdate enddate = self.enddate @@ -1194,6 +1228,7 @@ class TrainingPlan(models.Model): else: createmacrofillers(self) + class TrainingPlanForm(ModelForm): class Meta: model = TrainingPlan @@ -1563,6 +1598,8 @@ class TrainingMacroCycle(models.Model): meso.save() else: createmesofillers(self) + + class TrainingMacroCycleForm(ModelForm): class Meta: @@ -1649,6 +1686,7 @@ class TrainingMesoCycle(models.Model): else: createmicrofillers(self) + class TrainingMicroCycle(models.Model): plan = models.ForeignKey(TrainingMesoCycle) name = models.CharField(max_length=150,blank=True) @@ -1863,6 +1901,14 @@ class PlannedSession(models.Model): def save(self, *args, **kwargs): if self.sessionvalue <= 0: self.sessionvalue = 1 + + 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" + ) + # sort units if self.sessionmode == 'distance': diff --git a/rowers/tests/test_aavirtualevents.py b/rowers/tests/test_aavirtualevents.py index 73ad8504..2ca74dbb 100644 --- a/rowers/tests/test_aavirtualevents.py +++ b/rowers/tests/test_aavirtualevents.py @@ -69,12 +69,14 @@ class VirtualEventViewTest(TestCase): yesterday = nu-datetime.timedelta(days=1) tomorrow = nu+datetime.timedelta(days=1) nextweek = nu+datetime.timedelta(days=7) + intwoweeks = nu+datetime.timedelta(days=14) lastweek = nu-datetime.timedelta(days=7) self.yesterday = yesterday self.tomorrow = tomorrow self.nextweek = nextweek self.lastweek = lastweek + self.intwoweeks = intwoweeks # erg races @@ -396,8 +398,8 @@ class VirtualEventViewTest(TestCase): 'registration_form':'deadline', 'registration_closure_0': self.nextweek.strftime('%Y-%m-%d'), 'registration_closure_1': self.nextweek.strftime('%H:%M:%S'), - 'evaluation_closure_0': self.nextweek.strftime('%Y-%m-%d'), - 'evaluation_closure_1': self.nextweek.strftime('%H:%M:%S'), + 'evaluation_closure_0': self.intwoweeks.strftime('%Y-%m-%d'), + 'evaluation_closure_1': self.intwoweeks.strftime('%H:%M:%S'), 'contact_phone': '', 'contact_email': self.u.email, 'timezone': 'UTC' @@ -440,8 +442,8 @@ class VirtualEventViewTest(TestCase): 'registration_form':'deadline', 'registration_closure_0': self.nextweek.strftime('%Y-%m-%d'), 'registration_closure_1': self.nextweek.strftime('%H:%M:%S'), - 'evaluation_closure_0': self.nextweek.strftime('%Y-%m-%d'), - 'evaluation_closure_1': self.nextweek.strftime('%H:%M:%S'), + 'evaluation_closure_0': self.intwoweeks.strftime('%Y-%m-%d'), + 'evaluation_closure_1': self.intwoweeks.strftime('%H:%M:%S'), 'contact_phone': '', 'contact_email': self.u.email, 'timezone': 'UTC' @@ -485,8 +487,8 @@ class VirtualEventViewTest(TestCase): 'registration_form':'deadline', 'registration_closure_0': self.nextweek.strftime('%Y-%m-%d'), 'registration_closure_1': self.nextweek.strftime('%H:%M:%S'), - 'evaluation_closure_0': self.nextweek.strftime('%Y-%m-%d'), - 'evaluation_closure_1': self.nextweek.strftime('%H:%M:%S'), + 'evaluation_closure_0': self.intwoweeks.strftime('%Y-%m-%d'), + 'evaluation_closure_1': self.intwoweeks.strftime('%H:%M:%S'), 'contact_phone': '', 'contact_email': self.u.email, } @@ -522,8 +524,8 @@ class VirtualEventViewTest(TestCase): 'registration_form':'deadline', 'registration_closure_0': self.nextweek.strftime('%Y-%m-%d'), 'registration_closure_1': self.nextweek.strftime('%H:%M:%S'), - 'evaluation_closure_0': self.nextweek.strftime('%Y-%m-%d'), - 'evaluation_closure_1': self.nextweek.strftime('%H:%M:%S'), + 'evaluation_closure_0': self.intwoweeks.strftime('%Y-%m-%d'), + 'evaluation_closure_1': self.intwoweeks.strftime('%H:%M:%S'), 'contact_phone': '', 'contact_email': self.u.email, } diff --git a/rowers/tests/test_permissions.py b/rowers/tests/test_permissions.py index 88f64e22..8c6e2618 100644 --- a/rowers/tests/test_permissions.py +++ b/rowers/tests/test_permissions.py @@ -2,6 +2,8 @@ from statements import * from django.utils import timezone nu = datetime.datetime.now(tz=timezone.utc) +from django.db import transaction + # set up import rowers.teams as teams @@ -50,6 +52,30 @@ class PermissionsBasicsTests(TestCase): 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'], @@ -64,35 +90,169 @@ class PermissionsBasicsTests(TestCase): - ## TeamPro, TeamCoach, TeamSelfCoach + ## TeamPro, TeamCoach, TeamSelfCoach + + self.teampro = Team.objects.create( + name=faker.word(), + notes=faker.text(), + manager=self.upro2) + + self.teamplan = Team.objects.create( + name=faker.word(), + notes=faker.text(), + manager=self.uplan2) + + self.teamcoach = Team.objects.create( + name=faker.word(), + notes=faker.text(), + manager=self.ucoach) # Requirements ## Low level ## Coach can have any number of groups + def test_plan_groupmanager(self): + team1 = Team.objects.create( + name = 'FirstTeam', + notes = faker.text(), + manager = self.ucoach, + ) + + self.assertEqual(team1.manager,self.ucoach) + + team2 = Team.objects.create( + name = 'SecondTeam', + notes = faker.text(), + manager = self.ucoach, + ) + + self.assertEqual(team2.manager,self.ucoach) + + + team3 = Team.objects.create( + name = 'SecondTeam', + notes = faker.text(), + manager = self.ucoach, + ) + + self.assertEqual(team3.manager,self.ucoach) + + ## Basic athletes can be member of Coach led group + def test_add_coach(self): + self.rbasic.team.add(self.teamcoach) + self.assertIn(self.teamcoach,self.rbasic.team.all()) - ## Coach can create planned sessions and team planned sessions ## Self coach can create one group - ## Self coach cannot create more than one group + def test_plan_groupmanager(self): + team1 = Team.objects.create( + name = 'FirstTeam', + notes = faker.text(), + manager = self.uplan, + ) + + self.assertEqual(team1.manager,self.uplan) + + with self.assertRaises(ValidationError): + team2 = Team.objects.create( + name = 'SecondTeam', + notes = faker.text(), + manager = self.uplan, + ) + + ## Pro users (and higher) can join group led by other Pro (or higher) user + def test_add_proplan_pro_or_plan(self): + self.rpro.team.add(self.teamplan) + self.assertIn(self.teamplan,self.rpro.team.all()) + + self.rpro.team.add(self.teampro) + self.assertIn(self.teampro,self.rpro.team.all()) + + self.rplan.team.add(self.teamplan) + self.assertIn(self.teamplan,self.rplan.team.all()) + + self.rplan.team.add(self.teampro) + self.assertIn(self.teampro,self.rplan.team.all()) + + self.rcoach.team.add(self.teamplan) + self.assertIn(self.teamplan,self.rcoach.team.all()) + + self.rcoach.team.add(self.teampro) + self.assertIn(self.teampro,self.rcoach.team.all()) + - ## Self Coach can create planned sessions and team planned sessions + + + ## 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.uplan, + name=faker.word(), + comment=faker.text() + ) + self.assertEqual(ps.manager,self.uplan) + + def test_coach_create_session(self): + ps = PlannedSession.objects.create( + manager=self.ucoach, + name=faker.word(), + comment=faker.text() + ) + self.assertEqual(ps.manager,self.ucoach) ## Pro can have one group - ## Pro cannot create more than one group + def test_pro_groupmanager(self): + team1 = Team.objects.create( + name = 'FirstTeam', + notes = faker.text(), + manager = self.upro, + ) + + self.assertEqual(team1.manager,self.upro) + + with self.assertRaises(ValidationError): + team2 = Team.objects.create( + name = 'SecondTeam', + notes = faker.text(), + manager = self.upro, + ) - ## Pro cannot create planned sessions or team planned sessions + + ## Pro or Basic cannot create planned sessions or team planned sessions + def test_pro_create_plannedsession(self): + with self.assertRaises(ValidationError): + ps = PlannedSession.objects.create( + manager=self.upro, + name = faker.word(), + comment = faker.text() + ) + + def test_basic_create_plannedsession(self): + with self.assertRaises(ValidationError): + ps = PlannedSession.objects.create( + manager=self.ubasic, + name = faker.word(), + comment = faker.text() + ) ## Basic cannot join groups led by Pro or Self Coach + def test_add_basic_pro_or_plan(self): + with transaction.atomic(): + with self.assertRaises(ValidationError): + self.rbasic.team.add(self.teamplan) + + with transaction.atomic(): + with self.assertRaises(ValidationError): + self.rbasic.team.add(self.teampro) - ## Basic can join group led by Coach ## Basic cannot manage a group def test_basic_groupmanager(self): @@ -104,52 +264,652 @@ class PermissionsBasicsTests(TestCase): private = 'open', viewing = 'allmembers') + ## On downgrade, Coach users lose all but their oldest team + # View based +@override_settings(TESTING=True) +class PermissionsViewTests(TestCase): + def setUp(self): + self.c = Client() + ## Users - Pro, Basic, Coach & Self Coach -## Coach can have any number of groups + self.ucoach = UserFactory(username='coachuser') + self.rcoach = Rower.objects.create(user=self.ucoach, + birthdate=faker.profile()['birthdate'], + gdproptin=True,gdproptindate=timezone.now(), + rowerplan='coach') -## Basic athletes can be member of Coach led group + self.ucoach_workouts = WorkoutFactory.create_batch(5, user=self.rcoach) + self.factory = RequestFactory() + self.ucoachpassword = faker.word() + self.ucoach.set_password(self.ucoachpassword) + self.ucoach.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') -## Coach can create planned sessions and team planned sessions + self.uplan_workouts = WorkoutFactory.create_batch(5, user=self.rplan) + self.factory = RequestFactory() + self.uplanpassword = faker.word() + self.uplan.set_password(self.uplanpassword) + 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') -## Coach can edit on behalf of athlete + self.upro_workouts = WorkoutFactory.create_batch(5, user=self.rpro) + self.factory = RequestFactory() + self.upropassword = faker.word() + self.upro.set_password(self.upropassword) + self.upro.save() -## Coach can run analytics for athlete + self.uplan2 = UserFactory(username='planuser2') + self.rplan2 = Rower.objects.create(user=self.uplan2, + birthdate=faker.profile()['birthdate'], + gdproptin=True,gdproptindate=timezone.now(), + rowerplan='plan') -## Coach can upload on behalf of athlete + self.uplan2_workouts = WorkoutFactory.create_batch(5, user=self.rplan2) + self.factory = RequestFactory() + self.uplan2password = faker.word() + self.uplan2.set_password(self.uplan2password) + 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') -## Coach can edit athlete's workout + self.upro2_workouts = WorkoutFactory.create_batch(5, user=self.rpro2) + self.factory = RequestFactory() + self.upro2password = faker.word() + self.upro2.set_password(self.upro2password) + self.upro2.save() -## Self coach can create one group + 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 coach cannot create more than one group + 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() + + + + ## TeamPro, TeamCoach, TeamSelfCoach -## Pro users (and higher) can join group led by other Pro (or higher) user + self.teampro = Team.objects.create( + name=faker.word(), + notes=faker.text(), + manager=self.upro2) -## Self Coach can create planned sessions and team planned sessions + self.teamplan = Team.objects.create( + name=faker.word(), + notes=faker.text(), + manager=self.uplan2) -## Self Coach cannot edit on behalf of athlete + self.teamcoach = Team.objects.create( + name=faker.word(), + notes=faker.text(), + manager=self.ucoach) -## Self Coach cannot run analytics on behalf of athlete -## Self Coach cannot upload on behalf of athlete + ## Coach can have any number of groups + def test_coach_groups_create(self): + login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) + self.assertTrue(login) + + url = reverse('team_create_view') -## Pro can have one group + response = self.c.get(url) + self.assertTrue(response.status_code,200) -## Pro cannot create more than one group + # Create 1st new team + form_data = { + 'name': faker.word(), + 'notes': faker.text(), + 'private': 'open', + 'viewing': 'allmembers' + } -## Pro cannot create planned sessions or team planned sessions + form = TeamForm(form_data) + if not form.is_valid(): + print form.errors + + self.assertTrue(form.is_valid()) -## Pro can create planned sessions and team planned sessions + expected_url = reverse('rower_teams_view') -## Pro cannot edit on behalf of athlete + response = self.c.post(url,form_data,follow=True) + self.assertRedirects(response, + expected_url=expected_url, + status_code=302,target_status_code=200) -## Pro cannot run analytics on behalf of athlete -## Basic cannot join groups from Pro or Self Coach users (redirects to paid plans) + # Create 2nd new team + form_data = { + 'name': faker.word(), + 'notes': faker.text(), + 'private': 'open', + 'viewing': 'allmembers' + } -## Pro users can see team members' workout, but not edit + form = TeamForm(form_data) + if not form.is_valid(): + print form.errors + + self.assertTrue(form.is_valid()) -## Self Coach users can see team members' workout, but not edit + expected_url = reverse('rower_teams_view') + + response = self.c.post(url,form_data,follow=True) + self.assertRedirects(response, + expected_url=expected_url, + status_code=302,target_status_code=200) + + ## Basic athletes can be member of Coach led group + + ## Coach can create planned sessions and team planned sessions + def test_coach_create_session(self): + login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) + self.assertTrue(login) + + url = reverse('plannedsession_create_view') + + startdate = nu.date() + enddate = (nu+datetime.timedelta(days=3)).date() + preferreddate = startdate + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + post_data = { + 'comment': faker.text(), + 'criterium': 'none', + 'enddate': enddate.strftime("%Y-%m-%d"), + 'preferreddate': preferreddate.strftime("%Y-%m-%d"), + 'startdate': startdate.strftime("%Y-%m-%d"), + 'sessionmode':'time', + 'sessiontype':'session', + 'sessionunit':'min', + 'sessionvalue': '60', + 'name': faker.word(), + } + + print 'posting to sessions/create' + + form = PlannedSessionForm(post_data) + self.assertTrue(form.is_valid()) + + response = self.c.post(url,post_data) + self.assertEqual(response.status_code,200) + + + + ## Coach can edit on behalf of athlete + def test_coach_edit_athlete_settings(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,200) + + ## 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) + + 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,200) + + + ## Coach can upload on behalf of athlete + @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) + + 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+1) + + ## Coach can edit athlete's workout + def test_coach_edit_athlete_workout(self): + self.rbasic.team.add(self.teamcoach) + + login = self.c.login(username=self.ucoach.username, password=self.ucoachpassword) + self.assertTrue(login) + + url = reverse('workout_edit_view', + kwargs={'id':encoder.encode_hex(self.ubasic_workouts[0].id)} + ) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + ## Self coach can create one group + ## Self coach cannot create more than one group + def test_plan_groups_create(self): + login = self.c.login(username=self.uplan.username, password=self.uplanpassword) + self.assertTrue(login) + + url = reverse('team_create_view') + + response = self.c.get(url) + self.assertTrue(response.status_code,200) + + # Create 1st new team + form_data = { + 'name': faker.word(), + 'notes': faker.text(), + 'private': 'open', + 'viewing': 'allmembers' + } + + form = TeamForm(form_data) + if not form.is_valid(): + print form.errors + + self.assertTrue(form.is_valid()) + + expected_url = reverse('rower_teams_view') + + response = self.c.post(url,form_data,follow=True) + self.assertRedirects(response, + expected_url=expected_url, + status_code=302,target_status_code=200) + + + # Create 2nd new team - should redirect to paid plans + form_data = { + 'name': faker.word(), + 'notes': faker.text(), + 'private': 'open', + 'viewing': 'allmembers' + } + + form = TeamForm(form_data) + if not form.is_valid(): + print form.errors + + self.assertTrue(form.is_valid()) + + expected_url = reverse('paidplans') + + response = self.c.post(url,form_data,follow=True) + self.assertRedirects(response, + expected_url=expected_url, + status_code=302,target_status_code=200) + + + ## Self Coach can create planned sessions and team planned sessions + def test_plan_create_session(self): + login = self.c.login(username=self.uplan2.username, password=self.uplan2password) + self.assertTrue(login) + + url = reverse('plannedsession_create_view') + + startdate = nu.date() + enddate = (nu+datetime.timedelta(days=3)).date() + preferreddate = startdate + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + post_data = { + 'comment': faker.text(), + 'criterium': 'none', + 'enddate': enddate.strftime("%Y-%m-%d"), + 'preferreddate': preferreddate.strftime("%Y-%m-%d"), + 'startdate': startdate.strftime("%Y-%m-%d"), + 'sessionmode':'time', + 'sessiontype':'session', + 'sessionunit':'min', + 'sessionvalue': '60', + 'name': faker.word(), + } + + print 'posting to sessions/create' + + form = PlannedSessionForm(post_data) + self.assertTrue(form.is_valid()) + + response = self.c.post(url,post_data) + self.assertEqual(response.status_code,200) + + + + ## Self Coach cannot edit on behalf of athlete + def test_plan_edit_athlete_settings(self): + self.rbasic.team.add(self.teamplan) + + login = self.c.login(username=self.uplan2.username, password=self.uplan2password) + self.assertTrue(login) + + url = reverse('rower_prefs_view',kwargs={'userid':self.ubasic.id}) + + response = self.c.get(url) + self.assertEqual(response.status_code,404) + + ## 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) + + login = self.c.login(username=self.uplan2.username, password=self.uplan2password) + self.assertTrue(login) + + + url = reverse('cumstats', + kwargs={ + 'theuser':self.ubasic.id, + } + ) + + response = self.c.get(url) + + self.assertEqual(response.status_code,404) + + ## 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) + + 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) + + + ## Pro can have one group + ## Pro cannot create more than one group + def test_pro_groups_create(self): + login = self.c.login(username=self.upro.username, password=self.upropassword) + self.assertTrue(login) + + url = reverse('team_create_view') + + response = self.c.get(url) + self.assertTrue(response.status_code,200) + + # Create 1st new team + form_data = { + 'name': faker.word(), + 'notes': faker.text(), + 'private': 'open', + 'viewing': 'allmembers' + } + + form = TeamForm(form_data) + if not form.is_valid(): + print form.errors + + self.assertTrue(form.is_valid()) + + expected_url = reverse('rower_teams_view') + + response = self.c.post(url,form_data,follow=True) + self.assertRedirects(response, + expected_url=expected_url, + status_code=302,target_status_code=200) + + + # Create 2nd new team - should redirect to paid plans + form_data = { + 'name': faker.word(), + 'notes': faker.text(), + 'private': 'open', + 'viewing': 'allmembers' + } + + form = TeamForm(form_data) + if not form.is_valid(): + print form.errors + + self.assertTrue(form.is_valid()) + + expected_url = reverse('paidplans') + + response = self.c.post(url,form_data,follow=True) + self.assertRedirects(response, + expected_url=expected_url, + status_code=302,target_status_code=200) + + + ## Pro cannot create planned sessions or team planned sessions + def test_pro_create_session(self): + login = self.c.login(username=self.upro2.username, password=self.upro2password) + self.assertTrue(login) + + url = reverse('plannedsession_create_view') + + startdate = nu.date() + enddate = (nu+datetime.timedelta(days=3)).date() + preferreddate = startdate + + 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 cannot edit on behalf of athlete + def test_pro_edit_athlete_settings(self): + self.rbasic.team.add(self.teampro) + + login = self.c.login(username=self.upro2.username, password=self.upro2password) + self.assertTrue(login) + + url = reverse('rower_prefs_view',kwargs={'userid':self.ubasic.id}) + + response = self.c.get(url) + self.assertEqual(response.status_code,404) + + ## 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) + + login = self.c.login(username=self.upro2.username, password=self.upro2password) + self.assertTrue(login) + + + url = reverse('cumstats', + kwargs={ + 'theuser':self.ubasic.id, + } + ) + + response = self.c.get(url) + + self.assertEqual(response.status_code,404) + + ## 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) + + 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) + + + ## 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) + + 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)} + ) + + response = self.c.get(url) + self.assertEqual(response.status_code,404) + + url = reverse('workout_view', + kwargs={'id':encoder.encode_hex(self.ubasic_workouts[0].id)} + ) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + + ## 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) + + login = self.c.login(username=self.uplan2.username, password=self.uplan2password) + self.assertTrue(login) + + url = reverse('workout_edit_view', + kwargs={'id':encoder.encode_hex(self.ubasic_workouts[0].id)} + ) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + url = reverse('workout_view', + kwargs={'id':encoder.encode_hex(self.ubasic_workouts[0].id)} + ) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + + ## 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) + + login = self.c.login(username=self.ubasic.username, password=self.ubasicpassword) + self.assertTrue(login) + + url = reverse('workout_edit_view', + kwargs={'id':encoder.encode_hex(self.uplan2_workouts[0].id)} + ) + + response = self.c.get(url) + self.assertEqual(response.status_code,404) + + url = reverse('workout_view', + kwargs={'id':encoder.encode_hex(self.uplan2_workouts[0].id)} + ) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + ## Pro users (and higher) can join group led by other Pro (or higher) user + def test_team_member_request_pro_pro(self): + login = self.c.login(username=self.upro.username,password=self.upropassword) + self.assertTrue(login) + + url = reverse('team_requestmembership_view', + kwargs = { + 'teamid':self.teampro.id, + 'userid':self.upro.id + }) + + response = self.c.get(url,follow=True) + self.assertEqual(response.status_code,200) + + expected_url = reverse('team_view',kwargs={'id':self.teampro.id}) + + self.assertRedirects(response, + expected_url = expected_url, + status_code=302,target_status_code=200) + + ## Basic cannot join groups from Pro or Self Coach users (redirects to paid plans) + def test_team_member_request_basic_pro(self): + login = self.c.login(username=self.ubasic.username,password=self.ubasicpassword) + self.assertTrue(login) + + url = reverse('team_requestmembership_view', + kwargs = { + 'teamid':self.teampro.id, + 'userid':self.upro.id + }) + + 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) -## Basic users can see team members' workout, but not edit diff --git a/rowers/tests/test_plans.py b/rowers/tests/test_plans.py index 51395c25..87f138c6 100644 --- a/rowers/tests/test_plans.py +++ b/rowers/tests/test_plans.py @@ -131,7 +131,7 @@ class TrainingPlanTest(TestCase): login = self.c.login(username=self.u.username, password=self.password) self.assertTrue(login) - url = '/rowers/sessions/create/' + url = reverse('plannedsession_create_view') startdate = nu.date() enddate = (nu+datetime.timedelta(days=3)).date() @@ -1051,6 +1051,8 @@ class PlannedSessionsView(TestCase): manager = self.u, ) + self.team.save() + self.r.team.add(self.team) self.r2.team.add(self.team) self.r.save() diff --git a/rowers/tests/testdata/testdata.csv.gz b/rowers/tests/testdata/testdata.csv.gz index b35d3210..3c6cc8df 100644 Binary files a/rowers/tests/testdata/testdata.csv.gz and b/rowers/tests/testdata/testdata.csv.gz differ diff --git a/rowers/views/teamviews.py b/rowers/views/teamviews.py index 06e8e894..a248c24b 100644 --- a/rowers/views/teamviews.py +++ b/rowers/views/teamviews.py @@ -261,7 +261,7 @@ def team_requestmembership_view(request,teamid,userid): else: messages.error(request,text) - url = reverse(team_view,kwargs={ + url = reverse('team_view',kwargs={ 'id':int(teamid), }) @@ -408,7 +408,7 @@ def team_create_view(request): viewing = cd['viewing'] res,message=teams.create_team(name,manager,private,notes, viewing) - url = reverse(rower_teams_view) + url = reverse('rower_teams_view') response = HttpResponseRedirect(url) return response diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 9f1c51cd..546405a3 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -4331,7 +4331,7 @@ def team_workout_upload_view(request,message="", url = reverse('team_workout_upload_view') response = HttpResponseRedirect(url) - w = Workout.objects.get(id=encoder.decode_hex(id)) + w = Workout.objects.get(id=id) r = getrower(request.user) if (make_plot):