"""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 base64 from urllib.parse import unquote from flask import g, jsonify, request from owsresponse import response from owsresponse.adaptors.flask import flaskify from socials import config from socials.api import app from socials.constants.timeseries import DEFAULT_START_DATE from socials.logic import stats, timeseries from connector_neo4j import Neo4jSession from owsrequest.flask_request import request_context_from_headers @app.route(config.HEALTH_CHECK, methods=['GET']) def health(): """Check the health of the application.""" return jsonify({'status': 'ok'}) @app.route('/socials/timeseries') def get_socials_timeseries(): """Return timeseries for a given filter, platform and url. Args: account_url (String): The encoded platform url for the artist timestamp_gte (String): The earliest timestamp to include """ if request.args.get('account_url'): try: account_url = base64.b64decode( unquote(request.args.get('account_url')) ).decode() except Exception as e: g.log.exception(e) message = 'account_url has invalid encoding' return flaskify(response.create_fatal_response(message)) else: message = 'Encoded Account URL not provided' return flaskify(response.create_fatal_response(message)) timestamp_gte = request.args.get('timestamp_gte', DEFAULT_START_DATE) timestamp_lte = request.args.get('timestamp_lte', None) return flaskify(timeseries.get_timeseries( account_url, timestamp_gte, timestamp_lte)) @app.route('/socials/stats') def get_socials_stats(): """Return stats for a given social account URL. Args: account_url (String): The encoded social account URL """ account_url = request.args.get('account_url') if account_url: try: account_url = base64.b64decode( unquote(account_url) ).decode() except Exception as e: g.log.exception(e) message = 'account_url has invalid encoding' return flaskify(response.create_fatal_response(message)) else: message = 'Encoded Account URL not provided' return flaskify(response.create_fatal_response(message)) return flaskify(stats.get_stats_by_account_url(account_url)) @app.route('/socials/artists//timeseries') @request_context_from_headers() @Neo4jSession(use_v2=True, database=config.NEO4J_DATABASE_NAME) def get_socials_timeseries_by_account_id(artist_id): """Return timeseries for a given filter, platform and url. Args: artist_id (String): Chartmetric artist id. """ timestamp_gte = request.args.get('timestamp_gte', DEFAULT_START_DATE) timestamp_lte = request.args.get('timestamp_lte', None) return flaskify(timeseries.get_timeseries_by_account_id( artist_id, g.request_context.identity_id, timestamp_gte, timestamp_lte)) @app.route('/socials/artists//stats') def get_socials_stats_by_account_id(artist_id): """Return timeseries for a given filter, platform and url. Args: artist_id (String): Chartmetric artist id. """ return flaskify(stats.get_stats( artist_id)) @app.route('/socials/artists//stats-by-account') @request_context_from_headers() @Neo4jSession(use_v2=True, database=config.NEO4J_DATABASE_NAME) def get_social_account_stats_by_account_id(artist_id): """Return timeseries for a given filter, platform and url. Args: artist_id (String): Chartmetric artist id. """ platform = request.args.get('platform', None) return flaskify(stats.get_stats_by_account( artist_id, g.request_context.identity_id, platform)) @app.route('/socials/artists/stats-by-artists-ids', methods=['POST']) def get_social_account_stats_by_artists_ids(): """Return stats for multiple artists by chartmetric ids.""" data = request.get_json() or {} artists_ids = data.get('artists_ids', []) if not artists_ids: return flaskify(response.Response([])) return flaskify(stats.get_stats_by_artists_ids(artists_ids)) @app.errorhandler(500) def exception_handler(error): """Handle error when uncaught exception is raised. Default exception handler. 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))