diff --git a/rowers/integrations/strava.py b/rowers/integrations/strava.py index b78281a0..f6e878c1 100644 --- a/rowers/integrations/strava.py +++ b/rowers/integrations/strava.py @@ -214,7 +214,7 @@ class StravaIntegration(SyncIntegration): def get_workout(self, id, *args, **kwargs) -> int: try: _ = self.open() - except NoTokenError("Strava error"): + except NoTokenError: return 0 record = create_or_update_syncrecord(self.rower, None, stravaid=id) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index c8a14c14..05a16994 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -479,7 +479,9 @@ def is_workout_team(user, workout): @rules.predicate def can_view_workout(user, workout): - if workout.privacy != 'private': + if workout.workoutsource == 'strava': + return user == workout.user.user + if workout.privacy not in ('hidden', 'private'): return True if user.is_anonymous: # pragma: no cover return False diff --git a/rowers/tests/test_api.py b/rowers/tests/test_api.py index 1187265e..d120c8b2 100644 --- a/rowers/tests/test_api.py +++ b/rowers/tests/test_api.py @@ -24,8 +24,229 @@ from rest_framework.test import APIRequestFactory, force_authenticate import json +# import BeautifulSoup +from bs4 import BeautifulSoup + from rowers.ownapistuff import * from rowers.views.apiviews import * +from rowers.teams import add_member, add_coach + +class TeamFactory(factory.DjangoModelFactory): + class Meta: + model = Team + + name = factory.LazyAttribute(lambda _: faker.word()) + notes = faker.text() + private = 'open' + viewing = 'allmembers' + +class StravaPrivacy(TestCase): + def setUp(self): + self.u = UserFactory() + self.u2 = UserFactory() + self.u3 = UserFactory() + + self.r = Rower.objects.create(user=self.u, + birthdate=faker.profile()['birthdate'], + gdproptin=True, ftpset=True,surveydone=True, + gdproptindate=timezone.now(), + rowerplan='coach',subscription_id=1) + + self.r.stravatoken = '12' + self.r.stravarefreshtoken = '123' + self.r.stravatokenexpirydate = arrow.get(datetime.datetime.now()-datetime.timedelta(days=1)).datetime + self.r.strava_owner_id = 4 + + self.r.save() + + self.c = Client() + self.user_workouts = WorkoutFactory.create_batch(5, user=self.r) + for w in self.user_workouts: + w.workoutsource = 'strava' + w.privacy = 'hidden' + w.save() + + self.factory = RequestFactory() + self.password = faker.word() + self.u.set_password(self.password) + self.u.save() + self.factory = APIRequestFactory() + + self.r2 = Rower.objects.create(user=self.u2, + birthdate=faker.profile()['birthdate'], + gdproptin=True, ftpset=True,surveydone=True, + gdproptindate=timezone.now(), + rowerplan='coach',clubsize=3) + + self.r3 = Rower.objects.create(user=self.u3, + birthdate=faker.profile()['birthdate'], + gdproptin=True, ftpset=True,surveydone=True, + gdproptindate=timezone.now(), + rowerplan='basic') + + self.c = Client() + + self.password2 = faker.word() + self.u2.set_password(self.password2) + self.u2.save() + + self.password3 = faker.word() + self.u3.set_password(self.password3) + self.u3.save() + + self.team = TeamFactory(manager=self.u2) + + # all are team members + add_member(self.team.id, self.r) + add_member(self.team.id, self.r2) + add_member(self.team.id, self.r3) + + # r2 coaches r + add_coach(self.r2, self.r) + + self.factory = APIRequestFactory() + + # Test if workout with workoutsource strava and privacy hidden can be seen by coach + def test_privacy_coach(self): + login = self.c.login(username=self.u2.username, password=self.password2) + self.assertTrue(login) + + w = self.user_workouts[0] + url = reverse('workout_view',kwargs={'id':encoder.encode_hex(w.id)}) + response = self.c.get(url) + self.assertEqual(response.status_code,403) + + # Test if workout with workoutsource strava and privacy hidden can be seen by team member + def test_privacy_member(self): + login = self.c.login(username=self.u3.username, password=self.password3) + self.assertTrue(login) + + w = self.user_workouts[0] + url = reverse('workout_view',kwargs={'id':encoder.encode_hex(w.id)}) + response = self.c.get(url) + self.assertEqual(response.status_code,403) + + # same test as above but with user r and the response code should be 200 + def test_privacy_owner(self): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + w = self.user_workouts[0] + url = reverse('workout_view',kwargs={'id':encoder.encode_hex(w.id)}) + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + + + # test if list_workouts returns all workouts for user r + def test_list_workouts(self): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + url = reverse('workouts_view') + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + # the response.content is html, so we need to parse it + soup = BeautifulSoup(response.content, 'html.parser') + # the workouts look like ... and there should be 5 unique ids + # the id is a hex string + workouts = set([a['href'].split('/')[3] for a in soup.find_all('a') if a['href'].startswith('/rowers/workout/')]) + + # throw out "c2import", "nkimport", "stravaimport", "concept2import", "sporttracksimport" from the set + workouts = set([w for w in workouts if w not in [ + 'upload', 'addmanual', 'c2import', 'polarimport', 'rp3import', 'nkimport', 'stravaimport', 'concept2import', 'sporttracksimport']]) + + self.assertEqual(len(workouts),5) + + # same test as above but list_workouts with team id = self.team.id + def test_list_workouts_team(self): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + url = reverse('workouts_view',kwargs={'teamid':self.team.id}) + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + # the response.content is html, so we need to parse it + soup = BeautifulSoup(response.content, 'html.parser') + # the workouts look like ... and there should be 5 unique ids + # the id is a hex string + workouts = set([a['href'].split('/')[3] for a in soup.find_all('a') if a['href'].startswith('/rowers/workout/')]) + + # throw out "c2import", "nkimport", "stravaimport", "concept2import", "sporttracksimport" from the set + workouts = set([w for w in workouts if w not in [ + 'upload', 'addmanual', 'c2import', 'polarimport', 'rp3import', 'nkimport', 'stravaimport', 'concept2import', 'sporttracksimport']]) + + self.assertEqual(len(workouts),0) + + # same test as the previous one but with self.r2 and the number of workouts found should 0 + def test_list_workouts_team_coach(self): + login = self.c.login(username=self.u2.username, password=self.password2) + self.assertTrue(login) + + url = reverse('workouts_view',kwargs={'teamid':self.team.id}) + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + # the response.content is html, so we need to parse it + soup = BeautifulSoup(response.content, 'html.parser') + # the workouts look like ... and there should be 5 unique ids + # the id is a hex string + workouts = set([a['href'].split('/')[3] for a in soup.find_all('a') if a['href'].startswith('/rowers/workout/')]) + + # throw out "c2import", "nkimport", "stravaimport", "concept2import", "sporttracksimport" from the set + workouts = set([w for w in workouts if w not in [ + 'upload', 'addmanual', 'c2import', 'polarimport', 'rp3import', 'nkimport', 'stravaimport', 'concept2import', 'sporttracksimport']]) + + self.assertEqual(len(workouts),0) + + # same test as the previous one but with self.r3 and the number of workouts found should 0 + def test_list_workouts_team_member(self): + login = self.c.login(username=self.u3.username, password=self.password3) + self.assertTrue(login) + + url = reverse('workouts_view',kwargs={'teamid':self.team.id}) + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + # the response.content is html, so we need to parse it + soup = BeautifulSoup(response.content, 'html.parser') + # the workouts look like ... and there should be 5 unique ids + # the id is a hex string + workouts = set([a['href'].split('/')[3] for a in soup.find_all('a') if a['href'].startswith('/rowers/workout/')]) + + # throw out "c2import", "nkimport", "stravaimport", "concept2import", "sporttracksimport" from the set + workouts = set([w for w in workouts if w not in [ + 'upload', 'addmanual', 'c2import', 'polarimport', 'rp3import', 'nkimport', 'stravaimport', 'concept2import', 'sporttracksimport']]) + + self.assertEqual(len(workouts),0) + + # now test strava import and test if the created workout has workoutsource strava and privacy hidden + @patch('rowers.utils.requests.get', side_effect=mocked_requests) + @patch('rowers.integrations.strava.requests.post', side_effect=mocked_requests) + @patch('rowers.dataprep.read_data') + def test_stravaimport(self, mock_get, mock_post, mocked_read_data): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + # remove all self.workouts + Workout.objects.filter(user=self.r).delete() + + # get the strava data like in test_strava_import in test_imports.py + response = self.c.get('/rowers/workout/stravaimport/12', follow=True) + expected_url = reverse('workout_import_view', kwargs={'source':'strava'}) + self.assertRedirects(response, expected_url, status_code=301, target_status_code=200) + + # check if the workout was created + ws = Workout.objects.filter(user=self.r) + self.assertEqual(len(ws),1) + w = ws[0] + self.assertEqual(w.workoutsource,'strava') + self.assertEqual(w.privacy,'hidden') + + + class OwnApi(TestCase): def setUp(self): @@ -35,17 +256,7 @@ class OwnApi(TestCase): birthdate=faker.profile()['birthdate'], gdproptin=True, ftpset=True,surveydone=True, gdproptindate=timezone.now(), - rowerplan='coach',subscription_id=1) - - - self.c = Client() - self.user_workouts = WorkoutFactory.create_batch(5, user=self.r) - self.factory = RequestFactory() - self.password = faker.word() - self.u.set_password(self.password) - self.u.save() - - self.factory = APIRequestFactory() + rowerplan='pro',subscription_id=1) def test_strokedataform(self): diff --git a/rowers/uploads.py b/rowers/uploads.py index 781271f7..83846898 100644 --- a/rowers/uploads.py +++ b/rowers/uploads.py @@ -141,6 +141,8 @@ def do_sync(w, options, quick=False): w.uploadedtostrava = options['stravaid'] # upload_to_strava = False do_strava_export = False + w.workoutsource = 'strava' + w.privacy = 'hidden' w.save() record = create_or_update_syncrecord(w.user, w, stravaid=options['stravaid']) except KeyError: