diff --git a/rowers/mailprocessing.py b/rowers/mailprocessing.py
index f27ca372..ffedcf99 100644
--- a/rowers/mailprocessing.py
+++ b/rowers/mailprocessing.py
@@ -91,6 +91,9 @@ def make_new_workout_from_email(rower, datafile, name, cntr=0,testing=False):
path='media/')[6:]
fileformat = fileformat[2]
+ if testing:
+ print 'Fileformat = ',fileformat
+
if fileformat == 'unknown':
# extension = datafilename[-4:].lower()
# fcopy = "media/"+datafilename[:-4]+"_copy"+extension
diff --git a/rowers/management/commands/processemail.py b/rowers/management/commands/processemail.py
index f2a1c65e..c8c3f2f9 100644
--- a/rowers/management/commands/processemail.py
+++ b/rowers/management/commands/processemail.py
@@ -20,6 +20,7 @@ from rowingdata import rowingdata as rrdata
import rowers.uploads as uploads
from rowers.mailprocessing import make_new_workout_from_email, send_confirm
+import rowers.polarstuff as polarstuff
workoutmailbox = Mailbox.objects.get(name='workouts')
failedmailbox = Mailbox.objects.get(name='Failed')
@@ -147,6 +148,9 @@ class Command(BaseCommand):
"""Run the Email processing command """
def handle(self, *args, **options):
+ polar_available = polarstuff.get_polar_notifications()
+ res = polarstuff.get_all_new_workouts(polar_available)
+
messages = Message.objects.filter(mailbox_id = workoutmailbox.id)
message_ids = [m.id for m in messages]
attachments = MessageAttachment.objects.filter(
diff --git a/rowers/models.py b/rowers/models.py
index 28abaf6f..5649595a 100644
--- a/rowers/models.py
+++ b/rowers/models.py
@@ -642,24 +642,29 @@ class Rower(models.Model):
c2token = models.CharField(default='',max_length=200,blank=True,null=True)
tokenexpirydate = models.DateTimeField(blank=True,null=True)
c2refreshtoken = models.CharField(default='',max_length=200,blank=True,null=True)
+ c2_auto_export = models.BooleanField(default=False)
sporttrackstoken = models.CharField(default='',max_length=200,blank=True,null=True)
sporttrackstokenexpirydate = models.DateTimeField(blank=True,null=True)
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)
tprefreshtoken = models.CharField(default='',max_length=1000,
blank=True,null=True)
+ trainingpeaks_auto_export = models.BooleanField(default=False)
polartoken = models.CharField(default='',max_length=1000,blank=True,null=True)
polartokenexpirydate = models.DateTimeField(blank=True,null=True)
polarrefreshtoken = models.CharField(default='',max_length=1000,
blank=True,null=True)
polaruserid = models.IntegerField(default=0)
+ polar_auto_import = models.BooleanField(default=False)
stravatoken = models.CharField(default='',max_length=200,blank=True,null=True)
stravaexportas = models.CharField(default="Rowing",
@@ -667,8 +672,10 @@ class Rower(models.Model):
choices=stravatypes,
verbose_name="Export Workouts to Strava as")
+ strava_auto_export = models.BooleanField(default=False)
runkeepertoken = models.CharField(default='',max_length=200,
blank=True,null=True)
+ runkeeper_auto_export = models.BooleanField(default=False)
# Plan
plans = (
@@ -1996,6 +2003,22 @@ class RowerPowerZonesForm(ModelForm):
return cleaned_data
+# Form to set rower's Auto Import and Export settings
+class RowerImportExportForm(ModelForm):
+ class Meta:
+ model = Rower
+ fields = [
+ 'polar_auto_import',
+ 'c2_auto_export',
+ 'mapmyfitness_auto_export',
+ 'runkeeper_auto_export',
+ 'sporttracks_auto_export',
+ 'strava_auto_export',
+ 'trainingpeaks_auto_export',
+ ]
+
+
+
# Form to set rower's Email and Weight category
class AccountRowerForm(ModelForm):
class Meta:
diff --git a/rowers/polarstuff.py b/rowers/polarstuff.py
index 3bad578a..5bf1a535 100644
--- a/rowers/polarstuff.py
+++ b/rowers/polarstuff.py
@@ -17,6 +17,7 @@ import os,sys
import gzip
import base64
import yaml
+from uuid import uuid4
# Django
from django.shortcuts import render_to_response
@@ -46,7 +47,8 @@ from rowsandall_app.settings import (
POLAR_CLIENT_ID, POLAR_REDIRECT_URI, POLAR_CLIENT_SECRET,
)
-baseurl = 'https://polaraccesslink.com/v3-example'
+#baseurl = 'https://polaraccesslink.com/v3-example'
+baseurl = 'https://polaraccesslink.com/v3'
# Custom exception handler, returns a 401 HTTP message
# with exception details in the json data
@@ -111,7 +113,6 @@ def get_token(code):
def make_authorization_url():
# 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": POLAR_CLIENT_ID,
@@ -124,6 +125,43 @@ def make_authorization_url():
return HttpResponseRedirect(url)
+def get_polar_notifications():
+ url = baseurl+'/notifications'
+ state = str(uuid4())
+ auth_string = '{id}:{secret}'.format(
+ id= POLAR_CLIENT_ID,
+ secret=POLAR_CLIENT_SECRET
+ )
+
+ headers = { 'Authorization': 'Basic %s' % base64.b64encode(auth_string) }
+
+ response = requests.get(url, headers=headers)
+
+ available_data = []
+
+ if response.status_code == 200:
+ available_data = response.json()['available-user-data']
+
+ return available_data
+
+def get_all_new_workouts(available_data,testing=False):
+ for record in available_data:
+ if testing:
+ print record
+ if record['data-type'] == 'EXERCISE':
+ try:
+ r = Rower.objects.get(polaruserid=record['user-id'])
+ u = r.user
+ if r.polar_auto_import:
+ exercise_list = get_polar_workouts(u)
+ if testing:
+ print exercise_list
+ except Rower.DoesNotExist:
+ pass
+
+ return 1
+
+
def get_polar_workouts(user):
r = Rower.objects.get(user=user)
@@ -180,8 +218,9 @@ def get_polar_workouts(user):
tcxuri = exerciseurl+'/tcx'
response = requests.get(tcxuri,headers=headers2)
if response.status_code == 200:
- filename = 'media/mailbox_attachments/polarimport{id}.tcx'.format(
- id = exercise_dict['id']
+ filename = 'media/mailbox_attachments/{code}_{id}.tcx'.format(
+ id = exercise_dict['id'],
+ code = uuid4().hex[:16]
)
with open(filename,'wb') as fop:
diff --git a/rowers/templates/imports.html b/rowers/templates/imports.html
index 17f27503..857802a2 100644
--- a/rowers/templates/imports.html
+++ b/rowers/templates/imports.html
@@ -53,6 +53,16 @@
Import workouts from MapMyFitness/UnderArmour
+
+
+
+
+
+
+
+
Import workouts from Polar Flow.
Note: No workout selection possible. Automatically imports all new workouts
+
+
@@ -60,7 +70,8 @@
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
+ You only need to do this once. After that, the site will have
+ access until you
revoke the authorization for the "rowingdata" app.
@@ -89,6 +100,31 @@
alt="connect with Polar" width="130">
+
+
Auto Import/Export Settings
+
+
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
+
+
+
diff --git a/rowers/uploads.py b/rowers/uploads.py
index a5a4fc61..a667e200 100644
--- a/rowers/uploads.py
+++ b/rowers/uploads.py
@@ -374,14 +374,14 @@ def make_private(w,options):
return 1
def do_sync(w,options):
- if 'upload_to_C2' in options and options['upload_to_C2']:
+ if ('upload_to_C2' in options and options['upload_to_C2']) or w.user.c2_auto_export:
try:
message,id = c2stuff.workout_c2_upload(w.user.user,w)
except c2stuff.C2NoTokenError:
id = 0
message = "Something went wrong with the Concept2 sync"
- if 'upload_to_Strava' in options and options['upload_to_Strava']:
+ if ('upload_to_Strava' in options and options['upload_to_Strava']) or w.user.strava_auto_export:
try:
message,id = stravastuff.workout_strava_upload(
w.user.user,w
@@ -390,8 +390,8 @@ def do_sync(w,options):
id = 0
message = "Please connect to Strava first"
-
- if 'upload_to_SportTracks' in options and options['upload_to_SportTracks']:
+
+ if ('upload_to_SportTracks' in options and options['upload_to_SportTracks']) or w.user.sporttracks_auto_export:
try:
message,id = sporttracksstuff.workout_sporttracks_upload(
w.user.user,w
@@ -401,7 +401,7 @@ def do_sync(w,options):
id = 0
- if 'upload_to_RunKeeper' in options and options['upload_to_RunKeeper']:
+ if ('upload_to_RunKeeper' in options and options['upload_to_RunKeeper']) or w.user.runkeeper_auto_export:
try:
message,id = runkeeperstuff.workout_runkeeper_upload(
w.user.user,w
@@ -410,7 +410,7 @@ def do_sync(w,options):
message = "Please connect to Runkeeper first"
id = 0
- if 'upload_to_MapMyFitness' in options and options['upload_to_MapMyFitness']:
+ if ('upload_to_MapMyFitness' in options and options['upload_to_MapMyFitness']) or w.user.mapmyfitness_auto_export:
try:
message,id = underarmourstuff.workout_ua_upload(
w.user.user,w
@@ -420,7 +420,7 @@ def do_sync(w,options):
id = 0
- if 'upload_to_TrainingPeaks' in options and options['upload_to_TrainingPeaks']:
+ if ('upload_to_TrainingPeaks' in options and options['upload_to_TrainingPeaks']) or w.user.trainingpeaks_auto_export:
try:
message,id = tpstuff.workout_tp_upload(
w.user.user,w
diff --git a/rowers/urls.py b/rowers/urls.py
index be8f6ea8..b8d22ef1 100644
--- a/rowers/urls.py
+++ b/rowers/urls.py
@@ -120,7 +120,8 @@ urlpatterns = [
url(r'^404/$', TemplateView.as_view(template_name='404.html'),name='404'),
url(r'^400/$', TemplateView.as_view(template_name='400.html'),name='400'),
url(r'^403/$', TemplateView.as_view(template_name='403.html'),name='403'),
- url(r'^imports/$', TemplateView.as_view(template_name='imports.html'), name='imports'),
+# url(r'^imports/$', TemplateView.as_view(template_name='imports.html'), name='imports'),
+ url(r'^imports/$', views.imports_view),
url(r'^exportallworkouts/?$',views.workouts_summaries_email_view),
url(r'^update_empower$',views.rower_update_empower_view),
url(r'^agegroupcp/(?P\d+)$',views.agegroupcpview),
diff --git a/rowers/views.py b/rowers/views.py
index 266bd7b3..1c41a8ba 100644
--- a/rowers/views.py
+++ b/rowers/views.py
@@ -72,7 +72,7 @@ from rowers.models import (
WorkoutComment,WorkoutCommentForm,RowerExportForm,
CalcAgePerformance,PowerTimeFitnessMetric,PlannedSessionForm,
PlannedSessionFormSmall,GeoCourseEditForm,VirtualRace,
- VirtualRaceForm,VirtualRaceResultForm,
+ VirtualRaceForm,VirtualRaceResultForm,RowerImportExportForm
)
from rowers.models import (
FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement,BasePlannedSessionFormSet
@@ -1234,7 +1234,10 @@ def add_workout_from_strokedata(user,importid,data,strokedata,
# Create CSV file name and save data to CSV file
- csvfilename ='media/Import_'+str(importid)+'.csv'
+ csvfilename ='media/{code}_{importid}.csv'.format(
+ importid=importid,
+ code = uuid4().hex[:16]
+ )
res = df.to_csv(csvfilename+'.gz',index_label='index',
compression='gzip')
@@ -1424,7 +1427,11 @@ def add_workout_from_runkeeperdata(user,importid,data):
timestr = strftime("%Y%m%d-%H%M%S")
- csvfilename ='media/Import_'+str(importid)+'.csv'
+# csvfilename ='media/Import_'+str(importid)+'.csv'
+ csvfilename ='media/{code}_{importid}.csv'.format(
+ importid=importid,
+ code = uuid4().hex[:16]
+ )
res = df.to_csv(csvfilename+'.gz',index_label='index',
compression='gzip')
@@ -1590,7 +1597,11 @@ def add_workout_from_stdata(user,importid,data):
timestr = strftime("%Y%m%d-%H%M%S")
- csvfilename ='media/Import_'+str(importid)+'.csv'
+# csvfilename ='media/Import_'+str(importid)+'.csv'
+ csvfilename ='media/{code}_{importid}.csv'.format(
+ importid=importid,
+ code = uuid4().hex[:16]
+ )
res = df.to_csv(csvfilename+'.gz',index_label='index',
compression='gzip')
@@ -1757,7 +1768,11 @@ def add_workout_from_underarmourdata(user,importid,data):
timestr = strftime("%Y%m%d-%H%M%S")
- csvfilename ='media/Import_'+str(importid)+'.csv'
+# csvfilename ='media/Import_'+str(importid)+'.csv'
+ csvfilename ='media/{code}_{importid}.csv'.format(
+ importid=importid,
+ code = uuid4().hex[:16]
+ )
res = df.to_csv(csvfilename+'.gz',index_label='index',
compression='gzip')
@@ -2612,12 +2627,31 @@ def rower_process_callback(request):
# The imports page
@login_required()
-def imports_view(request,successmessage="",message=""):
- messages.info(request,successmessage)
- messages.error(request,message)
+def imports_view(request):
+
+ r = getrower(request.user)
+
+ if request.method == 'POST':
+ form = RowerImportExportForm(request.POST)
+ if form.is_valid():
+ cd = form.cleaned_data
+
+ for attr, value in cd.items():
+ setattr(r,attr,value)
+
+ r.save()
+
+ # polar_auto_import = cd['polar_auto_import']
+ # r.polar_auto_import = polar_auto_import
+ # r.save()
+ else:
+ form = RowerImportExportForm(instance=r)
+
+
return render(request,"imports.html",
{
'teams':get_my_teams(request.user),
+ 'form':form,
})
# Just for testing purposes
@@ -9650,12 +9684,22 @@ def workout_underarmourimport_view(request,message=""):
def workout_polarimport_view(request):
exercises = polarstuff.get_polar_workouts(request.user)
workouts = []
+
+ try:
+ a = exercises.status_code
+ if a == 401:
+ messages.error(request,'Not authorized. You need to connect to Polar first')
+ url = reverse(imports_view)
+ return HttpResponseRedirect(url)
+ except:
+ pass
+
for exercise in exercises:
try:
d = exercise['distance']
except KeyError:
d = 0
-
+
i = exercise['id']
transactionid = exercise['transaction-id']
starttime = exercise['start-time']
@@ -9667,6 +9711,7 @@ def workout_polarimport_view(request):
res = dict(zip(keys,values))
workouts.append(res)
+
return render(request, 'polar_list_import.html',
{
'workouts':workouts,
@@ -10428,7 +10473,7 @@ def workout_upload_view(request,
request.session['async_tasks'] = [(jobid,'make_plot')]
# upload to C2
- if (upload_to_c2):
+ if (upload_to_c2) or (w.user.c2_auto_export):
try:
message,id = c2stuff.workout_c2_upload(request.user,w)
except C2NoTokenError:
@@ -10439,7 +10484,7 @@ def workout_upload_view(request,
else:
messages.error(request,message)
- if (upload_to_strava):
+ if (upload_to_strava) or (w.user.strava_auto_export):
try:
message,id = stravastuff.workout_strava_upload(
request.user,w
@@ -10452,7 +10497,7 @@ def workout_upload_view(request,
else:
messages.error(request,message)
- if (upload_to_st):
+ if (upload_to_st) or (w.user.sporttracks_auto_export):
try:
message,id = sporttracksstuff.workout_sporttracks_upload(
request.user,w
@@ -10465,7 +10510,7 @@ def workout_upload_view(request,
else:
messages.error(request,message)
- if (upload_to_rk):
+ if (upload_to_rk) or (w.user.runkeeper_auto_export):
try:
message,id = runkeeperstuff.workout_runkeeper_upload(
request.user,w
@@ -10480,7 +10525,7 @@ def workout_upload_view(request,
messages.error(request,message)
- if (upload_to_ua):
+ if (upload_to_ua) or (w.user.mapmyfitness_auto_export):
try:
message,id = underarmourstuff.workout_ua_upload(
request.user,w
@@ -10495,7 +10540,7 @@ def workout_upload_view(request,
messages.error(request,message)
- if (upload_to_tp):
+ if (upload_to_tp) or (w.user.trainingpeaks_auto_export):
try:
message,id = tpstuff.workout_tp_upload(
request.user,w