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 # All the functionality to connect to SportTracks import numpy import django_rq queue = django_rq.get_queue('default') queuelow = django_rq.get_queue('low') queuehigh = django_rq.get_queue('low') from rowers.utils import myqueue from rowers.imports import * import re 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 ) import rowers.mytypes as mytypes from rowers.rower_rules import is_workout_user from rowers.tasks import handle_sporttracks_sync oauth_data = { 'client_id': SPORTTRACKS_CLIENT_ID, 'client_secret': SPORTTRACKS_CLIENT_SECRET, 'redirect_uri': SPORTTRACKS_REDIRECT_URI, 'autorization_uri': "https://api.sporttracks.mobi/oauth2/authorize", 'content_type': 'application/json', 'tokenname': 'sporttrackstoken', 'refreshtokenname': 'sporttracksrefreshtoken', 'expirydatename': 'sporttrackstokenexpirydate', 'bearer_auth': False, 'base_url': "https://api.sporttracks.mobi/oauth2/token", 'scope':'write', } # Checks if user has SportTracks token, renews them if they are expired def sporttracks_open(user): return imports_open(user, oauth_data) # Refresh ST token using refresh token def do_refresh_token(refreshtoken): return imports_do_refresh_token(refreshtoken, oauth_data) # Exchange ST access code for long-lived ST access token def get_token(code): return imports_get_token(code,oauth_data) # Make authorization URL including random string def make_authorization_url(request): return imports_make_authorization_url(oauth_data) # 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) access_token = res[0] expires_in = res[1] refresh_token = res[2] expirydatetime = timezone.now()+timedelta(seconds=expires_in) r = Rower.objects.get(user=user) r.sporttrackstoken = access_token r.tokenexpirydate = expirydatetime r.sporttracksrefreshtoken = refresh_token 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): s = "Token doesn't exist. Need to authorize" return custom_exception_handler(401,s) elif (timezone.now()>r.sporttrackstokenexpirydate): s = "Token expired. Needs to refresh." return custom_exception_handler(401,s) else: # ready to fetch. Hurray authorizationstring = str('Bearer ' + r.sporttrackstoken) headers = {'Authorization': authorizationstring, 'user-agent': 'sanderroosendaal', 'Content-Type': 'application/json'} url = "https://api.sporttracks.mobi/api/v2/fitnessActivities" s = requests.get(url,headers=headers) return s # Get workout summary data by SportTracks ID def get_workout(user,sporttracksid): r = Rower.objects.get(user=user) if (r.sporttrackstoken == '') or (r.sporttrackstoken is None): return custom_exception_handler(401,s) s = "Token doesn't exist. Need to authorize" elif (timezone.now()>r.sporttrackstokenexpirydate): s = "Token expired. Needs to refresh." return custom_exception_handler(401,s) else: # ready to fetch. Hurray authorizationstring = str('Bearer ' + r.sporttrackstoken) headers = {'Authorization': authorizationstring, 'user-agent': 'sanderroosendaal', 'Content-Type': 'application/json'} url = "https://api.sporttracks.mobi/api/v2/fitnessActivities/"+str(sporttracksid) s = requests.get(url,headers=headers) data = s.json() strokedata = pd.DataFrame.from_dict({ key: pd.Series(value) for key, value in data.items() }) return data,strokedata # Create Workout Data for upload to SportTracks def createsporttracksworkoutdata(w): timezone = pytz.timezone(w.timezone) filename = w.csvfilename try: row = rowingdata(csvfile=filename) except: return 0 try: averagehr = int(row.df[' HRCur (bpm)'].mean()) maxhr = int(row.df[' HRCur (bpm)'].max()) except KeyError: averagehr = 0 maxhr = 0 duration = w.duration.hour*3600 duration += w.duration.minute*60 duration += w.duration.second duration += +1.0e-6*w.duration.microsecond # adding diff, trying to see if this is valid #t = row.df.loc[:,'TimeStamp (sec)'].values-10*row.df.ix[0,'TimeStamp (sec)'] t = row.df.loc[:,'TimeStamp (sec)'].values-row.df.loc[:,'TimeStamp (sec)'].iloc[0] t[0] = t[1] d = row.df.loc[:,'cum_dist'].values d[0] = d[1] t = t.astype(int) d = d.astype(int) spm = row.df[' Cadence (stokes/min)'].astype(int).values spm[0] = spm[1] hr = row.df[' HRCur (bpm)'].astype(int).values haslatlon=1 try: lat = row.df[' latitude'].values lon = row.df[' longitude'].values if not lat.std() and not lon.std(): haslatlon = 0 except KeyError: haslatlon = 0 haspower = 1 try: power = row.df[' Power (watts)'].astype(int).values except KeyError: haspower = 0 locdata = [] hrdata = [] spmdata = [] distancedata = [] powerdata = [] t = t.tolist() hr = hr.tolist() d = d.tolist() spm = spm.tolist() if haslatlon: lat = lat.tolist() lon = lon.tolist() power = power.tolist() for i in range(len(t)): hrdata.append(t[i]) hrdata.append(hr[i]) distancedata.append(t[i]) distancedata.append(d[i]) spmdata.append(t[i]) spmdata.append(spm[i]) if haslatlon: locdata.append(t[i]) locdata.append([lat[i],lon[i]]) if haspower: powerdata.append(t[i]) powerdata.append(power[i]) try: w.notes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com' except TypeError: w.notes = 'from '+w.workoutsource+' via rowsandall.com' st = w.startdatetime.astimezone(timezone) st = st.replace(microsecond=0) if haslatlon: data = { "type": "Rowing", "name": w.name, "start_time": st.isoformat(), "total_distance": int(w.distance), "duration": duration, "notes": w.notes, "avg_heartrate": averagehr, "max_heartrate": maxhr, "location": locdata, "distance": distancedata, "cadence": spmdata, "heartrate": hrdata, } else: data = { "type": "Rowing", "name": w.name, "start_time": st.isoformat(), "total_distance": int(w.distance), "duration": duration, "notes": w.notes, "avg_heartrate": averagehr, "max_heartrate": maxhr, "distance": distancedata, "cadence": spmdata, "heartrate": hrdata, } if haspower: data['power'] = powerdata return data # Obtain SportTracks Workout ID from the response returned on successful # upload def getidfromresponse(response): t = response.json() uri = t['uris'][0] regex = '.*?sporttracks\.mobi\/api\/v2\/fitnessActivities/(\d+)\.json$' m = re.compile(regex).match(uri).group(1) id = int(m) return int(id) def default(o): if isinstance(o, numpy.int64): return int(o) raise TypeError def workout_sporttracks_upload(user,w,asynchron=False): message = "Uploading to SportTracks" stid = 0 # ready to upload. Hurray r = w.user thetoken = sporttracks_open(user) if (is_workout_user(user,w)): data = createsporttracksworkoutdata(w) if not data: message = "Data error" stid = 0 return message,stid authorizationstring = str('Bearer ' + thetoken) headers = {'Authorization': authorizationstring, 'user-agent': 'sanderroosendaal', 'Content-Type': 'application/json'} url = "https://api.sporttracks.mobi/api/v2/fitnessActivities.json" if asynchron: job = myqueue(queue,handle_sporttracks_sync, w.id,url,headers,json.dumps(data,default=default)) return "Asynchronous sync",0 response = requests.post(url,headers=headers,data=json.dumps(data,default=default)) # check for duplicate error first if (response.status_code == 409 ): message = "Duplicate error" w.uploadedtosporttracks = -1 stid = -1 w.save() return message, stid elif (response.status_code == 201 or response.status_code==200): s= response.json() stid = getidfromresponse(response) w.uploadedtosporttracks = stid w.save() return 'Successfully synced to SportTracks',stid else: s = response message = "Something went wrong in workout_sporttracks_upload_view: %s" % s.reason stid = 0 return message,stid else: message = "You are not authorized to upload this workout" stid = 0 return message,stid return message,stid # Create workout from SportTracks Data, which are slightly different # than Strava or Concept2 data def add_workout_from_data(user,importid,data,strokedata,source='sporttracks', workoutsource='sporttracks'): try: workouttype = data['type'] except KeyError: workouttype = 'other' if workouttype not in [x[0] for x in Workout.workouttypes]: workouttype = 'other' try: comments = data['comments'] except: comments = '' r = Rower.objects.get(user=user) try: rowdatetime = iso8601.parse_date(data['start_time']) except iso8601.ParseError: try: rowdatetime = datetime.datetime.strptime(data['start_time'],"%Y-%m-%d %H:%M:%S") rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc) except: try: rowdatetime = dateutil.parser.parse(data['start_time']) rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc) except: rowdatetime = datetime.datetime.strptime(data['date'],"%Y-%m-%d %H:%M:%S") rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc) starttimeunix = arrow.get(rowdatetime).timestamp try: title = data['name'] except: title = "Imported data" try: res = splitstdata(data['distance']) distance = res[1] times_distance = res[0] except KeyError: try: res = splitstdata(data['heartrate']) times_distance = res[0] distance = 0*times_distance except KeyError: return (0,"No distance or heart rate data in the workout") try: l = data['location'] res = splitstdata(l) times_location = res[0] latlong = res[1] latcoord = [] loncoord = [] for coord in latlong: lat = coord[0] lon = coord[1] latcoord.append(lat) loncoord.append(lon) except: times_location = times_distance latcoord = np.zeros(len(times_distance)) loncoord = np.zeros(len(times_distance)) if workouttype in mytypes.otwtypes: workouttype = 'rower' try: res = splitstdata(data['cadence']) times_spm = res[0] spm = res[1] except KeyError: times_spm = times_distance spm = 0*times_distance try: res = splitstdata(data['heartrate']) hr = res[1] times_hr = res[0] except KeyError: times_hr = times_distance hr = 0*times_distance # create data series and remove duplicates distseries = pd.Series(distance,index=times_distance) distseries = distseries.groupby(distseries.index).first() latseries = pd.Series(latcoord,index=times_location) latseries = latseries.groupby(latseries.index).first() lonseries = pd.Series(loncoord,index=times_location) lonseries = lonseries.groupby(lonseries.index).first() spmseries = pd.Series(spm,index=times_spm) spmseries = spmseries.groupby(spmseries.index).first() hrseries = pd.Series(hr,index=times_hr) hrseries = hrseries.groupby(hrseries.index).first() # Create dicts and big dataframe d = { ' Horizontal (meters)': distseries, ' latitude': latseries, ' longitude': lonseries, ' Cadence (stokes/min)': spmseries, ' HRCur (bpm)' : hrseries, } df = pd.DataFrame(d) df = df.groupby(level=0).last() cum_time = df.index.values df[' ElapsedTime (sec)'] = cum_time velo = df[' Horizontal (meters)'].diff()/df[' ElapsedTime (sec)'].diff() df[' Power (watts)'] = 0.0*velo nr_rows = len(velo.values) df[' DriveLength (meters)'] = np.zeros(nr_rows) df[' StrokeDistance (meters)'] = np.zeros(nr_rows) df[' DriveTime (ms)'] = np.zeros(nr_rows) df[' StrokeRecoveryTime (ms)'] = np.zeros(nr_rows) df[' AverageDriveForce (lbs)'] = np.zeros(nr_rows) df[' PeakDriveForce (lbs)'] = np.zeros(nr_rows) df[' lapIdx'] = np.zeros(nr_rows) unixtime = cum_time+starttimeunix unixtime[0] = starttimeunix df['TimeStamp (sec)'] = unixtime dt = np.diff(cum_time).mean() wsize = round(5./dt) velo2 = ewmovingaverage(velo,wsize) df[' Stroke500mPace (sec/500m)'] = 500./velo2 df = df.fillna(0) df.sort_values(by='TimeStamp (sec)',ascending=True) timestr = strftime("%Y%m%d-%H%M%S") # csvfilename ='media/Import_'+str(importid)+'.csv' csvfilename ='media/{code}_{importid}.csv'.format( importid=importid, code = uuid4().hex[:16] ) res = df.to_csv(csvfilename+'.gz',index_label='index', compression='gzip') id,message = dataprep.save_workout_database(csvfilename,r, workouttype=workouttype, title=title, notes=comments, workoutsource='sporttracks') return (id,message)