Strava, ST, RK done
This commit is contained in:
@@ -1,282 +0,0 @@
|
|||||||
# All the functionality needed to connect to Runkeeper
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
from rowsandall_app.settings import (
|
|
||||||
C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET,
|
|
||||||
STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET,
|
|
||||||
RUNKEEPER_CLIENT_ID, RUNKEEPER_CLIENT_SECRET,RUNKEEPER_REDIRECT_URI,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Custom error class - to raise a NoTokenError
|
|
||||||
class RunKeeperNoTokenError(Exception):
|
|
||||||
def __init__(self,value):
|
|
||||||
self.value=value
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return repr(self.value)
|
|
||||||
|
|
||||||
# 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(RUNKEEPER_CLIENT_ID, RUNKEEPER_CLIENT_SECRET)
|
|
||||||
post_data = {"grant_type": "authorization_code",
|
|
||||||
"code": code,
|
|
||||||
"redirect_uri": RUNKEEPER_REDIRECT_URI,
|
|
||||||
"client_secret": RUNKEEPER_CLIENT_SECRET,
|
|
||||||
"client_id":RUNKEEPER_CLIENT_ID,
|
|
||||||
}
|
|
||||||
headers = {'user-agent': 'sanderroosendaal'}
|
|
||||||
response = requests.post("https://runkeeper.com/apps/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": RUNKEEPER_CLIENT_ID,
|
|
||||||
"response_type": "code",
|
|
||||||
"redirect_uri": RUNKEEPER_REDIRECT_URI,
|
|
||||||
}
|
|
||||||
import urllib
|
|
||||||
url = "https://www.runkeeper.com/opps/authorize" +urllib.urlencode(params)
|
|
||||||
|
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
|
|
||||||
# Get list of workouts available on Runkeeper
|
|
||||||
def get_runkeeper_workout_list(user):
|
|
||||||
r = Rower.objects.get(user=user)
|
|
||||||
if (r.runkeepertoken == '') or (r.runkeepertoken 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.runkeepertoken)
|
|
||||||
headers = {'Authorization': authorizationstring,
|
|
||||||
'user-agent': 'sanderroosendaal',
|
|
||||||
'Content-Type': 'application/json'}
|
|
||||||
url = "https://api.runkeeper.com/fitnessActivities"
|
|
||||||
s = requests.get(url,headers=headers)
|
|
||||||
|
|
||||||
return s
|
|
||||||
|
|
||||||
# Get workout summary data by Runkeeper ID
|
|
||||||
def get_runkeeper_workout(user,runkeeperid):
|
|
||||||
r = Rower.objects.get(user=user)
|
|
||||||
if (r.runkeepertoken == '') or (r.runkeepertoken is None):
|
|
||||||
return custom_exception_handler(401,s)
|
|
||||||
s = "Token doesn't exist. Need to authorize"
|
|
||||||
else:
|
|
||||||
# ready to fetch. Hurray
|
|
||||||
authorizationstring = str('Bearer ' + r.runkeepertoken)
|
|
||||||
headers = {'Authorization': authorizationstring,
|
|
||||||
'user-agent': 'sanderroosendaal',
|
|
||||||
'Content-Type': 'application/json'}
|
|
||||||
url = "https://api.runkeeper.com/fitnessActivities/"+str(runkeeperid)
|
|
||||||
s = requests.get(url,headers=headers)
|
|
||||||
|
|
||||||
return s
|
|
||||||
|
|
||||||
# Create Workout Data for upload to SportTracks
|
|
||||||
def createrunkeeperworkoutdata(w):
|
|
||||||
filename = w.csvfilename
|
|
||||||
try:
|
|
||||||
row = rowingdata(filename)
|
|
||||||
except:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
averagehr = int(row.df[' HRCur (bpm)'].mean())
|
|
||||||
maxhr = int(row.df[' HRCur (bpm)'].max())
|
|
||||||
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.ix[:,'TimeStamp (sec)'].values-10*row.df.ix[0,'TimeStamp (sec)']
|
|
||||||
t = row.df.ix[:,'TimeStamp (sec)'].values-row.df.ix[0,'TimeStamp (sec)']
|
|
||||||
t[0] = t[1]
|
|
||||||
|
|
||||||
d = row.df.ix[:,'cum_dist'].values
|
|
||||||
d[0] = d[1]
|
|
||||||
t = t.astype(int)
|
|
||||||
d = d.astype(int)
|
|
||||||
spm = row.df[' Cadence (stokes/min)'].astype(int)
|
|
||||||
spm[0] = spm[1]
|
|
||||||
hr = row.df[' HRCur (bpm)'].astype(int)
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
# path data
|
|
||||||
if haslatlon:
|
|
||||||
locdata = []
|
|
||||||
for e in zip(t,lat,lon):
|
|
||||||
point = {'timestamp':e[0],
|
|
||||||
'latitude':e[1],
|
|
||||||
'longitude':e[2],}
|
|
||||||
locdata.append(point)
|
|
||||||
|
|
||||||
hrdata = []
|
|
||||||
for e in zip(t,hr):
|
|
||||||
point = {'timestamp':e[0],
|
|
||||||
'heart_rate':e[1]
|
|
||||||
}
|
|
||||||
hrdata.append(point)
|
|
||||||
|
|
||||||
distancedata = []
|
|
||||||
for e in zip(t,d):
|
|
||||||
point = {'timestamp':e[0],
|
|
||||||
'distance':e[1]
|
|
||||||
}
|
|
||||||
distancedata.append(point)
|
|
||||||
|
|
||||||
start_time = w.startdatetime.strftime("%a, %d %b %Y %H:%M:%S")
|
|
||||||
|
|
||||||
if haslatlon:
|
|
||||||
data = {
|
|
||||||
"type": "Rowing",
|
|
||||||
"start_time": start_time,
|
|
||||||
"total_distance": int(w.distance),
|
|
||||||
"duration": duration,
|
|
||||||
"notes": w.notes,
|
|
||||||
"average_heart_rate": averagehr,
|
|
||||||
"path": locdata,
|
|
||||||
"distance": distancedata,
|
|
||||||
"heartrate": hrdata,
|
|
||||||
"post_to_twitter":"false",
|
|
||||||
"post_to_facebook":"false",
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
data = {
|
|
||||||
"type": "Rowing",
|
|
||||||
"start_time": start_time,
|
|
||||||
"total_distance": int(w.distance),
|
|
||||||
"duration": duration,
|
|
||||||
"notes": w.notes,
|
|
||||||
"avg_heartrate": averagehr,
|
|
||||||
"distance": distancedata,
|
|
||||||
"heartrate": hrdata,
|
|
||||||
"post_to_twitter":"false",
|
|
||||||
"post_to_facebook":"false",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
# Obtain Runkeeper Workout ID from the response returned on successful
|
|
||||||
# upload
|
|
||||||
def getidfromresponse(response):
|
|
||||||
uri = response.headers["Location"]
|
|
||||||
id = uri[len(uri)-9:]
|
|
||||||
|
|
||||||
return int(id)
|
|
||||||
|
|
||||||
|
|
||||||
# Get user id, having access token
|
|
||||||
# Handy for checking if the API access is working
|
|
||||||
def get_userid(access_token):
|
|
||||||
authorizationstring = str('Bearer ' + access_token)
|
|
||||||
headers = {'Authorization': authorizationstring,
|
|
||||||
'user-agent': 'sanderroosendaal',
|
|
||||||
'Content-Type': 'application/json'}
|
|
||||||
import urllib
|
|
||||||
url = "https://api.runkeeper.com/user"
|
|
||||||
response = requests.get(url,headers=headers)
|
|
||||||
|
|
||||||
|
|
||||||
me_json = response.json()
|
|
||||||
|
|
||||||
try:
|
|
||||||
res = me_json['userID']
|
|
||||||
except KeyError:
|
|
||||||
res = 0
|
|
||||||
|
|
||||||
return res
|
|
||||||
@@ -27,6 +27,7 @@ from rowingdata import rowingdata
|
|||||||
import pandas as pd
|
import pandas as pd
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from rowers.models import Rower,Workout
|
from rowers.models import Rower,Workout
|
||||||
|
from rowers.models import checkworkoutuser
|
||||||
import sys
|
import sys
|
||||||
import urllib
|
import urllib
|
||||||
from requests import Request, Session
|
from requests import Request, Session
|
||||||
@@ -60,13 +61,26 @@ def custom_exception_handler(exc,message):
|
|||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
# Check if workout is owned by this user
|
# Checks if user has Concept2 tokens, resets tokens if they are
|
||||||
def checkworkoutuser(user,workout):
|
# expired.
|
||||||
try:
|
def c2_open(user):
|
||||||
r = Rower.objects.get(user=user)
|
r = Rower.objects.get(user=user)
|
||||||
return (workout.user == r)
|
if (r.c2token == '') or (r.c2token is None):
|
||||||
except Rower.DoesNotExist:
|
s = "Token doesn't exist. Need to authorize"
|
||||||
return(False)
|
raise C2NoTokenError("User has no token")
|
||||||
|
else:
|
||||||
|
if (timezone.now()>r.tokenexpirydate):
|
||||||
|
res = rower_c2_token_refresh(user)
|
||||||
|
if res[0] != None:
|
||||||
|
thetoken = res[0]
|
||||||
|
else:
|
||||||
|
raise C2NoTokenError("User has no token")
|
||||||
|
else:
|
||||||
|
thetoken = r.c2token
|
||||||
|
|
||||||
|
return thetoken
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# convert datetime object to seconds
|
# convert datetime object to seconds
|
||||||
def makeseconds(t):
|
def makeseconds(t):
|
||||||
@@ -249,9 +263,13 @@ def createc2workoutdata(w):
|
|||||||
row = rowingdata(filename)
|
row = rowingdata(filename)
|
||||||
except IOError:
|
except IOError:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
averagehr = int(row.df[' HRCur (bpm)'].mean())
|
try:
|
||||||
maxhr = int(row.df[' HRCur (bpm)'].max())
|
averagehr = int(row.df[' HRCur (bpm)'].mean())
|
||||||
|
maxhr = int(row.df[' HRCur (bpm)'].max())
|
||||||
|
except ValueError:
|
||||||
|
averagehr = 0
|
||||||
|
maxhr = 0
|
||||||
|
|
||||||
# adding diff, trying to see if this is valid
|
# adding diff, trying to see if this is valid
|
||||||
t = 10*row.df.ix[:,'TimeStamp (sec)'].values-10*row.df.ix[0,'TimeStamp (sec)']
|
t = 10*row.df.ix[:,'TimeStamp (sec)'].values-10*row.df.ix[0,'TimeStamp (sec)']
|
||||||
@@ -265,7 +283,10 @@ def createc2workoutdata(w):
|
|||||||
p = p.astype(int)
|
p = p.astype(int)
|
||||||
spm = row.df[' Cadence (stokes/min)'].astype(int)
|
spm = row.df[' Cadence (stokes/min)'].astype(int)
|
||||||
spm[0] = spm[1]
|
spm[0] = spm[1]
|
||||||
hr = row.df[' HRCur (bpm)'].astype(int)
|
try:
|
||||||
|
hr = row.df[' HRCur (bpm)'].astype(int)
|
||||||
|
except ValueError:
|
||||||
|
hr = 0*d
|
||||||
stroke_data = []
|
stroke_data = []
|
||||||
for i in range(len(t)):
|
for i in range(len(t)):
|
||||||
thisrecord = {"t":t[i],"d":d[i],"p":p[i],"spm":spm[i],"hr":hr[i]}
|
thisrecord = {"t":t[i],"d":d[i],"p":p[i],"spm":spm[i],"hr":hr[i]}
|
||||||
@@ -521,34 +542,49 @@ def process_callback(request):
|
|||||||
# Uploading workout
|
# Uploading workout
|
||||||
def workout_c2_upload(user,w):
|
def workout_c2_upload(user,w):
|
||||||
response = 'trying C2 upload'
|
response = 'trying C2 upload'
|
||||||
r = Rower.objects.get(user=user)
|
thetoken = c2_open(user)
|
||||||
if (r.c2token == '') or (r.c2token is None):
|
|
||||||
s = "Token doesn't exist. Need to authorize"
|
|
||||||
return custom_exception_handler(401,s)
|
|
||||||
elif (timezone.now()>r.tokenexpirydate):
|
|
||||||
s = "Token expired. Needs to refresh."
|
|
||||||
return custom_exception_handler(401,s)
|
|
||||||
else:
|
|
||||||
# ready to upload. Hurray
|
|
||||||
if (checkworkoutuser(user,w)):
|
|
||||||
c2userid = get_userid(r.c2token)
|
|
||||||
data = createc2workoutdata(w)
|
|
||||||
authorizationstring = str('Bearer ' + r.c2token)
|
|
||||||
headers = {'Authorization': authorizationstring,
|
|
||||||
'user-agent': 'sanderroosendaal',
|
|
||||||
'Content-Type': 'application/json'}
|
|
||||||
import urllib
|
|
||||||
url = "https://log.concept2.com/api/users/%s/results" % (c2userid)
|
|
||||||
response = requests.post(url,headers=headers,data=json.dumps(data))
|
|
||||||
if (response.status_code == 201):
|
|
||||||
s= json.loads(response.text)
|
|
||||||
c2id = s['data']['id']
|
|
||||||
w.uploadedtoc2 = c2id
|
|
||||||
w.save()
|
|
||||||
else:
|
|
||||||
response = "You are not authorized to upload this workout"
|
|
||||||
|
|
||||||
return response
|
r = Rower.objects.get(user=user)
|
||||||
|
|
||||||
|
# ready to upload. Hurray
|
||||||
|
if (checkworkoutuser(user,w)):
|
||||||
|
c2userid = get_userid(r.c2token)
|
||||||
|
if not c2userid:
|
||||||
|
raise C2NoTokenError
|
||||||
|
|
||||||
|
data = createc2workoutdata(w)
|
||||||
|
if data == 0:
|
||||||
|
return "Error: No data file. Contact info@rowsandall.com if the problem persists",0
|
||||||
|
|
||||||
|
authorizationstring = str('Bearer ' + r.c2token)
|
||||||
|
headers = {'Authorization': authorizationstring,
|
||||||
|
'user-agent': 'sanderroosendaal',
|
||||||
|
'Content-Type': 'application/json'}
|
||||||
|
import urllib
|
||||||
|
url = "https://log.concept2.com/api/users/%s/results" % (c2userid)
|
||||||
|
response = requests.post(url,headers=headers,data=json.dumps(data))
|
||||||
|
|
||||||
|
if (response.status_code == 409 ):
|
||||||
|
message = "Duplicate error"
|
||||||
|
w.uploadedtoc2 = -1
|
||||||
|
c2id = -1
|
||||||
|
w.save()
|
||||||
|
elif (response.status_code == 201 or response.status_code == 200):
|
||||||
|
try:
|
||||||
|
s= json.loads(response.text)
|
||||||
|
c2id = s['data']['id']
|
||||||
|
w.uploadedtoc2 = c2id
|
||||||
|
w.save()
|
||||||
|
message = ""
|
||||||
|
except:
|
||||||
|
message = "Something went wrong in workout_c2_upload_view. Response code 200/201 but C2 sync failed: "+response.text
|
||||||
|
c2id = 0
|
||||||
|
|
||||||
|
else:
|
||||||
|
message = "You are not authorized to upload this workout"
|
||||||
|
c2id = 0
|
||||||
|
|
||||||
|
return message,c2id
|
||||||
|
|
||||||
# This is token refresh. Looks for tokens in our database, then refreshes
|
# This is token refresh. Looks for tokens in our database, then refreshes
|
||||||
def rower_c2_token_refresh(user):
|
def rower_c2_token_refresh(user):
|
||||||
|
|||||||
@@ -83,7 +83,21 @@ class UploadOptionsForm(forms.Form):
|
|||||||
choices=plotchoices,
|
choices=plotchoices,
|
||||||
initial='timeplot',
|
initial='timeplot',
|
||||||
label='Plot Type')
|
label='Plot Type')
|
||||||
upload_to_C2 = forms.BooleanField(initial=False,required=False)
|
upload_to_C2 = forms.BooleanField(initial=False,required=False,
|
||||||
|
label='Upload to Concept2 logbook')
|
||||||
|
upload_to_Strava = forms.BooleanField(initial=False,required=False,
|
||||||
|
label='Upload to Strava')
|
||||||
|
upload_to_SportTracks = forms.BooleanField(initial=False,required=False,
|
||||||
|
label='Upload to SportTracks')
|
||||||
|
upload_to_RunKeeper = forms.BooleanField(initial=False,required=False,
|
||||||
|
label='Upload to RunKeeper')
|
||||||
|
upload_to_MapMyFitness = forms.BooleanField(initial=False,
|
||||||
|
required=False,
|
||||||
|
label='Upload to MapMyFitness')
|
||||||
|
upload_to_TrainingPeaks = forms.BooleanField(initial=False,
|
||||||
|
required=False,
|
||||||
|
label='Upload to TrainingPeaks')
|
||||||
|
# do_physics = forms.BooleanField(initial=False,required=False,label='Power Estimate (OTW)')
|
||||||
makeprivate = forms.BooleanField(initial=False,required=False,
|
makeprivate = forms.BooleanField(initial=False,required=False,
|
||||||
label='Make Workout Private')
|
label='Make Workout Private')
|
||||||
|
|
||||||
|
|||||||
@@ -209,7 +209,6 @@ class Rower(models.Model):
|
|||||||
runkeepertoken = models.CharField(default='',max_length=200,
|
runkeepertoken = models.CharField(default='',max_length=200,
|
||||||
blank=True,null=True)
|
blank=True,null=True)
|
||||||
|
|
||||||
|
|
||||||
# runkeepertokenexpirydate = models.DateTimeField(blank=True,null=True)
|
# runkeepertokenexpirydate = models.DateTimeField(blank=True,null=True)
|
||||||
# runkeeperrefreshtoken = models.CharField(default='',max_length=200,
|
# runkeeperrefreshtoken = models.CharField(default='',max_length=200,
|
||||||
# blank=True,null=True)
|
# blank=True,null=True)
|
||||||
@@ -324,6 +323,15 @@ class BaseFavoriteFormSet(BaseFormSet):
|
|||||||
if not yparam2:
|
if not yparam2:
|
||||||
yparam2 = 'None'
|
yparam2 = 'None'
|
||||||
|
|
||||||
|
# Check if workout is owned by this user
|
||||||
|
def checkworkoutuser(user,workout):
|
||||||
|
try:
|
||||||
|
r = Rower.objects.get(user=user)
|
||||||
|
return (workout.user == r)
|
||||||
|
except Rower.DoesNotExist:
|
||||||
|
return(False)
|
||||||
|
|
||||||
|
|
||||||
# Workout
|
# Workout
|
||||||
class Workout(models.Model):
|
class Workout(models.Model):
|
||||||
workouttypes = (
|
workouttypes = (
|
||||||
@@ -398,7 +406,8 @@ class Workout(models.Model):
|
|||||||
uploadedtounderarmour = models.IntegerField(default=0)
|
uploadedtounderarmour = models.IntegerField(default=0)
|
||||||
uploadedtotp = models.IntegerField(default=0)
|
uploadedtotp = models.IntegerField(default=0)
|
||||||
uploadedtorunkeeper = models.IntegerField(default=0)
|
uploadedtorunkeeper = models.IntegerField(default=0)
|
||||||
|
runkeeperuri = models.CharField(default='',max_length=200)
|
||||||
|
|
||||||
# empower stuff
|
# empower stuff
|
||||||
inboard = models.FloatField(default=0.88)
|
inboard = models.FloatField(default=0.88)
|
||||||
oarlength = models.FloatField(default=2.89)
|
oarlength = models.FloatField(default=2.89)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ from django.contrib.auth.decorators import login_required
|
|||||||
# from .models import Profile
|
# from .models import Profile
|
||||||
from rowingdata import rowingdata
|
from rowingdata import rowingdata
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from rowers.models import Rower,Workout
|
from rowers.models import Rower,Workout,checkworkoutuser
|
||||||
|
|
||||||
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,
|
||||||
@@ -86,6 +86,17 @@ def custom_exception_handler(exc,message):
|
|||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
# Checks if user has SportTracks token, renews them if they are expired
|
||||||
|
def runkeeper_open(user):
|
||||||
|
r = Rower.objects.get(user=user)
|
||||||
|
if (r.runkeepertoken == '') or (r.runkeepertoken is None):
|
||||||
|
s = "Token doesn't exist. Need to authorize"
|
||||||
|
raise RunKeeperNoTokenError("User has no token")
|
||||||
|
else:
|
||||||
|
thetoken = r.runkeepertoken
|
||||||
|
|
||||||
|
return thetoken
|
||||||
|
|
||||||
# Exchange access code for long-lived access token
|
# Exchange access code for long-lived access token
|
||||||
def get_token(code):
|
def get_token(code):
|
||||||
client_auth = requests.auth.HTTPBasicAuth(RUNKEEPER_CLIENT_ID, RUNKEEPER_CLIENT_SECRET)
|
client_auth = requests.auth.HTTPBasicAuth(RUNKEEPER_CLIENT_ID, RUNKEEPER_CLIENT_SECRET)
|
||||||
@@ -261,6 +272,26 @@ def getidfromresponse(response):
|
|||||||
|
|
||||||
return int(id)
|
return int(id)
|
||||||
|
|
||||||
|
def geturifromid(access_token,id):
|
||||||
|
authorizationstring = str('Bearer ' + access_token)
|
||||||
|
headers = {'Authorization': authorizationstring,
|
||||||
|
'user-agent': 'sanderroosendaal',
|
||||||
|
'Content-Type': 'application/json'}
|
||||||
|
import urllib
|
||||||
|
url = "https://api.runkeeper.com/fitnessActivities/"+str(id)
|
||||||
|
response = requests.get(url,headers=headers)
|
||||||
|
try:
|
||||||
|
me_json = response.json()
|
||||||
|
except:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
try:
|
||||||
|
res = me_json['uri']
|
||||||
|
except KeyError:
|
||||||
|
res = ''
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
# Get user id, having access token
|
# Get user id, having access token
|
||||||
# Handy for checking if the API access is working
|
# Handy for checking if the API access is working
|
||||||
@@ -277,11 +308,66 @@ def get_userid(access_token):
|
|||||||
try:
|
try:
|
||||||
me_json = response.json()
|
me_json = response.json()
|
||||||
except:
|
except:
|
||||||
return 0
|
return ''
|
||||||
|
|
||||||
try:
|
try:
|
||||||
res = me_json['userID']
|
res = me_json['userID']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
res = 0
|
res = ''
|
||||||
|
|
||||||
|
print res,'userID'
|
||||||
|
return str(res)
|
||||||
|
|
||||||
|
def workout_runkeeper_upload(user,w):
|
||||||
|
message = ""
|
||||||
|
rkid = 0
|
||||||
|
|
||||||
|
r = w.user
|
||||||
|
|
||||||
|
|
||||||
|
thetoken = runkeeper_open(r.user)
|
||||||
|
|
||||||
|
# ready to upload. Hurray
|
||||||
|
|
||||||
|
if (checkworkoutuser(user,w)):
|
||||||
|
data = createrunkeeperworkoutdata(w)
|
||||||
|
if not data:
|
||||||
|
message = "Data error"
|
||||||
|
rkid = 0
|
||||||
|
return message, rkid
|
||||||
|
|
||||||
return res
|
authorizationstring = str('Bearer ' + thetoken)
|
||||||
|
headers = {'Authorization': authorizationstring,
|
||||||
|
'user-agent': 'sanderroosendaal',
|
||||||
|
'Content-Type': 'application/vnd.com.runkeeper.NewFitnessActivity+json',
|
||||||
|
'Content-Length':'nnn'}
|
||||||
|
|
||||||
|
url = "https://api.runkeeper.com/fitnessActivities"
|
||||||
|
response = requests.post(url,headers=headers,data=json.dumps(data))
|
||||||
|
|
||||||
|
# check for duplicate error first
|
||||||
|
if (response.status_code == 409 ):
|
||||||
|
message = "Duplicate error"
|
||||||
|
w.uploadedtorunkeeper = -1
|
||||||
|
rkid = -1
|
||||||
|
w.save()
|
||||||
|
return message, rkid
|
||||||
|
elif (response.status_code == 201 or response.status_code==200):
|
||||||
|
rkid = getidfromresponse(response)
|
||||||
|
rkuri = geturifromid(thetoken,rkid)
|
||||||
|
w.uploadedtorunkeeper = rkid
|
||||||
|
w.runkeeperuri = rkuri
|
||||||
|
w.save()
|
||||||
|
return '',rkid
|
||||||
|
else:
|
||||||
|
s = response
|
||||||
|
message = "Something went wrong in workout_runkeeper_upload_view: %s - %s" % (s.reason,s.text)
|
||||||
|
rkid = 0
|
||||||
|
return message, rkid
|
||||||
|
|
||||||
|
else:
|
||||||
|
message = "You are not authorized to upload this workout"
|
||||||
|
rkid = 0
|
||||||
|
return message, rkid
|
||||||
|
|
||||||
|
return message,rkid
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ from django.contrib.auth.decorators import login_required
|
|||||||
# from .models import Profile
|
# from .models import Profile
|
||||||
from rowingdata import rowingdata
|
from rowingdata import rowingdata
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from rowers.models import Rower,Workout
|
from rowers.models import Rower,Workout,checkworkoutuser
|
||||||
|
|
||||||
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
|
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
|
||||||
|
|
||||||
@@ -61,6 +61,21 @@ def custom_exception_handler(exc,message):
|
|||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
# Checks if user has SportTracks token, renews them if they are expired
|
||||||
|
def sporttracks_open(user):
|
||||||
|
r = Rower.objects.get(user=user)
|
||||||
|
if (r.sporttrackstoken == '') or (r.sporttrackstoken is None):
|
||||||
|
s = "Token doesn't exist. Need to authorize"
|
||||||
|
raise SportTracksNoTokenError("User has no token")
|
||||||
|
else:
|
||||||
|
if (timezone.now()>r.sporttrackstokenexpirydate):
|
||||||
|
thetoken = sporttracksstuff.rower_sporttracks_token_refresh(user)
|
||||||
|
else:
|
||||||
|
thetoken = r.sporttrackstoken
|
||||||
|
|
||||||
|
return thetoken
|
||||||
|
|
||||||
|
|
||||||
# Refresh ST token using refresh token
|
# Refresh ST token using refresh token
|
||||||
def do_refresh_token(refreshtoken):
|
def do_refresh_token(refreshtoken):
|
||||||
client_auth = requests.auth.HTTPBasicAuth(SPORTTRACKS_CLIENT_ID, SPORTTRACKS_CLIENT_SECRET)
|
client_auth = requests.auth.HTTPBasicAuth(SPORTTRACKS_CLIENT_ID, SPORTTRACKS_CLIENT_SECRET)
|
||||||
@@ -303,3 +318,51 @@ def getidfromresponse(response):
|
|||||||
return int(id)
|
return int(id)
|
||||||
|
|
||||||
|
|
||||||
|
def workout_sporttracks_upload(user,w):
|
||||||
|
message = ""
|
||||||
|
stid = 0
|
||||||
|
# ready to upload. Hurray
|
||||||
|
r = w.user
|
||||||
|
|
||||||
|
thetoken = sporttracks_open(user)
|
||||||
|
|
||||||
|
if (checkworkoutuser(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"
|
||||||
|
response = requests.post(url,headers=headers,data=json.dumps(data))
|
||||||
|
|
||||||
|
# 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= json.loads(response.text)
|
||||||
|
stid = getidfromresponse(response)
|
||||||
|
w.uploadedtosporttracks = stid
|
||||||
|
w.save()
|
||||||
|
return '',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
|
||||||
|
|||||||
@@ -29,8 +29,10 @@ from django.contrib.auth.decorators import login_required
|
|||||||
from rowingdata import rowingdata
|
from rowingdata import rowingdata
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from rowers.models import Rower,Workout
|
from rowers.models import Rower,Workout
|
||||||
|
from rowers.models import checkworkoutuser
|
||||||
|
|
||||||
import stravalib
|
import stravalib
|
||||||
|
from stravalib.exc import ActivityUploadFailed,TimeoutExceeded
|
||||||
|
|
||||||
from rowsandall_app.settings import C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET
|
from rowsandall_app.settings import C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET
|
||||||
|
|
||||||
@@ -77,6 +79,15 @@ def custom_exception_handler(exc,message):
|
|||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
# Custom error class - to raise a NoTokenError
|
||||||
|
class StravaNoTokenError(Exception):
|
||||||
|
def __init__(self,value):
|
||||||
|
self.value=value
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return repr(self.value)
|
||||||
|
|
||||||
|
|
||||||
# Exchange access code for long-lived access token
|
# Exchange access code for long-lived access token
|
||||||
def get_token(code):
|
def get_token(code):
|
||||||
client_auth = requests.auth.HTTPBasicAuth(STRAVA_CLIENT_ID, STRAVA_CLIENT_SECRET)
|
client_auth = requests.auth.HTTPBasicAuth(STRAVA_CLIENT_ID, STRAVA_CLIENT_SECRET)
|
||||||
@@ -277,3 +288,56 @@ def handle_stravaexport(f2,workoutname,stravatoken,description=''):
|
|||||||
return (res.id,message)
|
return (res.id,message)
|
||||||
|
|
||||||
|
|
||||||
|
def workout_strava_upload(user,w):
|
||||||
|
message = ""
|
||||||
|
stravaid=-1
|
||||||
|
r = Rower.objects.get(user=user)
|
||||||
|
res = -1
|
||||||
|
if (r.stravatoken == '') or (r.stravatoken is None):
|
||||||
|
s = "Token doesn't exist. Need to authorize"
|
||||||
|
raise StravaNoTokenError
|
||||||
|
else:
|
||||||
|
if (checkworkoutuser(user,w)):
|
||||||
|
try:
|
||||||
|
tcxfile = createstravaworkoutdata(w)
|
||||||
|
if tcxfile:
|
||||||
|
with open(tcxfile,'rb') as f:
|
||||||
|
res,mes = handle_stravaexport(f,w.name,
|
||||||
|
r.stravatoken,
|
||||||
|
description=w.notes+'\n from '+w.workoutsource+' via rowsandall.com')
|
||||||
|
if res==0:
|
||||||
|
message = mes
|
||||||
|
w.uploadedtostrava = -1
|
||||||
|
stravaid = -1
|
||||||
|
w.save()
|
||||||
|
try:
|
||||||
|
os.remove(tcxfile)
|
||||||
|
except WindowsError:
|
||||||
|
pass
|
||||||
|
return message,stravaid
|
||||||
|
|
||||||
|
w.uploadedtostrava = res
|
||||||
|
w.save()
|
||||||
|
try:
|
||||||
|
os.remove(tcxfile)
|
||||||
|
except WindowsError:
|
||||||
|
pass
|
||||||
|
message = ''
|
||||||
|
stravaid = res
|
||||||
|
return message,stravaid
|
||||||
|
else:
|
||||||
|
message = "Strava Upload error"
|
||||||
|
w.uploadedtostrava = -1
|
||||||
|
stravaid = -1
|
||||||
|
w.save()
|
||||||
|
return message, stravaid
|
||||||
|
|
||||||
|
except ActivityUploadFailed as e:
|
||||||
|
message = "Strava Upload error: %s" % e
|
||||||
|
w.uploadedtostrava = -1
|
||||||
|
stravaid = -1
|
||||||
|
w.save()
|
||||||
|
os.remove(tcxfile)
|
||||||
|
return message,stravaid
|
||||||
|
return message,stravaid
|
||||||
|
return message,stravaid
|
||||||
|
|||||||
@@ -35,7 +35,10 @@
|
|||||||
</table>
|
</table>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
You can select one static plot to be generated immediately for this workout. You can select to upload to Concept2 automatically. If you check "make private", this workout will not be visible to your followers and will not show up in your teams' workouts list.
|
You can select one static plot to be generated immediately for
|
||||||
|
this workout. You can select to upload to major fitness
|
||||||
|
platforms automatically.
|
||||||
|
If you check "make private", this workout will not be visible to your followers and will not show up in your teams' workouts list.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@@ -106,7 +106,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="grid_1 alpha">
|
<div class="grid_1 alpha">
|
||||||
<a href="https://runkeeper.com/fitnessActivity/{{ workout.uploadedtorunkeeper }}">
|
<a href="https://runkeeper.com/activity/{{ workout.uploadedtorunkeeper }}">
|
||||||
<img src="/static/img/rkchecked.png" alt="Runkeeper icon" width="60" height="60"></a>
|
<img src="/static/img/rkchecked.png" alt="Runkeeper icon" width="60" height="60"></a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import numpy as np
|
|||||||
|
|
||||||
lbstoN = 4.44822
|
lbstoN = 4.44822
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def serialize_list(value,token=','):
|
def serialize_list(value,token=','):
|
||||||
assert(isinstance(value, list) or isinstance(value, tuple) or isinstance(value,np.ndarray))
|
assert(isinstance(value, list) or isinstance(value, tuple) or isinstance(value,np.ndarray))
|
||||||
return token.join([unicode(s) for s in value])
|
return token.join([unicode(s) for s in value])
|
||||||
|
|||||||
272
rowers/views.py
272
rowers/views.py
@@ -51,12 +51,13 @@ import os,sys
|
|||||||
import datetime
|
import datetime
|
||||||
import iso8601
|
import iso8601
|
||||||
import c2stuff
|
import c2stuff
|
||||||
from c2stuff import C2NoTokenError
|
from c2stuff import C2NoTokenError,c2_open
|
||||||
from runkeeperstuff import RunKeeperNoTokenError
|
from runkeeperstuff import RunKeeperNoTokenError,runkeeper_open
|
||||||
from sporttracksstuff import SportTracksNoTokenError
|
from sporttracksstuff import SportTracksNoTokenError,sporttracks_open
|
||||||
from tpstuff import TPNoTokenError
|
from tpstuff import TPNoTokenError
|
||||||
from iso8601 import ParseError
|
from iso8601 import ParseError
|
||||||
import stravastuff
|
import stravastuff
|
||||||
|
from stravastuff import StravaNoTokenError
|
||||||
import sporttracksstuff
|
import sporttracksstuff
|
||||||
import underarmourstuff
|
import underarmourstuff
|
||||||
import tpstuff
|
import tpstuff
|
||||||
@@ -261,6 +262,7 @@ def splitstdata(lijst):
|
|||||||
return [np.array(t),np.array(latlong)]
|
return [np.array(t),np.array(latlong)]
|
||||||
|
|
||||||
from utils import geo_distance,serialize_list,deserialize_list
|
from utils import geo_distance,serialize_list,deserialize_list
|
||||||
|
from rowers.models import checkworkoutuser
|
||||||
|
|
||||||
# Check if a user is a Coach member
|
# Check if a user is a Coach member
|
||||||
def iscoachmember(user):
|
def iscoachmember(user):
|
||||||
@@ -369,14 +371,6 @@ def sendmail(request):
|
|||||||
else:
|
else:
|
||||||
return HttpResponseRedirect('/rowers/email/')
|
return HttpResponseRedirect('/rowers/email/')
|
||||||
|
|
||||||
# Check if workout belongs to this user
|
|
||||||
def checkworkoutuser(user,workout):
|
|
||||||
try:
|
|
||||||
r = Rower.objects.get(user=user)
|
|
||||||
managers = [team.manager for team in workout.team.all()]
|
|
||||||
return (workout.user == r or user in managers)
|
|
||||||
except Rower.DoesNotExist:
|
|
||||||
return(False)
|
|
||||||
|
|
||||||
# Create workout data from Strava or Concept2
|
# Create workout data from Strava or Concept2
|
||||||
# data and create the associated Workout object and save it
|
# data and create the associated Workout object and save it
|
||||||
@@ -1039,38 +1033,7 @@ def add_workout_from_underarmourdata(user,importid,data):
|
|||||||
|
|
||||||
return (id,message)
|
return (id,message)
|
||||||
|
|
||||||
# Checks if user has Concept2 tokens, resets tokens if they are
|
|
||||||
# expired.
|
|
||||||
def c2_open(user):
|
|
||||||
r = Rower.objects.get(user=user)
|
|
||||||
if (r.c2token == '') or (r.c2token is None):
|
|
||||||
s = "Token doesn't exist. Need to authorize"
|
|
||||||
raise C2NoTokenError("User has no token")
|
|
||||||
else:
|
|
||||||
if (timezone.now()>r.tokenexpirydate):
|
|
||||||
res = c2stuff.rower_c2_token_refresh(user)
|
|
||||||
if res[0] != None:
|
|
||||||
thetoken = res[0]
|
|
||||||
else:
|
|
||||||
raise C2NoTokenError("User has no token")
|
|
||||||
else:
|
|
||||||
thetoken = r.c2token
|
|
||||||
|
|
||||||
return thetoken
|
|
||||||
|
|
||||||
# Checks if user has SportTracks token, renews them if they are expired
|
|
||||||
def sporttracks_open(user):
|
|
||||||
r = Rower.objects.get(user=user)
|
|
||||||
if (r.sporttrackstoken == '') or (r.sporttrackstoken is None):
|
|
||||||
s = "Token doesn't exist. Need to authorize"
|
|
||||||
raise SportTracksNoTokenError("User has no token")
|
|
||||||
else:
|
|
||||||
if (timezone.now()>r.sporttrackstokenexpirydate):
|
|
||||||
thetoken = sporttracksstuff.rower_sporttracks_token_refresh(user)
|
|
||||||
else:
|
|
||||||
thetoken = r.sporttrackstoken
|
|
||||||
|
|
||||||
return thetoken
|
|
||||||
|
|
||||||
# Checks if user has UnderArmour token, renews them if they are expired
|
# Checks if user has UnderArmour token, renews them if they are expired
|
||||||
def underarmour_open(user):
|
def underarmour_open(user):
|
||||||
@@ -1109,16 +1072,6 @@ def tp_open(user):
|
|||||||
|
|
||||||
return thetoken
|
return thetoken
|
||||||
|
|
||||||
# Checks if user has SportTracks token, renews them if they are expired
|
|
||||||
def runkeeper_open(user):
|
|
||||||
r = Rower.objects.get(user=user)
|
|
||||||
if (r.runkeepertoken == '') or (r.runkeepertoken is None):
|
|
||||||
s = "Token doesn't exist. Need to authorize"
|
|
||||||
raise RunKeeperNoTokenError("User has no token")
|
|
||||||
else:
|
|
||||||
thetoken = r.runkeepertoken
|
|
||||||
|
|
||||||
return thetoken
|
|
||||||
|
|
||||||
# Export workout to TCX and send to user's email address
|
# Export workout to TCX and send to user's email address
|
||||||
@login_required()
|
@login_required()
|
||||||
@@ -1395,76 +1348,20 @@ def workout_c2_upload_view(request,id=0):
|
|||||||
raise Http404("Workout doesn't exist")
|
raise Http404("Workout doesn't exist")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
thetoken = c2_open(r.user)
|
message,c2id = c2stuff.workout_c2_upload(request.user,w)
|
||||||
except C2NoTokenError:
|
except C2NoTokenError:
|
||||||
return HttpResponseRedirect("/rowers/me/c2authorize/")
|
return HttpResponseRedirect("/rowers/me/c2authorize/")
|
||||||
|
|
||||||
if (checkworkoutuser(request.user,w)):
|
if message:
|
||||||
c2userid = c2stuff.get_userid(thetoken)
|
url = reverse(workout_export_view,
|
||||||
if not c2userid:
|
kwargs = {
|
||||||
return HttpResponseRedirect("/rowers/me/c2authorize")
|
'message':str(message),
|
||||||
|
'id':str(w.id),
|
||||||
data = c2stuff.createc2workoutdata(w)
|
})
|
||||||
if data == 0:
|
|
||||||
message = "Error: No data file. Contact info@rowsandall.com if this problem persists"
|
|
||||||
url = reverse(workout_export_view,
|
|
||||||
kwargs = {
|
|
||||||
'message':str(message),
|
|
||||||
'id':str(w.id),
|
|
||||||
})
|
|
||||||
|
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
|
|
||||||
authorizationstring = str('Bearer ' + thetoken)
|
|
||||||
headers = {'Authorization': authorizationstring,
|
|
||||||
'user-agent': 'sanderroosendaal',
|
|
||||||
'Content-Type': 'application/json'}
|
|
||||||
try:
|
|
||||||
url = "https://log.concept2.com/api/users/%s/results" % (c2userid)
|
|
||||||
response = requests.post(url,headers=headers,data=json.dumps(data))
|
|
||||||
except:
|
|
||||||
message = "Unexpected Error: "+str(sys.exc_info()[0])
|
|
||||||
with open("media/c2errors.log","a") as errorlog:
|
|
||||||
errorstring = str(sys.exc_info()[0])
|
|
||||||
timestr = strftime("%Y%m%d-%H%M%S")
|
|
||||||
errorlog.write(timestr+errorstring+"\r\n")
|
|
||||||
|
|
||||||
# check for duplicate error first
|
|
||||||
if (response.status_code == 409 ):
|
|
||||||
message = "Duplicate error"
|
|
||||||
w.uploadedtoc2 = -1
|
|
||||||
w.save()
|
|
||||||
elif (response.status_code == 201 or response.status_code == 200):
|
|
||||||
try:
|
|
||||||
s= json.loads(response.text)
|
|
||||||
c2id = s['data']['id']
|
|
||||||
w.uploadedtoc2 = c2id
|
|
||||||
w.save()
|
|
||||||
url = "/rowers/workout/"+str(w.id)+"/export"
|
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
except:
|
|
||||||
message = "Something went wrong in workout_c2_upload_view. Response code 200/201 but C2 sync failed: "+response.text
|
|
||||||
with open("media/c2errors.log","a") as errorlog:
|
|
||||||
errorstring = str(sys.exc_info()[0])
|
|
||||||
timestr = strftime("%Y%m%d-%H%M%S")
|
|
||||||
errorlog.write(timestr+errorstring+"\r\n")
|
|
||||||
|
|
||||||
|
|
||||||
else:
|
|
||||||
s = response
|
|
||||||
message = "Something went wrong in workout_c2_upload_view. C2 sync failed."
|
|
||||||
with open("media/c2errors.log","a") as errorlog:
|
|
||||||
errorstring = str(sys.exc_info()[0])
|
|
||||||
timestr = strftime("%Y%m%d-%H%M%S")
|
|
||||||
errorlog.write(timestr+errorstring+"\r\n")
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
message = "You are not authorized to upload this workout"
|
url = reverse(workout_export_view,
|
||||||
|
kwargs = {
|
||||||
url = reverse(workout_export_view,
|
'id':str(w.id),
|
||||||
kwargs = {
|
|
||||||
'message':str(message),
|
|
||||||
'id':str(w.id),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
@@ -4604,7 +4501,7 @@ def workout_otwpowerplot_view(request,id=0,message="",successmessage=""):
|
|||||||
'the_div':div,
|
'the_div':div,
|
||||||
'mayedit':mayedit})
|
'mayedit':mayedit})
|
||||||
|
|
||||||
# the page where you can chose where to export this workout
|
# the page where you can choose where to export this workout
|
||||||
@login_required()
|
@login_required()
|
||||||
def workout_export_view(request,id=0, message="", successmessage=""):
|
def workout_export_view(request,id=0, message="", successmessage=""):
|
||||||
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
|
request.session[translation.LANGUAGE_SESSION_KEY] = USER_LANGUAGE
|
||||||
@@ -5610,6 +5507,8 @@ def workout_getrunkeeperworkout_view(request,runkeeperid):
|
|||||||
id,message = add_workout_from_runkeeperdata(request.user,runkeeperid,data)
|
id,message = add_workout_from_runkeeperdata(request.user,runkeeperid,data)
|
||||||
w = Workout.objects.get(id=id)
|
w = Workout.objects.get(id=id)
|
||||||
w.uploadedtorunkeeper=runkeeperid
|
w.uploadedtorunkeeper=runkeeperid
|
||||||
|
thetoken = runkeeper_open(request.user)
|
||||||
|
w.runkeeperuri = runkeeperstuff.geturifromid(thetoken,runkeeperid)
|
||||||
w.save()
|
w.save()
|
||||||
if message:
|
if message:
|
||||||
url = reverse(workout_edit_view,
|
url = reverse(workout_edit_view,
|
||||||
@@ -5820,6 +5719,31 @@ def workout_upload_view(request,message="",
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
upload_toc2 = False
|
upload_toc2 = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
upload_tostrava = uploadoptions['upload_to_Strava']
|
||||||
|
except KeyError:
|
||||||
|
upload_tostrava = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
upload_tost = uploadoptions['upload_to_SportTracks']
|
||||||
|
except KeyError:
|
||||||
|
upload_tost = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
upload_tork = uploadoptions['upload_to_RunKeeper']
|
||||||
|
except KeyError:
|
||||||
|
upload_tork = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
upload_toua = uploadoptions['upload_to_MapMyFitness']
|
||||||
|
except KeyError:
|
||||||
|
upload_toua = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
upload_totp = uploadoptions['upload_to_TrainingPeaks']
|
||||||
|
except KeyError:
|
||||||
|
upload_totp = False
|
||||||
|
|
||||||
r = Rower.objects.get(user=request.user)
|
r = Rower.objects.get(user=request.user)
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = DocumentsForm(request.POST,request.FILES)
|
form = DocumentsForm(request.POST,request.FILES)
|
||||||
@@ -5836,6 +5760,11 @@ def workout_upload_view(request,message="",
|
|||||||
make_plot = optionsform.cleaned_data['make_plot']
|
make_plot = optionsform.cleaned_data['make_plot']
|
||||||
plottype = optionsform.cleaned_data['plottype']
|
plottype = optionsform.cleaned_data['plottype']
|
||||||
upload_to_c2 = optionsform.cleaned_data['upload_to_C2']
|
upload_to_c2 = optionsform.cleaned_data['upload_to_C2']
|
||||||
|
upload_to_strava = optionsform.cleaned_data['upload_to_Strava']
|
||||||
|
upload_to_st = optionsform.cleaned_data['upload_to_SportTracks']
|
||||||
|
upload_to_rk = optionsform.cleaned_data['upload_to_RunKeeper']
|
||||||
|
upload_to_ua = optionsform.cleaned_data['upload_to_MapMyFitness']
|
||||||
|
upload_to_tp = optionsform.cleaned_data['upload_to_TrainingPeaks']
|
||||||
makeprivate = optionsform.cleaned_data['makeprivate']
|
makeprivate = optionsform.cleaned_data['makeprivate']
|
||||||
|
|
||||||
uploadoptions = {
|
uploadoptions = {
|
||||||
@@ -5843,6 +5772,11 @@ def workout_upload_view(request,message="",
|
|||||||
'make_plot':make_plot,
|
'make_plot':make_plot,
|
||||||
'plottype':plottype,
|
'plottype':plottype,
|
||||||
'upload_to_C2':upload_to_c2,
|
'upload_to_C2':upload_to_c2,
|
||||||
|
'upload_to_Strava':upload_to_strava,
|
||||||
|
'upload_to_SportTracks':upload_to_st,
|
||||||
|
'upload_to_RunKeeper':upload_to_rk,
|
||||||
|
'upload_to_MapMyFitness':upload_to_ua,
|
||||||
|
'upload_to_TrainingPeaks':upload_to_tp,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -5933,65 +5867,49 @@ def workout_upload_view(request,message="",
|
|||||||
filename=fullpathimagename)
|
filename=fullpathimagename)
|
||||||
i.save()
|
i.save()
|
||||||
|
|
||||||
# upload to C2
|
# upload to C2
|
||||||
if (upload_to_c2):
|
if (upload_to_c2):
|
||||||
try:
|
try:
|
||||||
thetoken = c2_open(request.user)
|
c2message,c2id = c2stuff.workout_c2_upload(request.user,w)
|
||||||
except C2NoTokenError:
|
except C2NoTokenError:
|
||||||
return HttpResponseRedirect("/rowers/me/c2authorize/")
|
pass
|
||||||
try:
|
if (upload_to_strava):
|
||||||
c2userid = c2stuff.get_userid(thetoken)
|
try:
|
||||||
if not c2userid:
|
stravamessage,stravaid = stravastuff.workout_strava_upload(
|
||||||
return HttpResponseRedirect("/rowers/me/c2authorize")
|
request.user,w
|
||||||
data = c2stuff.createc2workoutdata(w)
|
)
|
||||||
authorizationstring = str('Bearer ' + thetoken)
|
except StravaNoTokenError:
|
||||||
headers = {'Authorization': authorizationstring,
|
pass
|
||||||
'user-agent': 'sanderroosendaal',
|
|
||||||
'Content-Type': 'application/json'}
|
|
||||||
|
|
||||||
url = "https://log.concept2.com/api/users/%s/results" % (c2userid)
|
if (upload_to_st):
|
||||||
response = requests.post(url,headers=headers,data=json.dumps(data))
|
try:
|
||||||
|
stmessage,stid = sporttracksstuff.workout_sporttracks_upload(
|
||||||
|
request.user,w
|
||||||
|
)
|
||||||
|
except SportTracksNoTokenError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if (upload_to_rk):
|
||||||
|
try:
|
||||||
|
rkmessage,rkid = runkeeperstuff.workout_runkeeper_upload(
|
||||||
|
request.user,w
|
||||||
|
)
|
||||||
|
except RunKeeperNoTokenError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if message:
|
||||||
|
url = reverse(workout_edit_view,
|
||||||
|
kwargs={
|
||||||
|
'message':message,
|
||||||
|
'id':w.id,
|
||||||
|
})
|
||||||
|
|
||||||
# response = c2stuff.workout_c2_upload(request.user,w)
|
else:
|
||||||
if (response.status_code != 201 and response.status_code != 200):
|
url = reverse(workout_edit_view,
|
||||||
if settings.DEBUG:
|
kwargs = {
|
||||||
return HttpResponse(response)
|
'id':w.id,
|
||||||
else:
|
})
|
||||||
message = "C2 upload failed"
|
return HttpResponseRedirect(url)
|
||||||
url = reverse(workout_edit_view,
|
|
||||||
kwargs={
|
|
||||||
'message':message,
|
|
||||||
'id':str(w.id),
|
|
||||||
})
|
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
else:
|
|
||||||
s= json.loads(response.text)
|
|
||||||
c2id = s['data']['id']
|
|
||||||
w.uploadedtoc2 = c2id
|
|
||||||
w.save()
|
|
||||||
except:
|
|
||||||
message = "C2 upload failed"
|
|
||||||
url = reverse(workout_edit_view,
|
|
||||||
kwargs={
|
|
||||||
'message':message,
|
|
||||||
'id':str(w.id),
|
|
||||||
})
|
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
|
|
||||||
|
|
||||||
if message:
|
|
||||||
url = reverse(workout_edit_view,
|
|
||||||
kwargs={
|
|
||||||
'message':message,
|
|
||||||
'id':w.id,
|
|
||||||
})
|
|
||||||
|
|
||||||
else:
|
|
||||||
url = reverse(workout_edit_view,
|
|
||||||
kwargs = {
|
|
||||||
'id':w.id,
|
|
||||||
})
|
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
else:
|
else:
|
||||||
response = render(request,
|
response = render(request,
|
||||||
'document_form.html',
|
'document_form.html',
|
||||||
|
|||||||
Reference in New Issue
Block a user