Private
Public Access
1
0

c2, strava done, almost done st

This commit is contained in:
Sander Roosendaal
2018-07-03 17:02:45 +02:00
parent 3421d1a685
commit ce53c29fee
7 changed files with 242 additions and 45 deletions

View File

@@ -4,7 +4,7 @@
# move that here.) # move that here.)
from rowers.imports import * from rowers.imports import *
import datetime
from requests import Request, Session from requests import Request, Session
from rowers.types import otwtypes from rowers.types import otwtypes
@@ -310,9 +310,9 @@ def createc2workoutdata_as_splits(w):
split_data.append(thisrecord) split_data.append(thisrecord)
try: try:
durationstr = datetime.strptime(str(w.duration),"%H:%M:%S.%f") durationstr = datetime.datetime.strptime(str(w.duration),"%H:%M:%S.%f")
except ValueError: except ValueError:
durationstr = datetime.strptime(str(w.duration),"%H:%M:%S") durationstr = datetime.datetime.strptime(str(w.duration),"%H:%M:%S")
try: try:
newnotes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com' newnotes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com'
@@ -379,13 +379,18 @@ def createc2workoutdata(w):
stroke_data.append(thisrecord) stroke_data.append(thisrecord)
try: try:
durationstr = datetime.strptime(str(w.duration),"%H:%M:%S.%f") durationstr = datetime.datetime.strptime(str(w.duration),"%H:%M:%S.%f")
except ValueError: except ValueError:
durationstr = datetime.strptime(str(w.duration),"%H:%M:%S") durationstr = datetime.datetime.strptime(str(w.duration),"%H:%M:%S")
workouttype = w.workouttype workouttype = w.workouttype
if workouttype in otwtypes: if workouttype in otwtypes:
workouttype = 'water' workouttype = 'water'
try:
startdatetime = w.startdatetime.isoformat()
except AttributeError:
startdate = datetime.datetime.combine(w.date,datetime.time())
data = { data = {
"type": workouttype, "type": workouttype,
@@ -673,7 +678,8 @@ def workout_c2_upload(user,w):
w.save() w.save()
elif (response.status_code == 201 or response.status_code == 200): elif (response.status_code == 201 or response.status_code == 200):
try: try:
s= json.loads(response.text) # s= json.loads(response.text)
s = response.json()
c2id = s['data']['id'] c2id = s['data']['id']
w.uploadedtoc2 = c2id w.uploadedtoc2 = c2id
w.save() w.save()
@@ -696,7 +702,7 @@ def rower_c2_token_refresh(user):
access_token = res[0] access_token = res[0]
expires_in = res[1] expires_in = res[1]
refresh_token = res[2] refresh_token = res[2]
expirydatetime = timezone.now()+timedelta(seconds=expires_in) expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
r.c2token = access_token r.c2token = access_token

View File

@@ -1,7 +1,7 @@
# All the functionality to connect to SportTracks # All the functionality to connect to SportTracks
from rowers.imports import * from rowers.imports import *
import re
from rowsandall_app.settings import ( from rowsandall_app.settings import (
C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET,
STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET,
@@ -54,7 +54,7 @@ def do_refresh_token(refreshtoken):
except (TypeError,ValueError): except (TypeError,ValueError):
expires_in = 0 expires_in = 0
return [thetoken,expires_in,refresh_token] return [thetoken,expires_in,refresh_token]
# Exchange ST access code for long-lived ST access token # Exchange ST access code for long-lived ST access token
@@ -74,7 +74,8 @@ def get_token(code):
response = requests.post(url, response = requests.post(url,
data=json.dumps(post_data), data=json.dumps(post_data),
headers=headers) headers=headers)
if response.status_code == 200 or response.status_code == 201: if response.status_code == 200 or response.status_code == 201:
token_json = response.json() token_json = response.json()
thetoken = token_json['access_token'] thetoken = token_json['access_token']
@@ -90,7 +91,6 @@ def get_token(code):
else: else:
return [0,0,0] return [0,0,0]
return [thetoken,expires_in,refresh_token] return [thetoken,expires_in,refresh_token]
# Make authorization URL including random string # Make authorization URL including random string
@@ -127,6 +127,7 @@ def rower_sporttracks_token_refresh(user):
r.sporttracksrefreshtoken = refresh_token r.sporttracksrefreshtoken = refresh_token
r.save() r.save()
return r.sporttrackstoken return r.sporttrackstoken
# Get list of workouts available on SportTracks # Get list of workouts available on SportTracks
@@ -201,9 +202,9 @@ def createsporttracksworkoutdata(w):
d[0] = d[1] d[0] = d[1]
t = t.astype(int) t = t.astype(int)
d = d.astype(int) d = d.astype(int)
spm = row.df[' Cadence (stokes/min)'].astype(int) spm = row.df[' Cadence (stokes/min)'].astype(int).values
spm[0] = spm[1] spm[0] = spm[1]
hr = row.df[' HRCur (bpm)'].astype(int) hr = row.df[' HRCur (bpm)'].astype(int).values
haslatlon=1 haslatlon=1
@@ -218,7 +219,7 @@ def createsporttracksworkoutdata(w):
haspower = 1 haspower = 1
try: try:
power = row.df[' Power (watts)'].values power = row.df[' Power (watts)'].astype(int).values
except KeyError: except KeyError:
haspower = 0 haspower = 0
@@ -289,9 +290,12 @@ def createsporttracksworkoutdata(w):
# Obtain SportTracks Workout ID from the response returned on successful # Obtain SportTracks Workout ID from the response returned on successful
# upload # upload
def getidfromresponse(response): def getidfromresponse(response):
t = json.loads(response.text) t = response.json()
uri = t['uris'][0] uri = t['uris'][0]
id = uri[len(uri)-13:len(uri)-5] regex = '.*?sporttracks\.mobi\/api\/v2\/fitnessActivity/(\d+)$'
m = re.compile(regex).match(uri).group(1)
id = int(m)
return int(id) return int(id)
@@ -310,7 +314,7 @@ def workout_sporttracks_upload(user,w):
message = "Data error" message = "Data error"
stid = 0 stid = 0
return message,stid return message,stid
authorizationstring = str('Bearer ' + thetoken) authorizationstring = str('Bearer ' + thetoken)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
@@ -327,7 +331,7 @@ def workout_sporttracks_upload(user,w):
w.save() w.save()
return message, stid return message, stid
elif (response.status_code == 201 or response.status_code==200): elif (response.status_code == 201 or response.status_code==200):
s= json.loads(response.text) s= response.json()
stid = getidfromresponse(response) stid = getidfromresponse(response)
w.uploadedtosporttracks = stid w.uploadedtosporttracks = stid
w.save() w.save()

View File

@@ -534,9 +534,10 @@ def workout_strava_upload(user,w):
tcxfile,tcxmesg = createstravaworkoutdata(w) tcxfile,tcxmesg = createstravaworkoutdata(w)
if tcxfile: if tcxfile:
with open(tcxfile,'rb') as f: with open(tcxfile,'rb') as f:
res,mes = handle_stravaexport(f,w.name, res,mes = handle_stravaexport(
r.stravatoken, f,w.name,
description=w.notes+'\n from '+w.workoutsource+' via rowsandall.com') r.stravatoken,
description=w.notes+'\n from '+w.workoutsource+' via rowsandall.com')
if res==0: if res==0:
message = mes message = mes
w.uploadedtostrava = -1 w.uploadedtostrava = -1

File diff suppressed because one or more lines are too long

View File

@@ -60,7 +60,28 @@ def mocked_requests(*args, **kwargs):
c2strokedata = json.load(infile) c2strokedata = json.load(infile)
c2workoutlist = json.load(open('rowers/testdata/c2workoutlist.txt')) c2workoutlist = json.load(open('rowers/testdata/c2workoutlist.txt'))
c2uploadjson = {
"data": {
"id": 339,
"user_id": 1,
"date": "2015-08-05 13:15:41",
"timezone": "Europe/London",
"date_utc": "2015-08-05 12:15:41",
"distance": 5649,
"type": "rower",
"time": 8649,
"time_formatted": "14:24.9",
"workout_type": "JustRow",
"source": "ErgData",
"weight_class": "H",
"verified": True,
"ranked": False
}
}
stravaworkoutlist = json.load(open('rowers/testdata/stravaworkoutlist.txt')) stravaworkoutlist = json.load(open('rowers/testdata/stravaworkoutlist.txt'))
sporttracksworkoutlist = json.load(open('rowers/testdata/sporttracksworkouts.txt'))
stravasummaryjson = json.load(open('rowers/testdata/stravaworkoutsummary.txt','r')) stravasummaryjson = json.load(open('rowers/testdata/stravaworkoutsummary.txt','r'))
stravatimejson = json.load(open('rowers/testdata/stravatimetestdata.txt','r')) stravatimejson = json.load(open('rowers/testdata/stravatimetestdata.txt','r'))
@@ -107,10 +128,14 @@ def mocked_requests(*args, **kwargs):
c2tester = re.compile('.*?log\.concept2\.com') c2tester = re.compile('.*?log\.concept2\.com')
stravatester = re.compile('.*?strava\.com') stravatester = re.compile('.*?strava\.com')
sttester = re.compile('.*?sporttracks\.mobi')
c2importregex = '.*?concept2.com\/api\/users\/me\/results\/\d+' c2importregex = '.*?concept2.com\/api\/users\/me\/results\/\d+'
c2importtester = re.compile(c2importregex) c2importtester = re.compile(c2importregex)
c2uploadregex = '.*?concept2.com\/api\/users\/\d+\/results$'
c2uploadtester = re.compile(c2uploadregex)
c2strokesregex = '.*?concept2.com\/api\/users\/me\/results\/\d+\/strokes' c2strokesregex = '.*?concept2.com\/api\/users\/me\/results\/\d+\/strokes'
c2strokestester = re.compile(c2strokesregex) c2strokestester = re.compile(c2strokesregex)
@@ -125,6 +150,31 @@ def mocked_requests(*args, **kwargs):
stravasummaryregex = '.*?strava\.com\/api\/v3\/activities\/\d+$' stravasummaryregex = '.*?strava\.com\/api\/v3\/activities\/\d+$'
stravasummarytester = re.compile(stravasummaryregex) stravasummarytester = re.compile(stravasummaryregex)
stuploadregex = '.*?sporttracks\.mobi\/api\/v2\/fitnessActivities.json$'
stuploadtester = re.compile(stuploadregex)
stworkoutlistregex = '.*?sporttracks\.mobi\/api\/v2\/fitnessActivities$'
stworkoutlisttester = re.compile(stworkoutlistregex)
if sttester.match(args[0]):
if 'oauth2/token' in args[0]:
json_data = {
'access_token': 'TA3n1vrNjuQJWw0TdCDHnjSmrjIPULhTlejMIWqq',
'expires_in': 604800,
'refresh_token': 'jHJhFzCfOOKB8oyiayubhLAlxaMkG3ruC1E8YxaR'
}
return MockResponse(json_data,200)
if stuploadtester.match(args[0]):
json_data = {
"uris": [
"https://api.sporttracks.mobi/api/v2/fitnessActivity/123456"
]
}
return MockResponse(json_data, 200)
if stworkoutlisttester.match(args[0]):
return MockResponse(sporttracksworkoutlist,200)
if stravatester.match(args[0]): if stravatester.match(args[0]):
if stravaworkoutlisttester.match(args[0]): if stravaworkoutlisttester.match(args[0]):
@@ -137,11 +187,13 @@ def mocked_requests(*args, **kwargs):
return MockResponse(stravasummaryjson,200) return MockResponse(stravasummaryjson,200)
if c2tester.match(args[0]): if c2tester.match(args[0]):
if c2uploadtester.match(args[0]):
return MockResponse(c2uploadjson,201)
if c2strokestester.match(args[0]): if c2strokestester.match(args[0]):
return MockResponse(c2strokedata,200) return MockResponse(c2strokedata,200)
elif c2importtester.match(args[0]): elif c2importtester.match(args[0]):
return MockResponse(c2workoutdata,200) return MockResponse(c2workoutdata,200)
elif c2workoutlisttester(args[0]): elif c2workoutlisttester.match(args[0]):
return MockResponse(c2workoutlist,200) return MockResponse(c2workoutlist,200)
elif 'access_token' in args[0]: elif 'access_token' in args[0]:
json_data = { json_data = {
@@ -185,10 +237,45 @@ class C2Objects(DjangoTestCase):
) )
self.r.c2token = '12' self.r.c2token = '12'
self.r.c2refreshtoken = 'ab'
self.r.tokenexpirydate = datetime.datetime.now()+datetime.timedelta(days=1) self.r.tokenexpirydate = datetime.datetime.now()+datetime.timedelta(days=1)
self.r.save() self.r.save()
self.c.login(username='john',password='koeinsloot') self.c.login(username='john',password='koeinsloot')
self.nu = datetime.datetime.now()
filename = 'rowers/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.ix[0,' ElapsedTime (sec)']
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='On-water',
user=self.r,date=self.nu.strftime('%Y-%m-%d'),
starttime=workoutstarttime,
startdatetime=row.rowdatetime,
duration=duration,distance=totaldist,
csvfilename=filename
)
@patch('rowers.c2stuff.requests.Session', side_effect=mocked_requests) @patch('rowers.c2stuff.requests.Session', side_effect=mocked_requests)
def test_c2_callback(self, mock_post): def test_c2_callback(self, mock_post):
response = self.c.get('/call_back?code=dsdoij232s',follow=True) response = self.c.get('/call_back?code=dsdoij232s',follow=True)
@@ -203,7 +290,18 @@ class C2Objects(DjangoTestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
@patch('rowers.c2stuff.requests.post', side_effect=mocked_requests)
@patch('rowers.c2stuff.requests.get', side_effect=mocked_requests)
def test_c2_upload(self, mock_get, mock_post):
response = self.c.get('/rowers/workout/1/c2uploadw/')
self.assertRedirects(response,
expected_url = '/rowers/workout/1/export',
status_code=302,target_status_code=200)
self.assertEqual(response.url, '/rowers/workout/1/export')
self.assertEqual(response.status_code, 302)
@patch('rowers.c2stuff.requests.post', side_effect=mocked_requests) @patch('rowers.c2stuff.requests.post', side_effect=mocked_requests)
def test_c2_list(self, mock_get): def test_c2_list(self, mock_get):
@@ -217,7 +315,7 @@ class C2Objects(DjangoTestCase):
response = self.c.get('/rowers/workout/c2import/12/',follow=True) response = self.c.get('/rowers/workout/c2import/12/',follow=True)
self.assertRedirects(response, self.assertRedirects(response,
expected_url='/rowers/workout/1/edit', expected_url='/rowers/workout/2/edit',
status_code=302,target_status_code=200) status_code=302,target_status_code=200)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
@@ -294,21 +392,111 @@ class StravaObjects(DjangoTestCase):
class STObjects(DjangoTestCase): class STObjects(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,
gdproptindate=timezone.now()
)
self.r.sporttrackstoken = '12'
self.r.sporttracksrefreshtoken = '12'
self.r.sporttrackstokenexpirydate = datetime.datetime.now()+datetime.timedelta(days=1)
self.r.save()
self.c.login(username='john',password='koeinsloot')
self.nu = datetime.datetime.now()
filename = 'rowers/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.ix[0,' ElapsedTime (sec)']
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='On-water',
user=self.r,date=self.nu.strftime('%Y-%m-%d'),
starttime=workoutstarttime,
startdatetime=row.rowdatetime,
duration=duration,distance=totaldist,
csvfilename=filename
)
@patch('rowers.sporttracksstuff.requests.post', side_effect=mocked_requests)
def test_sporttracks_callback(self, mock_post):
response = self.c.get('/sporttracks_callback?code=dsdoij232s',follow=True)
self.assertEqual(response.status_code, 200)
@patch('rowers.sporttracksstuff.requests.post', side_effect=mocked_requests)
def test_sporttracks_token_refresh(self, mock_post):
response = self.c.get('/rowers/me/sporttracksrefresh/',follow=True)
self.assertEqual(response.status_code, 200)
@patch('rowers.sporttracksstuff.requests.post', side_effect=mocked_requests)
@patch('rowers.sporttracksstuff.requests.get', side_effect=mocked_requests)
def test_sporttracks_upload(self, mock_get, mock_post):
response = self.c.get('/rowers/workout/1/sporttracksuploadw/')
self.assertRedirects(response,
expected_url = '/rowers/workout/1/export',
status_code=302,target_status_code=200)
self.assertEqual(response.url, '/rowers/workout/1/export')
self.assertEqual(response.status_code, 302)
@patch('rowers.sporttracksstuff.requests.get', side_effect=mocked_requests)
def test_sporttracks_list(self, mock_get):
response = self.c.get('/rowers/workout/sporttracksimport',follow=True)
self.assertEqual(response.status_code,200)
@patch('rowers.imports.requests.get', side_effect=mocked_requests)
def test_sporttracks_import(self, mock_get):
response = self.c.get('/rowers/workout/sporttracksimport/12/',follow=True)
self.assertRedirects(response,
expected_url='/rowers/workout/2/edit',
status_code=302,target_status_code=200)
self.assertEqual(response.status_code, 200)
def test_strokedata(self): def test_strokedata(self):
with open('rowers/testdata/sporttrackstestdata.txt','r') as infile: with open('rowers/testdata/sporttrackstestdata.txt','r') as infile:
data = json.load(infile) data = json.load(infile)
from rowers.sporttracksstuff import add_workout_from_data from rowers.sporttracksstuff import add_workout_from_data
u = User.objects.create_user('john', res = add_workout_from_data(self.u,1,data,data)
'sander@ds.ds',
'koeinsloot')
r = Rower.objects.create(user=u,gdproptin=True,
gdproptindate=timezone.now()
)
res = add_workout_from_data(u,1,data,data)
def test_strokedatanohr(self): def test_strokedatanohr(self):
with open('rowers/testdata/sporttrackstestnohr.txt','r') as infile: with open('rowers/testdata/sporttrackstestnohr.txt','r') as infile:
@@ -316,16 +504,9 @@ class STObjects(DjangoTestCase):
from rowers.sporttracksstuff import add_workout_from_data from rowers.sporttracksstuff import add_workout_from_data
u = User.objects.create_user('john',
'sander@ds.ds',
'koeinsloot')
r = Rower.objects.create(user=u,gdproptin=True,
gdproptindate=timezone.now()
)
res = add_workout_from_data(u,1,data,data) res = add_workout_from_data(self.u,1,data,data)
class TestErrorPages(TestCase): class TestErrorPages(TestCase):

View File

@@ -1661,7 +1661,6 @@ def workout_c2_upload_view(request,id=0):
w = get_workout(id) w = get_workout(id)
r = w.user r = w.user
try: try:
message,c2id = c2stuff.workout_c2_upload(request.user,w) message,c2id = c2stuff.workout_c2_upload(request.user,w)
except NoTokenError: except NoTokenError:
@@ -1678,7 +1677,9 @@ def workout_c2_upload_view(request,id=0):
'id':str(w.id), 'id':str(w.id),
}) })
return HttpResponseRedirect(url) response = HttpResponseRedirect(url)
return response
# Upload workout to RunKeeper # Upload workout to RunKeeper
@login_required() @login_required()
@@ -1821,6 +1822,7 @@ def workout_sporttracks_upload_view(request,id=0):
if (checkworkoutuser(request.user,w)): if (checkworkoutuser(request.user,w)):
data = sporttracksstuff.createsporttracksworkoutdata(w) data = sporttracksstuff.createsporttracksworkoutdata(w)
if not data: if not data:
message = "Data error" message = "Data error"
messages.error(request,message) messages.error(request,message)
@@ -1829,7 +1831,7 @@ def workout_sporttracks_upload_view(request,id=0):
'id':str(w.id), 'id':str(w.id),
}) })
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
authorizationstring = str('Bearer ' + thetoken) authorizationstring = str('Bearer ' + thetoken)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
@@ -1838,6 +1840,7 @@ def workout_sporttracks_upload_view(request,id=0):
url = "https://api.sporttracks.mobi/api/v2/fitnessActivities.json" url = "https://api.sporttracks.mobi/api/v2/fitnessActivities.json"
response = requests.post(url,headers=headers,data=json.dumps(data)) response = requests.post(url,headers=headers,data=json.dumps(data))
# check for duplicate error first # check for duplicate error first
if (response.status_code == 409 ): if (response.status_code == 409 ):
message = "Duplicate error" message = "Duplicate error"
@@ -1845,7 +1848,7 @@ def workout_sporttracks_upload_view(request,id=0):
w.uploadedtosporttracks = -1 w.uploadedtosporttracks = -1
w.save() w.save()
elif (response.status_code == 201 or response.status_code==200): elif (response.status_code == 201 or response.status_code==200):
s= json.loads(response.text) s= response.json()
sporttracksid = sporttracksstuff.getidfromresponse(response) sporttracksid = sporttracksstuff.getidfromresponse(response)
w.uploadedtosporttracks = sporttracksid w.uploadedtosporttracks = sporttracksid
w.save() w.save()

View File

@@ -415,3 +415,4 @@ try:
workoutemailbox = CFG['workoutemailbox'] workoutemailbox = CFG['workoutemailbox']
except KeyError: except KeyError:
workoutemailbox = 'workouts@rowsandall.com' workoutemailbox = 'workouts@rowsandall.com'