diff --git a/rowers/polarstuff.py b/rowers/polarstuff.py index ef201d44..0cb85c95 100644 --- a/rowers/polarstuff.py +++ b/rowers/polarstuff.py @@ -93,6 +93,10 @@ def get_token(code): data=post_data, headers=headers) + if response.status_code != 200: # pragma: no cover + dologging('polar.log','Getting token, got:') + dologging('polar.log',response.status_code) + dologging('polar.log',response.text) try: token_json = response.json() @@ -100,11 +104,17 @@ def get_token(code): expires_in = token_json['expires_in'] user_id = token_json['x_user_id'] dologging('polar.log',response.status_code) - dologging('polar.log',response.text) + try: + dologging('polar.log',response.text) + except AttributeError: + pass dologging('polar.log',token_json) - except (KeyError,JSONDecodeError) as e: + except (KeyError,JSONDecodeError) as e: # pragma: no cover dologging('polar.log',e) - dologging('polar.log',response.text) + try: + dologging('polar.log',response.text) + except AttributeError: + pass thetoken = 0 expires_in = 0 user_id = 0 @@ -112,7 +122,7 @@ def get_token(code): return [thetoken,expires_in,user_id] # Make authorization URL including random string -def make_authorization_url(): +def make_authorization_url(): # pragma: no cover # Generate a random string for the state parameter # Save it for use later to prevent xsrf attacks state = str(uuid4()) @@ -127,16 +137,17 @@ def make_authorization_url(): return HttpResponseRedirect(url) -def revoke_access(user): +def revoke_access(user): # pragma: no cover headers = { 'Authorization': 'Bearer {token}'.format(token=user.rower.polartoken) } - response = requests.delete('https://www.polaraccesslink.com/v3/users/{user-id}', params={ - 'user-id':user.rower.polaruserid, - }, headers = headers) + response = requests.delete('https://www.polaraccesslink.com/v3/users/{userid}'.format( + userid = user.rower.polaruserid + ), headers = headers) dologging('polar.log',response.text) + dologging('polar.log',response.reason) return 1 @@ -156,7 +167,7 @@ def get_polar_notifications(): try: response = requests.get(url, headers=headers) - except ConnectionError: + except ConnectionError: # pragma: no cover response = { 'status_code':400, } @@ -167,11 +178,14 @@ def get_polar_notifications(): if response.status_code == 200: available_data = response.json()['available-user-data'] dologging('polar.log',available_data) - else: + else: # pragma: no cover dologging('polar.log',response.status_code) - dologging('polar.log',reponse.text) - except AttributeError: - dologging('polar.log',response.text) + dologging('polar.log',response.text) + except AttributeError: # pragma: no cover + try: + dologging('polar.log',response.text) + except AttributeError: + pass pass return available_data @@ -181,7 +195,7 @@ from rowers.rower_rules import ispromember def get_all_new_workouts(available_data,testing=False): for record in available_data: dologging('polar.log',str(record)) - if testing: + if testing: # pragma: no cover print(record) if record['data-type'] == 'EXERCISE': try: @@ -190,9 +204,9 @@ def get_all_new_workouts(available_data,testing=False): if r.polar_auto_import and ispromember(u): exercise_list = get_polar_workouts(u) dologging('polar.log',exercise_list) - if testing: + if testing: # pragma: no cover print(exercise_list) - except Rower.DoesNotExist: + except Rower.DoesNotExist: # pragma: no cover pass return 1 @@ -206,7 +220,7 @@ def get_polar_workouts(user): if (r.polartoken == '') or (r.polartoken is None): s = "Token doesn't exist. Need to authorize" return custom_exception_handler(401,s) - elif (timezone.now()>r.polartokenexpirydate): + elif (timezone.now()>r.polartokenexpirydate): # pragma: no cover s = "Token expired. Needs to refresh" dologging('polar.log',s) return custom_exception_handler(401,s) @@ -264,7 +278,7 @@ def get_polar_workouts(user): workouttype = 'other' try: workouttype = mytypes.polaraccesslink_sports[exercise_dict['detailed-sport-info']] - except KeyError: + except KeyError: # pragma: no cover dologging('polar.log',exercise_dict['detailed-sport-info']) dologging('polar.log',workouttype) try: @@ -305,7 +319,7 @@ def get_polar_workouts(user): ) dologging('polar.log',response.status_code) - if response.status_code != 200: + if response.status_code != 200: # pragma: no cover try: dologging('polar.log',response.text) except: @@ -316,7 +330,7 @@ def get_polar_workouts(user): pass exercise_dict['filename'] = filename - else: + else: # pragma: no cover exercise_dict['filename'] = '' exercise_list.append(exercise_dict) @@ -332,6 +346,39 @@ def get_polar_workouts(user): return exercise_list +def register_user(user): + r = Rower.objects.get(user=user) + if (r.polartoken == '') or (r.polartoken is None): + s = "Token doesn't exist. Need to authorize" + return custom_exception_handler(401,s) + elif (timezone.now()>r.polartokenexpirydate): + s = "Token expired. Needs to refresh" + return custom_exception_handler(401,s) + + authorizationstring = str('Bearer ' + r.polartoken) + headers = { + 'Content-Type': 'application/xml', + 'Authorization':authorizationstring, + 'Accept': 'application/json' + } + + params = { + 'member-id': str(user.id) + } + + url = baseurl+'/users' + + dologging('polar.log','Registering user') + response = requests.post(url,params=params,headers=headers) + + if response.status_code != 200: + dologging('polar.log',response.status_code) + return {} + + polar_user_data = response.json() + + return polar_user_data + def get_polar_user_info(user,physical=False): r = Rower.objects.get(user=user) if (r.polartoken == '') or (r.polartoken is None): @@ -340,34 +387,34 @@ def get_polar_user_info(user,physical=False): elif (timezone.now()>r.polartokenexpirydate): s = "Token expired. Needs to refresh" return custom_exception_handler(401,s) - else: - authorizationstring = str('Bearer ' + r.polartoken) - headers = { - 'Authorization':authorizationstring, - 'Accept': 'application/json' + + authorizationstring = str('Bearer ' + r.polartoken) + headers = { + 'Authorization':authorizationstring, + 'Accept': 'application/json' + } + + + params = { + 'user-id': r.polaruserid } - - params = { - 'user-id': r.polaruserid - } - - if not physical: - url = baseurl+'/users/{userid}'.format( - userid = r.polaruserid + if not physical: + url = baseurl+'/users/{userid}'.format( + userid = r.polaruserid + ) + else: + url = 'https://www.polaraccesslink.com/v3/users/{userid}/physical-information-transactions/'.format( + userid = r.polaruserid ) - else: - url = 'https://www.polaraccesslink.com/v3/users/{userid}/physical-information-transactions/'.format( - userid = r.polaruserid - ) - if physical: - response = requests.post(url, headers=headers) - else: - response = requests.get(url, headers=headers) + if physical: + response = requests.post(url, headers=headers) + else: + response = requests.get(url, headers=headers) - return response + return response def get_polar_workout(user,id,transactionid): @@ -415,11 +462,15 @@ def get_polar_workout(user,id,transactionid): transactionid = transactionid, exerciseid = id ) + authorizationstring = str('Bearer ' + r.polartoken) + headers2 = { + 'Authorization':authorizationstring, + } response = requests.get(url,headers = headers2) if response.status_code == 200: - result = response.text + result = response.content # commit transaction url = baseurl+'/users/{userid}/exercise-transactions/{transactionid}'.format( transactionid = transactionid, @@ -427,9 +478,9 @@ def get_polar_workout(user,id,transactionid): ) response = requests.put(url,headers=headers) dologging('polar.log','Committing transaction on {url}'.format(url=url)) - else: + else: # pragma: no cover result = None return result - return None + return None # pragma: no cover diff --git a/rowers/tests/mocks.py b/rowers/tests/mocks.py index f4a31f68..a2584dc1 100644 --- a/rowers/tests/mocks.py +++ b/rowers/tests/mocks.py @@ -768,6 +768,25 @@ def mocked_requests(*args, **kwargs): ] } + polar_user_data = { + "polar-user-id": 475, + "member-id": "i09u9ujj", + "registration-date": "2011-10-14T12:50:37.000Z", + "first-name": "Eka", + "last-name": "Toka", + "birthdate": "1985-09-06", + "gender": "MALE", + "weight": 66, + "height": 170, + "field": [ + { + "value": "2", + "index": 0, + "name": "number-of-children" + } + ] + } + polar_exercise_dict = { "id": 1937529874, "upload-time": "2008-10-13T10:40:02Z", @@ -1021,6 +1040,7 @@ def mocked_requests(*args, **kwargs): polartester = re.compile(r'.*?polaraccesslink\.com') + polarremotetester = re.compile(r'.*?polarremote\.com') c2tester = re.compile(r'.*?log\.concept2\.com') stravatester = re.compile(r'.*?strava\.com') sttester = re.compile(r'.*?sporttracks\.mobi') @@ -1041,6 +1061,12 @@ def mocked_requests(*args, **kwargs): polartcxregex = r'.*?polaraccesslink\.com\/.*\/(\d+)\/tcx' polartcxtester = re.compile(polartcxregex) + polaruserregex = r'.*?polaraccesslink\.com\/.*\/users$' + polarusertester = re.compile(polaruserregex) + + polarnotificationregex = r'.*polaraccesslink\.com\/.*\/notifications' + polarnotificationtester = re.compile(polarnotificationregex) + c2importregex = r'.*?concept2.com\/api\/users\/me\/results\/\d+' c2importtester = re.compile(c2importregex) @@ -1134,8 +1160,35 @@ def mocked_requests(*args, **kwargs): json_data = stravaathletejson return MockResponse(json_data,200) + if polarremotetester.match(args[0]): + json_data = { + 'access_token':'aap', + 'expires_in':31535999, + 'x_user_id':475, + } + return MockResponse(json_data,200) + if polartester.match(args[0]): - if polartcxtester.match(args[0]): + if polarnotificationtester.match(args[0]): + json_data = { + "available-user-data": [ + { + "user-id": 475, + "data-type": "ACTIVITY_SUMMARY", + "url": "https://www.polaraccesslink.com/v3/users/475/activity-transactions" + }, + { + 'user-id': 475, + 'data-type': 'EXERCISE', + 'url': 'https://polaraccesslink.com/v3/users/40273947/exercise-transactions' + } + ] + } + return MockResponse(json_data,200) + elif polarusertester.match(args[0]): + json_data = polar_user_data + return MockResponse(json_data,200) + elif polartcxtester.match(args[0]): filename = 'rowers/tests/testdata/polar_response.tcx' return MockContentResponse(filename,200) elif polarexercisetester.match(args[0]): diff --git a/rowers/tests/test_imports.py b/rowers/tests/test_imports.py index 7bee6d14..8e33a456 100644 --- a/rowers/tests/test_imports.py +++ b/rowers/tests/test_imports.py @@ -774,6 +774,9 @@ class PolarObjects(DjangoTestCase): self.r.polartoken = '12' self.r.polarrefreshtoken = '123' + self.r.polaruserid = 475 + self.r.rowerplan = 'pro' + self.r.polar_auto_import = True self.r.polartokenexpirydate = arrow.get(datetime.datetime.now()+datetime.timedelta(days=100)).datetime @@ -821,6 +824,34 @@ class PolarObjects(DjangoTestCase): res = polarstuff.get_polar_workouts(self.r.user) self.assertEqual(len(res),2) + @patch('rowers.polarstuff.requests.post', side_effect=mocked_requests) + @patch('rowers.polarstuff.requests.get', side_effect=mocked_requests) + def test_polar_callback(self, mock_get, mock_post): + response = self.c.get('/polarflowcallback?code=abcdef&state=12sdss',follow=True) + + self.assertEqual(response.status_code,200) + + @patch('rowers.polarstuff.requests.post', side_effect=mocked_requests) + @patch('rowers.polarstuff.requests.get', side_effect=mocked_requests) + def test_polar_notifications(self, mock_get, mock_post): + data = polarstuff.get_polar_notifications() + + self.assertEqual(data[0]['user-id'],475) + + response = polarstuff.get_all_new_workouts(data) + self.assertEqual(response,1) + + @patch('rowers.polarstuff.requests.post', side_effect=mocked_requests) + @patch('rowers.polarstuff.requests.get', side_effect=mocked_requests) + def test_polar_get_workout(self, mock_get, mock_post): + transaction_id = 240522162 + id = 1937529874 + + response = polarstuff.get_polar_workout(self.u, id, transaction_id) + self.assertEqual(len(response),14836) + + + #@pytest.mark.django_db @override_settings(TESTING=True) class RP3Objects(DjangoTestCase): diff --git a/rowers/tests/testdata/testdata.tcx.gz b/rowers/tests/testdata/testdata.tcx.gz deleted file mode 100644 index 2469a2d9..00000000 Binary files a/rowers/tests/testdata/testdata.tcx.gz and /dev/null differ diff --git a/rowers/views/importviews.py b/rowers/views/importviews.py index 9d8acd99..0237c833 100644 --- a/rowers/views/importviews.py +++ b/rowers/views/importviews.py @@ -200,7 +200,7 @@ def rower_strava_authorize(request): # pragma: no cover # Polar Authorization @login_required() -def rower_polar_authorize(request): +def rower_polar_authorize(request): # pragma: no cover state = str(uuid4()) @@ -413,14 +413,14 @@ def rower_process_polarcallback(request): error = request.GET.get('error','no error') dologging('polar.log','Callback: {error}'.format(error=error)) - if error != 'no error': + if error != 'no error': # pragma: no cover messages.error(request,error) url = reverse('rower_exportsettings_view') return HttpResponseRedirect(url) try: code = request.GET['code'] - except MultiValueDictKeyError: + except MultiValueDictKeyError: # pragma: no cover try: message = request.GET['error'] except MultiValueDictKeyError: @@ -432,6 +432,15 @@ def rower_process_polarcallback(request): return HttpResponseRedirect(url) access_token, expires_in, user_id = polarstuff.get_token(code) + polar_user_data = polarstuff.register_user(request.user) + + try: + user_id2 = polar_user_data['polar-user-id'] + except KeyError: + user_id2 = 0 + + if user_id2 != user_id: + messages.error(request,'Polar User ID error') expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in) @@ -442,8 +451,11 @@ def rower_process_polarcallback(request): r.save() - successmessage = "Tokens stored. Good to go. Please check your import/export settings" - messages.info(request,successmessage) + if user_id2 == user_id: + successmessage = "Tokens stored. Good to go. Please check your import/export settings" + messages.info(request,successmessage) + else: + messages.error(request,"Please contact support@rowsandall.com for help.") url = reverse('rower_exportsettings_view') return HttpResponseRedirect(url)