diff --git a/rowers/sporttracksstuff.py b/rowers/sporttracksstuff.py index a2fce141..cdc1ccaf 100644 --- a/rowers/sporttracksstuff.py +++ b/rowers/sporttracksstuff.py @@ -1,3 +1,5 @@ +# All the functionality to connect to SportTracks + # Python import oauth2 as oauth import cgi @@ -31,6 +33,8 @@ from rowers.models import Rower,Workout from rowsandall_app.settings import C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET, SPORTTRACKS_CLIENT_SECRET, SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI +# Custom exception handler, returns a 401 HTTP message +# with exception details in the json data def custom_exception_handler(exc,message): response = { @@ -48,6 +52,7 @@ def custom_exception_handler(exc,message): return res +# Refresh ST token using refresh token def do_refresh_token(refreshtoken): client_auth = requests.auth.HTTPBasicAuth(SPORTTRACKS_CLIENT_ID, SPORTTRACKS_CLIENT_SECRET) post_data = {"grant_type": "refresh_token", @@ -75,7 +80,7 @@ def do_refresh_token(refreshtoken): return [thetoken,expires_in,refresh_token] - +# Exchange ST access code for long-lived ST access token def get_token(code): client_auth = requests.auth.HTTPBasicAuth(SPORTTRACKS_CLIENT_ID, SPORTTRACKS_CLIENT_SECRET) post_data = {"grant_type": "authorization_code", @@ -100,6 +105,7 @@ def get_token(code): return [thetoken,expires_in,refresh_token] +# Make authorization URL including random string def make_authorization_url(request): # Generate a random string for the state parameter # Save it for use later to prevent xsrf attacks @@ -118,7 +124,7 @@ def make_authorization_url(request): return HttpResponseRedirect(url) - +# This is token refresh. Looks for tokens in our database, then refreshes def rower_sporttracks_token_refresh(user): r = Rower.objects.get(user=user) res = do_refresh_token(r.sporttracksrefreshtoken) @@ -135,6 +141,7 @@ def rower_sporttracks_token_refresh(user): r.save() return r.sporttrackstoken +# Get list of workouts available on SportTracks def get_sporttracks_workout_list(user): r = Rower.objects.get(user=user) if (r.sporttrackstoken == '') or (r.sporttrackstoken is None): @@ -154,7 +161,7 @@ def get_sporttracks_workout_list(user): return s - +# Get workout summary data by SportTracks ID def get_sporttracks_workout(user,sporttracksid): r = Rower.objects.get(user=user) if (r.sporttrackstoken == '') or (r.sporttrackstoken is None): @@ -174,6 +181,7 @@ def get_sporttracks_workout(user,sporttracksid): return s +# Create Workout Data for upload to SportTracks def createsporttracksworkoutdata(w): filename = w.csvfilename try: @@ -272,6 +280,8 @@ def createsporttracksworkoutdata(w): return data +# Obtain SportTracks Workout ID from the response returned on successful +# upload def getidfromresponse(response): t = json.loads(response.text) uri = t['uris'][0] diff --git a/rowers/stravastuff.py b/rowers/stravastuff.py index 85ce5c7c..5a3d59a1 100644 --- a/rowers/stravastuff.py +++ b/rowers/stravastuff.py @@ -1,3 +1,5 @@ +# All the functionality needed to connect to Strava + # Python import oauth2 as oauth import cgi @@ -30,6 +32,9 @@ import stravalib from rowsandall_app.settings import C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET +# Exponentially weighted moving average +# Used for data smoothing of the jagged data obtained by Strava +# See bitbucket issue 72 def ewmovingaverage(interval,window_size): # Experimental code using Exponential Weighted moving average @@ -45,45 +50,11 @@ def ewmovingaverage(interval,window_size): return interval2 -def geo_distance(lat1,lon1,lat2,lon2): - """ Approximate distance and bearing between two points - defined by lat1,lon1 and lat2,lon2 - This is a slight underestimate but is close enough for our purposes, - We're never moving more than 10 meters between trackpoints - - Bearing calculation fails if one of the points is a pole. - - """ - - # radius of earth in km - R = 6373.0 - - # pi - pi = math.pi - - lat1 = math.radians(lat1) - lat2 = math.radians(lat2) - lon1 = math.radians(lon1) - lon2 = math.radians(lon2) - - dlon = lon2 - lon1 - dlat = lat2 - lat1 - - a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2 - c = 2 * atan2(sqrt(a), sqrt(1 - a)) - - distance = R * c - - tc1 = atan2(sin(lon2-lon1)*cos(lat2), - cos(lat1)*sin(lat2)-sin(lat1)*cos(lat2)*cos(lon2-lon1)) - - tc1 = tc1 % (2*pi) - - bearing = math.degrees(tc1) - - return [distance,bearing] +from utils import geo_distance +# Custom exception handler, returns a 401 HTTP message +# with exception details in the json data def custom_exception_handler(exc,message): response = { @@ -101,6 +72,7 @@ def custom_exception_handler(exc,message): return res +# Exchange access code for long-lived access token def get_token(code): client_auth = requests.auth.HTTPBasicAuth(STRAVA_CLIENT_ID, STRAVA_CLIENT_SECRET) post_data = {"grant_type": "authorization_code", @@ -118,7 +90,7 @@ def get_token(code): return [thetoken] - +# Make authorization URL including random string def make_authorization_url(request): # Generate a random string for the state parameter # Save it for use later to prevent xsrf attacks @@ -134,6 +106,7 @@ def make_authorization_url(request): return HttpResponseRedirect(url) +# Get list of workouts available on Strava def get_strava_workout_list(user): r = Rower.objects.get(user=user) if (r.stravatoken == '') or (r.stravatoken is None): @@ -150,6 +123,7 @@ def get_strava_workout_list(user): return s +# Get a Strava workout summary data and stroke data by ID def get_strava_workout(user,stravaid): r = Rower.objects.get(user=user) if (r.stravatoken == '') or (r.stravatoken is None): @@ -236,6 +210,7 @@ def get_strava_workout(user,stravaid): return [workoutsummary,df] +# Generate Workout data for Strava (a TCX file) def createstravaworkoutdata(w): filename = w.csvfilename try: @@ -247,6 +222,8 @@ def createstravaworkoutdata(w): return tcxfilename +# Upload the TCX file to Strava and set the workout activity type +# to rowing on Strava def handle_stravaexport(file,workoutname,stravatoken,description=''): # w = Workout.objects.get(id=workoutid) client = stravalib.Client(access_token=stravatoken) @@ -264,12 +241,6 @@ def handle_stravaexport(file,workoutname,stravatoken,description=''): errorlog.write(timestr+errorstring+"\r\n") errorlog.write("stravastuff.py line 262\r\n") - - - # w.uploadedtostrava = res.id - # w.save() - # file.close() - return res.id diff --git a/rowers/utils.py b/rowers/utils.py new file mode 100644 index 00000000..1d40f487 --- /dev/null +++ b/rowers/utils.py @@ -0,0 +1,42 @@ +import math + +def geo_distance(lat1,lon1,lat2,lon2): + """ Approximate distance and bearing between two points + defined by lat1,lon1 and lat2,lon2 + This is a slight underestimate but is close enough for our purposes, + We're never moving more than 10 meters between trackpoints + + Bearing calculation fails if one of the points is a pole. + (Hey, from the North pole you can walk South, East, North and end up + on the same spot!) + + """ + + # radius of our earth in km --> should be moved to settings if + # rowing takes off on other planets + R = 6373.0 + + # pi + pi = math.pi + + lat1 = math.radians(lat1) + lat2 = math.radians(lat2) + lon1 = math.radians(lon1) + lon2 = math.radians(lon2) + + dlon = lon2 - lon1 + dlat = lat2 - lat1 + + a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2 + c = 2 * atan2(sqrt(a), sqrt(1 - a)) + + distance = R * c + + tc1 = atan2(sin(lon2-lon1)*cos(lat2), + cos(lat1)*sin(lat2)-sin(lat1)*cos(lat2)*cos(lon2-lon1)) + + tc1 = tc1 % (2*pi) + + bearing = math.degrees(tc1) + + return [distance,bearing] diff --git a/rowers/views.py b/rowers/views.py index 0d35450e..0700767e 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -189,47 +189,7 @@ def splitstdata(lijst): return [np.array(t),np.array(latlong)] - -def geo_distance(lat1,lon1,lat2,lon2): - """ Approximate distance and bearing between two points - defined by lat1,lon1 and lat2,lon2 - This is a slight underestimate but is close enough for our purposes, - We're never moving more than 10 meters between trackpoints - - Bearing calculation fails if one of the points is a pole. - (Hey, from the North pole you can walk South, East, North and end up - on the same spot!) - - """ - - # radius of our earth in km --> should be moved to settings if - # rowing takes off on other planets - R = 6373.0 - - # pi - pi = math.pi - - lat1 = math.radians(lat1) - lat2 = math.radians(lat2) - lon1 = math.radians(lon1) - lon2 = math.radians(lon2) - - dlon = lon2 - lon1 - dlat = lat2 - lat1 - - a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2 - c = 2 * atan2(sqrt(a), sqrt(1 - a)) - - distance = R * c - - tc1 = atan2(sin(lon2-lon1)*cos(lat2), - cos(lat1)*sin(lat2)-sin(lat1)*cos(lat2)*cos(lon2-lon1)) - - tc1 = tc1 % (2*pi) - - bearing = math.degrees(tc1) - - return [distance,bearing] +from utils import geo_distance # Check if a user is a Pro member def promember(user):