diff --git a/rowers/nkstuff.py b/rowers/nkstuff.py new file mode 100644 index 00000000..f5dbfb61 --- /dev/null +++ b/rowers/nkstuff.py @@ -0,0 +1,107 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals +from __future__ import unicode_literals, absolute_import + +import time +from time import strftime + +import requests + +#https://oauth-stage.nkrowlink.com/oauth/authorizegrant_type=authorization_code&response_type=code&client_id=rowsandall-staging&scope=read&state=fc8fc3d8-ce0a-443e-838a-1c06fb5317c6&redirect_uri=https%3A%2F%2Fdunav.ngrok.io%2Fnk_callback%2F +#https://oauth-stage.nkrowlink.com/oauth/authorize?grant_type=authorization_code&response_type=code&client_id=rowsandall-staging&scope=read&state=1234&redirect_uri=https%3A%2F%2Fdev.rowsandall.com%2Fnk_callback + +import django_rq +queue = django_rq.get_queue('default') +queuelow = django_rq.get_queue('low') +queuehigh = django_rq.get_queue('low') + +from rowers.rower_rules import is_workout_user, ispromember + +from iso8601 import ParseError +from rowers.utils import myqueue + +import rowers.mytypes as mytypes +import gzip + +from rowsandall_app.settings import ( + NK_CLIENT_ID, NK_REDIRECT_URI, NK_CLIENT_SECRET, + SITE_URL, NK_API_LOCATION + ) + +try: + from json.decoder import JSONDecodeError +except ImportError: + JSONDecodeError = ValueError + +from rowers.imports import * + +oauth_data = { + 'client_id': NK_CLIENT_ID, + 'client_secret': NK_CLIENT_SECRET, + 'redirect_uri': NK_REDIRECT_URI, + 'autorization_uri': "https://oauth-stage.nkrowlink.com/oauth/authorize", + 'content_type': 'application/json', + 'tokenname': 'nktoken', + 'refreshtokenname': 'nkrefreshtoken', + 'expirydatename': 'nktokenexpirydate', + 'bearer_auth': True, + 'base_url': "https://oauth-stage.nkrowlink.com/oauth/token", + 'grant_type': 'refresh_token', + 'scope':'read', + } + +def get_token(code): + return imports_get_token(code, oauth_data) + +def nk_open(user): + t = time.localtime() + timestamp = time.strftime('%b-%d-%Y_%H%M', t) + token = imports_open(user,oauth_data) + return token + +def do_refresh_token(refreshtoken): + return imports_do_refresh_token(refreshtoken, oauth_data) + +def rower_nk_token_refresh(user): + r = Rower.objects.get(user=user) + res = do_refresh_token(r.nkrefreshtoken) + access_token = res[0] + expires_in = res[1] + refresh_token = res[2] + expirydatetime = timezone.now()+timedelta(seconds=expires_in) + + r.nktoken = access_token + r.nktokenexpirydate = expirydatetime + r.nkrefreshtoken = refresh_token + r.save() + + return r.nktoken + +def make_authorization_url(request): + return imports_make_authorization_url(oauth_data) + +def get_nk_workout_list(user,fake=False): + r = Rower.objects.get(user=user) + + if (r.nktoken == '') or (r.nktoken is None): + s = "Token doesn't exist. Need to authorize" + return custom_exception_handler(401,s) + elif (r.nktokenexpirydate is None or timezone.now()+timedelta(seconds=3599)>r.nktokenexpirydate): + s = "Token expired. Needs to refresh." + return custom_exception_handler(401,s) + else: + # ready to fetch. Hurray + authorizationstring = str('Bearer ' + r.nktoken) + headers = {'Authorization': authorizationstring, + 'user-agent': 'sanderroosendaal', + 'Content-Type': 'application/json'} + + url = NK_API_LOCATION+"api/v1/sessions" + + params = {} # start / end time + + s = requests.get(url,headers=headers,params=params) + + return s diff --git a/rowers/stravastuff.py b/rowers/stravastuff.py index 2cef3373..d0914b77 100644 --- a/rowers/stravastuff.py +++ b/rowers/stravastuff.py @@ -131,8 +131,6 @@ def strava_establish_push(): response = requests.post(url,data=post_data) - print(response.json()) - return response.status_code def strava_list_push(): diff --git a/rowers/templates/nk_list_import.html b/rowers/templates/nk_list_import.html new file mode 100644 index 00000000..44ae0554 --- /dev/null +++ b/rowers/templates/nk_list_import.html @@ -0,0 +1,58 @@ +{% extends "newbase.html" %} +{% load staticfiles %} +{% load rowerfilters %} + +{% block title %}Workouts{% endblock %} + +{% block main %} +
This imports all workouts that have not been imported to rowsandall.com. + The action may take a longer time to process, so please be patient. Click on Import in the list below to import an individual workout. +
+| Import | +Name | +Date | +Duration | +Distance | +New | +
|---|---|---|---|---|---|
| + Import | +{{ workout|lookup:'name' }} | +{{ workout|lookup:'starttime' }} | +{{ workout|lookup:'duration' }} | +{{ workout|lookup:'distance' }} m | +{{ workout|lookup:'new' }} | +
+ No workouts found +
+ {% endif %} + {% endblock %} + +{% block sidebar %} +{% include 'menu_workouts.html' %} +{% endblock %} diff --git a/rowers/tests/mocks.py b/rowers/tests/mocks.py index 35adee66..79fe3065 100644 --- a/rowers/tests/mocks.py +++ b/rowers/tests/mocks.py @@ -621,6 +621,8 @@ def mocked_requests(*args, **kwargs): stravaworkoutlist = json.load(open('rowers/tests/testdata/stravaworkoutlist.txt')) sporttracksworkoutlist = json.load(open('rowers/tests/testdata/sporttracksworkouts.txt')) + nkworkoutlist = json.load(open('rowers/tests/testdata/nkworkouts.txt')) + rkworkoutlistjson = json.load(open('rowers/tests/testdata/rkworkoutslist.txt','r')) uaworkoutlistjson = json.load(open('rowers/tests/testdata/uaworkoutlist.txt','r')) @@ -732,6 +734,7 @@ def mocked_requests(*args, **kwargs): rktester = re.compile('.*?runkeeper\.com') uatester = re.compile('.*?mapmyfitness\.com') tptester = re.compile('.*?trainingpeaks\.com') + nktester = re.compile('.*?nkrowlink\.com') garmintester = re.compile('.*?garmin\.com') c2importregex = '.*?concept2.com\/api\/users\/me\/results\/\d+' @@ -746,6 +749,9 @@ def mocked_requests(*args, **kwargs): c2workoutlistregex = '.*?concept2\.com\/api\/users\/me\/results\?page=\d' c2workoutlisttester = re.compile(c2workoutlistregex) + nkworkoutlistregex = '.*?nkrowlink\.com\/api\/v1\/sessions' + nkworkoutlisttester = re.compile(nkworkoutlistregex) + stravaathleteregex = '.*?strava\.com\/api\/v3\/athlete$' stravaathletetester = re.compile(stravaathleteregex) @@ -796,6 +802,7 @@ def mocked_requests(*args, **kwargs): garmindownloadregex = '.*?garmin\.com\/mockfile?id=1' garmindownloadtester = re.compile(garmindownloadregex) + if garmintester.match(args[0]): if garmindownloadtester.match(args[0]): return MockStreamResponse('rowers/tests/testdata/3x250m.fit',200) @@ -910,6 +917,20 @@ def mocked_requests(*args, **kwargs): return MockResponse(sporttracksworkoutlist,200) + if nktester.match(args[0]): + if nkworkoutlisttester.match(args[0]): + return MockResponse(nkworkoutlist,200) + elif 'token' in args[0]: + json_data = { + "token_type": "Bearer", + "access_token": "987654321234567898765432123456789", + "refresh_token": "1234567898765432112345678987654321", + "expires_at": arrow.now().timestamp()+7200 + } + + return MockResponse(json_data,200) + + if stravatester.match(args[0]): if stravaworkoutlisttester.match(args[0]): return MockResponse(stravaworkoutlist,200) diff --git a/rowers/tests/test_imports.py b/rowers/tests/test_imports.py index a83d534e..9b0fb73c 100644 --- a/rowers/tests/test_imports.py +++ b/rowers/tests/test_imports.py @@ -360,6 +360,70 @@ class C2ObjectsTokenExpired(DjangoTestCase): self.assertEqual(response.status_code, 200) +#@pytest.mark.django_db +@override_settings(TESTING=True) +class NKObjects(DjangoTestCase): + def setUp(self): + self.c = Client() + self.u = User.objects.create_user('john', + 'sander@ds.ds', + 'koeinsloot') + + self.u.first_name = 'John' + self.u.last_name = 'Sander' + self.u.save() + self.r = Rower.objects.create(user=self.u,gdproptin=True,surveydone=True, + gdproptindate=timezone.now() + ) + + self.r.nktoken = '12' + self.r.nkrefreshtoken = '123' + self.r.nktokenexpirydate = datetime.datetime.now()-datetime.timedelta(days=1) + + self.r.save() + self.c.login(username='john',password='koeinsloot') + self.nu = datetime.datetime.now() + + filename = 'rowers/tests/testdata/testdata.csv' + + rr = rrower(hrmax=self.r.max,hrut2=self.r.ut2, + hrut1=self.r.ut1,hrat=self.r.at, + hrtr=self.r.tr,hran=self.r.an,ftp=self.r.ftp) + row = rdata(filename,rower=rr) + totaldist = row.df['cum_dist'].max() + totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min() + totaltime = totaltime+row.df.loc[:,' ElapsedTime (sec)'].iloc[0] + + + hours = int(totaltime/3600.) + minutes = int((totaltime - 3600.*hours)/60.) + seconds = int(totaltime - 3600.*hours - 60.*minutes) + tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds)) + + duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths) + + + workoutdate = row.rowdatetime.strftime('%Y-%m-%d') + workoutstarttime = row.rowdatetime.strftime('%H:%M:%S') + + self.w = Workout.objects.create( + name='testworkout',workouttype='water', + user=self.r,date=self.nu.strftime('%Y-%m-%d'), + starttime=workoutstarttime, + startdatetime=row.rowdatetime, + duration=duration,distance=totaldist, + csvfilename=filename + ) + + @patch('rowers.nkstuff.requests.get', side_effect=mocked_requests) + @patch('rowers.nkstuff.requests.post', side_effect=mocked_requests) + def test_nk_list(self, mock_get, mockpost): + result = rowers.nkstuff.rower_nk_token_refresh(self.u) + self.assertEqual(result,"987654321234567898765432123456789") + response = self.c.get('/rowers/workout/nkimport/') + + self.assertEqual(response.status_code,200) + #@pytest.mark.django_db diff --git a/rowers/tests/testdata/nkworkoutlist.txt b/rowers/tests/testdata/nkworkoutlist.txt new file mode 100644 index 00000000..320a47cc --- /dev/null +++ b/rowers/tests/testdata/nkworkoutlist.txt @@ -0,0 +1,84 @@ +[ + { + "id": 1, + "name": "JustGo-7189M", + "type": 0, + "speedInput": 0, + "startTime": 1614264826, + "endTime": 1614269826, + "location": "Some Location", + "deviceId": 11, + "elapsedTime": 1140000, + "totalDistanceImp": 2920.69, + "totalDistanceGps": 2286.5, + "avgPaceImp": 195159.36302723, + "avgPaceGps": 250466.812537141, + "avgStrokeRate": 19.5, + "distStrokeImp": 8.51, + "distStrokeGps": 6.77, + "avgHeartRate": 158, + "totalStrokeCount": 343, + "totalCalories": 4959900, + "avgCalHour": 4482062.35252774, + "avgSpeedGps": 2, + "avgSpeedImp": 2.56, + "avgPower": 68.6979270660324, + "avgCatch": -46.7961491475831, + "avgSlip": 16.1616225246003, + "avgFinish": 48.1512290049444, + "avgWash": 22.9514686031976, + "avgForceAvg": 114.502194258813, + "avgWork": 210.785493336831, + "avgForceMax": 249.481436977143, + "avgMaxForceAngle": 4.75378911974861, + "startGpsLat": 39.7356346, + "startGpsLon": -75.5581928, + "intervals": [ + { + "id": 1, + "sessionId": 1, + "startTime": 1614264826, + "intervalNumber": 1, + "sessionStrokeStartIndex": 473, + "sessionStrokeEndIndex": 674, + "sessionStrokeCount": 91, + "elapsedTime": 1140000, + "totalDistanceImp": 2920.69, + "totalDistanceGps": 2286.5, + "avgPaceImp": 195159.36302723, + "avgPaceGps": 250466.812537141, + "avgStrokeRate": 19.5, + "distStrokeImp": 8.51, + "distStrokeGps": 6.77, + "avgHeartRate": 158, + "totalStrokeCount": 343, + "totalCalories": 4959900, + "avgCalHour": 4482062.35252774, + "avgSpeedGps": 2, + "avgSpeedImp": 2.56, + "avgPower": 68.6979270660324, + "avgCatch": -46.7961491475831, + "avgSlip": 16.1616225246003, + "avgFinish": 48.1512290049444, + "avgWash": 22.9514686031976, + "avgForceAvg": 114.502194258813, + "avgWork": 210.785493336831, + "avgForceMax": 249.481436977143, + "avgMaxForceAngle": 4.75378911974861, + "startGpsLat": 39.7356346, + "startGpsLon": -75.5581928 + } + ], + "oarlockSessions": [ + { + "id": 1, + "sessionId": 1, + "boatName": "Fast Boat", + "seatNumber": 3, + "portStarboard": 0, + "oarLength": 284, + "oarInboardLength": 85 + } + ] + } +] diff --git a/rowers/tests/testdata/nkworkouts.txt b/rowers/tests/testdata/nkworkouts.txt new file mode 100644 index 00000000..320a47cc --- /dev/null +++ b/rowers/tests/testdata/nkworkouts.txt @@ -0,0 +1,84 @@ +[ + { + "id": 1, + "name": "JustGo-7189M", + "type": 0, + "speedInput": 0, + "startTime": 1614264826, + "endTime": 1614269826, + "location": "Some Location", + "deviceId": 11, + "elapsedTime": 1140000, + "totalDistanceImp": 2920.69, + "totalDistanceGps": 2286.5, + "avgPaceImp": 195159.36302723, + "avgPaceGps": 250466.812537141, + "avgStrokeRate": 19.5, + "distStrokeImp": 8.51, + "distStrokeGps": 6.77, + "avgHeartRate": 158, + "totalStrokeCount": 343, + "totalCalories": 4959900, + "avgCalHour": 4482062.35252774, + "avgSpeedGps": 2, + "avgSpeedImp": 2.56, + "avgPower": 68.6979270660324, + "avgCatch": -46.7961491475831, + "avgSlip": 16.1616225246003, + "avgFinish": 48.1512290049444, + "avgWash": 22.9514686031976, + "avgForceAvg": 114.502194258813, + "avgWork": 210.785493336831, + "avgForceMax": 249.481436977143, + "avgMaxForceAngle": 4.75378911974861, + "startGpsLat": 39.7356346, + "startGpsLon": -75.5581928, + "intervals": [ + { + "id": 1, + "sessionId": 1, + "startTime": 1614264826, + "intervalNumber": 1, + "sessionStrokeStartIndex": 473, + "sessionStrokeEndIndex": 674, + "sessionStrokeCount": 91, + "elapsedTime": 1140000, + "totalDistanceImp": 2920.69, + "totalDistanceGps": 2286.5, + "avgPaceImp": 195159.36302723, + "avgPaceGps": 250466.812537141, + "avgStrokeRate": 19.5, + "distStrokeImp": 8.51, + "distStrokeGps": 6.77, + "avgHeartRate": 158, + "totalStrokeCount": 343, + "totalCalories": 4959900, + "avgCalHour": 4482062.35252774, + "avgSpeedGps": 2, + "avgSpeedImp": 2.56, + "avgPower": 68.6979270660324, + "avgCatch": -46.7961491475831, + "avgSlip": 16.1616225246003, + "avgFinish": 48.1512290049444, + "avgWash": 22.9514686031976, + "avgForceAvg": 114.502194258813, + "avgWork": 210.785493336831, + "avgForceMax": 249.481436977143, + "avgMaxForceAngle": 4.75378911974861, + "startGpsLat": 39.7356346, + "startGpsLon": -75.5581928 + } + ], + "oarlockSessions": [ + { + "id": 1, + "sessionId": 1, + "boatName": "Fast Boat", + "seatNumber": 3, + "portStarboard": 0, + "oarLength": 284, + "oarInboardLength": 85 + } + ] + } +] diff --git a/rowers/urls.py b/rowers/urls.py index e6d1fc04..977c80c9 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -567,6 +567,7 @@ urlpatterns = [ re_path(r'^workout/stravaimport/user/(?P