"""Methods related to participant logic.""" from typing import Dict, List from bulk_metadata_ingester_common.models.bulk_release import BulkRelease from bulk_metadata_ingester_common.utils.error import graphql_execute from config import graphql_gateway from constants import queries from constants.participant import ( ARTIST_FIELD_SEPARATOR, MAX_PERFORMER_COUNT) from exceptions import ProcessParticipantsException from graphql import GraphQLError def get_artist(participant_name: str, vendor_id: str, logger: object) -> Dict: """Format payload and call GraphQL query to get artist data.""" payload = { 'artistName': participant_name, 'vendorId': vendor_id } result = graphql_execute( graphql_gateway, queries.GET_ARTIST, payload, logger ) if result['data']['filterArtists']: return result['data']['filterArtists'][0] else: return None def create_artist( participant_name: str, vendor_id: str, subaccount_id: str, logger: object) -> Dict: """Format payload and call GraphQL mutation to create artist.""" payload = { 'create': [ { 'artistName': participant_name, 'vendorId': vendor_id, 'subaccountId': subaccount_id, } ] } result = graphql_execute( graphql_gateway, queries.SAVE_ARTIST, {'data': payload}, logger ) if result['data']['saveArtists']: return result['data']['saveArtists'][0] else: return None def create_label_participant( participant_name: str, vendor_id: str, subaccount_id: str, logger: object, spotify_uri: str = None, apple_id: str = None) -> Dict: """Format payload and call GraphQL mutation to create label participant.""" payload = { 'name': participant_name, 'spotifyId': spotify_uri, 'appleMusicId': apple_id } # Remove keys with null values payload = { key: value for key, value in payload.items() if value is not None} result = graphql_execute( graphql_gateway, queries.GET_OR_CREATE_LABEL_PARTICIPANT, { 'data': payload, 'vendorId': vendor_id, 'subaccountId': subaccount_id or 0, # Mutation expects 0 if there is no subaccountId }, logger ) return result['data']['createLabelParticipant'] def process_participants(model: BulkRelease, logger: object) -> BulkRelease: """Construct the participants section of the model. Raises: ProcessParticipantsException: In the event of GraphQL error, or processing error """ participants = get_participants(model) participants_dict = {} vendor_id = model.vendor_id subaccount_id = model.subaccount_id try: for participant in participants: logger.info(f'Processing Participant: {participant}') artist = get_artist(participant['name'], vendor_id, logger) if not artist: logger.info( f"Creating artist '{participant}' on vendor {vendor_id}, " f'subaccount {subaccount_id}') artist = create_artist( participant['name'], vendor_id, subaccount_id, logger ) label_participant = create_label_participant( participant['name'], vendor_id, subaccount_id, logger, participant['spotify_uri'], participant['apple_id'] ) participants_dict[participant['name']] = { 'name': participant['name'], 'artist_id': artist.get('artistId'), 'label_participant_id': label_participant.get('id'), 'label_participant_uuid': label_participant.get('uuid'), } if participant['name'] == model.project_artist: model.project_artist_id = artist.get('artistId') except GraphQLError as err: raise ProcessParticipantsException('Graphql error') from err except Exception as exp: raise ProcessParticipantsException( f'Error processing participants.\n{str(exp)}') from exp # Update participants in model. model.participants = [p for p in participants_dict.values()] return model def get_participants(model: BulkRelease) -> List[Dict]: """Merge all necessary data for each participant into a dict.""" participants_dict = {} all_participants = get_all_participants(model) for participant in all_participants: # Identity for porting name = participant if participants_dict.get(name) is None: participants_dict[name] = {'name': name} # We'll never get these in bulk upload documents participants_dict[name]['apple_id'] = None participants_dict[name]['spotify_uri'] = None return [p for p in participants_dict.values()] def get_all_participants(release: BulkRelease) -> List: """Collect all participants from parsed CSV, including duplicates.""" all_participants = [] # Orchard Artist - Do not parse cell separators - One artist, as written if release.project_artist: all_participants.append(release.project_artist) # Parse the rest of the fields for cell separators # Release Artist(s)-Primary Artist(s) if release.primary_artists.strip(): primary_artist = release.primary_artists for artist in primary_artist.split(ARTIST_FIELD_SEPARATOR): all_participants.append(artist.strip()) # Release Artist(s)-Featuring(s) if release.featurings and release.featurings.strip(): for artist in release.featurings.split(ARTIST_FIELD_SEPARATOR): all_participants.append(artist.strip()) # Release Artist(s)-Remixer(s) if release.remixers and release.remixers.strip(): for artist in release.remixers.split(ARTIST_FIELD_SEPARATOR): all_participants.append(artist.strip()) # Release Artist(s)-Producer(s) if release.producers and release.producers.strip(): for artist in release.producers.split(ARTIST_FIELD_SEPARATOR): all_participants.append(artist.strip()) # Release Artist(s)-Composer(s) if release.composers and release.composers.strip(): for artist in release.composers.split(ARTIST_FIELD_SEPARATOR): all_participants.append(artist.strip()) # Release Artist(s)-Orchestra(s) if release.orchestras and release.orchestras.strip(): for artist in release.orchestras.split(ARTIST_FIELD_SEPARATOR): all_participants.append(artist.strip()) # Release Artist(s)-Ensemble(s) if release.ensembles and release.ensembles.strip(): for artist in release.ensembles.split(ARTIST_FIELD_SEPARATOR): all_participants.append(artist.strip()) # Release Artist(s)-Conductor(s) if release.conductors and release.conductors.strip(): for artist in release.conductors.split(ARTIST_FIELD_SEPARATOR): all_participants.append(artist.strip()) for key, track in release.tracks.items(): # Track Artist(s)-Primary Artist(s) if track.artist and track.artist.strip(): for artist in track.artist.split(ARTIST_FIELD_SEPARATOR): all_participants.append(artist.strip()) # Track Artist(s)-Featuring(s) if track.featurings and track.featurings.strip(): for artist in track.featurings.split(ARTIST_FIELD_SEPARATOR): all_participants.append(artist.strip()) # Track Artist(s)-Remixer(s) if track.remixers and track.remixers.strip(): for artist in track.remixers.split(ARTIST_FIELD_SEPARATOR): all_participants.append(artist.strip()) # Track Artist(s)-Producer(s) if track.producers and track.producers.strip(): for artist in track.producers.split(ARTIST_FIELD_SEPARATOR): all_participants.append(artist.strip()) # Track Artist(s)-Composer(s) if track.composers and track.composers.strip(): for artist in track.composers.split(ARTIST_FIELD_SEPARATOR): all_participants.append(artist.strip()) # Track Artist(s)-Orchestra(s) if track.orchestras and track.orchestras.strip(): for artist in track.orchestras.split(ARTIST_FIELD_SEPARATOR): all_participants.append(artist.strip()) # Track Artist(s)-Ensemble(s) if track.ensembles and track.ensembles.strip(): for artist in track.ensembles.split(ARTIST_FIELD_SEPARATOR): all_participants.append(artist.strip()) # Track Artist(s)-Conductor(s) if track.conductors and track.conductors.strip(): for artist in track.conductors.split(ARTIST_FIELD_SEPARATOR): all_participants.append(artist.strip()) # Songwriter(s) if track.songwriters and track.songwriters.strip(): for artist in track.songwriters.split(ARTIST_FIELD_SEPARATOR): all_participants.append(artist.strip()) # Get track participants for i in range(1, MAX_PERFORMER_COUNT + 1): artist = getattr(track, f'performer_{i}_legal_name') if artist and artist.strip(): all_participants.append(artist.strip()) return all_participants