"""Format create product.""" from switchboard_consumer.connectors import log_central from switchboard_consumer.constants.deal_responsibilities import ( DIGITAL_DISTRIBUTION, IMPORTANT_COUNTRIES, INCLUDED, NOT_APPLICABLE, WORLDWIDE ) from switchboard_consumer.constants.genre import DEFAULT_SUBGENRE from switchboard_consumer.constants.languages import LANGUAGE_MAPPING from switchboard_consumer.constants.not_for_distribution \ import DISTRIBUTE_NORMALLY, SME_INGESTION_NOT_FOR_DISTRIBUTION, \ SWITCHBOARD_DUMMY from switchboard_consumer.constants.participant_roles import ( FEATURED_TO_PRIMARY ) from switchboard_consumer.constants.release_type import ( DIGITAL_AUDIO, FULL_LENGTH_FORMAT, GRAPHQL_PRODUCT_CONFIGURATION_MAPPING, GRAPHQL_PRODUCT_FORMAT_MAPPING ) from switchboard_consumer.constants.special_instructions import ( NOTE_FEATURED_TO_PRIMARY_TEXT, NUM_OF_COUNTRIES_TO_FORMAT, RESPONSIBILITY_TEXT, SPECIAL_INSTRUCTION_TEXT ) from switchboard_consumer.constants.system import ORCHARD from switchboard_consumer.formatters.common import format_title_fields from switchboard_consumer.utils.common import iterate_all from switchboard_consumer.utils.message import get_system_local_id class FormatterValidationError(Exception): """ValidationError Exception.""" pass def get_company_code(subaccount): """Get company code.""" try: return int(subaccount['companyCode']) except (TypeError, KeyError): return None def format_genres(data): """Format genre mapping.""" genres = data.get('genres') if not genres: return None, None try: genre = [genre for genre in genres if genre['system'] == ORCHARD][0] except (IndexError, KeyError): # We shouldn't have even got this message from SWB raise FormatterValidationError( 'No genre mapping for product' ) genre_id = genre.get('genreId') subgenres = genre.get('subGenres') subgenre_id = DEFAULT_SUBGENRE.get(genre_id) if subgenres: try: # Fetch first subGenreId subgenre_id = subgenres[0].get('subGenreId', subgenre_id) except (AttributeError, TypeError): pass return genre_id, subgenre_id def format_product_configuration(data): """Format release type into corresponding product configuration field.""" return GRAPHQL_PRODUCT_CONFIGURATION_MAPPING.get(data.get('releaseType'), DIGITAL_AUDIO) def format_create_product_input(upc, data, primary_artists): """Format createProduct input.""" cline = format_cline(data) subaccount_id = get_company_code(data.get('subAccount', None)) formal_title_text, formal_title_subtitle = format_title_fields(data) if data.get('smeAnalyticsIngest'): orchard_id = {'localId': data['projectId']} else: orchard_id = get_system_local_id(data['project']) genre_id, subgenre_id = format_genres(data) payload = { 'cLine': cline, 'productName': formal_title_text, 'productHighlights': '-', # TODO cannot be empty or null 'projectId': int(orchard_id['localId']), 'accountId': int(data['labelAccount']['companyCode']), 'genreId': genre_id, 'imprint': format_imprint(data), 'subgenreId': subgenre_id, 'metaLanguage': map_meta_language(data), 'subaccountId': subaccount_id, 'format': format_product_format(data), 'productCode': data.get('catalogNumber'), 'upc': upc, 'primaryArtists': primary_artists, 'deliveredVersion': formal_title_subtitle, 'productConfiguration': format_product_configuration(data), 'specialInstructions': format_special_instructions(data), 'pLine': format_pline(data), 'notForDistribution': format_not_for_distribution(data) } return payload def map_meta_language(data): """Format IETF meta language to Orchard format ISO 639-2.""" if data.get('metaLanguage'): return LANGUAGE_MAPPING.get(data.get('metaLanguage').upper()) def format_imprint(data): """Format imprint.""" imprint = data.get('imprint') imprint_name = None if imprint: imprint_name = imprint.get('name') return imprint_name def format_product_format(data): """Format release type into corresponding product format field.""" value = GRAPHQL_PRODUCT_FORMAT_MAPPING.get(data.get('releaseType'), FULL_LENGTH_FORMAT) return value def format_cline(data): """Format cLine.""" try: cline = data.get('cLine', {}).get('cLineText', '') if not cline: cline = format_pline(data) return cline except AttributeError: return format_pline(data) def format_pline(data): """Format pLine.""" try: pline = data.get('pLine', {}).get('pLineText', '') if not pline: pline = '' except AttributeError: pline = '' return pline def format_update_product_input(product_id, data, primary_artists): """Format updateProduct input.""" cline = format_cline(data) if not cline: cline = format_pline(data) formal_title_text, formal_title_subtitle = format_title_fields(data) genre_id, subgenre_id = format_genres(data) payload = { 'productId': int(product_id), 'cLine': cline, 'productCode': data.get('catalogNumber'), 'productName': formal_title_text, 'deliveredVersion': formal_title_subtitle, 'imprint': format_imprint(data), 'metaLanguage': map_meta_language(data), 'primaryArtists': primary_artists, # 'description': '-' , # TODO: SWITCH-753 'format': format_product_format(data), 'genreId': genre_id, 'subgenreId': subgenre_id, 'productConfiguration': format_product_configuration(data), 'specialInstructions': format_special_instructions(data), # 'vendorReleaseIdentifier': '-', # TODO: No field in switchboard? 'pLine': format_pline(data), 'notForDistribution': format_not_for_distribution(data) } return payload def format_result_id(insert_id, original_id): """Format result id.""" return [{ 'localId': str(insert_id), 'businessKey': original_id['businessKey'], 'businessKeyType': original_id['businessKeyType'], 'system': ORCHARD, }] def orchard_has_responsibility(logger, data, responsibility_name): """Check if Orchard has a specific responsibility.""" try: deal_responsibilities = data['switchboardExtensions']['governingDeal'][ 'dealResponsibilities'] except KeyError: logger.info( 'Could not find deal responsibilities for Product', custom_fields={ 'product_data': data, }) return False digital_distribution_responsibility = next( r for r in deal_responsibilities if r['dealResponsibilityType']['name'] == responsibility_name) assignments = digital_distribution_responsibility.get('assignments') if not assignments: logger.info('Could not find assignment for product', custom_fields={ 'product_data': data, }) return False orchard_assignment = next( (a for a in assignments if a['system'] == ORCHARD), None) logger.info(f'orchard_assignment for Product: {orchard_assignment}', custom_fields={ 'product_data': data, }) return orchard_assignment is not None def format_special_instructions(product_data): """Format special instructions.""" # TODO: We need a data structure to abstract the retrival of data try: deal_responsibilities = product_data['switchboardExtensions'][ 'governingDeal']['dealResponsibilities'] except (KeyError, TypeError): return '' formatted_special_instructions = [] for responsibility in deal_responsibilities: applies_to = responsibility.get('appliesTo') if applies_to == NOT_APPLICABLE: formatted_special_instructions.append( format_not_applicable_responsibility(responsibility) ) elif applies_to == WORLDWIDE: formatted_special_instructions.append( format_worldwide_responsibility(responsibility) ) else: for assignment in responsibility['assignments']: formatted_special_instructions.append( format_assigned_responsibility(responsibility, assignment, ) ) formatted_special_instructions.sort() product_data_values = list(iterate_all(product_data, 'value')) flag_featured_to_primary = any( [True for role in FEATURED_TO_PRIMARY if role in product_data_values] ) intro_text = SPECIAL_INSTRUCTION_TEXT if flag_featured_to_primary: intro_text = ( f'{NOTE_FEATURED_TO_PRIMARY_TEXT}\n{SPECIAL_INSTRUCTION_TEXT}' ) return '\n'.join( [intro_text] + formatted_special_instructions) def format_not_applicable_responsibility(responsibility): """Format not applicable responsibility.""" return RESPONSIBILITY_TEXT.format( responsibility['dealResponsibilityType']['name'], NOT_APPLICABLE, '').strip() def format_worldwide_responsibility(responsibility): """Format worldwide responsibility.""" owner = [assignment['system'] for assignment in responsibility['assignments']][0] return RESPONSIBILITY_TEXT.format( responsibility['dealResponsibilityType']['name'], owner, WORLDWIDE).strip() def format_assigned_responsibility(responsibility, assignment): """Format assigned responsibility.""" important_countries = { key: value for value, key in enumerate(IMPORTANT_COUNTRIES) } territories = assignment['territories'] # Sort important countries to the start of list territories.sort(key=lambda key: important_countries.get( key, len(important_countries) + 1)) formatted_territories = '{} {} {}'.format( 'includes' if assignment['scope'] == INCLUDED else 'excludes', ', '.join(territories[:NUM_OF_COUNTRIES_TO_FORMAT]) if territories else 'no territories', 'and some other countries' if len(territories) > NUM_OF_COUNTRIES_TO_FORMAT else '' ) return RESPONSIBILITY_TEXT.format( responsibility['dealResponsibilityType']['name'], assignment['system'] + ' -', formatted_territories).strip() def format_not_for_distribution(data): """Formats not for distribution value. Args: data: swb graphql data """ logger = log_central.get_current_logger() not_for_distribution = None if data.get('smeAnalyticsIngest'): not_for_distribution = SME_INGESTION_NOT_FOR_DISTRIBUTION elif orchard_has_responsibility(logger, data, DIGITAL_DISTRIBUTION): not_for_distribution = DISTRIBUTE_NORMALLY else: not_for_distribution = SWITCHBOARD_DUMMY logger.info( ('Setting notForDistribution value' f' on Product to: {not_for_distribution}'), custom_fields={ 'product_data': data, }) return not_for_distribution