rp3, untested
This commit is contained in:
@@ -2,3 +2,4 @@ from .c2 import C2Integration
|
|||||||
from .strava import StravaIntegration
|
from .strava import StravaIntegration
|
||||||
from .nk import NKIntegration
|
from .nk import NKIntegration
|
||||||
from .sporttracks import SportTracksIntegration
|
from .sporttracks import SportTracksIntegration
|
||||||
|
from .rp3 import RP3Integration
|
||||||
|
|||||||
252
rowers/integrations/rp3.py
Normal file
252
rowers/integrations/rp3.py
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
from .integrations import SyncIntegration, NoTokenError
|
||||||
|
from rowers.models import User, Rower, Workout, TombStone
|
||||||
|
|
||||||
|
from rowers.tasks import handle_rp3_async_workout
|
||||||
|
from rowsandall_app.settings import (
|
||||||
|
RP3_CLIENT_ID, RP3_CLIENT_KEY, RP3_REDIRECT_URI, RP3_CLIENT_SECRET,
|
||||||
|
UPLOAD_SERVICE_URL, UPLOAD_SERVICE_SECRET
|
||||||
|
)
|
||||||
|
|
||||||
|
from rowers.utils import myqueue, NoTokenError, dologging, uniqify
|
||||||
|
from django.utils import timezone
|
||||||
|
import requests
|
||||||
|
import pandas as pd
|
||||||
|
import arrow
|
||||||
|
import django_rq
|
||||||
|
queue = django_rq.get_queue('default')
|
||||||
|
queuelow = django_rq.get_queue('low')
|
||||||
|
queuehigh = django_rq.get_queue('high')
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
graphql_url = "https://rp3rowing-app.com/graphql"
|
||||||
|
|
||||||
|
|
||||||
|
class RP3Integration(SyncIntegration):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(RP3Integration, self).__init__(*args, **kwargs)
|
||||||
|
self.oauth_data = {
|
||||||
|
'client_id': RP3_CLIENT_ID,
|
||||||
|
'client_secret': RP3_CLIENT_SECRET,
|
||||||
|
'redirect_uri': RP3_REDIRECT_URI,
|
||||||
|
'autorization_uri': "https://rp3rowing-app.com/oauth/authorize?",
|
||||||
|
'content_type': 'application/x-www-form-urlencoded',
|
||||||
|
# 'content_type': 'application/json',
|
||||||
|
'tokenname': 'rp3token',
|
||||||
|
'refreshtokenname': 'rp3refreshtoken',
|
||||||
|
'expirydatename': 'rp3tokenexpirydate',
|
||||||
|
'bearer_auth': False,
|
||||||
|
'base_url': "https://rp3rowing-app.com/oauth/token",
|
||||||
|
'scope': 'read,write',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def createworkoutdata(self, w, *args, **kwargs):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def workout_export(self, workout, *args, **kwargs) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def get_workouts(self, *args, **kwargs) -> int:
|
||||||
|
auth_token = self.open()
|
||||||
|
|
||||||
|
r = self.rower
|
||||||
|
workouts_json = self.get_workout_list_json()
|
||||||
|
|
||||||
|
workouts_list = pd.json_normalize(workouts_json['data']['workouts'])
|
||||||
|
|
||||||
|
try:
|
||||||
|
rp3ids = workouts_list['id'].values
|
||||||
|
workouts_list.set_index('id',inpace=True)
|
||||||
|
except (KeyError, IndexError):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
knownrp3ids = uniqify([
|
||||||
|
w.uploadedtorp3 for w in Workout.objects.filter(user=rower)
|
||||||
|
])
|
||||||
|
|
||||||
|
dologging('rp3_import.log',rp3ids)
|
||||||
|
|
||||||
|
newids = [rp3id for rp3id in rp3ids if rp3id not in knownrp3ids]
|
||||||
|
|
||||||
|
dologging('rp3_import.log',newids)
|
||||||
|
|
||||||
|
for id in newids:
|
||||||
|
startdatetime = workouts_list.loc[id, 'executed_at_ios8601']
|
||||||
|
dologging('rp3_import.log', startdatetime)
|
||||||
|
|
||||||
|
_ = myqueue(
|
||||||
|
queuehigh,
|
||||||
|
handle_rp3_async_workout,
|
||||||
|
self.user.id,
|
||||||
|
auth_token,
|
||||||
|
id,
|
||||||
|
startdatetime,
|
||||||
|
20,
|
||||||
|
{'timezone':self.rower.defaulttimezone}
|
||||||
|
)
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def get_workout(self, id, *args, **kwargs) -> int:
|
||||||
|
startdatetime = kwargs.get('startdatetime', None)
|
||||||
|
if not startdatetime:
|
||||||
|
startdatetime = str(timezone.now())
|
||||||
|
|
||||||
|
auth_token = self.open()
|
||||||
|
_ = myqueue(
|
||||||
|
queuehigh,
|
||||||
|
handle_rp3_async_workout,
|
||||||
|
self.user.id,
|
||||||
|
auth_token,
|
||||||
|
id,
|
||||||
|
startdatetime,
|
||||||
|
20,
|
||||||
|
timezone = self.rower.defaulttimezone
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_workout_schema(self, *args, **kwargs) -> dict:
|
||||||
|
auth_token = self.open()
|
||||||
|
headers = {'Authorization': 'Bearer ' + auth_token}
|
||||||
|
get_schema = """{
|
||||||
|
__type(name:"Workout") {
|
||||||
|
name
|
||||||
|
fields {
|
||||||
|
name
|
||||||
|
description
|
||||||
|
type {
|
||||||
|
name
|
||||||
|
kind
|
||||||
|
ofType {
|
||||||
|
name
|
||||||
|
kind
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
url = graphql_url,
|
||||||
|
headers=headers,
|
||||||
|
json={'query':get_schema}
|
||||||
|
)
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
def get_workout_list_json(self, *args, **kwargs) -> dict:
|
||||||
|
auth_token = self.open()
|
||||||
|
r = self.rower
|
||||||
|
|
||||||
|
headers = {'Authorization': 'Bearer ' + auth_token}
|
||||||
|
|
||||||
|
get_workouts_list = """{
|
||||||
|
workouts{
|
||||||
|
id
|
||||||
|
executed_at_iso8601
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
url=graphql_url,
|
||||||
|
headers=headers,
|
||||||
|
json={'query': get_workouts_list}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (response.status_code != 200): # pragma: no cover
|
||||||
|
raise NoTokenError("Need to authorize")
|
||||||
|
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
def get_workout_list(self, *args, **kwargs) -> list:
|
||||||
|
r = self.rower
|
||||||
|
|
||||||
|
workouts_json = self.get_workout_list_json(*args, **kwargs)
|
||||||
|
|
||||||
|
workouts_list = pd.json_normalize(workouts_json['data']['workouts'])
|
||||||
|
|
||||||
|
knownrp3ids = uniqify([
|
||||||
|
w.uploadedtorp3 for w in Workout.objects.filter(user=r)
|
||||||
|
])
|
||||||
|
|
||||||
|
workouts = []
|
||||||
|
|
||||||
|
for key, data in workouts_list.iterrows():
|
||||||
|
print(data)
|
||||||
|
try:
|
||||||
|
i = data['id']
|
||||||
|
except KeyError: # pragma: no cover
|
||||||
|
i = 0
|
||||||
|
if i in knownrp3ids: # pragma: no cover
|
||||||
|
nnn = ''
|
||||||
|
else:
|
||||||
|
nnn = 'NEW'
|
||||||
|
|
||||||
|
try:
|
||||||
|
s = arrow.get(data['executed_at_iso8601']).isoformat()
|
||||||
|
except KeyError: # pragma: no cover
|
||||||
|
s = ''
|
||||||
|
|
||||||
|
keys = ['id', 'distance', 'duration', 'starttime',
|
||||||
|
'rowtype', 'source', 'name', 'new']
|
||||||
|
values = [i, '', '', s, '', 'rp3', '', nnn]
|
||||||
|
|
||||||
|
res = dict(zip(keys, values))
|
||||||
|
|
||||||
|
workouts.append(res)
|
||||||
|
|
||||||
|
|
||||||
|
return workouts
|
||||||
|
|
||||||
|
|
||||||
|
def make_authorization_url(self, *args, **kwargs) -> str: # pragma: no cover
|
||||||
|
return super(RP3Integration, self).make_authorization_url(*args, **krags)
|
||||||
|
|
||||||
|
def get_token(self, code, *args, **kwargs) -> (str, int, str):
|
||||||
|
post_data = {
|
||||||
|
"client_id": RP3_CLIENT_KEY,
|
||||||
|
"grant_type": "authorization_code",
|
||||||
|
"code": code,
|
||||||
|
"redirect_uri": RP3_REDIRECT_URI,
|
||||||
|
"client_secret": RP3_CLIENT_SECRET,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
"https://rp3rowing-app.com/oauth/token",
|
||||||
|
data=post_data, verify=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
token_json = response.json()
|
||||||
|
thetoken = token_json['access_token']
|
||||||
|
expires_in = token_json['expires_in']
|
||||||
|
refresh_token = token_json['refresh_token']
|
||||||
|
except KeyError:
|
||||||
|
thetoken = ""
|
||||||
|
expires_in = 0
|
||||||
|
refresh_token = ""
|
||||||
|
|
||||||
|
return thetoken, expires_in, refresh_token
|
||||||
|
|
||||||
|
|
||||||
|
def open(self, *args, **kwargs) -> str:
|
||||||
|
tokenexpirydate = self.user.rower.rp3tokenexpirydate
|
||||||
|
if tokenexpirydate is None:
|
||||||
|
raise NoTokenError("No Token")
|
||||||
|
if tokenexpirydate is not None and timezone.now()-timedelta(days=120)>tokenexpirydate:
|
||||||
|
self.rower.rp3tokenexpirydate = timezone.now()-timedelta(days=1)
|
||||||
|
self.rower.save()
|
||||||
|
raise NoTokenError("No Token")
|
||||||
|
return super(RP3Integration, self).open()
|
||||||
|
|
||||||
|
|
||||||
|
def token_refresh(self, *args, **kwargs) -> str:
|
||||||
|
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)
|
||||||
|
|
||||||
@@ -1,267 +0,0 @@
|
|||||||
from celery import Celery, app
|
|
||||||
from rowers.rower_rules import is_workout_user
|
|
||||||
import time
|
|
||||||
from django_rq import job
|
|
||||||
from rowers.tasks import handle_rp3_async_workout
|
|
||||||
from rowsandall_app.settings import (
|
|
||||||
C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET,
|
|
||||||
STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET,
|
|
||||||
RP3_CLIENT_ID, RP3_CLIENT_KEY, RP3_REDIRECT_URI, RP3_CLIENT_SECRET,
|
|
||||||
UPLOAD_SERVICE_URL, UPLOAD_SERVICE_SECRET
|
|
||||||
)
|
|
||||||
from rowers.utils import myqueue, NoTokenError
|
|
||||||
# All the functionality needed to connect to Runkeeper
|
|
||||||
from rowers.imports import *
|
|
||||||
|
|
||||||
# Python
|
|
||||||
import gzip
|
|
||||||
|
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
import base64
|
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
from rowers.utils import dologging
|
|
||||||
|
|
||||||
import django_rq
|
|
||||||
queue = django_rq.get_queue('default')
|
|
||||||
queuelow = django_rq.get_queue('low')
|
|
||||||
queuehigh = django_rq.get_queue('high')
|
|
||||||
|
|
||||||
|
|
||||||
oauth_data = {
|
|
||||||
'client_id': RP3_CLIENT_ID,
|
|
||||||
'client_secret': RP3_CLIENT_SECRET,
|
|
||||||
'redirect_uri': RP3_REDIRECT_URI,
|
|
||||||
'autorization_uri': "https://rp3rowing-app.com/oauth/authorize?",
|
|
||||||
'content_type': 'application/x-www-form-urlencoded',
|
|
||||||
# 'content_type': 'application/json',
|
|
||||||
'tokenname': 'rp3token',
|
|
||||||
'refreshtokenname': 'rp3refreshtoken',
|
|
||||||
'expirydatename': 'rp3tokenexpirydate',
|
|
||||||
'bearer_auth': False,
|
|
||||||
'base_url': "https://rp3rowing-app.com/oauth/token",
|
|
||||||
'scope': 'read,write',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
graphql_url = "https://rp3rowing-app.com/graphql"
|
|
||||||
|
|
||||||
|
|
||||||
# Checks if user has UnderArmour token, renews them if they are expired
|
|
||||||
def rp3_open(user):
|
|
||||||
tokenexpirydate = user.rower.rp3tokenexpirydate
|
|
||||||
if tokenexpirydate is None:
|
|
||||||
raise NoTokenError("No Token")
|
|
||||||
if tokenexpirydate is not None and timezone.now()-timedelta(days=120)>tokenexpirydate:
|
|
||||||
user.rower.rp3tokenexpirydate = timezone.now()-timedelta(days=1)
|
|
||||||
user.rower.save()
|
|
||||||
raise NoTokenError("No Token")
|
|
||||||
return imports_open(user, oauth_data)
|
|
||||||
|
|
||||||
# Refresh ST token using refresh token
|
|
||||||
|
|
||||||
|
|
||||||
def do_refresh_token(refreshtoken): # pragma: no cover
|
|
||||||
return imports_do_refresh_token(refreshtoken, oauth_data)
|
|
||||||
|
|
||||||
# Exchange access code for long-lived access token
|
|
||||||
|
|
||||||
|
|
||||||
def get_token(code): # pragma: no cover
|
|
||||||
post_data = {
|
|
||||||
"client_id": RP3_CLIENT_KEY,
|
|
||||||
"grant_type": "authorization_code",
|
|
||||||
"code": code,
|
|
||||||
"redirect_uri": RP3_REDIRECT_URI,
|
|
||||||
"client_secret": RP3_CLIENT_SECRET,
|
|
||||||
}
|
|
||||||
|
|
||||||
response = requests.post(
|
|
||||||
"https://rp3rowing-app.com/oauth/token",
|
|
||||||
data=post_data, verify=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
token_json = response.json()
|
|
||||||
thetoken = token_json['access_token']
|
|
||||||
expires_in = token_json['expires_in']
|
|
||||||
refresh_token = token_json['refresh_token']
|
|
||||||
except KeyError:
|
|
||||||
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 get_rp3_workout_list(user):
|
|
||||||
auth_token = rp3_open(user)
|
|
||||||
|
|
||||||
headers = {'Authorization': 'Bearer ' + auth_token}
|
|
||||||
|
|
||||||
get_workouts_list = """{
|
|
||||||
workouts{
|
|
||||||
id
|
|
||||||
executed_at
|
|
||||||
}
|
|
||||||
}"""
|
|
||||||
|
|
||||||
response = requests.post(
|
|
||||||
url=graphql_url,
|
|
||||||
headers=headers,
|
|
||||||
json={'query': get_workouts_list}
|
|
||||||
)
|
|
||||||
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def get_rp3_workouts(rower, do_async=True): # pragma: no cover
|
|
||||||
try:
|
|
||||||
auth_token = rp3_open(rower.user)
|
|
||||||
except NoTokenError:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
res = get_rp3_workout_list(rower.user)
|
|
||||||
|
|
||||||
if (res.status_code != 200):
|
|
||||||
return 0
|
|
||||||
|
|
||||||
s = '{d}'.format(d=res.json())
|
|
||||||
dologging('rp3_import.log', s)
|
|
||||||
workouts_list = pd.json_normalize(res.json()['data']['workouts'])
|
|
||||||
try:
|
|
||||||
rp3ids = workouts_list['id'].values
|
|
||||||
workouts_list.set_index('id', inplace=True)
|
|
||||||
except (KeyError, IndexError):
|
|
||||||
return 0
|
|
||||||
|
|
||||||
knownrp3ids = uniqify([
|
|
||||||
w.uploadedtorp3 for w in Workout.objects.filter(user=rower)
|
|
||||||
])
|
|
||||||
|
|
||||||
dologging('rp3_import.log',rp3ids)
|
|
||||||
|
|
||||||
newids = [rp3id for rp3id in rp3ids if rp3id not in knownrp3ids]
|
|
||||||
|
|
||||||
dologging('rp3_import.log',newids)
|
|
||||||
|
|
||||||
for id in newids:
|
|
||||||
startdatetime = workouts_list.loc[id, 'executed_at']
|
|
||||||
dologging('rp3_import.log', startdatetime)
|
|
||||||
|
|
||||||
_ = myqueue(
|
|
||||||
queuehigh,
|
|
||||||
handle_rp3_async_workout,
|
|
||||||
rower.user.id,
|
|
||||||
auth_token,
|
|
||||||
id,
|
|
||||||
startdatetime,
|
|
||||||
20,
|
|
||||||
)
|
|
||||||
|
|
||||||
return 1
|
|
||||||
|
|
||||||
|
|
||||||
def download_rp3_file(url, auth_token, filename): # pragma: no cover
|
|
||||||
headers = {'Authorization': 'Bearer ' + auth_token}
|
|
||||||
|
|
||||||
res = requests.get(url, headers=headers)
|
|
||||||
|
|
||||||
if res.status_code == 200:
|
|
||||||
with open(filename, 'wb') as f:
|
|
||||||
f.write(res.content)
|
|
||||||
|
|
||||||
return res.status_code
|
|
||||||
|
|
||||||
|
|
||||||
def get_rp3_workout_token(workout_id, auth_token, waittime=3, max_attempts=20): # pragma: no cover
|
|
||||||
headers = {'Authorization': 'Bearer ' + auth_token}
|
|
||||||
|
|
||||||
get_download_link = """{
|
|
||||||
download(workout_id: """ + str(workout_id) + """, type:csv){
|
|
||||||
id
|
|
||||||
status
|
|
||||||
link
|
|
||||||
}
|
|
||||||
}"""
|
|
||||||
|
|
||||||
have_link = False
|
|
||||||
download_url = ''
|
|
||||||
counter = 0
|
|
||||||
while not have_link:
|
|
||||||
response = requests.post(
|
|
||||||
url=graphql_url,
|
|
||||||
headers=headers,
|
|
||||||
json={'query': get_download_link}
|
|
||||||
)
|
|
||||||
|
|
||||||
if response.status_code != 200:
|
|
||||||
have_link = True
|
|
||||||
|
|
||||||
workout_download_details = pd.json_normalize(
|
|
||||||
response.json()['data']['download'])
|
|
||||||
|
|
||||||
if workout_download_details.iat[0, 1] == 'ready':
|
|
||||||
download_url = workout_download_details.iat[0, 2]
|
|
||||||
have_link = True
|
|
||||||
|
|
||||||
counter += 1
|
|
||||||
|
|
||||||
if counter > max_attempts:
|
|
||||||
have_link = True
|
|
||||||
|
|
||||||
time.sleep(waittime)
|
|
||||||
|
|
||||||
return download_url
|
|
||||||
|
|
||||||
|
|
||||||
def get_rp3_workout_link(user, workout_id, waittime=3, max_attempts=20): # pragma: no cover
|
|
||||||
auth_token = rp3_open(user)
|
|
||||||
|
|
||||||
return get_rp3_workout_token(workout_id, auth_token, waittime=waittime, max_attempts=max_attempts)
|
|
||||||
|
|
||||||
|
|
||||||
def get_rp3_workout(user, workout_id, startdatetime=None): # pragma: no cover
|
|
||||||
url = get_rp3_workout_link(user, workout_id)
|
|
||||||
filename = 'media/RP3Import_'+str(workout_id)+'.csv'
|
|
||||||
|
|
||||||
auth_token = rp3_open(user)
|
|
||||||
|
|
||||||
if not startdatetime:
|
|
||||||
startdatetime = str(timezone.now())
|
|
||||||
|
|
||||||
status_code = download_rp3_file(url, auth_token, filename)
|
|
||||||
|
|
||||||
if status_code != 200:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
userid = user.id
|
|
||||||
|
|
||||||
uploadoptions = {
|
|
||||||
'secret': UPLOAD_SERVICE_SECRET,
|
|
||||||
'user': userid,
|
|
||||||
'file': filename,
|
|
||||||
'workouttype': 'dynamic',
|
|
||||||
'boattype': '1x',
|
|
||||||
'rp3id': workout_id,
|
|
||||||
'startdatetime': startdatetime,
|
|
||||||
'timezone': str(user.rower.defaulttimezone)
|
|
||||||
}
|
|
||||||
|
|
||||||
session = requests.session()
|
|
||||||
newHeaders = {'Content-type': 'application/json', 'Accept': 'text/plain'}
|
|
||||||
session.headers.update(newHeaders)
|
|
||||||
|
|
||||||
response = session.post(UPLOAD_SERVICE_URL, json=uploadoptions)
|
|
||||||
|
|
||||||
if response.status_code != 200:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
return response.json()['id']
|
|
||||||
@@ -3158,6 +3158,9 @@ def handle_update_wps(rid, types, ids, mode, debug=False, **kwargs):
|
|||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
def handle_rp3_async_workout(userid, rp3token, rp3id, startdatetime, max_attempts, debug=False, **kwargs):
|
def handle_rp3_async_workout(userid, rp3token, rp3id, startdatetime, max_attempts, debug=False, **kwargs):
|
||||||
|
|
||||||
|
timezone = kwargs.get('timezone', 'UTC')
|
||||||
|
|
||||||
headers = {'Authorization': 'Bearer ' + rp3token}
|
headers = {'Authorization': 'Bearer ' + rp3token}
|
||||||
|
|
||||||
get_download_link = """{
|
get_download_link = """{
|
||||||
@@ -3239,8 +3242,11 @@ def handle_rp3_async_workout(userid, rp3token, rp3id, startdatetime, max_attempt
|
|||||||
'boattype': '1x',
|
'boattype': '1x',
|
||||||
'rp3id': int(rp3id),
|
'rp3id': int(rp3id),
|
||||||
'startdatetime': startdatetime,
|
'startdatetime': startdatetime,
|
||||||
|
'timezone': timezone,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print(uploadoptions)
|
||||||
|
|
||||||
session = requests.session()
|
session = requests.session()
|
||||||
newHeaders = {'Content-type': 'application/json', 'Accept': 'text/plain'}
|
newHeaders = {'Content-type': 'application/json', 'Accept': 'text/plain'}
|
||||||
session.headers.update(newHeaders)
|
session.headers.update(newHeaders)
|
||||||
|
|||||||
@@ -971,8 +971,8 @@ class RP3Objects(DjangoTestCase):
|
|||||||
csvfilename=filename
|
csvfilename=filename
|
||||||
)
|
)
|
||||||
|
|
||||||
@patch('rowers.rp3stuff.requests.get', side_effect=mocked_requests)
|
@patch('rowers.integrations.rp3.requests.get', side_effect=mocked_requests)
|
||||||
@patch('rowers.rp3stuff.requests.post', side_effect=mocked_requests)
|
@patch('rowers.integrations.rp3.requests.post', side_effect=mocked_requests)
|
||||||
@patch('rowers.dataprep.getsmallrowdata_db', side_effect=mocked_getsmallrowdata_db)
|
@patch('rowers.dataprep.getsmallrowdata_db', side_effect=mocked_getsmallrowdata_db)
|
||||||
def test_rp3_import(self, mock_get, mockpost,
|
def test_rp3_import(self, mock_get, mockpost,
|
||||||
mocked_getsmallrowdata_db):
|
mocked_getsmallrowdata_db):
|
||||||
@@ -1002,7 +1002,7 @@ class RP3Objects(DjangoTestCase):
|
|||||||
res = tasks.handle_rp3_async_workout(userid,rp3token,rp3id,startdatetime,max_attempts)
|
res = tasks.handle_rp3_async_workout(userid,rp3token,rp3id,startdatetime,max_attempts)
|
||||||
self.assertEqual(res,1)
|
self.assertEqual(res,1)
|
||||||
|
|
||||||
@patch('rowers.rp3stuff.requests.post', side_effect=mocked_requests)
|
@patch('rowers.integrations.rp3.requests.post', side_effect=mocked_requests)
|
||||||
def notest_rp3_callback(self, mock_post):
|
def notest_rp3_callback(self, mock_post):
|
||||||
response = self.c.get('/rp3_callback?code=absdef23&scope=read',follow=True)
|
response = self.c.get('/rp3_callback?code=absdef23&scope=read',follow=True)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|||||||
BIN
rowers/tests/testdata/testdata.tcx.gz
vendored
BIN
rowers/tests/testdata/testdata.tcx.gz
vendored
Binary file not shown.
@@ -235,9 +235,9 @@ def do_sync(w, options, quick=False):
|
|||||||
|
|
||||||
do_st_export = w.user.sporttracks_auto_export
|
do_st_export = w.user.sporttracks_auto_export
|
||||||
|
|
||||||
if options['sporttracksid'] != 0 and options['sporttracksid'] != '':
|
sporttracksid = options.get('sporttracksid','')
|
||||||
w.uploadedtosporttracks = options['sporttracksid']
|
if sporttracksid != 0 and sporttracksid != '':
|
||||||
|
w.uploadedtosporttracks = sporttracksid
|
||||||
w.save()
|
w.save()
|
||||||
do_st_export = False
|
do_st_export = False
|
||||||
try: # pragma: no cover
|
try: # pragma: no cover
|
||||||
|
|||||||
@@ -725,11 +725,9 @@ def rower_process_rp3callback(request): # pragma: no cover
|
|||||||
url = reverse('rower_exportsettings_view')
|
url = reverse('rower_exportsettings_view')
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
res = rp3stuff.get_token(code)
|
rp3_integration = RP3Integration(request.user)
|
||||||
|
access_token, expires_in, refresh_token = rp3_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)
|
||||||
@@ -803,55 +801,38 @@ def rower_process_testcallback(request): # pragma: no cover
|
|||||||
@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True)
|
@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True)
|
||||||
def workout_rp3import_view(request, userid=0):
|
def workout_rp3import_view(request, userid=0):
|
||||||
r = getrequestrower(request, userid=userid)
|
r = getrequestrower(request, userid=userid)
|
||||||
|
rp3_integration = RP3Integration(request.user)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_ = rp3stuff.rp3_open(request.user)
|
_ = rp3_integration.open()
|
||||||
except NoTokenError: # pragma: no cover
|
except NoTokenError: # pragma: no cover
|
||||||
url = reverse('rower_rp3_authorize')
|
url = reverse('rower_rp3_authorize')
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
res = rp3stuff.get_rp3_workout_list(request.user)
|
workouts = rp3_integration.get_workout_list()
|
||||||
|
datedict = {}
|
||||||
|
for workout in workouts:
|
||||||
|
datedict[workout['id']] = workout['starttime']
|
||||||
|
|
||||||
if (res.status_code != 200): # pragma: no cover
|
|
||||||
if (res.status_code == 401):
|
|
||||||
r = getrower(request.user)
|
|
||||||
if (r.stravatoken == '') or (r.stravatoken is None):
|
|
||||||
s = "Token doesn't exist. Need to authorize"
|
|
||||||
return HttpResponseRedirect("/rowers/me/stravaauthorize/")
|
|
||||||
message = "Something went wrong in workout_rp3import_view"
|
|
||||||
messages.error(request, message)
|
|
||||||
url = reverse('workouts_view')
|
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
|
|
||||||
workouts_list = pd.json_normalize(res.json()['data']['workouts'])
|
if request.method == "POST":
|
||||||
|
try: # pragma: no cover
|
||||||
|
tdict = dict(request.POST.lists())
|
||||||
|
ids = tdict['workoutid']
|
||||||
|
rp3ids = [int(id) for id in ids]
|
||||||
|
|
||||||
knownrp3ids = uniqify([
|
for rp3id in rp3ids:
|
||||||
w.uploadedtorp3 for w in Workout.objects.filter(user=r)
|
rp3_integration.get_workout(rp3id,startdatetime=datedict[rp3id])
|
||||||
])
|
# done, redirect to workouts list
|
||||||
|
messages.info(
|
||||||
workouts = []
|
request,
|
||||||
|
'Your RP3 workouts will be imported in the background.'
|
||||||
for key, data in workouts_list.iterrows():
|
' It may take a few minutes before they appear.')
|
||||||
try:
|
url = reverse('workouts_view')
|
||||||
i = data['id']
|
return HttpResponseRedirect(url)
|
||||||
except KeyError: # pragma: no cover
|
except KeyError: # pragma: no cover
|
||||||
i = 0
|
pass
|
||||||
if i in knownrp3ids: # pragma: no cover
|
|
||||||
nnn = ''
|
|
||||||
else:
|
|
||||||
nnn = 'NEW'
|
|
||||||
|
|
||||||
try:
|
|
||||||
s = data['executed_at']
|
|
||||||
except KeyError: # pragma: no cover
|
|
||||||
s = ''
|
|
||||||
|
|
||||||
keys = ['id', 'starttime', 'new']
|
|
||||||
values = [i, s, nnn]
|
|
||||||
|
|
||||||
res = dict(zip(keys, values))
|
|
||||||
|
|
||||||
workouts.append(res)
|
|
||||||
|
|
||||||
breadcrumbs = [
|
breadcrumbs = [
|
||||||
{
|
{
|
||||||
@@ -864,13 +845,18 @@ def workout_rp3import_view(request, userid=0):
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
return render(request, 'rp3_list_import.html',
|
checknew = request.GET.get('selectallnew', False)
|
||||||
|
|
||||||
|
|
||||||
|
return render(request, 'list_import.html',
|
||||||
{
|
{
|
||||||
'workouts': workouts,
|
'workouts': workouts,
|
||||||
'rower': r,
|
'rower': r,
|
||||||
'active': 'nav-workouts',
|
'active': 'nav-workouts',
|
||||||
'breadcrumbs': breadcrumbs,
|
'breadcrumbs': breadcrumbs,
|
||||||
'teams': get_my_teams(request.user)
|
'teams': get_my_teams(request.user),
|
||||||
|
'integration': 'RP3',
|
||||||
|
'checknew': checknew,
|
||||||
})
|
})
|
||||||
|
|
||||||
# The page where you select which Strava workout to import
|
# The page where you select which Strava workout to import
|
||||||
@@ -1461,7 +1447,8 @@ def workout_getrp3workout_all(request): # pragma: no cover
|
|||||||
|
|
||||||
r = getrequestrower(request)
|
r = getrequestrower(request)
|
||||||
|
|
||||||
result = rp3stuff.get_rp3_workouts(r, do_async=True)
|
rp3_integration = RP3Integration(request.user)
|
||||||
|
result = rp3_integration.get_workouts()
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
messages.info(
|
messages.info(
|
||||||
@@ -1582,21 +1569,12 @@ def workout_getrp3importview(request, externalid):
|
|||||||
r = getrequestrower(request)
|
r = getrequestrower(request)
|
||||||
if r.user != request.user: # pragma: no cover
|
if r.user != request.user: # pragma: no cover
|
||||||
messages.error(
|
messages.error(
|
||||||
request, 'You can only access your own workouts on the NK Logbook, not those of your athletes')
|
request, 'You can only access your own workouts on the RP3 Logbook, not those of your athletes')
|
||||||
url = reverse('workout_rp3import_view', kwargs={
|
url = reverse('workout_rp3import_view', kwargs={
|
||||||
'userid': request.user.id})
|
'userid': request.user.id})
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
token = rp3stuff.rp3_open(r.user)
|
rp3_integration = RP3Integration(request.user)
|
||||||
startdatetime = request.GET.get('startdatetime')
|
result = rp3_integration.get_workout(externalid)
|
||||||
|
|
||||||
_ = myqueue(queuehigh,
|
|
||||||
handle_rp3_async_workout,
|
|
||||||
r.user.id,
|
|
||||||
token,
|
|
||||||
externalid,
|
|
||||||
startdatetime,
|
|
||||||
20,
|
|
||||||
)
|
|
||||||
|
|
||||||
messages.info(request, 'The workout will be imported in the background')
|
messages.info(request, 'The workout will be imported in the background')
|
||||||
|
|
||||||
@@ -1684,8 +1662,11 @@ def workout_getimportview(request, externalid, source='c2', do_async=True):
|
|||||||
@login_required()
|
@login_required()
|
||||||
def workout_getsporttracksworkout_all(request):
|
def workout_getsporttracksworkout_all(request):
|
||||||
st_integration = SportTracksIntegration(request.user)
|
st_integration = SportTracksIntegration(request.user)
|
||||||
_ = st_integration.get_workouts()
|
try:
|
||||||
messages.info(request,"Your SportTracks workouts will be imported in the background")
|
_ = st_integration.get_workouts()
|
||||||
|
messages.info(request,"Your SportTracks workouts will be imported in the background")
|
||||||
|
except NoTokenError:
|
||||||
|
messages.error(request,"You have to connect to SportTracks first")
|
||||||
|
|
||||||
url = reverse('workouts_view')
|
url = reverse('workouts_view')
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ import sys
|
|||||||
import datetime
|
import datetime
|
||||||
import iso8601
|
import iso8601
|
||||||
import rowers.rojabo_stuff as rojabo_stuff
|
import rowers.rojabo_stuff as rojabo_stuff
|
||||||
from rowers.rp3stuff import rp3_open
|
|
||||||
from rowers.tpstuff import tp_open
|
from rowers.tpstuff import tp_open
|
||||||
from iso8601 import ParseError
|
from iso8601 import ParseError
|
||||||
|
|
||||||
@@ -206,7 +206,7 @@ import rowers.polarstuff as polarstuff
|
|||||||
from rowers.integrations import *
|
from rowers.integrations import *
|
||||||
|
|
||||||
import rowers.tpstuff as tpstuff
|
import rowers.tpstuff as tpstuff
|
||||||
import rowers.rp3stuff as rp3stuff
|
|
||||||
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
|
||||||
from rowsandall_app.settings import (
|
from rowsandall_app.settings import (
|
||||||
|
|||||||
Reference in New Issue
Block a user