From 80cbcb9512c7110ccc4967388cf5637ce8a80cf6 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Fri, 22 Jun 2018 16:45:19 +0200 Subject: [PATCH] not working yet, need to complete in tasks.py --- rowers/management/commands/processemail.py | 6 + rowers/models.py | 2 + rowers/stravastuff.py | 131 ++++++++++++++++++++- rowers/tasks.py | 99 ++++++++++++++++ 4 files changed, 236 insertions(+), 2 deletions(-) diff --git a/rowers/management/commands/processemail.py b/rowers/management/commands/processemail.py index e8f7f8d8..5183af9c 100644 --- a/rowers/management/commands/processemail.py +++ b/rowers/management/commands/processemail.py @@ -22,6 +22,7 @@ import rowers.uploads as uploads from rowers.mailprocessing import make_new_workout_from_email, send_confirm import rowers.polarstuff as polarstuff import rowers.c2stuff as c2stuff +import rowers.stravastuff as stravastuff workoutmailbox = Mailbox.objects.get(name='workouts') failedmailbox = Mailbox.objects.get(name='Failed') @@ -157,6 +158,11 @@ class Command(BaseCommand): rowers = Rower.objects.filter(c2_auto_import=True) for r in rowers: c2stuff.get_c2_workouts(r) + + # Strava + rowers = Rower.objects.filter(strava_auto_import=True) + for r in rowers: + stravastuff.get_strava_workouts(r) messages = Message.objects.filter(mailbox_id = workoutmailbox.id) message_ids = [m.id for m in messages] diff --git a/rowers/models.py b/rowers/models.py index b237088a..b6146910 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -674,6 +674,7 @@ class Rower(models.Model): verbose_name="Export Workouts to Strava as") strava_auto_export = models.BooleanField(default=False) + strava_auto_import = models.BooleanField(default=False) runkeepertoken = models.CharField(default='',max_length=200, blank=True,null=True) runkeeper_auto_export = models.BooleanField(default=False) @@ -2026,6 +2027,7 @@ class RowerImportExportForm(ModelForm): 'runkeeper_auto_export', 'sporttracks_auto_export', 'strava_auto_export', + 'strava_auto_import', 'trainingpeaks_auto_export', ] diff --git a/rowers/stravastuff.py b/rowers/stravastuff.py index c14e6817..c78fbd9c 100644 --- a/rowers/stravastuff.py +++ b/rowers/stravastuff.py @@ -16,6 +16,8 @@ from math import sin,cos,atan2,sqrt import os,sys import gzip +from pytz import timezone as tz,utc + # Django from django.shortcuts import render_to_response from django.http import HttpResponseRedirect, HttpResponse,JsonResponse @@ -32,7 +34,8 @@ from rowers.models import Rower,Workout from rowers.models import checkworkoutuser import dataprep from dataprep import columndict - +from utils import uniqify,isprorower +from uuid import uuid4 import stravalib from stravalib.exc import ActivityUploadFailed,TimeoutExceeded @@ -115,7 +118,7 @@ def get_token(code): 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": STRAVA_CLIENT_ID, @@ -144,6 +147,130 @@ def get_strava_workout_list(user): return s +def add_stroke_data(user,stravaid,workoutid,startdatetime,csvfilename): + r = Rower.objects.get(user=user) + + starttimeunix = arrow.get(startdatetime).timestamp + + job = myqueue(queue, + handle_strava_import_stroke_data, + r.stravatoken, + stravid, + workoutid, + starttimeunix, + csvfilename) + +# gets all new Strava workouts for a rower +def get_strava_workouts(rower): + + if not isprorower(rower): + return 0 + + res = get_strava_workout_list(rower.user) + + if (res.status_code != 200): + return 0 + else: + stravaids = [int(item['id']) for item in res.json()] + + alldata = {} + for item in res.json(): + alldata[item['id']] = item + + knownstravaids = uniqify([ + w.uploadedtostrava for w in Workout.objects.filter(user=rower) + ]) + newids = [stravaid for stravaid in stravaids if not stravaid in knownstravaids] + + print newids,'aap' + + for stravaid in newids: + workoutid = create_async_workout(alldata,rower.user,stravaid) + + return 1 + +def create_async_workout(alldata,user,stravaid): + data = alldata[stravid] + + distance = data['distance'] + stravaid = data['id'] + try: + workouttype = data['type'] + except: + workouttype = 'rower' + + if workouttype not in [x[0] for x in Workout.workouttypes]: + workouttype = 'other' + + try: + comments = data['comments'] + except: + comments = ' ' + + try: + thetimezone = tz(data['timezone']) + except: + thetimezone = 'UTC' + + try: + rowdatetime = iso8601.parse_date(data['date_utc']) + except KeyError: + rowdatetime = iso8601.parse_date(data['start_date']) + except ParseError: + rowdatetime = iso8601.parse_date(data['date']) + + try: + c2intervaltype = data['workout_type'] + + except KeyError: + c2intervaltype = '' + + try: + title = data['name'] + except KeyError: + title = "" + try: + t = data['comments'].split('\n', 1)[0] + title += t[:20] + except: + title = 'Imported' + + totaltime = data['elapsed_time'] + duration = dataprep.totaltime_sec_to_string(totaltime) + + weightcategory = 'hwt' + + # Create CSV file name and save data to CSV file + csvfilename ='media/{code}_{importid}.csv'.format( + importid=stravaid, + code = uuid4().hex[:16] + ) + + w = Workout( + user=r, + workouttype = workouttype, + name = title, + date = workoutdate, + starttime = starttime, + startdatetime = rowdatetime, + timezone = thetimezone, + duration = duration, + distance=distance, + weightcategory = weightcategory, + uploadedtostrava = stravaid, + csvfilename = csvfilename, + notes = '' + ) + + w.save() + + # Check if workout has stroke data, and get the stroke data + + result = add_stroke_data(user,stravaid,w.id,rowdatetime,csvfilename) + + return w.id + + # Get a Strava workout summary data and stroke data by ID def get_strava_workout(user,stravaid): r = Rower.objects.get(user=user) diff --git a/rowers/tasks.py b/rowers/tasks.py index b6fe15e2..d4a7dae1 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -77,6 +77,105 @@ def add(x, y): return x + y +@app.task +def handle_strava_import_stroke_data(stravatoken, + stravaid,workoutid, + starttimeunix, + csvfilename,debug=True,**kwargs): + # ready to fetch. Hurray + fetchresolution = 'high' + series_type = 'time' + authorizationstring = str('Bearer ' + r.stravatoken) + headers = {'Authorization': authorizationstring, + 'user-agent': 'sanderroosendaal', + 'Content-Type': 'application/json', + 'resolution': 'medium',} + url = "https://www.strava.com/api/v3/activities/"+str(stravaid) + workoutsummary = requests.get(url,headers=headers).json() + + workoutsummary['timezone'] = "Etc/UTC" + startdatetime = workoutsummary['start_date'] + + url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/cadence?resolution="+fetchresolution+"&series_type="+series_type + spmjson = requests.get(url,headers=headers) + url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/heartrate?resolution="+fetchresolution+"&series_type="+series_type + hrjson = requests.get(url,headers=headers) + url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/time?resolution="+fetchresolution+"&series_type="+series_type + timejson = requests.get(url,headers=headers) + url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/velocity_smooth?resolution="+fetchresolution+"&series_type="+series_type + velojson = requests.get(url,headers=headers) + url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/distance?resolution="+fetchresolution+"&series_type="+series_type + distancejson = requests.get(url,headers=headers) + url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/latlng?resolution="+fetchresolution+"&series_type="+series_type + latlongjson = requests.get(url,headers=headers) + + try: + t = np.array(timejson.json()[0]['data']) + nr_rows = len(t) + d = np.array(distancejson.json()[1]['data']) + if nr_rows == 0: + return (0,"Error: Time data had zero length") + except IndexError: + d = 0*t + # return (0,"Error: No Distance information in the Strava data") + except KeyError: + return (0,"something went wrong with the Strava import") + + try: + spm = np.array(spmjson.json()[1]['data']) + except: + spm = np.zeros(nr_rows) + + try: + hr = np.array(hrjson.json()[1]['data']) + except IndexError: + hr = np.zeros(nr_rows) + except KeyError: + hr = np.zeros(nr_rows) + + try: + velo = np.array(velojson.json()[1]['data']) + except IndexError: + velo = np.zeros(nr_rows) + except KeyError: + velo = np.zeros(nr_rows) + + dt = np.diff(t).mean() + wsize = round(5./dt) + + velo2 = ewmovingaverage(velo,wsize) + coords = np.array(latlongjson.json()[0]['data']) + try: + lat = coords[:,0] + lon = coords[:,1] + except IndexError: + lat = np.zeros(len(t)) + lon = np.zeros(len(t)) + except KeyError: + lat = np.zeros(len(t)) + lon = np.zeros(len(t)) + + strokelength = velo*60./(spm) + strokelength[np.isinf(strokelength)] = 0.0 + + pace = 500./(1.0*velo2) + pace[np.isinf(pace)] = 0.0 + + df = pd.DataFrame({'t':10*t, + 'd':10*d, + 'p':10*pace, + 'spm':spm, + 'hr':hr, + 'lat':lat, + 'lon':lon, + 'strokelength':strokelength, + }) + + # startdatetime = datetime.datetime.strptime(startdatetime,"%Y-%m-%d-%H:%M:%S") + + return [workoutsummary,df] + + @app.task def handle_c2_import_stroke_data(c2token, c2id,workoutid,