doing trainingpeaks, untested
This commit is contained in:
@@ -3,3 +3,5 @@ from .strava import StravaIntegration
|
|||||||
from .nk import NKIntegration
|
from .nk import NKIntegration
|
||||||
from .sporttracks import SportTracksIntegration
|
from .sporttracks import SportTracksIntegration
|
||||||
from .rp3 import RP3Integration
|
from .rp3 import RP3Integration
|
||||||
|
from .trainingpeaks import TPIntegration
|
||||||
|
|
||||||
|
|||||||
@@ -244,8 +244,4 @@ class RP3Integration(SyncIntegration):
|
|||||||
return super(RP3Integration, self).token_refresh(*args, **kwargs)
|
return super(RP3Integration, self).token_refresh(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
# just as a quick test during development
|
|
||||||
u = User.objects.get(id=1)
|
|
||||||
|
|
||||||
integration_1 = RP3Integration(u)
|
|
||||||
|
|
||||||
|
|||||||
134
rowers/integrations/trainingpeaks.py
Normal file
134
rowers/integrations/trainingpeaks.py
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
from .integrations import SyncIntegration, NoTokenError
|
||||||
|
from rowers.models import User, Rower, Workout, TombStone
|
||||||
|
|
||||||
|
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, dologging, myqueue
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from rowingdata import rowingdata
|
||||||
|
|
||||||
|
from rowers.rower_rules import is_workout_user
|
||||||
|
import time
|
||||||
|
from django_rq import job
|
||||||
|
|
||||||
|
from rowers.tasks import check_tp_workout_id, handle_workout_tp_upload
|
||||||
|
|
||||||
|
from rowsandall_app.settings import (
|
||||||
|
TP_CLIENT_ID, TP_CLIENT_SECRET,
|
||||||
|
TP_REDIRECT_URI, TP_CLIENT_KEY,TP_API_LOCATION,
|
||||||
|
TP_OAUTH_LOCATION,
|
||||||
|
)
|
||||||
|
|
||||||
|
import gzip
|
||||||
|
|
||||||
|
import base64
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
|
|
||||||
|
tpapilocation = TP_API_LOCATION
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class TPIntegration(SyncIntegration):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(TPIntegration, self).__init__(*args, **kwargs)
|
||||||
|
self.oauth_data = {
|
||||||
|
'client_id': TP_CLIENT_ID,
|
||||||
|
'client_secret': TP_CLIENT_SECRET,
|
||||||
|
'redirect_uri': TP_REDIRECT_URI,
|
||||||
|
'autorization_uri': "https://oauth.trainingpeaks.com/oauth/authorize?",
|
||||||
|
'content_type': 'application/x-www-form-urlencoded',
|
||||||
|
'tokenname': 'tptoken',
|
||||||
|
'refreshtokenname': 'tprefreshtoken',
|
||||||
|
'expirydatename': 'tptokenexpirydate',
|
||||||
|
'bearer_auth': False,
|
||||||
|
'base_url': "https://oauth.trainingpeaks.com/oauth/token",
|
||||||
|
'scope': 'write',
|
||||||
|
}
|
||||||
|
|
||||||
|
def createworkoutdata(self, w, *args, **kwargs):
|
||||||
|
filename = w.csvfilename
|
||||||
|
row = rowingdata(csvfile=filename)
|
||||||
|
tcxfilename = filename[:-4]+'.tcx'
|
||||||
|
try:
|
||||||
|
newnotes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com'
|
||||||
|
except TypeError:
|
||||||
|
newnotes = 'from '+w.workoutsource+' via rowsandall.com'
|
||||||
|
|
||||||
|
row.exporttotcx(tcxfilename, notes=newnotes)
|
||||||
|
|
||||||
|
return tcxfilename
|
||||||
|
|
||||||
|
|
||||||
|
def workout_export(self, workout, *args, **kwargs) -> str:
|
||||||
|
thetoken = self.open()
|
||||||
|
tcxfilename = self.createworkoutdata(workout)
|
||||||
|
job = myqueue(
|
||||||
|
queue,
|
||||||
|
handle_workout_tp_upload,
|
||||||
|
workout,
|
||||||
|
thetoken,
|
||||||
|
tcxfilename
|
||||||
|
)
|
||||||
|
return job.id
|
||||||
|
|
||||||
|
|
||||||
|
def get_workouts(self, *args, **kwargs) -> int:
|
||||||
|
raise NotImplementedError("not implemented")
|
||||||
|
|
||||||
|
def get_workout(self, id) -> int:
|
||||||
|
raise NotImplementedError("not implemented")
|
||||||
|
|
||||||
|
def get_workout_list(self, *args, **kwargs) -> list:
|
||||||
|
raise NotImplementedError("not implemented")
|
||||||
|
|
||||||
|
def make_authorization_url(self, *args, **kwargs) -> str: # pragma: no cover
|
||||||
|
return super(TPIntegration, self).make_authorization_url(self, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_token(self, code, *args, **kwargs) -> (str, int, str):
|
||||||
|
# client_auth = requests.auth.HTTPBasicAuth(TP_CLIENT_KEY, TP_CLIENT_SECRET)
|
||||||
|
post_data = {
|
||||||
|
"client_id": TP_CLIENT_KEY,
|
||||||
|
"grant_type": "authorization_code",
|
||||||
|
"code": code,
|
||||||
|
"redirect_uri": TP_REDIRECT_URI,
|
||||||
|
"client_secret": TP_CLIENT_SECRET,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
TP_OAUTH_LOCATION+"/oauth/token/",
|
||||||
|
data=post_data, verify=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
raise NoTokenError
|
||||||
|
|
||||||
|
try:
|
||||||
|
token_json = response.json()
|
||||||
|
thetoken = token_json['access_token']
|
||||||
|
expires_in = token_json['expires_in']
|
||||||
|
refresh_token = token_json['refresh_token']
|
||||||
|
except KeyError: # pragma: no cover
|
||||||
|
thetoken = ""
|
||||||
|
expires_in = 0
|
||||||
|
refresh_token = ""
|
||||||
|
|
||||||
|
return thetoken, expires_in, refresh_token
|
||||||
|
|
||||||
|
|
||||||
|
def open(self, *args, **kwargs) -> str:
|
||||||
|
return super(TPIntegration, self).open(*args, **kwargs)
|
||||||
|
|
||||||
|
def token_refresh(self, *args, **kwargs) -> str:
|
||||||
|
return super(TPIntegration, self).token_refresh(*args, **kwargs)
|
||||||
|
|
||||||
|
# just as a quick test during development
|
||||||
|
u = User.objects.get(id=1)
|
||||||
|
|
||||||
|
integration_1 = TPIntegration(u)
|
||||||
|
|
||||||
@@ -47,6 +47,8 @@ import sys
|
|||||||
import json
|
import json
|
||||||
import traceback
|
import traceback
|
||||||
from time import strftime
|
from time import strftime
|
||||||
|
import base64
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
from scipy import optimize
|
from scipy import optimize
|
||||||
from scipy.signal import savgol_filter
|
from scipy.signal import savgol_filter
|
||||||
@@ -105,7 +107,8 @@ except KeyError: # pragma: no cover
|
|||||||
NK_API_LOCATION = CFG["nk_api_location"]
|
NK_API_LOCATION = CFG["nk_api_location"]
|
||||||
TP_CLIENT_ID = CFG["tp_client_id"]
|
TP_CLIENT_ID = CFG["tp_client_id"]
|
||||||
TP_CLIENT_SECRET = CFG["tp_client_secret"]
|
TP_CLIENT_SECRET = CFG["tp_client_secret"]
|
||||||
|
TP_API_LOCATION = CFG["tp_api_location"]
|
||||||
|
tpapilocation = TP_API_LOCATION
|
||||||
|
|
||||||
from requests_oauthlib import OAuth1, OAuth1Session
|
from requests_oauthlib import OAuth1, OAuth1Session
|
||||||
|
|
||||||
@@ -344,6 +347,46 @@ def handle_add_workouts_team(ws, t, debug=False, **kwargs):
|
|||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
def uploadactivity(access_token, filename, description='',
|
||||||
|
name='Rowsandall.com workout'):
|
||||||
|
|
||||||
|
data_gz = BytesIO()
|
||||||
|
with open(filename, 'rb') as inF:
|
||||||
|
s = inF.read()
|
||||||
|
with gzip.GzipFile(fileobj=data_gz, mode="w") as gzf:
|
||||||
|
gzf.write(s)
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Authorization': 'Bearer %s' % access_token
|
||||||
|
}
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"UploadClient": "rowsandall",
|
||||||
|
"Filename": filename,
|
||||||
|
"SetWorkoutPublic": True,
|
||||||
|
"Title": name,
|
||||||
|
"Type": "rowing",
|
||||||
|
"Comment": description,
|
||||||
|
"Data": base64.b64encode(data_gz.getvalue()).decode("ascii")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp = requests.post(tpapilocation+"/v3/file",
|
||||||
|
data=json.dumps(data),
|
||||||
|
headers=headers, verify=False)
|
||||||
|
|
||||||
|
if resp.status_code not in (200, 202): # pragma: no cover
|
||||||
|
dologging('tp_export.log',resp.status_code)
|
||||||
|
dologging('tp_export.log',resp.reason)
|
||||||
|
dologging('tp_export.log',json.dumps(data))
|
||||||
|
return 0, resp.reason, resp.status_code, headers
|
||||||
|
else:
|
||||||
|
return 1, "ok", 200, resp.headers
|
||||||
|
|
||||||
|
return 0, 0, 0, 0 # pragma: no cover
|
||||||
|
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
def check_tp_workout_id(workout, location, attempts=5, debug=False, **kwargs):
|
def check_tp_workout_id(workout, location, attempts=5, debug=False, **kwargs):
|
||||||
authorizationstring = str('Bearer ' + workout.user.tptoken)
|
authorizationstring = str('Bearer ' + workout.user.tptoken)
|
||||||
@@ -361,6 +404,37 @@ def check_tp_workout_id(workout, location, attempts=5, debug=False, **kwargs):
|
|||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
@app.task
|
||||||
|
def handle_workout_tp_upload(w, thetoken, tcxfilename, debug=False, **kwargs):
|
||||||
|
tpid = 0
|
||||||
|
r = w.user
|
||||||
|
if not tcxfilename:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
res, reason, status_code, headers = uploadactivity(
|
||||||
|
thetoken, tcxfilename,
|
||||||
|
name=w.name
|
||||||
|
)
|
||||||
|
|
||||||
|
if res == 0:
|
||||||
|
w.tpid = -1
|
||||||
|
try:
|
||||||
|
os.remove(tcxfilename)
|
||||||
|
except WindowsError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
w.save()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
w.uploadedtotp = res
|
||||||
|
tpid = res
|
||||||
|
w.save()
|
||||||
|
os.remove(tcxfilename)
|
||||||
|
|
||||||
|
check_tp_workout_id(w,headers['Location'])
|
||||||
|
|
||||||
|
return tpid
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
def instroke_static(w, metric, debug=False, **kwargs):
|
def instroke_static(w, metric, debug=False, **kwargs):
|
||||||
f1 = w.csvfilename[6:-4]
|
f1 = w.csvfilename[6:-4]
|
||||||
|
|||||||
@@ -234,10 +234,6 @@
|
|||||||
<a href="/rowers/me/rp3authorize">
|
<a href="/rowers/me/rp3authorize">
|
||||||
Connect to RP3
|
Connect to RP3
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
|
||||||
<a href="https://rp3rowing-app.com/home">
|
|
||||||
RP3
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
<li id="export-csv">
|
<li id="export-csv">
|
||||||
|
|||||||
@@ -1373,15 +1373,15 @@ class TPObjects(DjangoTestCase):
|
|||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
@patch('rowers.tpstuff.requests.post', side_effect=mocked_requests)
|
@patch('rowers.integrations.trainingpeaks.requests.post', side_effect=mocked_requests)
|
||||||
def test_tp_token_refresh(self, mock_post):
|
def test_tp_token_refresh(self, mock_post):
|
||||||
response = self.c.get('/rowers/me/tprefresh/',follow=True)
|
response = self.c.get('/rowers/me/tprefresh/',follow=True)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
@patch('rowers.tpstuff.requests.post', side_effect=mocked_requests)
|
@patch('rowers.integrations.trainingpeaks.requests.post', side_effect=mocked_requests)
|
||||||
@patch('rowers.tpstuff.requests.get', side_effect=mocked_requests)
|
@patch('rowers.integrations.trainingpeaks.requests.get', side_effect=mocked_requests)
|
||||||
def test_tp_upload(self, mock_get, mock_post):
|
def test_tp_upload(self, mock_get, mock_post):
|
||||||
url = '/rowers/workout/'+encoded1+'/tpuploadw/'
|
url = '/rowers/workout/'+encoded1+'/tpuploadw/'
|
||||||
|
|
||||||
|
|||||||
BIN
rowers/tests/testdata/testdata.tcx.gz
vendored
BIN
rowers/tests/testdata/testdata.tcx.gz
vendored
Binary file not shown.
@@ -1,232 +0,0 @@
|
|||||||
from celery import Celery, app
|
|
||||||
from rowers.rower_rules import is_workout_user
|
|
||||||
import time
|
|
||||||
from django_rq import job
|
|
||||||
# All the functionality needed to connect to Runkeeper
|
|
||||||
from rowers.imports import *
|
|
||||||
from rowers.utils import dologging
|
|
||||||
from rowers.tasks import check_tp_workout_id
|
|
||||||
|
|
||||||
import django_rq
|
|
||||||
queue = django_rq.get_queue('default')
|
|
||||||
queuelow = django_rq.get_queue('low')
|
|
||||||
queuehigh = django_rq.get_queue('low')
|
|
||||||
|
|
||||||
from rowers.utils import myqueue
|
|
||||||
|
|
||||||
# Python
|
|
||||||
import gzip
|
|
||||||
|
|
||||||
import base64
|
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
|
|
||||||
from rowsandall_app.settings import (
|
|
||||||
C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET,
|
|
||||||
STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET,
|
|
||||||
TP_CLIENT_ID, TP_CLIENT_SECRET,
|
|
||||||
TP_REDIRECT_URI, TP_CLIENT_KEY,TP_API_LOCATION,
|
|
||||||
TP_OAUTH_LOCATION,
|
|
||||||
)
|
|
||||||
|
|
||||||
tpapilocation = TP_API_LOCATION
|
|
||||||
|
|
||||||
oauth_data = {
|
|
||||||
'client_id': TP_CLIENT_ID,
|
|
||||||
'client_secret': TP_CLIENT_SECRET,
|
|
||||||
'redirect_uri': TP_REDIRECT_URI,
|
|
||||||
'autorization_uri': "https://oauth.trainingpeaks.com/oauth/authorize?",
|
|
||||||
'content_type': 'application/x-www-form-urlencoded',
|
|
||||||
# 'content_type': 'application/json',
|
|
||||||
'tokenname': 'tptoken',
|
|
||||||
'refreshtokenname': 'tprefreshtoken',
|
|
||||||
'expirydatename': 'tptokenexpirydate',
|
|
||||||
'bearer_auth': False,
|
|
||||||
'base_url': "https://oauth.trainingpeaks.com/oauth/token",
|
|
||||||
'scope': 'write',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Checks if user has UnderArmour token, renews them if they are expired
|
|
||||||
def tp_open(user):
|
|
||||||
return imports_open(user, oauth_data)
|
|
||||||
|
|
||||||
# Refresh ST token using refresh token
|
|
||||||
|
|
||||||
|
|
||||||
def do_refresh_token(refreshtoken):
|
|
||||||
return imports_do_refresh_token(refreshtoken, oauth_data)
|
|
||||||
|
|
||||||
# Exchange access code for long-lived access token
|
|
||||||
|
|
||||||
def get_token(code):
|
|
||||||
# client_auth = requests.auth.HTTPBasicAuth(TP_CLIENT_KEY, TP_CLIENT_SECRET)
|
|
||||||
post_data = {
|
|
||||||
"client_id": TP_CLIENT_KEY,
|
|
||||||
"grant_type": "authorization_code",
|
|
||||||
"code": code,
|
|
||||||
"redirect_uri": TP_REDIRECT_URI,
|
|
||||||
"client_secret": TP_CLIENT_SECRET,
|
|
||||||
}
|
|
||||||
|
|
||||||
response = requests.post(
|
|
||||||
TP_OAUTH_LOCATION+"/oauth/token/",
|
|
||||||
data=post_data, verify=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
if response.status_code != 200:
|
|
||||||
return 0,0,0
|
|
||||||
|
|
||||||
try:
|
|
||||||
token_json = response.json()
|
|
||||||
thetoken = token_json['access_token']
|
|
||||||
expires_in = token_json['expires_in']
|
|
||||||
refresh_token = token_json['refresh_token']
|
|
||||||
except KeyError: # pragma: no cover
|
|
||||||
thetoken = 0
|
|
||||||
expires_in = 0
|
|
||||||
refresh_token = 0
|
|
||||||
|
|
||||||
return thetoken, expires_in, refresh_token
|
|
||||||
|
|
||||||
# Make authorization URL including random string
|
|
||||||
|
|
||||||
|
|
||||||
def make_authorization_url(request): # pragma: no cover
|
|
||||||
return imports_make_authorization_url(oauth_data)
|
|
||||||
|
|
||||||
|
|
||||||
def getidfromresponse(response): # pragma: no cover
|
|
||||||
t = json.loads(response.text)
|
|
||||||
|
|
||||||
links = t["_links"]
|
|
||||||
|
|
||||||
id = links["self"][0]["id"]
|
|
||||||
|
|
||||||
return int(id)
|
|
||||||
|
|
||||||
|
|
||||||
def createtpworkoutdata(w):
|
|
||||||
filename = w.csvfilename
|
|
||||||
row = rowingdata(csvfile=filename)
|
|
||||||
tcxfilename = filename[:-4]+'.tcx'
|
|
||||||
try:
|
|
||||||
newnotes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com'
|
|
||||||
except TypeError:
|
|
||||||
newnotes = 'from '+w.workoutsource+' via rowsandall.com'
|
|
||||||
|
|
||||||
row.exporttotcx(tcxfilename, notes=newnotes)
|
|
||||||
|
|
||||||
return tcxfilename
|
|
||||||
|
|
||||||
|
|
||||||
def tp_check(access_token): # pragma: no cover
|
|
||||||
headers = {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'authorization': 'Bearer %s' % access_token
|
|
||||||
}
|
|
||||||
|
|
||||||
resp = requests.post(tpapilocation+"/v2/info/version",
|
|
||||||
headers=headers, verify=False)
|
|
||||||
|
|
||||||
return resp
|
|
||||||
|
|
||||||
|
|
||||||
def uploadactivity(access_token, filename, description='',
|
|
||||||
name='Rowsandall.com workout'):
|
|
||||||
|
|
||||||
data_gz = BytesIO()
|
|
||||||
with open(filename, 'rb') as inF:
|
|
||||||
s = inF.read()
|
|
||||||
with gzip.GzipFile(fileobj=data_gz, mode="w") as gzf:
|
|
||||||
gzf.write(s)
|
|
||||||
|
|
||||||
headers = {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Authorization': 'Bearer %s' % access_token
|
|
||||||
}
|
|
||||||
|
|
||||||
data = {
|
|
||||||
"UploadClient": "rowsandall",
|
|
||||||
"Filename": filename,
|
|
||||||
"SetWorkoutPublic": True,
|
|
||||||
"Title": name,
|
|
||||||
"Type": "rowing",
|
|
||||||
"Comment": description,
|
|
||||||
"Data": base64.b64encode(data_gz.getvalue()).decode("ascii")
|
|
||||||
}
|
|
||||||
|
|
||||||
#resp = requests.post(tpapilocation+"/v2/file/synchronous",
|
|
||||||
# data=json.dumps(data),
|
|
||||||
# headers=headers, verify=False)
|
|
||||||
|
|
||||||
resp = requests.post(tpapilocation+"/v3/file",
|
|
||||||
data=json.dumps(data),
|
|
||||||
headers=headers, verify=False)
|
|
||||||
|
|
||||||
if resp.status_code not in (200, 202): # pragma: no cover
|
|
||||||
dologging('tp_export.log',resp.status_code)
|
|
||||||
dologging('tp_export.log',resp.reason)
|
|
||||||
dologging('tp_export.log',json.dumps(data))
|
|
||||||
return 0, resp.reason, resp.status_code, headers
|
|
||||||
else:
|
|
||||||
return 1, "ok", 200, resp.headers
|
|
||||||
|
|
||||||
return 0, 0, 0, 0 # pragma: no cover
|
|
||||||
|
|
||||||
|
|
||||||
def workout_tp_upload(user, w): # pragma: no cover
|
|
||||||
message = "Uploading to TrainingPeaks"
|
|
||||||
tpid = 0
|
|
||||||
r = w.user
|
|
||||||
|
|
||||||
thetoken = tp_open(r.user)
|
|
||||||
|
|
||||||
# need some code if token doesn't refresh
|
|
||||||
|
|
||||||
if (is_workout_user(user, w)):
|
|
||||||
tcxfile = createtpworkoutdata(w)
|
|
||||||
if tcxfile:
|
|
||||||
res, reason, status_code, headers = uploadactivity(
|
|
||||||
thetoken, tcxfile,
|
|
||||||
name=w.name
|
|
||||||
)
|
|
||||||
if res == 0:
|
|
||||||
message = "Upload to TrainingPeaks failed with status code " + \
|
|
||||||
str(status_code)+": "+reason
|
|
||||||
w.tpid = -1
|
|
||||||
try:
|
|
||||||
os.remove(tcxfile)
|
|
||||||
except WindowsError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return message, tpid
|
|
||||||
|
|
||||||
else: # res != 0
|
|
||||||
w.uploadedtotp = res
|
|
||||||
tpid = res
|
|
||||||
w.save()
|
|
||||||
os.remove(tcxfile)
|
|
||||||
|
|
||||||
job = myqueue(queuelow,
|
|
||||||
check_tp_workout_id,
|
|
||||||
w,
|
|
||||||
headers['Location'])
|
|
||||||
|
|
||||||
return 'Successfully synchronized to TrainingPeaks', tpid
|
|
||||||
|
|
||||||
else: # no tcxfile
|
|
||||||
dologging('tp_export.log','Failed to create tcx file')
|
|
||||||
message = "Upload to TrainingPeaks failed"
|
|
||||||
w.uploadedtotp = -1
|
|
||||||
tpid = -1
|
|
||||||
w.save()
|
|
||||||
return message, tpid
|
|
||||||
else: # not allowed to upload
|
|
||||||
message = "You are not allowed to export this workout to TP"
|
|
||||||
tpid = 0
|
|
||||||
return message, tpid
|
|
||||||
|
|
||||||
return message, tpid
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
from rowers.mytypes import workouttypes, boattypes, otwtypes, workoutsources, workouttypes_ordered
|
from rowers.mytypes import workouttypes, boattypes, otwtypes, workoutsources, workouttypes_ordered
|
||||||
|
|
||||||
from rowers.rower_rules import is_promember
|
from rowers.rower_rules import is_promember
|
||||||
import rowers.tpstuff as tpstuff
|
|
||||||
|
|
||||||
from rowers.integrations import *
|
from rowers.integrations import *
|
||||||
from rowers.utils import (
|
from rowers.utils import (
|
||||||
@@ -270,9 +269,8 @@ def do_sync(w, options, quick=False):
|
|||||||
upload_to_st = False
|
upload_to_st = False
|
||||||
if do_tp_export:
|
if do_tp_export:
|
||||||
try:
|
try:
|
||||||
_, id = tpstuff.workout_tp_upload(
|
tp_integration = TPIntegration(w.user.user)
|
||||||
w.user.user, w
|
id = tp_integration.workout_export(w)
|
||||||
)
|
|
||||||
dologging('tp_export.log',
|
dologging('tp_export.log',
|
||||||
'exported workout {wid} for user {uid}'.format(
|
'exported workout {wid} for user {uid}'.format(
|
||||||
wid = w.id,
|
wid = w.id,
|
||||||
|
|||||||
@@ -27,8 +27,11 @@ def workout_tp_upload_view(request, id=0):
|
|||||||
message = ""
|
message = ""
|
||||||
r = getrower(request.user)
|
r = getrower(request.user)
|
||||||
res = -1
|
res = -1
|
||||||
|
|
||||||
|
tp_integration = TPIntegration(request.user)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_ = tp_open(r.user)
|
_ = tp_integration.open()
|
||||||
except NoTokenError: # pragma: no cover
|
except NoTokenError: # pragma: no cover
|
||||||
return HttpResponseRedirect("/rowers/me/tpauthorize/")
|
return HttpResponseRedirect("/rowers/me/tpauthorize/")
|
||||||
|
|
||||||
@@ -36,38 +39,9 @@ def workout_tp_upload_view(request, id=0):
|
|||||||
w = get_workout_by_opaqueid(request, id)
|
w = get_workout_by_opaqueid(request, id)
|
||||||
r = w.user
|
r = w.user
|
||||||
|
|
||||||
tcxfile = tpstuff.createtpworkoutdata(w)
|
jobid = tp_integration.workout_export(w)
|
||||||
if tcxfile:
|
messages.info(request,'Your workout will be exported to TrainingPeaks in the background')
|
||||||
res, reason, status_code, headers = tpstuff.uploadactivity(
|
|
||||||
r.tptoken, tcxfile,
|
|
||||||
name=w.name
|
|
||||||
)
|
|
||||||
if res == 0: # pragma: no cover
|
|
||||||
message = "Upload to TrainingPeaks failed with status code " + \
|
|
||||||
str(status_code)+": "+reason
|
|
||||||
try:
|
|
||||||
os.remove(tcxfile)
|
|
||||||
except WindowsError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
messages.error(request, message)
|
|
||||||
|
|
||||||
else: # res != 0
|
|
||||||
w.uploadedtotp = res
|
|
||||||
w.save()
|
|
||||||
os.remove(tcxfile)
|
|
||||||
job = myqueue(queuelow,
|
|
||||||
check_tp_workout_id,
|
|
||||||
w,
|
|
||||||
headers['Location'])
|
|
||||||
|
|
||||||
messages.info(request, 'Uploaded to TrainingPeaks')
|
|
||||||
|
|
||||||
else: # pragma: no cover # no tcxfile
|
|
||||||
message = "Upload to TrainingPeaks failed"
|
|
||||||
w.uploadedtotp = -1
|
|
||||||
w.save()
|
|
||||||
messages.error(request, message)
|
|
||||||
|
|
||||||
url = reverse(r.defaultlandingpage,
|
url = reverse(r.defaultlandingpage,
|
||||||
kwargs={
|
kwargs={
|
||||||
@@ -301,20 +275,8 @@ def rower_c2_token_refresh(request):
|
|||||||
@login_required()
|
@login_required()
|
||||||
def rower_tp_token_refresh(request):
|
def rower_tp_token_refresh(request):
|
||||||
r = getrower(request.user)
|
r = getrower(request.user)
|
||||||
res = tpstuff.do_refresh_token(
|
tp_integration = TPIntegration(request.user)
|
||||||
r.tprefreshtoken,
|
token = tp_integration.token_refresh()
|
||||||
)
|
|
||||||
access_token = res[0]
|
|
||||||
expires_in = res[1]
|
|
||||||
refresh_token = res[2]
|
|
||||||
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
|
|
||||||
|
|
||||||
r = getrower(request.user)
|
|
||||||
r.tptoken = access_token
|
|
||||||
r.tptokenexpirydate = expirydatetime
|
|
||||||
r.tprefreshtoken = refresh_token
|
|
||||||
|
|
||||||
r.save()
|
|
||||||
|
|
||||||
successmessage = "Tokens refreshed. Good to go"
|
successmessage = "Tokens refreshed. Good to go"
|
||||||
messages.info(request, successmessage)
|
messages.info(request, successmessage)
|
||||||
@@ -759,11 +721,8 @@ def rower_process_tpcallback(request):
|
|||||||
url = reverse('rower_exportsettings_view')
|
url = reverse('rower_exportsettings_view')
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
res = tpstuff.get_token(code)
|
tp_integration = TPIntegration(request.user)
|
||||||
|
access_token, expires_in, refresh_token = tp_integration.get_token(code)
|
||||||
access_token = res[0]
|
|
||||||
expires_in = res[1]
|
|
||||||
refresh_token = res[2]
|
|
||||||
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
|
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
|
||||||
|
|
||||||
r = getrower(request.user)
|
r = getrower(request.user)
|
||||||
@@ -1560,7 +1519,7 @@ importsources = {
|
|||||||
'polar': polarstuff,
|
'polar': polarstuff,
|
||||||
'ownapi': ownapistuff,
|
'ownapi': ownapistuff,
|
||||||
'sporttracks': SportTracksIntegration,
|
'sporttracks': SportTracksIntegration,
|
||||||
'trainingpeaks': tpstuff,
|
'trainingpeaks': TPIntegration,
|
||||||
'nk': NKIntegration,
|
'nk': NKIntegration,
|
||||||
'rp3':RP3Integration,
|
'rp3':RP3Integration,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,7 +194,6 @@ import datetime
|
|||||||
import iso8601
|
import iso8601
|
||||||
import rowers.rojabo_stuff as rojabo_stuff
|
import rowers.rojabo_stuff as rojabo_stuff
|
||||||
|
|
||||||
from rowers.tpstuff import tp_open
|
|
||||||
from iso8601 import ParseError
|
from iso8601 import ParseError
|
||||||
|
|
||||||
import rowers.rojabo_stuff as rojabo_stuff
|
import rowers.rojabo_stuff as rojabo_stuff
|
||||||
@@ -205,7 +204,6 @@ import rowers.polarstuff as polarstuff
|
|||||||
|
|
||||||
from rowers.integrations import *
|
from rowers.integrations import *
|
||||||
|
|
||||||
import rowers.tpstuff as tpstuff
|
|
||||||
|
|
||||||
import rowers.ownapistuff as ownapistuff
|
import rowers.ownapistuff as ownapistuff
|
||||||
from rowers.ownapistuff import TEST_CLIENT_ID, TEST_CLIENT_SECRET, TEST_REDIRECT_URI
|
from rowers.ownapistuff import TEST_CLIENT_ID, TEST_CLIENT_SECRET, TEST_REDIRECT_URI
|
||||||
|
|||||||
@@ -5636,9 +5636,6 @@ def workout_upload_view(request,
|
|||||||
except NoTokenError:
|
except NoTokenError:
|
||||||
id = 0
|
id = 0
|
||||||
message = "Something went wrong with the Concept2 sync"
|
message = "Something went wrong with the Concept2 sync"
|
||||||
if id > 1:
|
|
||||||
messages.info(request, message)
|
|
||||||
else:
|
|
||||||
messages.error(request, message)
|
messages.error(request, message)
|
||||||
|
|
||||||
if (upload_to_strava): # pragma: no cover
|
if (upload_to_strava): # pragma: no cover
|
||||||
@@ -5648,9 +5645,6 @@ def workout_upload_view(request,
|
|||||||
except NoTokenError:
|
except NoTokenError:
|
||||||
id = 0
|
id = 0
|
||||||
message = "Please connect to Strava first"
|
message = "Please connect to Strava first"
|
||||||
if id > 1:
|
|
||||||
messages.info(request, message)
|
|
||||||
else:
|
|
||||||
messages.error(request, message)
|
messages.error(request, message)
|
||||||
|
|
||||||
if (upload_to_st): # pragma: no cover
|
if (upload_to_st): # pragma: no cover
|
||||||
@@ -5660,23 +5654,14 @@ def workout_upload_view(request,
|
|||||||
except NoTokenError:
|
except NoTokenError:
|
||||||
message = "Please connect to SportTracks first"
|
message = "Please connect to SportTracks first"
|
||||||
id = 0
|
id = 0
|
||||||
if id > 1:
|
|
||||||
messages.info(request, message)
|
|
||||||
else:
|
|
||||||
messages.error(request, message)
|
messages.error(request, message)
|
||||||
|
|
||||||
if (upload_to_tp): # pragma: no cover
|
if (upload_to_tp): # pragma: no cover
|
||||||
|
tp_integration = TPIntegration(request.user)
|
||||||
try:
|
try:
|
||||||
message, id = tpstuff.workout_tp_upload(
|
id = tp_integration.workout_export(w)
|
||||||
request.user, w
|
|
||||||
)
|
|
||||||
except NoTokenError:
|
except NoTokenError:
|
||||||
message = "Please connect to TrainingPeaks first"
|
message = "Please connect to TrainingPeaks first"
|
||||||
id = 0
|
|
||||||
|
|
||||||
if id > 1:
|
|
||||||
messages.info(request, message)
|
|
||||||
else:
|
|
||||||
messages.error(request, message)
|
messages.error(request, message)
|
||||||
|
|
||||||
if int(registrationid) < 0: # pragma: no cover
|
if int(registrationid) < 0: # pragma: no cover
|
||||||
|
|||||||
Reference in New Issue
Block a user