"""Participant Handlers.""" from switchboard_consumer.constants import entity_type, system from switchboard_consumer.constants.exceptions import ( ParticipantHandlerError) from switchboard_consumer.constants.genre import CLASSICAL from switchboard_consumer.constants.track_participant_roles import ( CLASSICAL_TRACK_ARTIST_CONTRIBUTOR_ROLE_MAPPING, CLASSICAL_TRACK_ARTIST_ROLE_MAPPING, TRACK_ARTIST_CONTRIBUTOR_ROLE_MAPPING, TRACK_ARTIST_ROLE_MAPPING, TRACK_ARTISTS, TRACK_WRITER_ROLE_MAPPING, TRACK_WRITERS ) from switchboard_consumer.formatters.product import format_genres from switchboard_consumer.logic.participant.base_participant_handler \ import BaseParticipantHandler from switchboard_consumer.utils.message import get_system_local_id CONTRIBUTOR = 'contributor' DISPLAY_ARTIST = 'display_artist' MAPPING_LISTS = [*list(TRACK_ARTIST_ROLE_MAPPING.keys()), *list(TRACK_WRITER_ROLE_MAPPING.keys()), *list(TRACK_ARTIST_CONTRIBUTOR_ROLE_MAPPING.keys()) ] class TrackParticipantHandler(BaseParticipantHandler): """"Handle participant creation for tracks.""" def __init__( self, product, logger, orchard_client, message, vendor_id, subaccount_id): """Create a TrackParticipantHandler instance. Args: product: SWB Product data logger: Logger instance orchard_client: OrchardClient instance message: Incoming KafkaMessage vendor_id: vendor_id to create participants against subaccount_id: target subaccount id """ # This Participant handler needs to be aware of genres # to be able to set role mappings now. This will need # more thought as part of a refactoring effort. self.is_classical = format_genres(product)[0] == CLASSICAL self.track_artist_role_mapping = ( CLASSICAL_TRACK_ARTIST_ROLE_MAPPING if self.is_classical else TRACK_ARTIST_ROLE_MAPPING ) self.track_artist_contributor_role_mapping = ( CLASSICAL_TRACK_ARTIST_CONTRIBUTOR_ROLE_MAPPING if self.is_classical else TRACK_ARTIST_CONTRIBUTOR_ROLE_MAPPING ) self.track_writer_role_mapping = TRACK_WRITER_ROLE_MAPPING super().__init__(logger, orchard_client, message, vendor_id, subaccount_id) def process(self, track_data): """Process track participants. Args: track_data: SWB track data """ processing_results = [] artists = [] writers = [] display_artists = track_data.get('displayArtists', []) contributors = track_data.get('contributors', []) # If we have no valid display artists -> track_artists mapping # throw an exception, as we should have at least one valid artist if (not self._validate_participants(display_artists, self.track_artist_role_mapping)): message = ('Could not map any displayArtists to Orchard ' 'track_artists for track ' 'ISRC: {}'.format(track_data['isrc'])) raise ParticipantHandlerError(message) handled_artists = [ # Only map to track_artists from display_artists *self._process_participations( display_artists, self.track_artist_role_mapping, DISPLAY_ARTIST, TRACK_ARTISTS), # We only care about producer and remixer from contributors # So use a mapping that only looks for those roles *self._process_participations( contributors, self.track_artist_contributor_role_mapping, CONTRIBUTOR, TRACK_ARTISTS), ] for (processing_result, artist) in handled_artists: if processing_result: processing_results.append(processing_result) artists.append(artist) # Look for specific track_writer mappings in display_artists # and contributors handled_writers = [ *self._process_participations( display_artists, self.track_writer_role_mapping, DISPLAY_ARTIST, TRACK_WRITERS), *self._process_participations( contributors, self.track_writer_role_mapping, CONTRIBUTOR, TRACK_WRITERS) ] for (processing_result, writer) in handled_writers: if processing_result: processing_results.append(processing_result) writers.append(writer) return processing_results, artists, writers def _process_participations( self, participations, role_mapping, participant_type, mapping_for): handled_participants = [] for participation in participations: participant = participation['participant'] participant_members = participant.get('members', []) if self._validate_participant_members(participant_members, role_mapping): # We're dealing with a compound for member in participant_members: if role_mapping.get(member['role']): role = role_mapping.get(member['role']) member_part = member['participant'] handled_participants.append( self._process_participant(member_part, role)) else: # If the role isn't in any map that we use for tracks if member['role'] not in MAPPING_LISTS: self.logger.info( ('Skipped handling participant -' ' unsupported role'), custom_fields={ 'role': member['role'], 'entity_type': entity_type.TRACK, 'participant_type': participant_type + '_member', 'mapping_for': mapping_for } ) else: # Standard none compound participant (or band / group) role = participation['role'] if role_mapping.get(role): mapped_role = role_mapping.get(role) handled_participants.append( self._process_participant(participant, mapped_role)) else: if role not in MAPPING_LISTS: self.logger.info( ('Skipped handling participant -' ' unsupported role'), custom_fields={ 'role': role, 'entity_type': entity_type.TRACK, 'participant_type': participant_type, 'mapping_for': mapping_for } ) return handled_participants def _process_participant(self, participant, role): source_mapping = get_system_local_id( participant, system.SONY) target_mapping = get_system_local_id( participant) return self.handle_participant(participant, role, source_mapping, target_mapping) def _validate_participants(self, participants, role_mapping): """Check if participants have a role that can be mapped.""" valid_roles = [] for participant in participants: if participant['participant'].get('members', []): valid_roles.append(self._validate_participant_members( participant['participant'].get('members', []), role_mapping ) or role_mapping.get(participant['role'])) else: valid_roles.append(role_mapping.get(participant['role'])) return any(valid_roles) def _validate_participant_members(self, members, role_mapping): """Check if participant members have a role that can be mapped.""" roles = role_mapping.keys() return any(member['role'] in roles for member in members if members)