from .integrations import SyncIntegration, NoTokenError, create_or_update_syncrecord, get_known_ids 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" 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)