Private
Public Access
1
0
Files
rowsandall/rowers/integrations/rp3.py
2025-10-24 12:13:30 +02:00

260 lines
7.4 KiB
Python

from .integrations import SyncIntegration, NoTokenError, create_or_update_syncrecord, get_known_ids
from rowers.models import User, Rower, Workout, TombStone
from rowers.upload_tasks import handle_rp3_async_workout
from rowsandall_app.settings import (
RP3_CLIENT_ID, RP3_CLIENT_KEY, RP3_REDIRECT_URI, RP3_CLIENT_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"
import urllib
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,
'authorization_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 get_name(self):
return "RP3 Logbook"
def get_shortname(self):
return "rp3"
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',inplace=True)
except (KeyError, IndexError):
return 0
knownrp3ids = uniqify([
w.uploadedtorp3 for w in Workout.objects.filter(user=r)
])
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_iso8601']
dologging('rp3_import.log', startdatetime)
_ = myqueue(
queuelow,
handle_rp3_async_workout,
self.user.id,
auth_token,
id,
startdatetime,
3,
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())
record = create_or_update_syncrecord(self.rower, None, rp3id=id)
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 = get_known_ids(r, 'rp3id')
workouts = []
for key, data in workouts_list.iterrows():
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
params = {"client_id": RP3_CLIENT_KEY,
"response_type": "code",
"redirect_uri": RP3_REDIRECT_URI,
}
url = "https://rp3rowing-app.com/oauth/authorize/?" + \
urllib.parse.urlencode(params)
return url
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)