from __future__ import absolute_import from __future__ import division from __future__ import print_function from __future__ import unicode_literals # 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') from rowers.utils import myqueue from rowers.models import C2WorldClassAgePerformance from django.core.exceptions import PermissionDenied 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'] #poly_coefficients = np.polyfit(ages,powers,6) fitfunc = lambda pars, x: 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]))) errfunc = lambda pars, x,y: 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 = np.polyval(poly_coefficients,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): s = "Token doesn't exist. Need to authorize" raise NoTokenError("User has no token") else: if (timezone.now()>r.tokenexpirydate): res = rower_c2_token_refresh(user) if res == None: # pragma: no cover raise NoTokenError("User has no token") if res[0] != 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,do_async=True): try: thetoken = c2_open(rower.user) except NoTokenError: # pragma: no cover return 0 res = get_c2_workout_list(rower.user,page=1) 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 not c2id 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 res = myqueue(queuehigh, handle_c2_async_workout, alldata, rower.user.id, rower.c2token, c2id, counter, rower.defaulttimezone ) #res = handle_c2_async_workout(alldata,rower.user.id,rower.c2token,c2id,counter) counter = counter+1 else: workoutid = 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 distance = data['distance'] c2id = data['id'] workouttype = data['type'] verified = data['verified'] startdatetime = iso8601.parse_date(data['date']) weightclass = data['weight_class'] try: title = data['name'] except KeyError: title = "" try: t = data['comments'].split('\n', 1)[0] title += t[:40] except: title = '' weightcategory = 'hwt' if weightclass == "L": weightcategory = 'lwt' # Create CSV file name and save data to CSV file csvfilename ='media/Import_'+str(c2id)+'.csv.gz' totaltime = data['time']/10. duration = dataprep.totaltime_sec_to_string(totaltime) try: timezone_str = data['timezone'] except: # pragma: no cover timezone_str = 'UTC' workoutdate = startdatetime.astimezone( pytz.timezone(timezone_str) ).strftime('%Y-%m-%d') starttime = startdatetime.astimezone( pytz.timezone(timezone_str) ).strftime('%H:%M:%S') try: notes = data['comments'] name = notes[:40] except (KeyError,TypeError): notes = 'C2 Import Workout from {startdatetime}'.format(startdatetime=startdatetime) name = notes 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 spmav = int(row.df[' Cadence (stokes/min)'][mask].mean()) hrav = int(row.df[' HRCur (bpm)'][mask].mean()) 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()': w.timezone = 'UTC' w.save() wendtime = w.startdatetime.astimezone(pytz.timezone(w.timezone))+datetime.timedelta(seconds=makeseconds(durationstr)) data = { "type": mytypes.c2mapping[workouttype], "date": wendtime.strftime('%Y-%m-%d %H:%M:%S'), #w.startdatetime.isoformat(), "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_count": int(row.stroke_count), '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) thetoken = c2_open(user) job = 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) else: # 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'] id = 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 thetoken = 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 job = 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