"""Example news feed data by profile.""" import argparse import getpass import json import keyring import stream from tabulate import tabulate def main(): """Start of execution flow.""" args = parse_args() feed = init_feed( args.env, args.profile_id, args.profile_type ) last_seen_id = None batch_size = args.batch_size # loop until user stops or no more data while True: # fetch batch of data events = fetch_batch(feed, batch_size, last_seen_id) table = [format_event(x) for x in events] print(tabulate(table, tablefmt='fancy_grid')) # end if no more data to fetch if not events or len(events) < batch_size: print('No more results!') exit(0) # save iterator spot and continue last_seen_id = events[-1]['id'] if not query_yes_no('Continue?'): exit(0) def format_event(event): """Translate event object to row. Args: event (dict): details about specific event in news feed Returns: tuple: - time (str): when event happened - verb (str): category of event - activity_source (str): followed entity causing event forwarding - message (str): human readable details about event """ data = event['object'] activity_source = 'unknown' if 'activity_source' in data: activity_source = data['activity_source'] elif 'activity_sources' in data: activity_source = ', '.join(data['activity_sources']) verb = event['verb'] time = event['time'] message = '-' details = '-' if verb == 'playlist_placements': isrc = data['sound_recording']['isrc'] song_name = data['sound_recording'].get('name', 'MISSING DATA') playlist_dsp = data['playlist']['dsp'] playlist_name = data['playlist']['name'] playlist_id = data['playlist']['id'] message = f'"{song_name}" has been added to "{playlist_name}" on {playlist_dsp}!' # noqa:E501 details = f'{isrc} {playlist_id}' elif verb == 'trending_tracks': track_name = data['track']['name'] track_id = data['track']['id'] isrc = data['track']['isrc'] region = data['region'] diff = data['percent_diff'] dsp = data.get('dsp', 'MISSING DATA') message = f'"{track_name}" had a {diff}% increase in streams on {dsp} in {region}.' # noqa:E501 details = f'{isrc} {track_id}' elif verb.startswith('social_spike'): artist_name = data['name'] new_followers = data['new_followers'] platform = verb.split('_')[-1] message = f'"{artist_name}" gained {new_followers} {platform} followers since yesterday!' # noqa:E501 return (time, verb, activity_source, message, details) def parse_args(): """Read command line args.""" parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument( '--id', type=int, required=True, dest='profile_id', help='unique profile identifier combined with type' ) parser.add_argument( '--type', type=str, required=True, dest='profile_type', choices=['Insights', 'Artist', 'Label'], help='profile type combined with id' ) parser.add_argument( '-n', type=int, required=False, default=15, dest='batch_size', help='size of each batch' ) parser.add_argument( '--env', type=str, required=True, help='environment to set/get GetStream.io credentials for' ) args = parser.parse_args() return args def init_client(environment): """Initialize GetStream.io client. Args: environment (str): namespace to set/get keypair """ sep = ':' keyname = f'getstream-{environment}' keypair = keyring.get_password(keyname, keyname) if not keypair: print() print('GetStream.io API creds needed') print(' - one time setup') print(f' - will be stored in your keyring as "{keyname}"') print() print('1. Visit https://getstream.io/dashboard/organization/24979/') print('2. Selection application') print('3. View or create credentials') print('4. Enter below') api_key = input('Key: ') api_secret = getpass.getpass('Secret: ') keypair = sep.join([api_key, api_secret]) keyring.set_password(keyname, keyname, keypair) (app_key, app_secret) = keypair.split(sep) return stream.connect( app_key, app_secret, location='us-east' ) def init_feed(environment, profile_id, profile_type): """Initialize GetStream.io feed object. Args: profile_id (int): unique profile identifier profile_type (str): profile category Returns: stream.feed """ client = init_client(environment) feed_group = 'user_mobile_push' feed_id = f'{profile_type}Profile_{profile_id}' return client.feed( feed_group, feed_id ) def fetch_batch(feed, limit, last_id=None): """Return items from GetStream.io news feed. Args: feed (stream.feed): representation of news feed limit (int): how many items to fetch last_id (str): fetch items 'older' than this id Returns: list: events in new feed with json details loaded """ try: if not last_id: response = feed.get( limit=limit ) else: response = feed.get( limit=limit, id_lt=last_id ) for event in response['results']: event['object'] = json.loads(event['object']) return response['results'] except stream.exceptions.ApiKeyException: print('Invalid stream credentials!') exit(1) def query_yes_no(question, default='yes'): """Query user for yes/no input. Args: question (str): prompt for user default (str): set option with if no user input Returns: bool: user selection """ valid = { 'yes': True, 'y': True, 'no': False, 'n': False } if default is None: prompt = '[y/n]' elif default == 'yes': prompt = '[Y/n]' elif default == 'no': prompt = '[y/N]' else: raise ValueError(f'invalid default answer: "{default}"') while True: print(f'{question} {prompt}: ', end='') choice = input().lower() if default is not None and choice == '': return valid[default] elif choice in valid: return valid[choice] else: print(f'invalid voice "{choice}" from {[x for x in valid.keys()]}') if __name__ == '__main__': main()