# All the functionality needed to connect to Strava # Python import oauth2 as oauth import cgi import requests import requests.auth import json from django.utils import timezone from datetime import datetime import numpy as np from dateutil import parser import time import math from math import sin,cos,atan2,sqrt import os,sys # Django from django.shortcuts import render_to_response from django.http import HttpResponseRedirect, HttpResponse,JsonResponse from django.conf import settings from django.contrib.auth import authenticate, login, logout from django.contrib.auth.models import User from django.contrib.auth.decorators import login_required # Project # from .models import Profile from rowingdata import rowingdata import pandas as pd from rowers.models import Rower,Workout 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 try: intervaldf = pd.DataFrame({'v':interval}) idf_ewma1 = intervaldf.ewm(span=window_size) idf_ewma2 = intervaldf[::-1].ewm(span=window_size) i_ewma1 = idf_ewma1.mean().ix[:,'v'] i_ewma2 = idf_ewma2.mean().ix[:,'v'] interval2 = np.vstack((i_ewma1,i_ewma2[::-1])) interval2 = np.mean( interval2, axis=0) # average except ValueError: interval2 = interval return interval2 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 = { "errors": [ { "code": str(exc), "detail": message, } ] } res = HttpResponse(message) res.status_code = 401 res.json = json.dumps(response) 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", "code": code, "redirect_uri": STRAVA_REDIRECT_URI, "client_secret": STRAVA_CLIENT_SECRET, "client_id":STRAVA_CLIENT_ID, } headers = {'user-agent': 'sanderroosendaal'} response = requests.post("https://www.strava.com/oauth/token", data=post_data, headers=headers) try: token_json = response.json() thetoken = token_json['access_token'] except KeyError: thetoken = 0 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 from uuid import uuid4 state = str(uuid4()) params = {"client_id": STRAVA_CLIENT_ID, "response_type": "code", "redirect_uri": STRAVA_REDIRECT_URI, "scope":"write"} import urllib url = "https://www.strava.com/oauth/authorize" +urllib.urlencode(params) 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): s = "Token doesn't exist. Need to authorize" return custom_exception_handler(401,s) else: # ready to fetch. Hurray authorizationstring = str('Bearer ' + r.stravatoken) headers = {'Authorization': authorizationstring, 'user-agent': 'sanderroosendaal', 'Content-Type': 'application/json'} url = "https://www.strava.com/api/v3/athlete/activities" s = requests.get(url,headers=headers) 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): s = "Token doesn't exist. Need to authorize" return custom_exception_handler(401,s) else: # ready to fetch. Hurray fetchresolution = 'high' authorizationstring = str('Bearer ' + r.stravatoken) headers = {'Authorization': authorizationstring, 'user-agent': 'sanderroosendaal', 'Content-Type': 'application/json', 'resolution': 'medium',} url = "https://www.strava.com/api/v3/activities/"+str(stravaid) workoutsummary = requests.get(url,headers=headers).json() workoutsummary['timezone'] = "Etc/UTC" startdatetime = workoutsummary['start_date'] url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/cadence?resolution="+fetchresolution spmjson = requests.get(url,headers=headers) url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/heartrate?resolution="+fetchresolution hrjson = requests.get(url,headers=headers) url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/time?resolution="+fetchresolution timejson = requests.get(url,headers=headers) url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/velocity_smooth?resolution="+fetchresolution velojson = requests.get(url,headers=headers) url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/distance?resolution="+fetchresolution distancejson = requests.get(url,headers=headers) url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/latlng?resolution="+fetchresolution latlongjson = requests.get(url,headers=headers) t = np.array(timejson.json()[0]['data']) d = np.array(distancejson.json()[0]['data']) nr_rows = len(t) try: spm = np.array( spmjson.json()[1]['data']) except IndexError: spm = np.zeros(nr_rows) try: hr = np.array(hrjson.json()[1]['data']) except IndexError: hr = np.zeros(nr_rows) try: velo = np.array(velojson.json()[1]['data']) except IndexError: velo = np.zeros(nr_rows) dt = np.diff(t).mean() wsize = round(5./dt) velo2 = ewmovingaverage(velo,wsize) coords = np.array(latlongjson.json()[0]['data']) try: lat = coords[:,0] lon = coords[:,1] except IndexError: lat = np.zeros(len(t)) lon = np.zeros(len(t)) strokelength = velo*60./(spm) strokelength[np.isinf(strokelength)] = 0.0 pace = 500./(1.0*velo2) pace[np.isinf(pace)] = 0.0 df = pd.DataFrame({'t':10*t, 'd':10*d, 'p':10*pace, 'spm':spm, 'hr':hr, 'lat':lat, 'lon':lon, 'strokelength':strokelength, }) # startdatetime = datetime.datetime.strptime(startdatetime,"%Y-%m-%d-%H:%M:%S") return [workoutsummary,df] # Generate Workout data for Strava (a TCX file) def createstravaworkoutdata(w): filename = w.csvfilename try: row = rowingdata(filename) tcxfilename = filename[:-4]+'.tcx' row.exporttotcx(tcxfilename,notes=w.notes) except: tcxfilename = 0 return tcxfilename # Upload the TCX file to Strava and set the workout activity type # to rowing on Strava def handle_stravaexport(f2,workoutname,stravatoken,description=''): # w = Workout.objects.get(id=workoutid) client = stravalib.Client(access_token=stravatoken) act = client.upload_activity(f2,'tcx',name=workoutname) try: res = act.wait(poll_interval=5.0,timeout=30) message = 'Workout successfully synchronized to Strava' except: res = 0 # description doesn't work yet. Have to wait for stravalib to update if res: act = client.update_activity(res.id,activity_type='Rowing',description=description) else: message = 'Strava upload timed out.' return (0,message) return (res.id,message)