diff --git a/rowers/admin.py b/rowers/admin.py index 9b6be6ab..5c179e88 100644 --- a/rowers/admin.py +++ b/rowers/admin.py @@ -46,8 +46,6 @@ class RowerInline(admin.StackedInline): 'sporttrackstoken','sporttrackstokenexpirydate', 'sporttracksrefreshtoken', 'sporttracks_auto_export', - 'underarmourtoken','underarmourtokenexpirydate', - 'underarmourrefreshtoken', 'mapmyfitness_auto_export', 'tptoken','tptokenexpirydate','tprefreshtoken', 'trainingpeaks_auto_export', diff --git a/rowers/c2stuff.py b/rowers/c2stuff.py index 3f3346ac..e533a713 100644 --- a/rowers/c2stuff.py +++ b/rowers/c2stuff.py @@ -22,15 +22,7 @@ from scipy import optimize from json.decoder import JSONDecodeError from pytz.exceptions import UnknownTimeZoneError - -def dologging(s): - tstamp = time.localtime() - timestamp = time.strftime('%b-%d-%Y %H:%M:%S', tstamp) - with open('debuglog.log','a') as f: - f.write('\n') - f.write(timestamp) - f.write(' ') - f.write(s) +from rowers.utils import dologging from rowsandall_app.settings import ( C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, @@ -39,6 +31,7 @@ from rowsandall_app.settings import ( 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') @@ -840,64 +833,15 @@ def get_workout(user,c2id,do_async=False): elif (timezone.now()>r.tokenexpirydate): s = "Token expired. Needs to refresh." return custom_exception_handler(401,s),0 - 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/"+str(c2id) - s = requests.get(url,headers=headers) - if s.status_code != 200: # pragma: no cover - if s.status_code == 404: - raise PermissionDenied("You have no access to this resource") - else: - s = "Something went wrong with the import" - return custom_exception_handler(401,s), 0 + job = myqueue(queuehigh, + handle_c2_getworkout, + user.id, + r.c2token, + c2id, + r.defaulttimezone) - data = s.json()['data'] - alldata = {c2id:data} - splitdata = None - #with open('c2temp.json','w') as f: - # f.write(json.dumps(s.json())) - # print(s.json()) - - if do_async: - print('aap',alldata) - job = myqueue(queuehigh, - handle_c2_async_workout, - alldata, - r.user.id, - r.c2token, - c2id, - 0, - r.defaulttimezone) - - return data, pd.DataFrame() - - - if 'workout' in data: - if 'splits' in data['workout']: # pragma: no cover - splitdata = data['workout']['splits'] - elif 'intervals' in data['workout']: # pragma: no cover - splitdata = data['workout']['intervals'] - else: # pragma: no cover - splitdata = None - - # Check if workout has stroke data, and get the stroke data - - if data['stroke_data']: - res2 = get_c2_workout_strokes(user,c2id) - if res2.status_code == 200: - strokedata = pd.DataFrame.from_dict(res2.json()['data']) - else: # pragma: no cover - strokedata = pd.DataFrame() - else: # pragma: no cover - strokedata = pd.DataFrame() - - - return data,strokedata + return 1 # Get stroke data belonging to C2 ID def get_c2_workout_strokes(user,c2id): @@ -1027,9 +971,9 @@ def workout_c2_upload(user,w,asynchron=False): if not c2userid: # pragma: no cover raise NoTokenError("User has no token") - dologging('Upload to C2 user {userid}'.format(userid=user.id)) + dologging('debuglog.log','Upload to C2 user {userid}'.format(userid=user.id)) data = createc2workoutdata(w) - dologging(json.dumps(data)) + 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 diff --git a/rowers/models.py b/rowers/models.py index d5449391..7b72f243 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -988,10 +988,6 @@ class Rower(models.Model): sporttracksrefreshtoken = models.CharField(default='',max_length=200, blank=True,null=True) sporttracks_auto_export = models.BooleanField(default=False) - underarmourtoken = models.CharField(default='',max_length=200,blank=True,null=True) - underarmourtokenexpirydate = models.DateTimeField(blank=True,null=True) - underarmourrefreshtoken = models.CharField(default='',max_length=200, - blank=True,null=True) mapmyfitness_auto_export = models.BooleanField(default=False) tptoken = models.CharField(default='',max_length=1000,blank=True,null=True) tptokenexpirydate = models.DateTimeField(blank=True,null=True) @@ -3201,7 +3197,6 @@ class Workout(models.Model): maxhr = models.BigIntegerField(blank=True,null=True) uploadedtostrava = models.BigIntegerField(default=0) uploadedtosporttracks = models.BigIntegerField(default=0) - uploadedtounderarmour = models.BigIntegerField(default=0) uploadedtotp = models.BigIntegerField(default=0) uploadedtorunkeeper = models.BigIntegerField(default=0) uploadedtogarmin = models.BigIntegerField(default=0) @@ -3283,7 +3278,6 @@ class TombStone(models.Model): uploadedtoc2 = models.IntegerField(default=0) uploadedtostrava = models.BigIntegerField(default=0) uploadedtosporttracks = models.BigIntegerField(default=0) - uploadedtounderarmour = models.BigIntegerField(default=0) uploadedtotp = models.BigIntegerField(default=0) uploadedtorunkeeper = models.BigIntegerField(default=0) uploadedtonk = models.BigIntegerField(default=0) @@ -3294,7 +3288,6 @@ def create_tombstone_on_delete(sender, instance, **kwargs): user=instance.user, uploadedtoc2 = instance.uploadedtoc2, uploadedtostrava = instance.uploadedtostrava, - uploadedtounderarmour = instance.uploadedtounderarmour, uploadedtotp = instance.uploadedtotp, uploadedtorunkeeper = instance.uploadedtorunkeeper, uploadedtonk = instance.uploadedtonk diff --git a/rowers/tasks.py b/rowers/tasks.py index 72a9528a..df1a1c5d 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -25,6 +25,8 @@ from rowingdata import rowingdata as rdata from datetime import timedelta from sqlalchemy import create_engine +from rowers.imports import splituadata + #from celery import app from rowers.celery import app from celery import shared_task @@ -64,7 +66,7 @@ from django_rq import job from django.utils import timezone from django.utils.html import strip_tags -from rowers.utils import deserialize_list,ewmovingaverage,wavg +from rowers.utils import deserialize_list,ewmovingaverage,wavg,dologging from rowers.emails import htmlstrip from rowers import mytypes @@ -125,15 +127,6 @@ from rowers.courseutils import ( InvalidTrajectoryError ) -def dologging(s): - tstamp = time.localtime() - timestamp = time.strftime('%b-%d-%Y %H:%M:%S', tstamp) - with open('debuglog.log','a') as f: - f.write('\n') - f.write(timestamp) - f.write(' ') - f.write(s) - # Concept2 logbook sends over split data for each interval # We use it here to generate a custom summary # Some users complained about small differences @@ -2985,7 +2978,23 @@ def handle_nk_async_workout(alldata,userid,nktoken,nkid,delaysec,defaulttimezone # return return workoutid +@app.task +def handle_c2_getworkout(userid,c2token,c2id,defaulttimezone,debug=False,**kwargs): + authorizationstring = str('Bearer ' + c2token) + headers = {'Authorization': authorizationstring, + 'user-agent': 'sanderroosendaal', + 'Content-Type': 'application/json'} + url = "https://log.concept2.com/api/users/me/results/"+str(c2id) + s = requests.get(url,headers=headers) + if s.status_code != 200: # pragma: no cover + return 0 + + data = s.json()['data'] + alldata = {c2id:data} + splitdata = None + + return handle_c2_async_workout(alldata,userid,c2token,c2id,0,defaulttimezone) @app.task def handle_c2_async_workout(alldata,userid,c2token,c2id,delaysec,defaulttimezone,debug=False,**kwargs): @@ -3007,8 +3016,8 @@ def handle_c2_async_workout(alldata,userid,c2token,c2id,delaysec,defaulttimezone weightclass = data['weight_class'] s = 'User {userid}, C2 ID {c2id}'.format(userid=userid,c2id=c2id) - dologging(s) - dologging(json.dumps(data)) + dologging('debuglog.log',s) + dologging('debuglog.log',json.dumps(data)) try: title = data['name'] @@ -3041,7 +3050,7 @@ def handle_c2_async_workout(alldata,userid,c2token,c2id,delaysec,defaulttimezone s = 'Time zone {timezone}, stardatetime {startdatetime}, duration {duration}'.format( timezone=timezone,startdatetime=startdatetime, duration=duration) - dologging(s) + dologging('debuglog.log',s) @@ -3246,6 +3255,7 @@ def handle_c2_async_workout(alldata,userid,c2token,c2id,delaysec,defaulttimezone return workoutid + @app.task def fetch_strava_workout(stravatoken,oauth_data,stravaid,csvfilename,userid,debug=False,**kwargs): fetchresolution = 'high' diff --git a/rowers/templates/export.html b/rowers/templates/export.html deleted file mode 100644 index e28275c0..00000000 --- a/rowers/templates/export.html +++ /dev/null @@ -1,209 +0,0 @@ -{% extends "base.html" %} -{% load static %} -{% load rowerfilters %} - -{% block title %}Export {% endblock %} - -{% block content %} -
- -Click one of the below logos to connect to the service of your choice. - You only need to do this once. After that, the site will have access until you - revoke the authorization for the "rowingdata" app.
- - - - - - - -Import workouts from the Concept2 logbook
-Import workouts from SportTracks
-Click one of the below logos to connect to the service of your choice. - You only need to do this once. After that, the site will have - access until you - revoke the authorization for the "rowingdata" app.
- - - - - - - -Use the form below to set your auto import/export settings. - As we implement auto import/export for various partner sites, this form will be expanded.
- -Auto Import = rowsandall.com will regularly poll the partner site for new - workouts and automatically import new workouts -
- -Auto Export = New workouts uploaded to rowsandall.com will be automatically synchronized - to the partner site
- -These settings only have effect if you are a user on one - of the paid plans
- -Strava Auto Import also imports activity changes on Strava to Rowsandall, except when you delete @@ -80,7 +77,6 @@
-
Available on MapMyFitness (UnderArmour)
-{% if workouts %}
-
| Import | -Date/Time | -Duration | -Total Distance | -Type | -
|---|---|---|---|---|
| - Import | -{{ workout|ualookup:'starttime' }} | -{{ workout|ualookup:'duration' }} | -{{ workout|ualookup:'distance' }} m | -{{ workout|ualookup:'type' }} | -
No workouts found. We only list workouts with time data series.
-{% endif %} -{% endblock %} - -{% block sidebar %} -{% include 'menu_workouts.html' %} -{% endblock %} diff --git a/rowers/tests/test_imports.py b/rowers/tests/test_imports.py index 7da2bcdf..d49285a0 100644 --- a/rowers/tests/test_imports.py +++ b/rowers/tests/test_imports.py @@ -1149,109 +1149,6 @@ class RunKeeperObjects(DjangoTestCase): -@pytest.mark.django_db -@override_settings(TESTING=True) -class UAObjects(DjangoTestCase): - def setUp(self): - self.c = Client() - self.u = User.objects.create_user('john', - 'sander@ds.ds', - 'koeinsloot') - - self.u.first_name = 'John' - self.u.last_name = 'Sander' - self.u.save() - self.r = Rower.objects.create(user=self.u,gdproptin=True,surveydone=True, - gdproptindate=timezone.now() - ) - - - self.r.underarmourtoken = '12' - self.r.underarmourrefreshtoken = '12' - self.r.underarmourtokenexpirydate = arrow.get(datetime.datetime.now()+datetime.timedelta(days=1)).datetime - self.r.save() - - self.c.login(username='john',password='koeinsloot') - - self.nu = datetime.datetime.now() - - filename = 'rowers/tests/testdata/testdata.csv' - - rr = rrower(hrmax=self.r.max,hrut2=self.r.ut2, - hrut1=self.r.ut1,hrat=self.r.at, - hrtr=self.r.tr,hran=self.r.an,ftp=self.r.ftp) - row = rdata(csvfile=filename,rower=rr) - totaldist = row.df['cum_dist'].max() - totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min() - totaltime = totaltime+row.df.loc[:,' ElapsedTime (sec)'].iloc[0] - - - hours = int(totaltime/3600.) - minutes = int((totaltime - 3600.*hours)/60.) - seconds = int(totaltime - 3600.*hours - 60.*minutes) - tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds)) - - duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths) - - - workoutdate = row.rowdatetime.strftime('%Y-%m-%d') - workoutstarttime = row.rowdatetime.strftime('%H:%M:%S') - - self.w = Workout.objects.create( - name='testworkout',workouttype='water', - user=self.r,date=self.nu.strftime('%Y-%m-%d'), - starttime=workoutstarttime, - startdatetime=row.rowdatetime, - duration=duration,distance=totaldist, - csvfilename=filename - ) - - @patch('rowers.imports.requests.post', side_effect=mocked_requests) - def test_underarmour_callback(self, mock_post): - response = self.c.get('/underarmour_callback?code=dsdoij232s',follow=True) - - - self.assertEqual(response.status_code, 200) - - - @patch('rowers.underarmourstuff.requests.post', side_effect=mocked_requests) - def test_underarmour_token_refresh(self, mock_post): - response = self.c.get('/rowers/me/underarmourrefresh/',follow=True) - - self.assertEqual(response.status_code, 200) - - - @patch('rowers.underarmourstuff.requests.post', side_effect=mocked_requests) - @patch('rowers.underarmourstuff.requests.get', side_effect=mocked_requests) - def test_underarmour_upload(self, mock_get, mock_post): - response = self.c.get('/rowers/workout/'+encoded1+'/underarmouruploadw/') - - self.assertRedirects(response, - expected_url = '/rowers/workout/'+encoded1+'/edit/', - status_code=302,target_status_code=200) - - self.assertEqual(response.url, '/rowers/workout/'+encoded1+'/edit/') - self.assertEqual(response.status_code, 302) - - @patch('rowers.underarmourstuff.requests.get', side_effect=mocked_requests) - def test_underarmour_list(self, mock_get): - response = self.c.get('/rowers/workout/underarmourimport',follow=True) - - self.assertEqual(response.status_code,200) - - @patch('rowers.imports.requests.get', side_effect=mocked_requests) - @patch('rowers.dataprep.create_engine') - def test_underarmour_import(self, mock_get, mocked_sqlalchemy): - - response = self.c.get('/rowers/workout/underarmourimport/12/',follow=True) - - self.assertRedirects(response, - expected_url='/rowers/workout/'+encoded2+'/edit/', - status_code=302,target_status_code=200) - - self.assertEqual(response.status_code, 200) - - #@pytest.mark.django_db @override_settings(TESTING=True) class TPObjects(DjangoTestCase): diff --git a/rowers/traverselinktest.py b/rowers/traverselinktest.py index 3856fb44..0b07477f 100644 --- a/rowers/traverselinktest.py +++ b/rowers/traverselinktest.py @@ -99,7 +99,6 @@ class TraverseLinksTest(TestCase): '.*authorize.*', '.*youtu.*', '.*earth.*', - '.*underarmour.*', '.*runkeeper.*', '.*c2list.*', '.*stravaimport.*', diff --git a/rowers/underarmourstuff.py b/rowers/underarmourstuff.py deleted file mode 100644 index 33003a22..00000000 --- a/rowers/underarmourstuff.py +++ /dev/null @@ -1,544 +0,0 @@ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals -from __future__ import unicode_literals, absolute_import -from rowers.imports import * - -import numpy - -import rowers.mytypes as mytypes -from rowers.mytypes import otwtypes -from rowers.rower_rules import is_workout_user - -from rowsandall_app.settings import ( - UNDERARMOUR_CLIENT_KEY, - UNDERARMOUR_CLIENT_SECRET, - UNDERARMOUR_REDIRECT_URI, - ) - -oauth_data = { - 'client_id': UNDERARMOUR_CLIENT_KEY, - 'client_secret': UNDERARMOUR_CLIENT_SECRET, - 'redirect_uri': UNDERARMOUR_REDIRECT_URI, - 'autorization_uri': "https://www.mapmyfitness.com/v7.1/oauth2/uacf/authorize/", - 'content_type': 'application/x-www-form-urlencoded', - 'tokenname': 'underarmourtoken', - 'refreshtokenname': 'underarmourrefreshtoken', - 'expirydatename': 'underarmourtokenexpirydate', - 'bearer_auth': True, - 'base_url': "https://api.ua.com/v7.1/oauth2/access_token/", - 'scope':'write', - } - -# Checks if user has UnderArmour token, renews them if they are expired -def underarmour_open(user): - return imports_open(user,oauth_data) - -# Refresh ST token using refresh token -def do_refresh_token(refreshtoken,access_token): - return imports_do_refresh_token( - refreshtoken,oauth_data,access_token=access_token - ) - -# Exchange access code for long-lived access token -def get_token(code): - return imports_get_token(code,oauth_data) - -# Make authorization URL including random string -def make_authorization_url(request): - return imports_make_authorization_url(oauth_data) # pragma: no cover - -# Get list of workouts available on Underarmour -def get_underarmour_workout_list(user): - r = Rower.objects.get(user=user) - if (r.underarmourtoken == '') or (r.underarmourtoken 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.underarmourtoken) - headers = {'Authorization': authorizationstring, - 'Api-Key': UNDERARMOUR_CLIENT_KEY, - 'user-agent': 'sanderroosendaal', - 'Content-Type': 'application/json'} - url = "https://api.ua.com/v7.1/workout/?user="+str(get_userid(r.underarmourtoken))+"&order_by=-start_datetime" - - s = requests.get(url,headers=headers) - - - return s - -# Get workout summary data by Underarmour ID -def get_workout(user,underarmourid,do_async=False): - r = Rower.objects.get(user=user) - if (r.underarmourtoken == '') or (r.underarmourtoken is None): # pragma: no cover - return custom_exception_handler(401,s) - s = "Token doesn't exist. Need to authorize" - else: - # ready to fetch. Hurray - authorizationstring = str('Bearer ' + r.underarmourtoken) - headers = {'Authorization': authorizationstring, - 'Api-Key': UNDERARMOUR_CLIENT_KEY, - 'user-agent': 'sanderroosendaal', - 'Content-Type': 'application/json'} - url = "https://api.ua.com/v7.1/workout/"+str(underarmourid)+"/?field_set=time_series" - s = requests.get(url,headers=headers) - - data = s.json() - - strokedata = pd.DataFrame.from_dict({ - key: pd.Series(value) for key, value in data.items() - }) - - return data,strokedata - - -# Create Workout Data for upload to Underarmour -def createunderarmourworkoutdata(w): - filename = w.csvfilename - try: - row = rowingdata(csvfile=filename) - except: # pragma: no cover - return 0 - - st = w.startdatetime.astimezone(pytz.timezone(w.timezone)) - start_time = st.isoformat() - - averagehr = int(row.df[' HRCur (bpm)'].mean()) - minhr = int(row.df[' HRCur (bpm)'].min()) - maxhr = int(row.df[' HRCur (bpm)'].max()) - averagespm = int(row.df[' Cadence (stokes/min)'].mean()/2.) - minspm = int(row.df[' Cadence (stokes/min)'].min()/2.) - maxspm = int(row.df[' Cadence (stokes/min)'].max()/2.) - 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 - name = w.name - try: - notes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com' - except TypeError: - notes = 'from '+w.workoutsource+' via rowsandall.com' - - # adding diff, trying to see if this is valid - #t = row.df.loc[:,'TimeStamp (sec)'].values-10*row.df.ix[0,'TimeStamp (sec)'] - t = row.df.loc[:,'TimeStamp (sec)'].values #-row.df.ix[0,'TimeStamp (sec)'] - - # t += arrow.get(st).timestamp() - - # t[0] = t[1] - - d = row.df.loc[:,'cum_dist'].values - d[0] = d[1] - t = t.astype(float).tolist() - - - - - d = d.astype(int).tolist() - spm = row.df[' Cadence (stokes/min)'].astype(int).tolist() - spm[0] = spm[1] - hr = row.df[' HRCur (bpm)'].astype(int).tolist() - speed = row.df[' AverageBoatSpeed (m/s)'] - speedmin = float(row.df[' AverageBoatSpeed (m/s)'].min()) - speedmax = float(row.df[' AverageBoatSpeed (m/s)'].max()) - speedmean = float(row.df[' AverageBoatSpeed (m/s)'].mean()) - speed = speed.replace(np.inf,0).tolist() - - - - haslatlon=1 - - try: # pragma: no cover - lat = row.df[' latitude'] - lon = row.df[' longitude'] - if not lat.std() and not lon.std(): - haslatlon = 0 - except KeyError: - haslatlon = 0 - - - # path data - if haslatlon: # pragma: no cover - locdata = [] - for e in zip(t,lat.values,lon.values): - point = { - 'lat':e[1], - 'lng':e[2], - 'elevation':0, - } - locdata.append([e[0],point]) - - hrdata = [] - for e in zip(t,hr): - point = [e[0], - e[1] - ] - hrdata.append(point) - - distancedata = [] - for e in zip(t,d): - point = [e[0], - e[1] - ] - distancedata.append(point) - - spmdata = [] - for e in zip(t,spm): - spmdata.append([e[0],e[1]]) - - - timeseries = { - "distance": distancedata, - "heartrate": hrdata, - "cadence": spmdata, - } - - - - aggregates = { - "elapsed_time_total": int(duration), - "active_time_total": int(duration), - "distance_total": int(max(d)), - "heartrate_avg": averagehr, - "heart_rate_min": minhr, - "heart_rate_max": maxhr, - "speed_min": speedmin, - "speed_max": speedmax, - "speed_avg": speedmean, - "cadence_min": minspm, - "cadence_max": maxspm, - "cadence_avg": averagespm, - } - - - if haslatlon: # pragma: no cover - timeseries["position"] = locdata - - data = { - "start_datetime": start_time, - "name": name, - "has_time_series": True, - "time_series": timeseries, - "aggregates": aggregates, - "start_locale_timezone": "Etc/UTC", - "activity_type": "/v7.1/activity_type/128/", - "notes": notes, - } - - return data - -# Obtain Underarmour Workout ID and activity type -def get_idfromuri(user,links): - id = links['self'][0]['id'] - typeid = links['activity_type'][0]['id'] - - typename = get_typefromid(typeid,user) - - return id,typename - -def getidfromresponse(response): - t = response.json() - - links = t["_links"] - - id = links["self"][0]["id"] - - return int(id) - -def refresh_ua_actlist(user): # pragma: no cover - r = Rower.objects.get(user=user) - authorizationstring = str('Bearer ' + r.underarmourtoken) - headers = {'Authorization': authorizationstring, - 'Api-Key': UNDERARMOUR_CLIENT_KEY, - 'user-agent': 'sanderroosendaal', - 'Content-Type': 'application/json'} - url = "https://api.ua.com/v7.1/activity_type/" - response = requests.get(url,headers=headers) - - me_json = response.json() - types = me_json["_embedded"]["activity_types"] - w = {int(t["_links"]["self"][0]["id"]):t["name"] for t in types} - wdf = pd.Series(w,name='Name') - wdf.to_csv('static/rigging/ua2.csv',index_label='id',header=True) - return w - -try: - activities = pd.read_csv('static/rigging/ua2.csv',index_col='id') - actdict = activities.to_dict()['Name'] -except: # pragma: no cover - actdict = {} - - -def get_typefromid(typeid,user): - r = Rower.objects.get(user=user) - try: - res = actdict[int(typeid)] - except KeyError: # pragma: no cover - authorizationstring = str('Bearer ' + r.underarmourtoken) - headers = {'Authorization': authorizationstring, - 'Api-Key': UNDERARMOUR_CLIENT_KEY, - 'user-agent': 'sanderroosendaal', - 'Content-Type': 'application/json'} - url = "https://api.ua.com/v7.1/activity_type/"+str(typeid) - response = requests.get(url,headers=headers) - - me_json = response.json() - - try: - res = me_json['name'] - except KeyError: - res = 0 - - 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, - 'Api-Key': UNDERARMOUR_CLIENT_KEY, - 'user-agent': 'sanderroosendaal', - 'Content-Type': 'application/json'} - - url = "https://api.ua.com/v7.1/user/self/" - response = requests.get(url,headers=headers) - - me_json = response.json() - - try: - res = me_json['id'] - except KeyError: # pragma: no cover - res = 0 - - return res - -def default(o): # pragma: no cover - if isinstance(o, numpy.int64): return int(o) - raise TypeError - - -def workout_ua_upload(user,w): # pragma: no cover - message = "Uploading to MapMyFitness" - uaid = 0 - - r = w.user - - thetoken = underarmour_open(r.user) - - # ready to upload. Hurray - - if (is_workout_user(user,w)): - data = createunderarmourworkoutdata(w) -# return HttpResponse(json.dumps(data)) - if not data: - message = "Data error" - uaid = 0 - return message, uaid - - authorizationstring = str('Bearer ' + thetoken) - headers = {'Authorization': authorizationstring, - 'Api-Key': UNDERARMOUR_CLIENT_KEY, - 'user-agent': 'sanderroosendaal', - 'Content-Type': 'application/json', - } - - url = "https://api.ua.com/v7.1/workout/" - response = requests.post(url,headers=headers,data=json.dumps(data,default=default)) - - # check for duplicate error first - if (response.status_code == 409 ): - message = "Duplicate error" - w.uploadedtounderarmour = -1 - uaid = -1 - w.save() - elif (response.status_code == 201 or response.status_code==200): - uaid = getidfromresponse(response) - w.uploadedtounderarmour = uaid - w.save() - return 'Successfully synchronized with MapMyFitness',uaid - else: - s = response - message = "Something went wrong in workout_underarmour_upload_view: %s - %s" % (s.reason,s.text) - uaid = 0 - return message, uaid - - else: - message = "You are not authorized to upload this workout" - uaid = 0 - return message, uaid - - return message, uaid - -# Create workout from SportTracks Data, which are slightly different -# than Strava or Concept2 data -def add_workout_from_data(user,importid,data,strokedata, - source='mapmyfitness', - workoutsource='mapmyfitness'): - workouttype = 'water' - - try: - comments = data['notes'] - except: # pragma: no cover - comments = '' - - try: - thetimezone = tz(data['start_locale_timezone']) - except: - thetimezone = 'UTC' - - r = Rower.objects.get(user=user) - try: - rowdatetime = iso8601.parse_date(data['start_datetime']) - except iso8601.ParseError: # pragma: no cover - try: - rowdatetime = datetime.strptime(data['start_datetime'],"%Y-%m-%d %H:%M:%S") - rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc) - except: - try: - rowdatetime = parser.parse(data['start_datetime']) - rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc) - except: - rowdatetime = datetime.strptime(data['date'],"%Y-%m-%d %H:%M:%S") - rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc) - starttimeunix = arrow.get(rowdatetime).timestamp() - - - try: - title = data['name'] - except: # pragma: no cover - title = "Imported data" - - timeseries = data['time_series'] - - # position, distance, speed, cadence, power, - - try: - res = splituadata(timeseries['distance']) - distance = res[1] - times_distance = res[0] - except KeyError: # pragma: no cover - message = "Error. No distance data" - return (0,message) - - - try: - l = timeseries['position'] - - res = splituadata(l) - times_location = res[0] - latlong = res[1] - latcoord = [] - loncoord = [] - - for coord in latlong: - lat = coord['lat'] - lon = coord['lng'] - latcoord.append(lat) - loncoord.append(lon) - except: # pragma: no cover - times_location = times_distance - latcoord = np.zeros(len(times_distance)) - loncoord = np.zeros(len(times_distance)) - if workouttype in otwtypes: - workouttype = 'rower' - - try: - res = splituadata(timeseries['cadence']) - times_spm = res[0] - spm = res[1] - except KeyError: # pragma: no cover - times_spm = times_distance - spm = 0*times_distance - - try: - res = splituadata(timeseries['heartrate']) - hr = res[1] - times_hr = res[0] - except KeyError: # pragma: no cover - times_hr = times_distance - hr = 0*times_distance - - - # create data series and remove duplicates - distseries = pd.Series(distance,index=times_distance) - distseries = distseries.groupby(distseries.index).first() - latseries = pd.Series(latcoord,index=times_location) - latseries = latseries.groupby(latseries.index).first() - lonseries = pd.Series(loncoord,index=times_location) - lonseries = lonseries.groupby(lonseries.index).first() - spmseries = pd.Series(spm,index=times_spm) - spmseries = spmseries.groupby(spmseries.index).first() - hrseries = pd.Series(hr,index=times_hr) - hrseries = hrseries.groupby(hrseries.index).first() - - - # Create dicts and big dataframe - d = { - ' Horizontal (meters)': distseries, - ' latitude': latseries, - ' longitude': lonseries, - ' Cadence (stokes/min)': spmseries, - ' HRCur (bpm)' : hrseries, - } - - - - df = pd.DataFrame(d) - - df = df.groupby(level=0).last() - - cum_time = df.index.values - df[' ElapsedTime (sec)'] = cum_time - - velo = df[' Horizontal (meters)'].diff()/df[' ElapsedTime (sec)'].diff() - - df[' Power (watts)'] = 0.0*velo - - nr_rows = len(velo.values) - - df[' DriveLength (meters)'] = np.zeros(nr_rows) - df[' StrokeDistance (meters)'] = np.zeros(nr_rows) - df[' DriveTime (ms)'] = np.zeros(nr_rows) - df[' StrokeRecoveryTime (ms)'] = np.zeros(nr_rows) - df[' AverageDriveForce (lbs)'] = np.zeros(nr_rows) - df[' PeakDriveForce (lbs)'] = np.zeros(nr_rows) - df[' lapIdx'] = np.zeros(nr_rows) - - - - unixtime = cum_time+starttimeunix - unixtime[0] = starttimeunix - - df['TimeStamp (sec)'] = unixtime - - - dt = np.diff(cum_time).mean() - wsize = round(5./dt) - - df = df.fillna(0) - - df.sort_values(by='TimeStamp (sec)',ascending=True) - - timestr = strftime("%Y%m%d-%H%M%S") - - csvfilename ='media/{code}_{importid}.csv'.format( - importid=importid, - code = uuid4().hex[:16] - ) - - res = df.to_csv(csvfilename+'.gz',index_label='index', - compression='gzip') - - id,message = dataprep.save_workout_database(csvfilename,r, - workouttype=workouttype, - workoutsource='mapmyfitness', - dosmooth=r.dosmooth, - title=title, - notes=comments) - - return (id,message) diff --git a/rowers/uploads.py b/rowers/uploads.py index dd2d7cb1..43706369 100644 --- a/rowers/uploads.py +++ b/rowers/uploads.py @@ -125,7 +125,6 @@ def matchsync(line): tester4 = tester+'(.*)(strava)' tester5 = tester+'(.*)((st)|(sporttracks))' tester6 = tester+'(.*)((rk)|(runkeeper))' - tester7 = tester+'(.*)((mapmyfitness)|(underarmour)|(ua))' tester = re.compile(tester) @@ -262,8 +261,6 @@ def getsyncoptions(uploadoptions,values): # pragma: no cover uploadoptions['upload_to_SportTracks'] = True if v in ['rk','runkeeper']: uploadoptions['upload_to_RunKeeper'] = True - if v in ['ua','underarmour','mapmyfitness']: - uploadoptions['upload_to_MapMyFitness'] = True except AttributeError: pass @@ -491,7 +488,7 @@ import rowers.c2stuff as c2stuff import rowers.stravastuff as stravastuff import rowers.sporttracksstuff as sporttracksstuff import rowers.runkeeperstuff as runkeeperstuff -import rowers.underarmourstuff as underarmourstuff + import rowers.tpstuff as tpstuff from rowers.rower_rules import is_promember @@ -653,14 +650,6 @@ def do_sync(w,options, quick=False): message = "Please connect to Runkeeper first" id = 0 - if ('upload_to_MapMyFitness' in options and options['upload_to_MapMyFitness']) or (w.user.mapmyfitness_auto_export): # pragma: no cover - try: - message,id = underarmourstuff.workout_ua_upload( - w.user.user,w - ) - except NoTokenError: - message = "Please connect to MapMyFitness first" - id = 0 if ('upload_to_TrainingPeaks' in options and options['upload_to_TrainingPeaks']) or (w.user.trainingpeaks_auto_export): # pragma: no cover diff --git a/rowers/urls.py b/rowers/urls.py index 7f002f21..8e91944d 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -510,14 +510,11 @@ urlpatterns = [ re_path(r'^workout/polarimport/user/(?P