"""Application Handlers. Requests are redirected to handlers, which are responsible for getting information from the URL and passing it down to the logic layer. The way each layer talks to each other is through Response objects which defines the type status of the data and the data itself. Please note: the Orchard uses the term handlers over views as convention for clarity See: oto.response for more details. """ import datetime from flask import g from flask import jsonify from flask import request from oto import response from oto import status from oto.adaptors.flask import flaskify from owsrequest import flask_request from social_analytics import config from social_analytics.api import app from social_analytics.constants import error from social_analytics.constants import header from social_analytics.logic import artist_url from social_analytics.logic import collector from social_analytics.logic import hello from social_analytics.logic import metrics from social_analytics.logic import new_social_profile from social_analytics.logic import platform from social_analytics.logic import social_profile from social_analytics.logic import user_interaction @app.route('/collector/health', methods=['GET']) def collector_health(): """Show social profiles for collecting, and last processed dates.""" return flaskify(response.Response()) @app.route('/collector/social-profile/', methods=['GET']) def collect_data_for_profile(social_profile_id): """Collect data for a single social profile.""" return flaskify(response.Response()) @app.route('/artist//social-profiles', methods=['GET']) def get_artist_social_profiles(artist_id): """Get all social profiles for an artist.""" return flaskify(social_profile.get_social_profiles_by_artist_id(artist_id)) @app.route('/social-profile/suggestions', methods=['POST']) def social_profile_suggestions(): """Get suggestions for profiles for a potential social profile. Returns: flask.Response: containing the suggested social profile as dict. """ data = request.get_json(silent=True, force=True) or {} return flaskify(collector.recommend_profile_for_social_network(data)) @app.route('/social-profile', methods=['POST']) def create_or_update_social_profile(): """Create or update a social profile. Returns: flask.Response: containing the created social profile as dict. """ created_by = request.headers.get(header.GRASS_ORCHARD_USER_ID, '') data = request.get_json(silent=True, force=True) or {} if created_by: data['created_by'] = created_by return flaskify(new_social_profile.create_or_update_social_profile(data)) @app.route( '/social-profile//artist/', methods=['DELETE']) def delete_artist_social_profile(social_profile_id, artist_id): """Delete an artist social profile. Args: social_profile_id (str): The social_profile_id. artist_id (str): The artist_id. Returns: flask.Response: containing the deleted artist social profile dict, not found if no profile was deleted or Bad Params. """ return flaskify( artist_url.delete_social_profile( int(social_profile_id), int(artist_id))) @app.route('/artist//latest-metrics', methods=['GET']) def get_artist_metrics(artist_id): """Get all metrics for the social profiles of this artist.""" return flaskify(metrics.get_latest_metrics_by_artist_id(int(artist_id))) @app.route( '/social-profile//platform//' 'metric/', methods=['GET']) def get_time_series_data_for_platform( social_profile_id, platform_id, metric_id): """Get time series data for one metric for a given period of time. Args: social_profile_id (str): The social profile id. platform_id (str): The platform id. metric_id (str): The metric_id. Returns: flask.Response: containing a list of metric time series data. """ start_date_timestamp = request.args.get('start_date', '') end_date_timestamp = request.args.get('end_date', '') start_date = None end_date = None if start_date_timestamp: start_date = datetime.datetime.utcfromtimestamp( int(start_date_timestamp)) if end_date_timestamp: end_date = datetime.datetime.utcfromtimestamp( int(end_date_timestamp)) time_series_query_params = { 'social_profile_id': int(social_profile_id), 'platform_id': int(platform_id), 'metric_id': int(metric_id), 'start_date': start_date, 'end_date': end_date } if not all(val for key, val in time_series_query_params.items()): return flaskify( response.create_error_response( status.BAD_REQUEST, error.ERROR_MESSAGE_BAD_PARAMS)) return flaskify( metrics.get_time_series_data_for_period(time_series_query_params)) @app.route('/search-artists', methods=['GET']) def search_artists(): """Search artists and which of them have linked social profiles.""" # Parse query params. params = { 'token': request.args.get('search_token', ''), 'context': request.args.get('context', ''), 'term': request.args.get('term', '') } # Return a bad barams response if any of the params has no value. if not all(value for key, value in params.items()): return flaskify(response.create_error_response( status.BAD_REQUEST, error.ERROR_MESSAGE_BAD_PARAMS)) # Parse Grass Headers. (account_type, account_id) = flask_request.get_grass_headers(request) headers = { header.GRASS_ACCOUNT_TYPE: account_type, header.GRASS_ACCOUNT_ID: account_id } # Return a bad barams response if any of the headers has no value. if not all(value for key, value in headers.items()): return flaskify( response.create_error_response( status.BAD_REQUEST, error.ERROR_CODE_BAD_GRASS_REQUEST)) return flaskify( artist_url.search_artists(params, headers)) @app.route('/linked-artists', methods=['GET']) def get_artists_for_account(): """Get artists for an account and its total followers.""" user_id = request.headers.get(header.GRASS_ORCHARD_USER_ID, '') if not user_id: return flaskify( response.create_error_response( 400, error.ERROR_MESSAGE_BAD_PARAMS)) return flaskify(artist_url.get_last_viewed_artists(user_id)) @app.route('/last-viewed-artists', methods=['GET']) def get_last_viewed_artists(): """Get last viewed artists for a specific user. Returns: flask.Response: containing the last viewed artists with their social profiles and latest metrics. """ user_id = request.headers.get(header.GRASS_ORCHARD_USER_ID, '') if not user_id: return flaskify( response.create_error_response( 400, error.ERROR_MESSAGE_BAD_PARAMS)) return flaskify(artist_url.get_last_viewed_artists(user_id)) @app.route('/platform/instagram/user/', methods=['GET']) def get_instagram_user(platform_id): """Get Instagram user details. Args: platform_id (string): the Instagram ID of the user. Returns: flask.Response: containing the user details. """ return flaskify(platform.get_instagram_user(platform_id)) @app.route('/platform/spotify/user/', methods=['GET']) def get_spotify_user(platform_id): """Get Spotify user details. Args: platform_id (string): the Spotify ID of the user. Returns: flask.Response: containing the user details. """ return flaskify(platform.get_spotify_user(platform_id)) @app.route('/social-analytics/user-interaction', methods=['POST']) def create_user_interaction(): """Create User Interaction. Returns: flask.Response: containing the created user interaction. """ data = request.get_json(silent=True, force=True) or {} user_id = request.headers.get(header.GRASS_ORCHARD_USER_ID, '') if not user_id: return flaskify( response.create_error_response( 400, error.ERROR_MESSAGE_BAD_PARAMS)) data['user_id'] = user_id data['artist_id'] = str(data['artist_id']) return flaskify(user_interaction.create_user_interaction(data)) @app.route('/social-analytics/migrate-to-art-relations', methods=['POST']) def migrate_artist_social_profile_to_art_relations(): """Migrate artist social profile to art_relations. Returns: flask.Response: containing the migrated artist url. """ data = request.get_json(silent=True, force=True) or {} return flaskify( artist_url.migrate_artist_social_profile_to_artist_url(data)) @app.route('/', methods=['GET']) def hello_world(): """Hello World with an optional GET param "name".""" name = request.args.get('name', '') return flaskify(hello.say_hello(name)) @app.route('/', methods=['GET']) def hello_world_username(username): """Hello World on /. Args: username (str): the user's username. """ return flaskify(hello.say_hello(username)) @app.route(config.HEALTH_CHECK, methods=['GET']) def health(): """Check the health of the application.""" return jsonify({'status': 'ok'}) @app.errorhandler(500) def exception_handler(error): """Default handler when uncaught exception is raised. Note: Exception will also be sent to Sentry if config.SENTRY is set. Returns: flask.Response: A 500 response with JSON 'code' & 'message' payload. """ message = ( 'The server encountered an internal error ' 'and was unable to complete your request.') g.log.exception(error) return flaskify(response.create_fatal_response(message))