Private
Public Access
1
0

Merge branch 'feature/polarflow' into develop

This commit is contained in:
Sander Roosendaal
2018-06-06 10:53:09 +02:00
14 changed files with 663 additions and 34 deletions
+3
View File
@@ -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
@@ -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')
@@ -47,6 +48,8 @@ def processattachment(rower, fileobj, title, uploadoptions,testing=False):
filename = fileobj.name
except AttributeError:
filename = fileobj[6:]
if testing:
print 'Attribute Error', filename
# test if file exists and is not empty
@@ -54,12 +57,20 @@ def processattachment(rower, fileobj, title, uploadoptions,testing=False):
with open('media/'+filename,'r') as fop:
line = fop.readline()
except IOError:
if testing:
print 'IOError',filename,'media/'+filename
return 0
if testing:
print 'Creating workout from email'
workoutid = [
make_new_workout_from_email(rower, filename, title,testing=testing)
]
if testing:
print 'Workout id = {workoutid}'.format(workoutid=workoutid)
if workoutid[0]:
link = settings.SITE_URL+reverse(
rower.defaultlandingpage,
@@ -137,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(
@@ -185,6 +199,9 @@ class Command(BaseCommand):
)
else:
# move attachment and make workout
if testing:
print name
print attachment.document
workoutid = processattachment(
rower, attachment.document, name, uploadoptions,
testing=testing
+29
View File
@@ -642,18 +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",
@@ -661,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 = (
@@ -1990,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:
+351
View File
@@ -0,0 +1,351 @@
# All the functionality needed to connect to Strava
# Python
import oauth2 as oauth
import cgi
import requests
import requests.auth
import json
from django.utils import timezone
from datetime import datetime
import numpy as np
from dateutil import parser
import time
import math
from math import sin,cos,atan2,sqrt
import os,sys
import gzip
import base64
import yaml
from uuid import uuid4
# Django
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect, HttpResponse,JsonResponse
from django.conf import settings
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
# Project
# from .models import Profile
from rowingdata import rowingdata
import pandas as pd
from rowers.models import Rower,Workout
from rowers.models import checkworkoutuser
import dataprep
from dataprep import columndict
from io import StringIO
import stravalib
from stravalib.exc import ActivityUploadFailed,TimeoutExceeded
from django_mailbox.models import Message,Mailbox,MessageAttachment
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'
# Custom exception handler, returns a 401 HTTP message
# with exception details in the json data
def custom_exception_handler(exc,message):
response = {
"errors": [
{
"code": str(exc),
"detail": message,
}
]
}
res = HttpResponse(message)
res.status_code = 401
res.json = json.dumps(response)
return res
# Custom error class - to raise a NoTokenError
class PolarNoTokenError(Exception):
def __init__(self,value):
self.value=value
def __str__(self):
return repr(self.value)
# Exchange access code for long-lived access token
def get_token(code):
post_data = {"grant_type": "authorization_code",
"code": code,
"redirect_uri": POLAR_REDIRECT_URI,
}
auth_string = '{id}:{secret}'.format(
id= POLAR_CLIENT_ID,
secret=POLAR_CLIENT_SECRET
)
headers = { 'Authorization': 'Basic %s' % base64.b64encode(auth_string) }
response = requests.post("https://polarremote.com/v2/oauth2/token",
data=post_data,
headers=headers)
try:
token_json = response.json()
thetoken = token_json['access_token']
expires_in = token_json['expires_in']
user_id = token_json['x_user_id']
except KeyError:
thetoken = 0
expires_in = 0
return [thetoken,expires_in,user_id]
# Make authorization URL including random string
def make_authorization_url():
# Generate a random string for the state parameter
# Save it for use later to prevent xsrf attacks
state = str(uuid4())
params = {"client_id": POLAR_CLIENT_ID,
"response_type": "code",
"redirect_uri": POLAR_REDIRECT_URI,
"scope":"write"}
import urllib
url = "https://flow.polar.com/oauth2/authorization" +urllib.urlencode(params)
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)
exercise_list = []
if (r.polartoken == '') or (r.polartoken is None):
s = "Token doesn't exist. Need to authorize"
return custom_exception_handler(401,s)
elif (timezone.now()>r.polartokenexpirydate):
s = "Token expired. Needs to refresh"
return custom_exception_handler(401,s)
else:
authorizationstring = str('Bearer ' + r.polartoken)
headers = {'Authorization':authorizationstring,
'Accept': 'application/json'}
headers2 = {
'Authorization':authorizationstring,
}
url = baseurl+'/users/{userid}/exercise-transactions'.format(
userid = r.polaruserid
)
response = requests.post(url, headers=headers)
if response.status_code == 201:
workoutsbox = Mailbox.objects.filter(name='workouts')[0]
uploadoptions = {
'makeprivate':False,
}
bodyyaml = yaml.safe_dump(
uploadoptions,
default_flow_style=False
)
transactionid = response.json()['transaction-id']
url = baseurl+'/users/{userid}/exercise-transactions/{transactionid}'.format(
transactionid = transactionid,
userid = r.polaruserid
)
response = requests.get(url, headers=headers)
if response.status_code == 200:
exerciseurls = response.json()['exercises']
for exerciseurl in exerciseurls:
response = requests.get(exerciseurl,headers=headers)
if response.status_code == 200:
exercise_dict = response.json()
tcxuri = exerciseurl+'/tcx'
response = requests.get(tcxuri,headers=headers2)
if response.status_code == 200:
filename = 'media/mailbox_attachments/{code}_{id}.tcx'.format(
id = exercise_dict['id'],
code = uuid4().hex[:16]
)
with open(filename,'wb') as fop:
fop.write(response.content)
msg = Message(mailbox=workoutsbox,
from_header=user.email,
subject = 'Import from Polar Flow',
body=bodyyaml)
msg.save()
a = MessageAttachment(message=msg,document=filename[6:])
a.save()
exercise_dict['filename'] = filename
else:
exercise_dict['filename'] = ''
exercise_list.append(exercise_dict)
# commit transaction
requests.put(url, headers=headers)
return exercise_list
def get_polar_user_info(user,physical=False):
r = Rower.objects.get(user=user)
if (r.polartoken == '') or (r.polartoken is None):
s = "Token doesn't exist. Need to authorize"
return custom_exception_handler(401,s)
elif (timezone.now()>r.polartokenexpirydate):
s = "Token expired. Needs to refresh"
return custom_exception_handler(401,s)
else:
authorizationstring = str('Bearer ' + r.polartoken)
headers = {
'Authorization':authorizationstring,
'Accept': 'application/json'
}
params = {
'user-id': r.polaruserid
}
if not physical:
url = baseurl+'/users/{userid}'.format(
userid = r.polaruserid
)
else:
url = 'https://www.polaraccesslink.com/v3/users/{userid}/physical-information-transactions/'.format(
userid = r.polaruserid
)
if physical:
response = requests.post(url, headers=headers)
else:
response = requests.get(url, headers=headers)
return response
def get_polar_workout(user,id,transactionid):
r = Rower.objects.get(user=user)
if (r.polartoken == '') or (r.polartoken is None):
s = "Token doesn't exist. Need to authorize"
return custom_exception_handler(401,s)
elif (timezone.now()>r.polartokenexpirydate):
s = "Token expired. Needs to refresh"
return custom_exception_handler(401,s)
else:
authorizationstring = str('Bearer ' + r.polartoken)
headers = {
'Authorization':authorizationstring,
'Accept': 'application/json'
}
url = baseurl+'/users/{userid}/exercise-transactions'.format(
userid = r.polaruserid
)
response = requests.post(url, headers=headers)
if response.status_code == 201:
transactionid = response.json()['transaction-id']
url = baseurl+'/users/{userid}/exercise-transactions/{transactionid}'.format(
transactionid = transactionid,
userid = r.polaruserid
)
response = requests.get(url, headers=headers)
if response.status_code == 200:
exerciseurls = response.json()['exercises']
for exerciseurl in exerciseurls:
response = requests.gedt(exerciseurl,headers=headers)
if response.status_code == 200:
exercise_dict = response.json()
thisid = exercise_dict['id']
if thisid == id:
url = baseurl+'/users/{userid}/exercise-transactions/{transactionid}/exercises/{exerciseid}/tcx'.format(
userid = r.polaruserid,
transactionid = transactionid,
exerciseid = id
)
response = requests.get(url,headers = headers2)
if response.status_code == 200:
print response.text
result = response.text
# commit transaction
response = requests.put(url,headers=headers)
else:
result = None
return result
return None
+1 -1
View File
@@ -92,7 +92,7 @@ When the site is down, this is the appropriate channel to look for apologies, up
602 00 Brno<br />
Czech Republic<br />
IČ: 070 48 572<br />
DIČ: CZ 070 48 572<br />
DIČ: CZ 070 48 572 (Nejsme plátce DPH)<br />
Datová schránka: 7897syr<br />
Email: <a href="mailto:info@rowsandall.com">info@rowsandall.com</a><br />
The company is registered in the business register at the
+45 -5
View File
@@ -1,9 +1,9 @@
{% extends "base.html" %}
{% block title %}Contact Us{% endblock title %}
{% block title %}Import Workouts{% endblock title %}
{% block content %}
<div class="grid_6 alpha">
<h3>Import Workouts</h3>
<h2>Import Workouts</h2>
<div class="grid_6">
<div class="grid_3 alpha">
@@ -53,14 +53,25 @@
<p>Import workouts from MapMyFitness/UnderArmour</p>
</div>
</div>
<div class="grid_6">
<div class="grid_3 alpha">
<p>
<a href="/rowers/workout/polarimport"><img src="/static/img/Polar_connectwith_btn_white.png" alt="Polar logo" width="140"></a>
</p>
</div>
<div class="grid_3 omega">
<p>Import workouts from Polar Flow. </p><p><b>Note: No workout selection possible. Automatically imports all new workouts</b></p>
</div>
</div>
</div>
<div class="grid_6 omega">
<h3>Connect</h3>
<h2>Connect</h2>
<div class="grid_6 alpha">
<p>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.</p>
<div class="grid_2 alpha">
@@ -81,10 +92,39 @@
<div class="grid_2 alpha">
<p><a href="/rowers/me/runkeeperauthorize/"><img src="/static/img/rk-logo.png" alt="connect with RunKeeper" width="120"></a></p>
</div>
<div class="grid_2 suffix_2 omega">
<div class="grid_2">
<p><a href="/rowers/me/underarmourauthorize/"><img src="/static/img/UAbtn.png" alt="connect with Under Armour" width="120"></a></p>
</div>
<div class="grid_2 omega">
<p><a href="/rowers/me/polarauthorize/"><img src="/static/img/Polar_connectwith_btn_white.png"
alt="connect with Polar" width="130"></a></p>
</div>
</div>
<h2>Auto Import/Export Settings</h2>
<p>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.</p>
<p>Auto Import = rowsandall.com will regularly poll the partner site for new
workouts and automatically import new workouts
</p>
<p>Auto Export = New workouts uploaded to rowsandall.com will be automatically synchronized
to the partner site</p>
<div class="grid_6 alpha">
<form id="importexportform" method="post">
<table width=70%>
{{ form.as_table }}
</table>
{% csrf_token %}
<div id="formbutton" class="grid_2 prefix_2 alpha">
<input class="button green grid_2" type="submit" value="Save">
</div>
</form>
</div>
</div>
+49
View File
@@ -0,0 +1,49 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}Workouts{% endblock %}
{% block content %}
<h1>New Workouts Imported From Polar Flow</h1>
<p>Due to a limitation in Polar Flow's API, we can only access new workouts. We
have imported the following workouts and are now processing them. You will
receive email when the workouts are ready. </p>
{% if workouts %}
<div class="grid_12 alpha">
<table width="70%" class="listtable">
<thead>
<tr>
<th> Imported </th>
<th> Date</th>
<th> Duration </th>
<th> Distance </th>
<th> Type</th>
</tr>
</thead>
<tbody>
{% for workout in workouts %}
<tr>
<td>
{% if workout|lookup:'filename' == '' %}
NO
{% else %}
YES
{% endif %}
</td>
<td>{{ workout|lookup:'starttime' }}</td>
<td>{{ workout|lookup:'duration' }} </td>
<td>{{ workout|lookup:'distance' }} m</td>
<td>{{ workout|lookup:'type' }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p> No new workouts found </p>
{% endif %}
{% endblock %}
+7 -7
View File
@@ -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
+4 -1
View File
@@ -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<age>\d+)$',views.agegroupcpview),
@@ -328,6 +329,7 @@ urlpatterns = [
url(r'^workout/sporttracksimport/$',views.workout_sporttracksimport_view),
url(r'^workout/sporttracksimport/(?P<sporttracksid>\d+)/$',views.workout_getsporttracksworkout_view),
url(r'^workout/sporttracksimport/all/$',views.workout_getsporttracksworkout_all),
url(r'^workout/polarimport/$',views.workout_polarimport_view),
url(r'^workout/runkeeperimport/$',views.workout_runkeeperimport_view),
url(r'^workout/runkeeperimport/(?P<runkeeperid>\d+)/$',views.workout_getrunkeeperworkout_view),
url(r'^workout/underarmourimport/$',views.workout_underarmourimport_view),
@@ -379,6 +381,7 @@ urlpatterns = [
url(r'^rower/edit/(?P<rowerid>\d+)$',views.rower_edit_view),
url(r'^me/edit/(.+.*)/$',views.rower_edit_view),
url(r'^me/c2authorize/$',views.rower_c2_authorize),
url(r'^me/polarauthorize/$',views.rower_polar_authorize),
url(r'^me/revokeapp/(?P<id>\d+)$',views.rower_revokeapp_view),
url(r'^me/stravaauthorize/$',views.rower_strava_authorize),
url(r'^me/sporttracksauthorize/$',views.rower_sporttracks_authorize),
+151 -20
View File
@@ -21,6 +21,9 @@ from django.views.decorators.csrf import csrf_exempt
from matplotlib.backends.backend_agg import FigureCanvas
import gc
from pyparsing import ParseException
from uuid import uuid4
import isodate
from django.shortcuts import render
from django.http import (
@@ -69,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
@@ -93,6 +96,8 @@ from sporttracksstuff import SportTracksNoTokenError,sporttracks_open
from tpstuff import TPNoTokenError,tp_open
from iso8601 import ParseError
import stravastuff
import polarstuff
from polarstuff import PolarNoTokenError
from stravastuff import StravaNoTokenError
import sporttracksstuff
import underarmourstuff
@@ -104,6 +109,7 @@ from ownapistuff import TEST_CLIENT_ID, TEST_CLIENT_SECRET, TEST_REDIRECT_URI
from rowsandall_app.settings import (
C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET,
STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET,
POLAR_CLIENT_ID, POLAR_REDIRECT_URI, POLAR_CLIENT_SECRET,
SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI,
SPORTTRACKS_CLIENT_SECRET,
UNDERARMOUR_CLIENT_ID, UNDERARMOUR_REDIRECT_URI,
@@ -1228,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')
@@ -1418,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')
@@ -1584,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')
@@ -1751,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')
@@ -2358,7 +2379,7 @@ def workout_sporttracks_upload_view(request,id=0):
def rower_c2_authorize(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())
scope = "user:read,results:write"
params = {"client_id": C2_CLIENT_ID,
@@ -2373,7 +2394,7 @@ def rower_c2_authorize(request):
def rower_strava_authorize(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,
@@ -2385,12 +2406,30 @@ def rower_strava_authorize(request):
return HttpResponseRedirect(url)
# Polar Authorization
@login_required()
def rower_polar_authorize(request):
state = str(uuid4())
params = {"client_id": POLAR_CLIENT_ID,
"response_type": "code",
"redirect_uri": POLAR_REDIRECT_URI,
"state": state,
# "scope":"accesslink.read_all"
}
url = "https://flow.polar.com/oauth2/authorization?" +urllib.urlencode(params)
return HttpResponseRedirect(url)
# Runkeeper authorization
@login_required()
def rower_runkeeper_authorize(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": RUNKEEPER_CLIENT_ID,
@@ -2408,7 +2447,7 @@ def rower_runkeeper_authorize(request):
def rower_sporttracks_authorize(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": SPORTTRACKS_CLIENT_ID,
@@ -2426,7 +2465,7 @@ def rower_sporttracks_authorize(request):
def rower_underarmour_authorize(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())
redirect_uri = UNDERARMOUR_REDIRECT_URI
@@ -2443,7 +2482,7 @@ def rower_underarmour_authorize(request):
def rower_tp_authorize(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": TP_CLIENT_KEY,
"response_type": "code",
@@ -2588,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
@@ -2608,6 +2666,36 @@ def test_reverse_view(request):
def rower_process_twittercallback(request):
return "dummy"
# Process Polar Callback
@login_required()
def rower_process_polarcallback(request):
try:
code = request.GET['code']
except MultiValueDictKeyError:
try:
message = request.GET['error']
except MultiValueDictKeyError:
message = "access error"
messages.error(request,message)
return imports_view(request)
access_token, expires_in, user_id = polarstuff.get_token(code)
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
r = getrower(request.user)
r.polartoken = access_token
r.polartokenexpirydate = expirydatetime
r.polaruserid = user_id
r.save()
successmessage = "Tokens stored. Good to go"
messages.info(request,successmessage)
return imports_view(request)
# Process Strava Callback
@login_required()
def rower_process_stravacallback(request):
@@ -9591,6 +9679,48 @@ def workout_underarmourimport_view(request,message=""):
return HttpResponse(res)
# the page where you select which Polar workout to Import
@login_required()
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']
rowtype = exercise['sport']
durationstring = exercise['duration']
duration = isodate.parse_duration(durationstring)
keys = ['id','distance','duration','starttime','type','transactionid']
values = [i,d,duration,starttime,rowtype,transactionid]
res = dict(zip(keys,values))
workouts.append(res)
return render(request, 'polar_list_import.html',
{
'workouts':workouts,
'teams':get_my_teams(request.user),
})
# The page where you select which SportTracks workout to import
@login_required()
def workout_sporttracksimport_view(request,message=""):
@@ -9767,6 +9897,7 @@ def workout_c2import_view(request,page=1,message=""):
'page':page,
})
# Import a workout from Strava
@login_required()
def workout_getstravaworkout_view(request,stravaid):
@@ -10342,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:
@@ -10353,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
@@ -10366,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
@@ -10379,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
@@ -10394,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
@@ -10409,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
+5
View File
@@ -254,6 +254,11 @@ RUNKEEPER_CLIENT_ID = CFG['runkeeper_client_id']
RUNKEEPER_CLIENT_SECRET = CFG['runkeeper_client_secret']
RUNKEEPER_REDIRECT_URI = CFG['runkeeper_callback']
# Polar Flow
POLAR_CLIENT_ID = CFG['polarflow_client_id']
POLAR_CLIENT_SECRET = CFG['polarflow_client_secret']
POLAR_REDIRECT_URI = CFG['polarflow_callback']
# Under Armour
+1
View File
@@ -71,6 +71,7 @@ urlpatterns += [
url(r'^stravacall\_back',rowersviews.rower_process_stravacallback),
url(r'^sporttracks\_callback',rowersviews.rower_process_sporttrackscallback),
url(r'^underarmour\_callback',rowersviews.rower_process_underarmourcallback),
url(r'^polarflowcallback',rowersviews.rower_process_polarcallback),
url(r'^runkeeper\_callback',rowersviews.rower_process_runkeepercallback),
url(r'^tp\_callback',rowersviews.rower_process_tpcallback),
url(r'^twitter\_callback',rowersviews.rower_process_twittercallback),
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB