"""Helpers for tracks formatting.""" from switchboard_consumer.constants import system from switchboard_consumer.constants.country_mappings import COUNTRY_MAPPING from switchboard_consumer.constants.languages import LANGUAGE_MAPPING from switchboard_consumer.constants.master_ownership_types import \ OWNERSHIP_TYPE_MAPPING from switchboard_consumer.constants.system import ORCHARD from switchboard_consumer.constants.track_participant_roles import \ TRACK_ARTIST_ROLE_MAPPING, TRACK_WRITER_ROLE_MAPPING from switchboard_consumer.formatters.common import ( format_pline, format_title_fields ) from switchboard_consumer.logic.participant.track_participant_handler import \ CONTRIBUTOR, DISPLAY_ARTIST, MAPPING_LISTS from switchboard_consumer.logic.performer.track_performer_handler import \ TrackPerformerHandler from switchboard_consumer.utils.message import get_system_local_id def _parse_explicit(data): """Parse explicit field.""" parental_warning_type = data['parentalWarningType'] parental_warning_type_mapping = { 'EXPLICIT': 'Y', 'NOT_EXPLICIT': 'N', 'EXPLICIT_CONTENT_EDITED': 'E' } return parental_warning_type_mapping.get(parental_warning_type, '') def _parse_track_type(data): """Parse trackMedia field.""" if data.get('trackMedia') == 'VIDEO': return 'video' else: return 'music' def format_writers(writers): """Format writers, de-dupe on ID + name.""" formatted_writers = [] for writer in writers: if not any(_writer_matches(writer, fw) for fw in formatted_writers): formatted_writers.append({ 'artistInfoId': writer['artistInfoId'], 'writerName': writer['artistName'], 'writerType': 'writer' }) return formatted_writers def _writer_matches(writer, formatted_writer): return writer['artistInfoId'] == formatted_writer['artistInfoId'] and \ writer['artistName'] == formatted_writer['writerName'] def format_artists(artists): """Format track artists, de-dupe on ID + name + role.""" formatted_artists = [] for artist in artists: if not any(_artist_matches(art, artist) for art in formatted_artists): formatted_artists.append(artist) return formatted_artists def _artist_matches(artist1, artist2): return artist1['artistInfoId'] == artist2['artistInfoId'] and \ artist1['artistName'] == artist2['artistName'] and \ artist1['artistType'] == artist2['artistType'] def _format_publishers(track_item): """Format track publishers.""" result = [] names = [] for publisher in (track_item.get('publishers', []) or []): names.append(publisher['name']) for unique_name in list(sorted(set(names))): result.append({ 'name': unique_name }) return result def format_switchboard_product_track_to_orchard_track( product_track_item, artists, writers, performers, logger): """Format switchboard productTrack item to orchard track.""" orchard_track_item = { 'volumeNumber': product_track_item['componentNumber'], 'trackNumber': product_track_item['sequenceNumber'] } orchard_track_item.update( format_switchboard_to_orchard_track( product_track_item['track'], artists, writers, performers, logger)) return orchard_track_item def format_switchboard_to_orchard_track( track_item, artists, writers, performers, logger): """Format switchboard track to orchard graphql format.""" rec_country_id = None if COUNTRY_MAPPING.get(track_item.get('recordingCountry', '')): rec_country_id = COUNTRY_MAPPING.get(track_item['recordingCountry']) owner_country_id = None if COUNTRY_MAPPING.get(track_item.get('copyrightOwnerCountry', '')): owner_country_id = COUNTRY_MAPPING.get( track_item['copyrightOwnerCountry']) formal_title_text, formal_title_subtitle = format_title_fields(track_item) result = { 'trackName': formal_title_text, 'explicit': _parse_explicit(track_item), 'pInfo': track_item.get('pLine'), 'metaLanguageCode': map_meta_language(track_item), 'isrc': track_item.get('isrc', ''), 'trackType': _parse_track_type(track_item), 'recordingCountryId': rec_country_id, 'artists': format_artists(artists), 'writers': format_writers(writers), 'performers': performers, 'originalRightsHolderCountryId': owner_country_id, 'publishers': _format_publishers(track_item), 'ownershipRights': map_ownership_rights(track_item), 'samples': format_track_samples( track_item.get('samples') or [], logger), 'version': formal_title_subtitle # 'upc' not available for now # 'previewStartTime' not available for now } if track_item.get('lyrics'): result['lyrics'] = track_item['lyrics'] return result def map_ownership_rights(track_item): """Format Switchboard masterOwnershipRights to Orchard format.""" if track_item.get('masterOwnershipType'): return OWNERSHIP_TYPE_MAPPING.get( track_item.get('masterOwnershipType').upper()) 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()) # TODO: This whole function needs refactoring, it is NOT just formatting def format_switchboard_product_tracks_to_orchard_tracks_batch( data, logger, participant_handler): """Format batch of switchboard productTrack items to orchard tracks.""" processing_results = [] tracks = [] for component in data.get('components', []): for side in component.get('sides'): for item in side.get('productTracks', []): (processing_results, artists, writers) = participant_handler.process(item['track']) track_performer_handler = \ TrackPerformerHandler(item['track'], logger) performers = track_performer_handler.process() formatted_track = format_switchboard_product_track_to_orchard_track( # noqa item, artists, writers, performers, logger ) tracks.append(formatted_track) return processing_results, tracks def get_all_product_tracks(data): """Returns all productTracks as a list""" tracks = [] for component in data.get('components', []): for side in component.get('sides'): for item in side.get('productTracks', []): tracks.append(item) return tracks def get_product_track_by_isrc(data, isrc): """Get Product Track by matching ISRC""" for component in data.get('components', []): for side in component.get('sides'): for item in side.get('productTracks', []): if item['track']['isrc'] == isrc: return item def format_switchboard_product_tracks_for_release_corrections( data, logger, participant_handler): """ Format batch of switchboard productTrack items to orchard tracks for rc.""" processing_results = [] tracks = [] for component in data.get('components', []): for side in component.get('sides'): for item in side.get('productTracks', []): (processing_results, artists, writers) = participant_handler.process(item['track']) track_performer_handler = \ TrackPerformerHandler(item['track'], logger) performers = track_performer_handler.process() formatted_track = format_switchboard_product_track_to_orchard_track( # noqa item, artists, writers, performers, logger ) formatted_track['tuids'] = \ [int(ids['localId']) for ids in item['track']['ids'] if ids['system'] == ORCHARD] tracks.append(formatted_track) return processing_results, tracks def format_result_id(tuid, isrc): """Format result id for TRACK.""" return [{ 'localId': str(tuid), 'businessKey': isrc, 'businessKeyType': 'ISRC', 'system': ORCHARD }] def format_track_samples(track_samples, logger): """Format track samples data batch.""" return [_format_track_sample(track_sample, logger) for track_sample in track_samples] def _participants_have_correct_role(members, role_mapping): """Check if we have members of valid roles.""" roles = role_mapping.keys() return any(member['role'] in roles for member in members if members) def _extract_participants_for_sample( participants, role_mapping, participant_type, mapping_for, logger): """Extract participants from sample displayArtists/contributors. Args: participants (list): Source of participants extraction. role_mapping (dict): Map to define participant role. participant_type (string): displayArtists or contributors source. mapping_for (string): if destination of extraction is artists or writers or producers. Returns: list: List of extracted participants. """ handled_participants = [] for participant in participants: participant_members = participant['participant'].get('members', []) if _participants_have_correct_role(participant_members, role_mapping): for member in participant_members: if role_mapping.get(member['role']): handled_participants.append(member['participant']['name']) else: if member['role'] not in MAPPING_LISTS: custom_fields = { 'role': member['role'], 'participant_type': participant_type + '_member', 'mapping_for': mapping_for} logger.info(( 'Skipped sample participant - unsupported role'), custom_fields=custom_fields ) else: # Standard non-compound participant (or band / group) if role_mapping.get(participant['role']): handled_participants.append( participant['participant']['name']) else: if participant['role'] not in MAPPING_LISTS: custom_fields = { 'role': participant['role'], 'participant_type': participant_type, 'mapping_for': mapping_for } logger.info( 'Skipped handling participant - unsupported role', custom_fields=custom_fields) return handled_participants def _format_track_sample(track_sample, logger): """Format track sample data.""" result = { 'sampleType': (track_sample['sampleType'] or '').upper(), 'trackName': track_sample['displayTitle'], 'isrc': track_sample.get('isrc'), 'pLine': format_pline(track_sample.get('pLine')), 'trackLength': track_sample['durationInSeconds'], 'notes': track_sample['notes'], } artists = [] writers = [] producers = [] TRACK_SAMPLE_ARTIST_ROLE_MAPPING = TRACK_ARTIST_ROLE_MAPPING.copy() TRACK_SAMPLE_ARTIST_ROLE_MAPPING.pop('Producer') artists += _extract_participants_for_sample( track_sample['displayArtists'], TRACK_SAMPLE_ARTIST_ROLE_MAPPING, DISPLAY_ARTIST, 'sample_artists', logger) artists += _extract_participants_for_sample( track_sample['contributors'], TRACK_SAMPLE_ARTIST_ROLE_MAPPING, CONTRIBUTOR, 'sample_artists', logger) writers += _extract_participants_for_sample( track_sample['displayArtists'], TRACK_WRITER_ROLE_MAPPING, DISPLAY_ARTIST, 'sample_writers', logger) writers += _extract_participants_for_sample( track_sample['contributors'], TRACK_WRITER_ROLE_MAPPING, CONTRIBUTOR, 'sample_writers', logger) producers += _extract_participants_for_sample( track_sample['displayArtists'], {'Producer': 'producer'}, DISPLAY_ARTIST, 'sample_producers', logger) producers += _extract_participants_for_sample( track_sample['contributors'], {'Producer': 'producer'}, CONTRIBUTOR, 'sample_producers', logger) sample_artists = [] for artist in set(artists): sample_artists.append({ 'artistName': artist, 'artistType': 'ARTIST' }) for writer in set(writers): sample_artists.append({ 'artistName': writer, 'artistType': 'SONGWRITER' }) for producer in set(producers): sample_artists.append({ 'artistName': producer, 'artistType': 'PRODUCER' }) result['artists'] = sample_artists return result def format_track_unmapped_publishers(track_item): """Format unmapped publishers for single track.""" unmapped_track_publishers = [] for publisher in (track_item.get('publishers', []) or []): local_id = get_system_local_id(publisher) if not local_id: swb_id = get_system_local_id(publisher, system=system.SONY) unmapped_track_publishers.append([publisher['name'], swb_id]) return unmapped_track_publishers def format_tracks_unmapped_publishers_batch(data): """Format product tracks unmapped publishers.""" result = {} try: for component in data.get('components', []): for side in component.get('sides', []): for product_track in side.get('productTracks', []): track = product_track['track'] isrc = track['isrc'] result[isrc] = format_track_unmapped_publishers(track) except (AttributeError, TypeError): pass return result def format_rearrange_tracks(data, isrc_to_tuid_mapping): """Format rearrange tracks payload.""" arrangement = [] try: for component in data.get('components', []): for side in component.get('sides', []): for product_track in side.get('productTracks', []): isrc = product_track['track']['isrc'] tuid = int(isrc_to_tuid_mapping[isrc]) arrangement.append({ 'volumeNumber': product_track['componentNumber'], 'trackNumber': product_track['sequenceNumber'], 'tuid': tuid }) except (AttributeError, TypeError): pass return arrangement