def get_strava_token(): """Return a valid strava token. This will initiate the OAUTH dance if needed (register or refresh), or just get the token from disk.""" if get_tokens_from_disk() == {}: client = Client() url = client.authorization_url(client_id=CLIENT_ID, redirect_uri='http://127.0.0.1:5000/authorization') print("Please open this in your browser:" + url) server_address = ('', 5000) httpd = PainfulHTTPServer(server_address, StravaOauthRequestHandler) httpd.serve_until_stopped() # Now the file really shouldn't be empty anymore. tokens = get_tokens_from_disk() # Is our token expired? now = datetime.datetime.now() if now.timestamp() >= (tokens["expires_at"] - 10000): # 10 seconds leeway client = Client() refresh_response = client.refresh_access_token( client_id=CLIENT_ID, client_secret=CLIENT_SECRET, refresh_token=tokens["refresh_token"]) with open(".hurtlocker", "w") as thefile: thefile.write(json.dumps(refresh_response)) tokens = get_tokens_from_disk() return tokens["access_token"]
def pull_data(self, members, challenges): client = Client() for m in members: logger.debug("processing member: {}".format(m)) # Check if the access token needs renewing. Should last 6hrs, so only needs doing once regardless of how many calls we make. if time.time() > m.access_token_expiry: logger.info("Renewing token") refresh_response = client.refresh_access_token( client_id=config.strava_client_id, client_secret=config.strava_client_secret, refresh_token=m.refresh_token) m.access_token = refresh_response['access_token'] m.refresh_token = refresh_response['refresh_token'] m.access_token_expiry = refresh_response['expires_at'] m.save() client.access_token = m.access_token client.refresh_token = m.refresh_token client.token_expires_at = m.access_token_expiry for c in challenges: logger.debug("Processing challenge: {}".format(c)) efforts = client.get_segment_efforts( c.segment_id, start_date_local=c.date_from, end_date_local=c.date_to) for e in efforts: logger.debug("Processing effort: {}".format(e)) Attempt.add(e, m, c)
def load_strava_tracks(self, strava_config: str) -> typing.List[Track]: tracks = [] tracks_names = [] if self.cache_dir: self.strava_cache_file = os.path.join(self.cache_dir, strava_config) if os.path.isfile(self.strava_cache_file): with open(self.strava_cache_file) as f: strava_cache_data = json.load(f) tracks = [self._strava_cache_to_track(i) for i in strava_cache_data] tracks_names = [track.file_names[0] for track in tracks] with open(strava_config) as f: strava_data = json.load(f) filter_type = strava_data.pop("activity_type", None) client = Client() response = client.refresh_access_token(**strava_data) client.access_token = response["access_token"] filter_dict = {"before": datetime.datetime.utcnow()} if tracks: max_time = max(track.start_time() for track in tracks) filter_dict = {"after": max_time - datetime.timedelta(days=2)} for activity in client.get_activities(**filter_dict): # tricky to pass the timezone if str(activity.id) in tracks_names: continue if filter_type and activity.type not in ( [filter_type] if isinstance(filter_type, str) else filter_type ): # pylint: disable=superfluous-parens continue t = Track() t.load_strava(activity) tracks.append(t) self._store_strava_tracks_to_cache(tracks) return self._filter_and_merge_tracks(tracks)
def load_strava_tracks(self, strava_config: str) -> typing.List[Track]: tracks = [] tracks_names = [] if self.cache_dir: self.strava_cache_file = os.path.join(self.cache_dir, strava_config) if os.path.isfile(self.strava_cache_file): with open(self.strava_cache_file) as f: strava_cache_data = json.load(f) tracks = [ self._strava_cache_to_track(i) for i in strava_cache_data ] tracks_names = [track.file_names[0] for track in tracks] with open(strava_config) as f: strava_data = json.load(f) client = Client() response = client.refresh_access_token(**strava_data) client.access_token = response["access_token"] fliter_dict = {"before": datetime.datetime.utcnow()} if tracks: max_time = max(track.start_time for track in tracks) if max_time: fliter_dict = {"after": max_time - datetime.timedelta(days=2)} for activate in client.get_activities(**fliter_dict): # tricky to pass the timezone if str(activate.id) in tracks_names: continue t = Track() t.load_strava(activate) tracks.append(t) self._store_strava_tracks_to_cache(tracks) return self._filter_and_merge_tracks(tracks)
async def refresh_access_token(athlete): client = Client() response = client.refresh_access_token(client_id=STRAVA_CLIENT_ID, client_secret=STRAVA_CLIENT_SECRET, refresh_token=athlete.refresh_token) await athlete.update(access_token=response['access_token'], refresh_token=response['refresh_token'], token_expiration_datetime=datetime.utcfromtimestamp( response['expires_at']).isoformat()) return athlete
def refresh_access_token(): """ Client posts a valid access token and refresh token and receives a dict containing 'access_token', 'refresh_token', and 'expires_at'. The function then saves this to Secret Manager """ client = Client(sm.access_token) auth_dict = client.refresh_access_token(client_id=sm.client_id, client_secret=sm.client_secret, refresh_token=sm.refresh_token) logger.debug('Auth Dict: %s', auth_dict) # Save the dict back to Secret Manager sm.set_auth_dict(auth_dict)
def main(action, filename): if action != "DOWNLOAD": return 0 try: with open(STRAVA_CREDENTIALS_FILE, "rb") as f: token_data = pickle.load(f) access_token = token_data["access_token"] if token_data["expires_at"] <= time.time(): client = Client() token_data = client.refresh_access_token( client_id=CLIENT_ID, client_secret=CLIENT_SECRET, refresh_token=token_data["refresh_token"], ) access_token = token_data["access_token"] with open(STRAVA_CREDENTIALS_FILE, "wb") as fw: pickle.dump(token_data, fw, 0) except (FileNotFoundError, KeyError): print("No Strava credentials provided.") print("You first need to run the script to fetch the credentials") print("./40-upload_to_strava.py") return -1 try: client = Client(access_token=access_token) print("Uploading {}: ".format(os.path.basename(filename)), end="") with open(filename, "rb") as f: upload = client.upload_activity( activity_file=f, data_type="fit", private=STRAVA_UPLOAD_PRIVATE, ) except (ActivityUploadFailed, FileNotFoundError) as err: print("FAILED") print("Reason:", err) return -1 print("SUCCESS") return 0
def refresh_current_token(): print("Refreshing current token") refresh_token = get_string_from_file('refresh_token') client_id = get_string_from_file('client_id') client_secret = get_string_from_file('client_secret') if not refresh_token: print("No refresh token present.") request_user_login() else: strava = Client() refresh_response = strava.refresh_access_token(client_id=client_id, client_secret=client_secret, refresh_token=refresh_token) write_string_to_file("access_token", refresh_response['access_token']) write_string_to_file("refresh_token", refresh_response['refresh_token']) print("Token expires at " + str(refresh_response['expires_at'])) check_if_access_token_valid()
def main(action, filename): if action != "DOWNLOAD": return 0 try: with open(STRAVA_CREDENTIALS_FILE, 'rb') as f: token_data = pickle.load(f) access_token = token_data['access_token'] if token_data['expires_at'] <= time.time(): client = Client() token_data = client.refresh_access_token( client_id=CLIENT_ID, client_secret=CLIENT_SECRET, refresh_token=token_data['refresh_token']) access_token = token_data['access_token'] with open(STRAVA_CREDENTIALS_FILE, 'wb') as fw: pickle.dump(token_data, fw, 0) except (FileNotFoundError, KeyError): print('No Strava credentials provided.') print('You first need to run the script to fetch the credentials') print('./40-upload_to_strava.py') return -1 try: client = Client(access_token=access_token) print('Uploading {}: '.format(os.path.basename(filename)), end='') with open(filename, 'rb') as f: upload = client.upload_activity( activity_file=f, data_type='fit', private=STRAVA_UPLOAD_PRIVATE, ) except (ActivityUploadFailed, FileNotFoundError) as err: print('FAILED') print('Reason:', err) return -1 print('SUCCESS') return 0
def strava_access_token(request): user_settings = request.user.settings if user_settings.strava_access_token: token = json.loads(user_settings.strava_access_token) if time.time() < token['expires_at']: return Response({'strava_access_token': token['access_token'], 'expires_at': token['expires_at']}) client = StravaClient() try: access_token = client.refresh_access_token( client_id=settings.MY_STRAVA_CLIENT_ID, client_secret=settings.MY_STRAVA_CLIENT_SECRET, refresh_token=token['refresh_token'] ) except Exception: user_settings.strava_access_token = '' user_settings.save() return Response({}) user_settings.strava_access_token = json.dumps(access_token) user_settings.save() return Response({'strava_access_token': access_token['access_token'], 'expires_at': access_token['expires_at']}) return Response({})
def update_token(self): print("Update token") client = Client(access_token=self.bearer.get("access_token")) if time.time() > self.bearer["expires_at"]: print(self.bearer["refresh_token"]) refresh_response = client.refresh_access_token( client_id=self.client_id, client_secret=self.client_secret, refresh_token=self.bearer["refresh_token"]) self.bearer["refresh_token"] = refresh_response["refresh_token"] self.bearer["access_token"] = refresh_response["access_token"] self.bearer["expires_at"] = refresh_response["expires_at"] self.dbh.modify_user(user_hash=self.user_hash, key="strava_bearer", value=self.bearer, mode="update") else: dt = self.bearer["expires_at"] - time.time() print(f"Token valid for another {dt} seconds.")
def get_segments(lowerlat, lowerlong, upperlat, upperlong): # Accepts lower and upper bounds for a given coordinate area # Returns an object with route data, as parsed in application.py # Sets our key # As per: https://github.com/hozn/stravalib/blob/master/docs/usage/auth.rst#id5 client = Client() token_response = client.refresh_access_token( client_id=30378, client_secret="67b60f8a8c450a837a2337fe9eb57515915d8fba", refresh_token="ed64949d3614e9a0574ecba44d5514de593c2c0b") key = token_response['access_token'] # Sets the client and registers the token client = Client() client = Client(access_token=key) # Calls user data for the given range for running routes data = client.explore_segments([lowerlat, lowerlong, upperlat, upperlong], activity_type='running') # Returns the object return data
try: with open(CONFIG_FILE) as inf: new_dict = json.load(inf) except FileNotFoundError: new_dict = DEFAULT_DICT.copy() save_config(new_dict) return new_dict settings = load_config() client = Client() if time.time() > int(settings['expires_at']): print("Renewing tokens") refresh_response = client.refresh_access_token( client_id=settings['client_id'], client_secret=settings['client_secret'], refresh_token=settings['refresh_token']) settings['refresh_token'] = refresh_response['refresh_token'] settings['access_token'] = refresh_response['access_token'] settings['expires_at'] = refresh_response['expires_at'] save_config(settings) else: client.access_token = settings['access_token'] client.refresh_token = settings['refresh_token'] client.expires_at = settings['expires_at'] activities = client.get_activities(limit=10) for activity in activities: if activity.type == 'Run': ms = activity.average_speed
def refresh_access_token(refresh_token): client = Client() return client.refresh_access_token( client_id=settings.STRAVA_CLIENT_ID, client_secret=settings.STRAVA_CLIENT_SECRET, refresh_token=refresh_token)
Challenge.add(segment, year, month) if __name__ == '__main__': m = Member.objects.filter(refresh_token__gte="").limit(1) print(list(m)) m = list(m)[0] client.access_token = m.access_token client.refresh_token = m.refresh_token client.token_expires_at = m.access_token_expiry # Check if the access token needs renewing. Should last 6hrs, so only needs doing once regardless of how many calls we make. if time.time() > m.access_token_expiry: logger.info("Renewing token") refresh_response = client.refresh_access_token( client_id=config.strava_client_id, client_secret=config.strava_client_secret, refresh_token=m.refresh_token) m.access_token = refresh_response['access_token'] m.refresh_token = refresh_response['refresh_token'] m.access_token_expiry = refresh_response['expires_at'] m.save() add_challenge(4896037, 2019, 2) add_challenge(13028666, 2019, 3) add_challenge(5993929, 2019, 4) add_challenge(4837370, 2019, 8)
class Tracker: def __init__(self): # Strava client to hold information for tracker. self.client = Client() # Time token expires at. self.token_expires_at_ = None # Client information. self.client_id = None self.client_secret = None # Time in seconds between refreshes. self.sleep_time_ = 300 # Number of target activities per week. self.target_ = 4 # Private display object. self.display_ = Display() # Activity tracking variables. self.start_date = datetime.datetime.utcnow().date() self.next_week = self.start_date + datetime.timedelta(weeks=1) self.week_streak = 0 self.num_activities = 0 # Filename of save file. self.save_file_ = 'streak.yaml' def set_expiration(self, token_expires_at): self.token_expires_at_ = token_expires_at def set_client_info(self, client_id, client_secret): self.client_id = client_id self.client_secret = client_secret def save_status(self): save_obj = {'start_date' : self.start_date, 'next_week' : self.next_week, 'week_streak' : self.week_streak, 'num_activities' : self.num_activities} with open(self.save_file_, 'wb') as save_file: pickle.dump(save_obj, save_file) def load_status(self): save_obj = None try: with open(self.save_file_, 'rb') as save_file: save_obj = pickle.load(save_file) self.start_date = save_obj['start_date'] self.next_week = save_obj['next_week'] self.week_streak = save_obj['week_streak'] self.num_activities = save_obj['num_activities'] except (OSError, IOError, EOFError) as e: print('Nothing in this save file, going to save defaults.') self.save_status() def update(self): self.save_status() self.display_.show(self.week_streak, self.target_ - self.num_activities, self.target_) def run(self): # Save start date/time # Ask Strava for activities since 1 week ago # If len(activities) >= goal increment streak # Check that token won't expire in 12 hours # If token needs refreshing, refresh it # Sleep for sleep_time self.load_status() self.update() while(True): # Refresh token if necessary. if time.time() > self.token_expires_at_: refresh_response = self.client.refresh_access_token(client_id=self.client_id, client_secret=self.client_secret, refresh_token=self.client.refresh_token) self.token_expires_at_ = refresh_response['expires_at'] print('Refreshing token, new one expires at {}' .format(str(refresh_response['expires_at']))) new_activities = len(list(self.client.get_activities(after = self.start_date.isoformat()))) # Handle null return from Strava servers. if not new_activities: new_activities = 0 print(new_activities) if new_activities != self.num_activities: print("New activities detected!") self.num_activities = new_activities self.update() for activity in self.client.get_activities(after = self.start_date.isoformat()): print("{0.name} {0.moving_time}".format(activity)) # Check if we've hit the target for this week. if self.num_activities >= self.target_: self.week_streak += 1 self.update() cur_date = datetime.datetime.utcnow().date() # Check if it's next week. if cur_date == self.next_week: # Check if we haven't hit our target and reset. if self.num_activities < self.target_: self.week_streak = 0 # Advance the date to a week from now. self.start_date = cur_date self.next_week = cur_date + datetime.timedelta(weeks=1) self.update() time.sleep(self.sleep_time_)
class Strava: def __init__(self, client_id, client_secret): self.client = Client() self.client_id = client_id self.client_secret = client_secret def _check_and_refresh_token(self, member=None): if not member: member = Member.objects.filter( refresh_token__gte="").order_by('refresh_token').limit(1) member = list(member)[0] logger.info("Using access token for: {}".format(member)) self.client.access_token = member.access_token self.client.refresh_token = member.refresh_token self.client.token_expires_at = member.access_token_expiry # Check if the access token needs renewing. Should last 6hrs, so only needs doing once regardless of how many calls we make. if time.time() > member.access_token_expiry: logger.info("Renewing token") refresh_response = self.client.refresh_access_token( client_id=self.client_id, client_secret=self.client_secret, refresh_token=member.refresh_token) member.access_token = refresh_response['access_token'] member.refresh_token = refresh_response['refresh_token'] member.access_token_expiry = refresh_response['expires_at'] member.save() def register_new_member(self, code): token_response = self.client.exchange_code_for_token( client_id=self.client_id, client_secret=self.client_secret, code=code) access_token = token_response['access_token'] refresh_token = token_response['refresh_token'] expires_at = token_response['expires_at'] self.client.access_token = access_token self.client.refresh_token = refresh_token self.client.token_expires_at = expires_at athlete = self.client.get_athlete() logger.debug("Retrieved the authenticated athlete: {}".format(athlete)) return athlete, access_token, refresh_token, expires_at def get_member_avatar(self, athlete_id, medium=False): logger.debug("Getting an avatar for member {} that is sized {}".format( athlete_id, medium)) member = Member.objects.get(id=str(athlete_id)) self._check_and_refresh_token(member) athlete = self.client.get_athlete() if medium: return athlete.profile_medium else: return athlete.profile def get_redirect_url(self, client_id, redirect_url, state): return self.client.authorization_url(client_id, redirect_url, state=state)
def main(event, context): """Triggered from a message on a Cloud Pub/Sub topic. Args: event (dict): Event payload. context (google.cloud.functions.Context): Metadata for the event. """ STRAVA_CLIENT_ID = os.environ.get('strava_client_id') STRAVA_CLIENT_SECRET = os.environ.get('strava_client_secret') pubsub_message = base64.b64decode(event['data']).decode('utf-8') pubsub_dict = json.loads(pubsub_message.replace("'", '"')) print(pubsub_dict) datastore_client = datastore.Client(project='strava-int') bq_client = bigquery.Client(project='strava-int') strava_client = Client() # event notification from strava aspect_type = pubsub_dict['aspect_type'] object_id = pubsub_dict['object_id'] owner_id = pubsub_dict['owner_id'] object_type = pubsub_dict['object_type'] event_time = pubsub_dict['event_time'] event_datetime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(event_time)) if object_type == 'activity': now = time.time() if aspect_type == 'delete': athlete_activity_dict = [{ 'activity_hub_seq': hashlib.md5(str(object_id).encode()).hexdigest(), 'sat_load_date': event_datetime, 'delete_ind': True }] # athlete_activity_obj = AthleteActivity(athlete_activity_dict) # sat_athlete_activity = athlete_activity_obj.satellite() sat_table_ref = bq_client.dataset('strava_datavault').table( 'activity_sat') bq_client.load_table_from_json(athlete_activity_dict, sat_table_ref) if aspect_type != 'delete': # stored athlete from datastore athlete_key = datastore_client.key('Athlete', owner_id) athlete = datastore_client.get(athlete_key) if now > athlete['expires_at']: access_token = strava_client.refresh_access_token( client_id=STRAVA_CLIENT_ID, client_secret=STRAVA_CLIENT_SECRET, refresh_token=athlete['refresh_token']) athlete.update(access_token) datastore_client.put(athlete) # create new client for authenticated athlete athlete_client = Client(access_token=athlete['access_token']) activity = athlete_client.get_activity(object_id) activity_dict = activity.to_dict() supplement = { 'athlete_id': owner_id, 'activity_id': object_id, 'load_date': event_datetime } activity_dict.update(supplement) # GCS Storage upload_blob(activity_dict, owner_id, object_id, event_time, aspect_type) converted_units = { 'distance_mi': unithelper.mile(getattr(activity, 'distance', None)).get_num(), 'average_speed_mi': unithelper.mph(getattr(activity, 'average_speed', None)).get_num(), 'max_speed_mi': unithelper.mph(getattr(activity, 'max_speed', None)).get_num(), 'elapsed_time_s': int( unithelper.timedelta_to_seconds( getattr(activity, 'elapsed_time', None))), 'moving_time_s': int( unithelper.timedelta_to_seconds( getattr(activity, 'moving_time', None))) } activity_dict.update(converted_units) athlete_activity_obj = AthleteActivity(activity_dict) sat_athlete_activity = athlete_activity_obj.satellite() print(sat_athlete_activity) # BQ insert sat_table_ref = bq_client.dataset('strava_datavault').table( 'activity_sat') bq_client.load_table_from_json(sat_athlete_activity, sat_table_ref) if aspect_type == 'create': link_athlete_activity = athlete_activity_obj.link() link_table_ref = bq_client.dataset('strava_datavault').table( 'athlete_activity_link') bq_client.load_table_from_json(link_athlete_activity, link_table_ref) hub_activity = athlete_activity_obj.hub() hub_table_ref = bq_client.dataset('strava_datavault').table( 'activity_hub') bq_client.load_table_from_json(hub_activity, hub_table_ref)