Private
Public Access
1
0
Files
rowsandall/rowers/c2stuff.py
2022-04-14 16:16:02 +02:00

1021 lines
30 KiB
Python

from django.core.exceptions import PermissionDenied
from rowers.models import C2WorldClassAgePerformance
from rowers.utils import myqueue
# The interactions with the Concept2 logbook API
# All C2 related functions should be defined here
# (There is still some stuff defined directly in views.py. Need to
# move that here.)
from rowers.imports import *
import datetime
from requests import Request, Session
import rowers.mytypes as mytypes
from rowers.mytypes import otwtypes
from rowers.rower_rules import is_workout_user, ispromember
from iso8601 import ParseError
import numpy
import json
from scipy import optimize
from json.decoder import JSONDecodeError
from pytz.exceptions import UnknownTimeZoneError
from rowers.utils import dologging
from rowsandall_app.settings import (
C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET,
UPLOAD_SERVICE_URL, UPLOAD_SERVICE_SECRET
)
from rowers.tasks import (
handle_c2_import_stroke_data, handle_c2_sync, handle_c2_async_workout,
handle_c2_getworkout
)
import django_rq
queue = django_rq.get_queue('default')
queuelow = django_rq.get_queue('low')
queuehigh = django_rq.get_queue('high')
def getagegrouprecord(age, sex='male', weightcategory='hwt',
distance=2000, duration=None, indf=pd.DataFrame()):
if not indf.empty: # pragma: no cover
if not duration:
df = indf[indf['distance'] == distance]
else:
duration = 60*int(duration)
df = indf[indf['duration'] == duration]
else:
if not duration:
df = pd.DataFrame(
list(
C2WorldClassAgePerformance.objects.filter(
distance=distance,
sex=sex,
weightcategory=weightcategory
).values()
)
)
else:
duration = 60*int(duration)
df = pd.DataFrame(
list(
C2WorldClassAgePerformance.objects.filter(
duration=duration,
sex=sex,
weightcategory=weightcategory
).values()
)
)
if not df.empty:
ages = df['age']
powers = df['power']
def fitfunc(pars, x):
return np.abs(pars[0])*(1-x/max(120, pars[1]))-np.abs(
pars[2])*np.exp(-x/np.abs(pars[3]))+np.abs(pars[4])*(np.sin(np.pi*x/max(50, pars[5])))
def errfunc(pars, x, y):
return fitfunc(pars, x)-y
p0 = [700, 120, 700, 10, 100, 100]
try:
p1, success = optimize.leastsq(errfunc, p0[:],
args=(ages, powers))
except: # pragma: no cover
p1 = p0
success = 0
if success:
power = fitfunc(p1, float(age))
power = 0.5*(np.abs(power)+power)
else: # pragma: no cover
power = 0
else:
power = 0
return power
oauth_data = {
'client_id': C2_CLIENT_ID,
'client_secret': C2_CLIENT_SECRET,
'redirect_uri': C2_REDIRECT_URI,
'autorization_uri': "https://log.concept2.com/oauth/authorize",
'content_type': 'application/x-www-form-urlencoded',
'tokenname': 'c2token',
'refreshtokenname': 'c2refreshtoken',
'expirydatename': 'tokenexpirydate',
'bearer_auth': True,
'base_url': "https://log.concept2.com/oauth/access_token",
'scope': 'write',
}
# 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):
raise NoTokenError("User has no token")
else:
if (timezone.now() > r.tokenexpirydate):
res = rower_c2_token_refresh(user)
if res is None: # pragma: no cover
raise NoTokenError("User has no token")
if res[0] is not None:
thetoken = res[0]
else: # pragma: no cover
raise NoTokenError("User has no token")
else:
thetoken = r.c2token
return thetoken
def get_c2_workouts(rower, page=1, do_async=True):
try:
_ = c2_open(rower.user)
except NoTokenError: # pragma: no cover
return 0
res = get_c2_workout_list(rower.user, page=page)
if (res.status_code != 200): # pragma: no cover
return 0
else:
c2ids = [item['id'] for item in res.json()['data']]
alldata = {}
for item in res.json()['data']:
alldata[item['id']] = item
knownc2ids = [
w.uploadedtoc2 for w in Workout.objects.filter(user=rower)
]
tombstones = [
t.uploadedtoc2 for t in TombStone.objects.filter(user=rower)
]
# get "blocked" c2ids
parkedids = []
try:
with open('c2blocked.json', 'r') as c2blocked:
jsondata = json.load(c2blocked)
parkedids = jsondata['ids']
except FileNotFoundError: # pragma: no cover
pass
knownc2ids = uniqify(knownc2ids+tombstones+parkedids)
newids = [c2id for c2id in c2ids if c2id not in knownc2ids]
if settings.TESTING:
newids = c2ids
newparkedids = uniqify(newids+parkedids)
with open('c2blocked.json', 'wt') as c2blocked:
data = {'ids': newparkedids}
json.dump(data, c2blocked)
counter = 0
for c2id in newids:
if do_async: # pragma: no cover
_ = myqueue(queuehigh,
handle_c2_async_workout,
alldata,
rower.user.id,
rower.c2token,
c2id,
counter,
rower.defaulttimezone)
counter = counter+1
else:
_ = create_async_workout(alldata, rower.user, c2id)
return 1
# get workout metrics, then relay stroke data to an asynchronous task
def create_async_workout(alldata, user, c2id):
data = alldata[c2id]
splitdata = None
c2id = data['id']
workouttype = data['type']
startdatetime = iso8601.parse_date(data['date'])
try:
title = data['name']
except KeyError:
title = ""
try:
t = data['comments'].split('\n', 1)[0]
title += t[:40]
except:
title = ''
# Create CSV file name and save data to CSV file
csvfilename = 'media/Import_'+str(c2id)+'.csv.gz'
r = Rower.objects.get(user=user)
authorizationstring = str('Bearer ' + r.c2token)
headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'}
# url2 = "https://log.concept2.com/api/users/me/results"+str(c2id)
url = "https://log.concept2.com/api/users/me/results/"+str(c2id)+"/strokes"
try:
s = requests.get(url, headers=headers)
except ConnectionError: # pragma: no cover
return 0
if s.status_code != 200: # pragma: no cover
return 0
strokedata = pd.DataFrame.from_dict(s.json()['data'])
res = make_cumvalues(0.1*strokedata['t'])
cum_time = res[0]
lapidx = res[1]
starttimeunix = arrow.get(startdatetime).timestamp()
starttimeunix = starttimeunix-cum_time.max()
unixtime = cum_time+starttimeunix
# unixtime[0] = starttimeunix
seconds = 0.1*strokedata.loc[:, 't']
nr_rows = len(unixtime)
try: # pragma: no cover
latcoord = strokedata.loc[:, 'lat']
loncoord = strokedata.loc[:, 'lon']
except:
latcoord = np.zeros(nr_rows)
loncoord = np.zeros(nr_rows)
try:
strokelength = strokedata.loc[:, 'strokelength']
except:
strokelength = np.zeros(nr_rows)
dist2 = 0.1*strokedata.loc[:, 'd']
try:
spm = strokedata.loc[:, 'spm']
except KeyError: # pragma: no cover
spm = 0*dist2
try:
hr = strokedata.loc[:, 'hr']
except KeyError: # pragma: no cover
hr = 0*spm
pace = strokedata.loc[:, 'p']/10.
pace = np.clip(pace, 0, 1e4)
pace = pace.replace(0, 300)
velo = 500./pace
power = 2.8*velo**3
if workouttype == 'bike': # pragma: no cover
velo = 1000./pace
df = pd.DataFrame({'TimeStamp (sec)': unixtime,
' Horizontal (meters)': dist2,
' Cadence (stokes/min)': spm,
' HRCur (bpm)': hr,
' longitude': loncoord,
' latitude': latcoord,
' Stroke500mPace (sec/500m)': pace,
' Power (watts)': power,
' DragFactor': np.zeros(nr_rows),
' DriveLength (meters)': np.zeros(nr_rows),
' StrokeDistance (meters)': strokelength,
' DriveTime (ms)': np.zeros(nr_rows),
' StrokeRecoveryTime (ms)': np.zeros(nr_rows),
' AverageDriveForce (lbs)': np.zeros(nr_rows),
' PeakDriveForce (lbs)': np.zeros(nr_rows),
' lapIdx': lapidx,
' WorkoutState': 4,
' ElapsedTime (sec)': seconds,
'cum_dist': dist2
})
df.sort_values(by='TimeStamp (sec)', ascending=True)
res = df.to_csv(csvfilename, index_label='index',
compression='gzip')
userid = r.user.id
uploadoptions = {
'secret': UPLOAD_SERVICE_SECRET,
'user': userid,
'file': csvfilename,
'title': title,
'workouttype': workouttype,
'boattype': '1x',
'c2id': c2id,
}
session = requests.session()
newHeaders = {'Content-type': 'application/json', 'Accept': 'text/plain'}
session.headers.update(newHeaders)
response = session.post(UPLOAD_SERVICE_URL, json=uploadoptions)
if response.status_code != 200: # pragma: no cover
return 0
try:
workoutid = response.json()['id']
except KeyError: # pragma: no cover
workoutid = 1
newc2id = Workout.objects.get(id=workoutid).uploadedtoc2
parkedids = []
with open('c2blocked.json', 'r') as c2blocked:
jsondata = json.load(c2blocked)
parkedids = jsondata['ids']
newparkedids = [id for id in parkedids if id != newc2id]
with open('c2blocked.json', 'wt') as c2blocked:
data = {'ids': newparkedids}
c2blocked.seek(0)
json.dump(data, c2blocked)
# summary
if 'workout' in data: # pragma: no cover
if 'splits' in data['workout']:
splitdata = data['workout']['splits']
elif 'intervals' in data['workout']:
splitdata = data['workout']['intervals']
else:
splitdata = False
else:
splitdata = False
if splitdata: # pragma: no cover
summary, sa, results = c2stuff.summaryfromsplitdata(
splitdata, data, csvfilename, workouttype=workouttype)
w = Workout.objects.get(id=workoutid)
w.summary = summary
w.save()
from rowingdata.trainingparser import getlist
if sa:
values = getlist(sa)
units = getlist(sa, sel='unit')
types = getlist(sa, sel='type')
rowdata = rdata(w.csvfilename)
if rowdata:
rowdata.updateintervaldata(values,
units, types, results)
rowdata.write_csv(w.csvfilename, gzip=True)
dataprep.update_strokedata(w.id, rowdata.df)
return workoutid
# convert datetime object to seconds
def makeseconds(t):
seconds = t.hour*3600.+t.minute*60.+t.second+0.1*int(t.microsecond/1.e5)
return seconds
# convert our weight class code to Concept2 weight class code
def c2wc(weightclass):
if (weightclass == "lwt"): # pragma: no cover
res = "L"
else:
res = "H"
return res
# Concept2 logbook sends over split data for each interval
# We use it here to generate a custom summary
# Some users complained about small differences
def summaryfromsplitdata(splitdata, data, filename, sep='|', workouttype='rower'):
workouttype = workouttype.lower()
totaldist = data['distance']
totaltime = data['time']/10.
try:
spm = data['stroke_rate']
except KeyError: # pragma: no cover
spm = 0
try:
resttime = data['rest_time']/10.
except KeyError: # pragma: no cover
resttime = 0
try:
restdistance = data['rest_distance']
except KeyError: # pragma: no cover
restdistance = 0
try:
avghr = data['heart_rate']['average']
except KeyError: # pragma: no cover
avghr = 0
try:
maxhr = data['heart_rate']['max']
except KeyError: # pragma: no cover
maxhr = 0
try:
avgpace = 500.*totaltime/totaldist
except (ZeroDivisionError, OverflowError): # pragma: no cover
avgpace = 0.
try:
restpace = 500.*resttime/restdistance
except (ZeroDivisionError, OverflowError): # pragma: no cover
restpace = 0.
velo = totaldist/totaltime
avgpower = 2.8*velo**(3.0)
if workouttype in ['bike', 'bikeerg']: # pragma: no cover
velo = velo/2.
avgpower = 2.8*velo**(3.0)
velo = velo*2
try:
restvelo = restdistance/resttime
except (ZeroDivisionError, OverflowError): # pragma: no cover
restvelo = 0
restpower = 2.8*restvelo**(3.0)
if workouttype in ['bike', 'bikeerg']: # pragma: no cover
restvelo = restvelo/2.
restpower = 2.8*restvelo**(3.0)
restvelo = restvelo*2
try:
avgdps = totaldist/data['stroke_count']
except (ZeroDivisionError, OverflowError, KeyError): # pragma: no cover
avgdps = 0
from rowingdata import summarystring, workstring, interval_string
sums = summarystring(totaldist, totaltime, avgpace, spm, avghr, maxhr,
avgdps, avgpower, readFile=filename,
separator=sep)
sums += workstring(totaldist, totaltime, avgpace, spm, avghr, maxhr,
avgdps, avgpower, separator=sep, symbol='W')
sums += workstring(restdistance, resttime, restpace, 0, 0, 0, 0, restpower,
separator=sep,
symbol='R')
sums += '\nWorkout Details\n'
sums += '#-{sep}SDist{sep}-Split-{sep}-SPace-{sep}-Pwr-{sep}SPM-{sep}AvgHR{sep}MaxHR{sep}DPS-\n'.format(
sep=sep
)
intervalnr = 0
sa = []
results = []
try:
timebased = data['workout_type'] in [
'FixedTimeSplits', 'FixedTimeInterval']
except KeyError: # pragma: no cover
timebased = False
for interval in splitdata:
try:
idist = interval['distance']
except KeyError: # pragma: no cover
idist = 0
try:
itime = interval['time']/10.
except KeyError: # pragma: no cover
itime = 0
try:
ipace = 500.*itime/idist
except (ZeroDivisionError, OverflowError): # pragma: no cover
ipace = 180.
try:
ispm = interval['stroke_rate']
except KeyError: # pragma: no cover
ispm = 0
try:
irest_time = interval['rest_time']/10.
except KeyError: # pragma: no cover
irest_time = 0
try:
iavghr = interval['heart_rate']['average']
except KeyError: # pragma: no cover
iavghr = 0
try:
imaxhr = interval['heart_rate']['average']
except KeyError: # pragma: no cover
imaxhr = 0
# create interval values
iarr = [idist, 'meters', 'work']
resarr = [itime]
if timebased: # pragma: no cover
iarr = [itime, 'seconds', 'work']
resarr = [idist]
if irest_time > 0:
iarr += [irest_time, 'seconds', 'rest']
try:
resarr += [interval['rest_distance']]
except KeyError: # pragma: no cover
resarr += [np.nan]
sa += iarr
results += resarr
if itime != 0:
ivelo = idist/itime
ipower = 2.8*ivelo**(3.0)
if workouttype in ['bike', 'bikeerg']: # pragma: no cover
ipower = 2.8*(ivelo/2.)**(3.0)
else: # pragma: no cover
ivelo = 0
ipower = 0
sums += interval_string(intervalnr, idist, itime, ipace, ispm,
iavghr, imaxhr, 0, ipower, separator=sep)
intervalnr += 1
return sums, sa, results
# Create the Data object for the stroke data to be sent to Concept2 logbook
# API
def createc2workoutdata(w):
filename = w.csvfilename
try:
row = rowingdata(csvfile=filename)
except IOError: # pragma: no cover
return 0
try:
averagehr = int(row.df[' HRCur (bpm)'].mean())
maxhr = int(row.df[' HRCur (bpm)'].max())
except (ValueError, KeyError): # pragma: no cover
averagehr = 0
maxhr = 0
# Calculate intervalstats
itime, idist, itype = row.intervalstats_values()
try:
lapnames = row.df[' lapIdx'].unique()
except KeyError: # pragma: no cover
lapnames = range(len(itime))
nrintervals = len(itime)
if len(lapnames) != nrintervals:
newlapnames = []
for name in lapnames:
newlapnames += [name, name]
lapnames = newlapnames
intervaldata = []
for i in range(nrintervals):
if itime[i] > 0:
mask = (row.df[' lapIdx'] == lapnames[i]) & (
row.df[' WorkoutState'] == itype[i])
try:
spmav = int(row.df[' Cadence (stokes/min)']
[mask].mean().astype(int))
hrav = int(row.df[' HRCur (bpm)'][mask].mean().astype(int))
except AttributeError: # pragma: no cover
try:
spmav = int(row.df[' Cadence (stokes/min)'][mask].mean())
hrav = int(row.df[' HRCur (bpm)'][mask].mean())
except ValueError:
spmav = 0
try:
hrav = int(row.df[' HRCur (bpm)'][mask].mean())
except ValuError:
hrav = 0
intervaldict = {
'type': 'distance',
'time': int(10*itime[i]),
'distance': int(idist[i]),
'heart_rate': {
'average': hrav,
},
'stroke_rate': spmav,
}
intervaldata.append(intervaldict)
# adding diff, trying to see if this is valid
t = 10*row.df.loc[:, 'TimeStamp (sec)'].values - \
10*row.df.loc[:, 'TimeStamp (sec)'].iloc[0]
try:
t[0] = t[1]
except IndexError: # pragma: no cover
pass
d = 10*row.df.loc[:, ' Horizontal (meters)'].values
try:
d[0] = d[1]
except IndexError: # pragma: no cover
pass
p = abs(10*row.df.loc[:, ' Stroke500mPace (sec/500m)'].values)
p = np.clip(p, 0, 3600)
if w.workouttype == 'bike': # pragma: no cover
p = 2.0*p
t = t.astype(int)
d = d.astype(int)
p = p.astype(int)
spm = row.df[' Cadence (stokes/min)'].astype(int)
try:
spm[0] = spm[1]
except (KeyError, IndexError): # pragma: no cover
spm = 0*t
try:
hr = row.df[' HRCur (bpm)'].astype(int)
except ValueError: # pragma: no cover
hr = 0*d
stroke_data = []
t = t.tolist()
d = d.tolist()
p = p.tolist()
spm = spm.tolist()
hr = hr.tolist()
for i in range(len(t)):
thisrecord = {"t": t[i],
"d": d[i],
"p": p[i],
"spm": spm[i],
"hr": hr[i]}
stroke_data.append(thisrecord)
try:
durationstr = datetime.datetime.strptime(
str(w.duration), "%H:%M:%S.%f")
except ValueError:
durationstr = datetime.datetime.strptime(str(w.duration), "%H:%M:%S")
workouttype = w.workouttype
if workouttype in otwtypes:
workouttype = 'water'
if w.timezone == 'tzutc()': # pragma: no cover
w.timezone = 'UTC'
w.save()
wendtime = w.startdatetime.astimezone(pytz.timezone(
w.timezone))+datetime.timedelta(seconds=makeseconds(durationstr))
data = {
"type": mytypes.c2mapping[workouttype],
# w.startdatetime.isoformat(),
"date": wendtime.strftime('%Y-%m-%d %H:%M:%S'),
"stroke_count": int(row.stroke_count),
"timezone": w.timezone,
"distance": int(w.distance),
"time": int(10*makeseconds(durationstr)),
"weight_class": c2wc(w.weightcategory),
"comments": w.notes,
'stroke_rate': int(row.df[' Cadence (stokes/min)'].mean()),
'drag_factor': int(row.dragfactor),
"heart_rate": {
"average": averagehr,
"max": maxhr,
},
"stroke_data": stroke_data,
'workout': {
'splits': intervaldata,
}
}
return data
# Refresh Concept2 authorization token
def do_refresh_token(refreshtoken):
scope = "results:write,user:read"
# client_auth = requests.auth.HTTPBasicAuth(C2_CLIENT_ID, C2_CLIENT_SECRET)
post_data = {"grant_type": "refresh_token",
"client_secret": C2_CLIENT_SECRET,
"client_id": C2_CLIENT_ID,
"refresh_token": refreshtoken,
}
headers = {'user-agent': 'sanderroosendaal'}
url = "https://log.concept2.com/oauth/access_token"
s = Session()
req = Request('POST', url, data=post_data, headers=headers)
prepped = req.prepare()
prepped.body += "&scope="
prepped.body += scope
response = s.send(prepped)
try:
token_json = response.json()
except JSONDecodeError: # pragma: no cover
return [None, None, None]
try:
thetoken = token_json['access_token']
expires_in = token_json['expires_in']
refresh_token = token_json['refresh_token']
except: # pragma: no cover
with open("media/c2errors.log", "a") as errorlog:
errorstring = str(sys.exc_info()[0])
timestr = time.strftime("%Y%m%d-%H%M%S")
errorlog.write(timestr+errorstring+"\r\n")
errorlog.write(str(token_json)+"\r\n")
thetoken = None
expires_in = None
refresh_token = None
return [thetoken, expires_in, refresh_token]
# Exchange authorization code for authorization token
def get_token(code):
messg = ''
scope = "user:read,results:write"
# client_auth = requests.auth.HTTPBasicAuth(C2_CLIENT_ID, C2_CLIENT_SECRET)
post_data = {"grant_type": "authorization_code",
"code": code,
"redirect_uri": C2_REDIRECT_URI,
"client_secret": C2_CLIENT_SECRET,
"client_id": C2_CLIENT_ID,
}
headers = {'user-agent': 'sanderroosendaal'}
url = "https://log.concept2.com/oauth/access_token"
s = Session()
req = Request('POST', url, data=post_data, headers=headers)
prepped = req.prepare()
prepped.body += "&scope="
prepped.body += scope
response = s.send(prepped)
token_json = response.json()
try:
status_code = response.status_code
# status_code = token_json['status_code']
except AttributeError: # pragma: no cover
# except KeyError:
return (0, response.text)
try:
status_code = token_json.status_code
except AttributeError: # pragma: no cover
return (0, 'Attribute Error on c2_get_token')
if status_code == 200:
thetoken = token_json['access_token']
expires_in = token_json['expires_in']
refresh_token = token_json['refresh_token']
else: # pragma: no cover
return (0, token_json['message'])
return (thetoken, expires_in, refresh_token, messg)
# Make URL for authorization and load it
def make_authorization_url(request): # pragma: no cover
# Generate a random string for the state parameter
# Save it for use later to prevent xsrf attacks
from uuid import uuid4
# state = str(uuid4())
scope = "user:read,results:write"
params = {"client_id": C2_CLIENT_ID,
"response_type": "code",
"redirect_uri": C2_REDIRECT_URI}
url = "https://log.concept2.com/oauth/authorize?" + \
urllib.parse.urlencode(params)
url += "&scope="+scope
return HttpResponseRedirect(url)
# Get workout from C2 ID
def get_workout(user, c2id, do_async=True):
r = Rower.objects.get(user=user)
_ = c2_open(user)
_ = myqueue(queuehigh,
handle_c2_getworkout,
user.id,
r.c2token,
c2id,
r.defaulttimezone)
return 1
# Get list of C2 workouts. We load only the first page,
# assuming that users don't want to import their old workouts
def get_c2_workout_list(user, page=1):
r = Rower.objects.get(user=user)
if (r.c2token == '') or (r.c2token is None): # pragma: no cover
s = "Token doesn't exist. Need to authorize"
return custom_exception_handler(401, s)
elif (timezone.now() > r.tokenexpirydate): # pragma: no cover
s = "Token expired. Needs to refresh."
return custom_exception_handler(401, s)
# ready to fetch. Hurray
authorizationstring = str('Bearer ' + r.c2token)
headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'}
url = "https://log.concept2.com/api/users/me/results"
url += "?page={page}".format(page=page)
s = requests.get(url, headers=headers)
return s
# Get username, having access token.
# Handy for checking if the API access is working
def get_username(access_token): # pragma: no cover
authorizationstring = str('Bearer ' + access_token)
headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'}
import urllib
url = "https://log.concept2.com/api/users/me"
response = requests.get(url, headers=headers)
me_json = response.json()
try:
res = me_json['data']['username']
_ = me_json['data']['id']
except KeyError:
res = None
return res
# 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://log.concept2.com/api/users/me"
try:
response = requests.get(url, headers=headers)
except: # pragma: no cover
return 0
try:
me_json = response.json()
except: # pragma: no cover
return 0
try:
res = me_json['data']['id']
except KeyError: # pragma: no cover
return 0
return res
# For debugging purposes
def process_callback(request): # pragma: no cover
# need error handling
code = request.GET['code']
access_token = get_token(code)
username, id = get_username(access_token)
return HttpResponse("got a user name: %s" % username)
def default(o): # pragma: no cover
if isinstance(o, numpy.int64):
return int(o)
raise TypeError
# Uploading workout
def workout_c2_upload(user, w, asynchron=False):
message = 'trying C2 upload'
try:
if mytypes.c2mapping[w.workouttype] is None: # pragma: no cover
return "This workout type cannot be uploaded to Concept2", 0
except KeyError: # pragma: no cover
return "This workout type cannot be uploaded to Concept2", 0
_ = c2_open(user)
r = Rower.objects.get(user=user)
# ready to upload. Hurray
if (is_workout_user(user, w)):
c2userid = get_userid(r.c2token)
if not c2userid: # pragma: no cover
raise NoTokenError("User has no token")
dologging('debuglog.log',
'Upload to C2 user {userid}'.format(userid=user.id))
data = createc2workoutdata(w)
dologging('debuglog.log', json.dumps(data))
if data == 0: # pragma: no cover
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)
if not asynchron: # pragma: no cover
response = requests.post(
url, headers=headers, data=json.dumps(data, default=default))
if (response.status_code == 409): # pragma: no cover
message = "Concept2 Duplicate error"
w.uploadedtoc2 = -1
c2id = -1
w.save()
elif (response.status_code == 201 or response.status_code == 200):
# s= json.loads(response.text)
s = response.json()
c2id = s['data']['id']
w.uploadedtoc2 = c2id
w.save()
message = "Upload to Concept2 was successful"
else: # pragma: no cover
message = "Something went wrong in workout_c2_upload_view." \
" Response code 200/201 but C2 sync failed: "+response.text
c2id = 0
else: # pragma: no cover
_ = myqueue(queue,
handle_c2_sync,
w.id,
url,
headers,
json.dumps(data, default=default))
c2id = 0
return message, c2id
# This is token refresh. Looks for tokens in our database, then refreshes
def rower_c2_token_refresh(user):
r = Rower.objects.get(user=user)
res = do_refresh_token(r.c2refreshtoken)
if res[0]:
access_token = res[0]
expires_in = res[1]
refresh_token = res[2]
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
r = Rower.objects.get(user=user)
r.c2token = access_token
r.tokenexpirydate = expirydatetime
r.c2refreshtoken = refresh_token
r.save()
return r.c2token
else: # pragma: no cover
return None