"""GraphQL logic.""" from collections import OrderedDict from integration_scripts import logger as log from integration_scripts.connectors.graphql import GraphQLBackoffConnector, \ GraphQLError from integration_scripts.connectors.sentry import sentry_wrap, sentry_client from integration_scripts.constants import graphql_queries from integration_scripts.graphql_utils import format_label_participant, \ format_track_list_update_data, format_project_name_data, \ format_not_for_distribution, format_approve_product from constants.graphql import GRAPHQL_CHUNK_LEN, NOT_FOR_DIST_KEY from logic.bulk_upload_feeder_logic import get_file_participant_rows, \ get_sme_project_names from util.db_utils import get_logged_release_ids from integration_scripts.common_config import GRAPHQL_GATEWAY_URL, \ APPLICATION_NAME, OA_USER @sentry_wrap def create_file_participants(table_name): """Parse input table for participants, and create entries using GraphQL.""" # Notify user msg = 'Collecting vendor_id / subaccount / participant rows for GraphQL ' \ 'upserts.' log.info(msg) # Get artists and related ids from table_name participants = get_file_participant_rows(table_name) # Notify user msg = '{} GraphQL calls will be made.'.format(len(participants)) log.info(msg) # Init method result object results = { 'responses': list(), 'errors': list() } # Loop through rows and fire GraphQL calls for row in participants: if not row['spotify_uri'] and not row['apple_id']: continue # Loop through distinct label_participant = format_label_participant( row['participant_name'], row['orchlabelid'], row['subaccount_id'], row['spotify_uri'], row['apple_id']) # create GraphQL connector graphql_conn = GraphQLBackoffConnector( GRAPHQL_GATEWAY_URL, APPLICATION_NAME) graphql_conn.set_headers({'Orchard-User-Id': OA_USER}) try: # Fire single GraphQL call response = graphql_conn.execute( graphql_queries.set_label_participant, label_participant ) # Notify user success msg = 'GraphQL Call Completed - Vendor ID: {} - Sub ID: {} - ' \ 'Participant Name: {}' \ .format(row['orchlabelid'], row['subaccount_id'] if row['subaccount_id'] else 0, row['participant_name']) log.info(msg) # Push success metadata to method result object results['responses'].append(response) except GraphQLError as ge: # Push error metadata to method result object results['errors'].append({ 'vendor_id': row['orchlabelid'], 'subaccount_id': row['subaccount_id'], 'participant_name': row['participant_name'], 'status': 'graphql_error', 'error_code': ge.code, 'status_text': 'GraphQL Error', 'message': ge.message }) # Notify user of GraphQL error msg = 'GraphQL Error - Vendor ID: {} - Sub ID: {} - ' \ 'Participant Name: {} - ' \ 'Error Code: graphql_error - {} : GraphQL Error : {}' \ .format(row['orchlabelid'], row['subaccount_id'], row['participant_name'], ge.code, ge.message) log.error(msg) with sentry_client() as sentry_sdk: sentry_sdk.capture_exception(ge) except Exception as e: # Fashion default error fields status = 'python_error' err_code = 0 status_text = 'Python Error' err_msg = str(e) # Push error metadata to method result object results['errors'].append({ 'vendor_id': row['orchlabelid'], 'subaccount_id': row['subaccount_id'], 'participant_name': row['participant_name'], 'status': status, 'error_code': err_code, 'status_text': status_text, 'message': err_msg }) # Notify user of Python logic error msg = 'Python Error - Vendor ID: {} - Sub ID: {} - Participant ' \ 'Name: {} - Error Code: {} - {} : {} : {}' \ .format( row['orchlabelid'], row['subaccount_id'], row['participant_name'], status, err_code, status_text, err_msg) log.error(msg) with sentry_client() as sentry_sdk: sentry_sdk.capture_exception(e) return results @sentry_wrap def set_default_track_fields(session_id): """Set default track values for SMEAnalytics not-for-dist using GraphQL.""" # Init method result object results = { 'responses': list(), 'errors': list() } # Notify user msg = 'Getting tuid\'s for tracks.' log.info(msg) tuid_list = get_bulk_tuid_from_release_ids_graph(session_id) if not tuid_list or not len(tuid_list): log.warning('No eligible tuid\'s found to update in graph.') raise RuntimeError( 'No eligible tuid\'s found for GraphQL calls. Terminating.') # Notify user msg = '{} GraphQL calls will be made.'.format(len(tuid_list)) log.info(msg) # create GraphQL connector graphql_conn = GraphQLBackoffConnector( GRAPHQL_GATEWAY_URL, APPLICATION_NAME) graphql_conn.set_headers({'Orchard-User-Id': OA_USER}) tuid_list_len = len(tuid_list) for i in range(0, tuid_list_len, GRAPHQL_CHUNK_LEN): # Loop through distinct default_track_data = format_track_list_update_data( tuid_list=tuid_list[i:i + GRAPHQL_CHUNK_LEN]) try: # Fire single GraphQL call response = graphql_conn.execute( graphql_queries.save_tracks, {"data": default_track_data} ) # Calculate true upper bound for console log. upper_bound = min(tuid_list_len, i + GRAPHQL_CHUNK_LEN) # TODO Fix Console logging # Notify user success msg = 'GraphQL Call Completed - {}-{} of {} tuids processed' \ .format(i, upper_bound, tuid_list_len) log.info(msg) # Push success metadata to method result object results['responses'].append(response) except GraphQLError as ge: # Push error metadata to method result object results['errors'].append({ 'tuid_list': tuid_list[i:i + GRAPHQL_CHUNK_LEN], 'status': 'graphql_error', 'error_code': ge.code, 'status_text': 'GraphQL Error', 'message': ge.message }) # Notify user of GraphQL error msg = 'GraphQL Call Error - tuids {} - {} in list of {} failed -' \ ' Error Code: graphql_error - {} : GraphQL Error : {}' \ .format(i, i + GRAPHQL_CHUNK_LEN, tuid_list_len, ge.code, ge.message) log.error(msg) with sentry_client() as sentry_sdk: sentry_sdk.capture_exception(ge) except Exception as e: # Fashion default error fields status = 'python_error' err_code = 0 status_text = 'Python Error' err_msg = str(e) # Push error metadata to method result object results['errors'].append({ 'tuid_list': tuid_list[i:i + GRAPHQL_CHUNK_LEN], 'status': status, 'error_code': err_code, 'status_text': status_text, 'message': err_msg }) # Notify user of Python logic error msg = 'Python Error - tuids {}-{} in list of {} - Error Code: ' \ '{} - {} : {} : {}'.format(i, i + GRAPHQL_CHUNK_LEN, tuid_list_len, status, err_code, status_text, err_msg) log.error(msg) with sentry_client() as sentry_sdk: sentry_sdk.capture_exception(e) return results @sentry_wrap def set_not_for_distribution(session_id): """Set all products in a session to be not for distribution.""" # Init method result object results = { 'responses': list(), 'errors': list() } # Notify user msg = 'Getting product id\'s for releases.' log.info(msg) product_id_list = get_logged_release_ids(session_id) if not len(product_id_list): log.warning('No eligible releases found to update in graph.') return # Notify user msg = '{} GraphQL calls will be made.'.format(len(product_id_list)) log.info(msg) # create GraphQL connector graphql_conn = GraphQLBackoffConnector( GRAPHQL_GATEWAY_URL, APPLICATION_NAME) graphql_conn.set_headers({'Orchard-User-Id': OA_USER}) for product_id in product_id_list: # Loop through distinct not_for_dist_data = format_not_for_distribution( product_id=product_id, key=NOT_FOR_DIST_KEY) try: # Fire single GraphQL call response = graphql_conn.execute( graphql_queries.update_product, {"data": not_for_dist_data} ) # TODO Fix Console logging # Notify user success msg = 'GraphQL Call Completed - Product Id {} ' \ 'not_for_distribution set to {}'\ .format(product_id, NOT_FOR_DIST_KEY) log.info(msg) # Push success metadata to method result object results['responses'].append(response) except GraphQLError as ge: # Push error metadata to method result object results['errors'].append({ 'product_id': product_id, 'status': 'graphql_error', 'error_code': ge.code, 'status_text': 'GraphQL Error', 'message': ge.message }) # Notify user of GraphQL error msg = 'GraphQL Call Error - Product Id {} not_for_distribution ' \ 'call failed - Error Code: graphql_error - {} : GraphQL ' \ 'Error : {}'.format(product_id, ge.code, ge.message) log.error(msg) with sentry_client() as sentry_sdk: sentry_sdk.capture_exception(ge) except Exception as e: # Fashion default error fields status = 'python_error' err_code = 0 status_text = 'Python Error' err_msg = str(e) # Push error metadata to method result object results['errors'].append({ 'product_id': product_id, 'status': status, 'error_code': err_code, 'status_text': status_text, 'message': err_msg }) # Notify user of Python logic error msg = 'Python Error - Product Id {} not_for_distribution call ' \ 'failed - Error Code: {} - {} : {} : {}'\ .format(product_id, status, err_code, status_text, err_msg) log.error(msg) with sentry_client() as sentry_sdk: sentry_sdk.capture_exception(e) return results @sentry_wrap def set_project_names(session_id): """Set the project name received from SME labelcopy table using GraphQL.""" # Init method result object results = { 'responses': list(), 'errors': list() } # Notify user msg = 'Getting project_names for session.' log.info(msg) project_names_dict = get_sme_project_names(session_id) if not len(project_names_dict.keys()): log.warning('No eligible project_name\'s found to update in graph.') return # Notify user msg = '{} GraphQL calls will be made.'.format(len(project_names_dict)) log.info(msg) # create GraphQL connector graphql_conn = GraphQLBackoffConnector( GRAPHQL_GATEWAY_URL, APPLICATION_NAME) graphql_conn.set_headers({'Orchard-User-Id': OA_USER}) # upc_list_len = len(upc_list) for project_id, project_name in project_names_dict.items(): if project_id in ['0', '-1']: log.warning('Skipping project_id: {}'.format(project_id)) continue if project_name == 'UNKNOWN': log.warning( 'Skipping project name \'UNKNOWN\' on ' 'project_id: {}. Check that this is the ' 'intended name.'.format(project_id)) continue if not project_name: log.warning( 'No valid project name for project id: {}. Skipping'.format( project_id)) continue # Loop through distinct project_data = format_project_name_data( project_id=project_id, project_name=project_name) try: # Fire single GraphQL call response = graphql_conn.execute( graphql_queries.update_project, {"data": project_data} ) # TODO Fix Console logging # Notify user success msg = 'GraphQL Call Completed - Project {} named \'{}\''\ .format(project_id, project_name) log.info(msg) # Push success metadata to method result object results['responses'].append(response) except GraphQLError as ge: # Push error metadata to method result object results['errors'].append({ 'project_id': project_id, 'project_name': project_name, 'status': 'graphql_error', 'error_code': ge.code, 'status_text': 'GraphQL Error', 'message': ge.message }) # Notify user of GraphQL error msg = 'GraphQL Call Error - Project: {} - {} failed - ' \ 'Error Code: graphql_error - {} : GraphQL Error : {}' \ .format(project_id, project_name, ge.code, ge.message) log.error(msg) with sentry_client() as sentry_sdk: sentry_sdk.capture_exception(ge) except Exception as e: # Fashion default error fields status = 'python_error' err_code = -1 status_text = 'Python Error' err_msg = str(e) # Push error metadata to method result object results['errors'].append({ 'project_id': project_id, 'project_name': project_name, 'status': status, 'error_code': err_code, 'status_text': status_text, 'message': err_msg }) # Notify user of Python logic error msg = 'Python Error - Project: {} - {} failed- Error Code: ' \ '{} - {} : {} : {}'.format(project_id, project_name, status, err_code, status_text, err_msg) log.error(msg) with sentry_client() as sentry_sdk: sentry_sdk.capture_exception(e) return @sentry_wrap def approve_products(session_id): """Set all products in a session to be in_content.""" # Init method result object results = { 'responses': list(), 'errors': list() } # Notify user msg = 'Getting product id\'s for releases.' log.info(msg) product_id_list = get_logged_release_ids(session_id) if not len(product_id_list): log.warning('No eligible releases found to approve in graph.') return # Notify user msg = '{} GraphQL calls will be made.'.format(len(product_id_list)) log.info(msg) # create GraphQL connector graphql_conn = GraphQLBackoffConnector( GRAPHQL_GATEWAY_URL, APPLICATION_NAME) graphql_conn.set_headers({'Orchard-User-Id': OA_USER}) for product_id in product_id_list: # Loop through distinct approve_product_data = format_approve_product(product_id=product_id) try: # Fire single GraphQL call response = graphql_conn.execute( graphql_queries.approve_product, {"data": approve_product_data} ) # TODO Fix Console logging # Notify user success msg = 'GraphQL Call Completed - Product Id {} ' \ 'approved.'.format(product_id) log.info(msg) # Push success metadata to method result object results['responses'].append(response) except GraphQLError as ge: # Push error metadata to method result object results['errors'].append({ 'product_id': product_id, 'status': 'graphql_error', 'error_code': ge.code, 'status_text': 'GraphQL Error', 'message': ge.message }) # Notify user of GraphQL error msg = 'GraphQL Call Error - Product Id {} product approval ' \ 'call failed - Error Code: graphql_error - {} : GraphQL ' \ 'Error : {}'.format(product_id, ge.code, ge.message) log.error(msg) with sentry_client() as sentry_sdk: sentry_sdk.capture_exception(ge) except Exception as e: # Fashion default error fields status = 'python_error' err_code = 0 status_text = 'Python Error' err_msg = str(e) # Push error metadata to method result object results['errors'].append({ 'product_id': product_id, 'status': status, 'error_code': err_code, 'status_text': status_text, 'message': err_msg }) # Notify user of Python logic error msg = 'Python Error - Product Id {} product approval call ' \ 'failed - Error Code: {} - {} : {} : {}' \ .format(product_id, status, err_code, status_text, err_msg) log.error(msg) with sentry_client() as sentry_sdk: sentry_sdk.capture_exception(e) return results def get_bulk_tuid_from_release_ids_graph(session_id): """Get a list of tuid's from a session_id Args: session_id: (int) The session_id associated with the tuids Returns: (list) tuid's as integers """ results = { 'responses': list(), 'errors': list() } # Notify user msg = 'Getting track id\'s for releases.' log.info(msg) product_id_list = get_logged_release_ids(session_id) if not len(product_id_list): log.warning( 'No eligible releases found. No track id\'s will be returned.') return # Notify user msg = '{} GraphQL calls will be made.'.format(len(product_id_list)) log.info(msg) # create GraphQL connector graphql_conn = GraphQLBackoffConnector( GRAPHQL_GATEWAY_URL, APPLICATION_NAME) graphql_conn.set_headers({'Orchard-User-Id': OA_USER}) tuid_list = [] # Loop through distinct for product_id in product_id_list: try: # Fire single GraphQL call response = graphql_conn.execute( graphql_queries.get_tuids, {"productId": str(product_id)} ) # TODO Fix Console logging # Notify user success msg = 'GraphQL Call Completed - Track id list for Product Id {} ' \ 'retrieved. {} track id\'s found.'\ .format(product_id, len(response['data']['product']['tracks'])) log.info(msg) # Push success metadata to method result object results['responses'].append(response) except GraphQLError as ge: # Push error metadata to method result object results['errors'].append({ 'product_id': product_id, 'status': 'graphql_error', 'error_code': ge.code, 'status_text': 'GraphQL Error', 'message': ge.message }) # Notify user of GraphQL error msg = 'GraphQL Call Error - Product Id {} get tuid list ' \ 'call failed - Error Code: graphql_error - {} : GraphQL ' \ 'Error : {}'.format(product_id, ge.code, ge.message) log.error(msg) with sentry_client() as sentry_sdk: sentry_sdk.capture_exception(ge) except Exception as e: # Fashion default error fields status = 'python_error' err_code = 0 status_text = 'Python Error' err_msg = str(e) # Push error metadata to method result object results['errors'].append({ 'product_id': product_id, 'status': status, 'error_code': err_code, 'status_text': status_text, 'message': err_msg }) # Notify user of Python logic error msg = 'Python Error - Product Id {} get tuid list call ' \ 'failed - Error Code: {} - {} : {} : {}' \ .format(product_id, status, err_code, status_text, err_msg) log.error(msg) with sentry_client() as sentry_sdk: sentry_sdk.capture_exception(e) else: # Parse out tuid's and store if response: release_tuid_list = [ x['tuid'] for x in response['data']['product']['tracks'] ] tuid_list += release_tuid_list return list(OrderedDict.fromkeys(tuid_list))