"""Collection of functions used to transform data for Proper feeds. Functions used in data transformation, specified in conf/settings.py NEW_RELEASES_MAP and consumed by proper.py during CSV construction. *** PLEASE FOLLOW THE FOLLOWING METHOD SIGNATURE CONVENTION *** def transform_field(field_value, **kwargs): You may then pull your dependent values from kwargs by the keys defined in NEW_RELEASES_MAP 'depends' list. """ from functools import partial import json import re import time from owsrequest import request from feed_sender.flows.proper_new_releases_tracks import exceptions from feed_sender.flows.proper_new_releases_tracks.conf import ( product_physical_packaging_mapping) from feed_sender.flows.proper_new_releases_tracks.conf import settings from feed_sender.flows.proper_new_releases_tracks.util import ( distribution_format_mapping) def transform_title(org_title, **kwargs): """Transform Orchard Title to titles conforming to Proper format. Args: org_title (str): Track Title from Art Relations. kwargs (dict): NOT USED - necessary for transform function contract Returns: str: New title transformed to Proper format. """ new_title = org_title.strip().upper() title_words = new_title.split() if len(title_words) > 1 and title_words[0] == 'THE': new_title = '{rest_of_value},{the}'.format( the=title_words[0], rest_of_value=' '.join(title_words[1:])) return new_title def transform_genre_subgenre(genre_id, **kwargs): """Transform Orchard genre id to Proper value. Args: genre_id (str): Orchard genre id. kwargs (dict): 'subgenre': subgenre id, 'genre_subgenre_mapping': a store's genre-subgenre mapping Returns: str: Proper genre name. """ subgenre_id = kwargs.get('subgenre') genre_mapping = kwargs.get('genre_subgenre_mapping') key = '{genre_id}_{subgenre_id}'.format( genre_id=genre_id, subgenre_id=subgenre_id) if key in genre_mapping: return genre_mapping.get(key).get('genre') raise ValueError( 'No Mapping Found for Genre and Subgenre {key}'.format(key=key)) def transform_subgenre_genre(subgenre_id, **kwargs): """Transform Orchard subgenre id to Proper value. Args: subgenre_id (str): Orchard subgenre id. kwargs (dict): 'genre': genre id, 'genre_subgenre_mapping': a store's genre-subgenre mapping Returns: str: Proper sub_genre name. """ genre_id = kwargs.get('genre') genre_mapping = kwargs.get('genre_subgenre_mapping') key = '{genre_id}_{subgenre_id}'.format( genre_id=genre_id, subgenre_id=subgenre_id) if key in genre_mapping: return genre_mapping.get(key).get('subgenre') raise exceptions.TransformException( 'No Mapping Found for Genre and Subgenre {key}'.format(key=key)) def transform_artist(name, **kwargs): """Transform Orchard Artist name to names conforming to Proper format. Args: name (str): Artist Name from Art Relations. kwargs (dict): 'individual': 'Y' or 'N' Returns: str: New name transformed to Proper format. """ if name is None: return '' new_name = name.strip().upper() artist_type = kwargs.get('individual', 'N') if artist_type == 'Y': name_words = new_name.split() if len(name_words) > 1 and name_words[0] == 'THE': new_name = '{rest_of_value},{the}'.format( the=name_words[0], rest_of_value=' '.join(name_words[1:])) elif len(name_words) > 1: new_name = '{last_word},{rest_of_words}'.format( last_word=name_words[-1], rest_of_words=' '.join( name_words[:-1])) return new_name else: return new_name def transform_formid(key, **kwargs): """Transform Orchard distribution form_id to Proper value. Args: key(str): Distribution format_id kwargs (dict): NOT USED - necessary for transform function contract Returns: str: Proper form_id. """ mapping = distribution_format_mapping.get_distribution_format_mapping() if key and key in mapping: proper_val = mapping.get(key) return proper_val.get('formid') raise exceptions.TransformException( 'No Form_id found for {key}'.format(key=key)) def transform_format_id(key, **kwargs): """Transform Orchard distribution form_id to Proper value. Args: key(str): Distribution format_id kwargs (dict): NOT USED - necessary for transform function contract Returns: str: Proper form_id. """ mapping = distribution_format_mapping.get_distribution_format_mapping() if key and key in mapping: proper_val = mapping.get(key) return proper_val.get('format') raise exceptions.TransformException( 'No Format_id found for {key}'.format(key=key)) def transform_packageid(key, **kwargs): """Transform Orchard packaging id to Proper value. Args: key(str): Orchard packaging id. kwargs (dict): NOT USED - necessary for transform function contract Returns: str: Proper packaging id, or empty string if no mapped value found. """ if (key and key in product_physical_packaging_mapping. product_physical_packaging_mapping): proper_val = ( product_physical_packaging_mapping. product_physical_packaging_mapping.get(key)) return proper_val return '' def transform_carveout(upc, **kwargs): """Transform carveout information into export_only Y or '' flag. Note that correlation id is. Args: upc(int): Orchard UPC. kwargs (dict): Dictionary with keys carveout_service_name, carveout_service_resource_path, application_name, application_env and correlation_id. Returns: str: 'Y' or ''. Raises: Exception: Custom exception with a microservice's error message. """ service_name = kwargs.get('carveout_service_name') resource = kwargs.get('carveout_service_resource_path') application_name = kwargs.get('application_name') application_env = kwargs.get('application_env') get_request = partial(request.process, application_name, application_env) correlation_id = kwargs.get('correlation_id') retry = settings.REQUEST_RETRY successful_request = False wait_time = settings.INITIAL_REQUEST_WAIT_TIME while not successful_request and retry > 0: if retry < settings.REQUEST_RETRY: time.sleep(wait_time) wait_time *= settings.RETRY_WAIT_EXPONENT response = get_request( 'GET', service_name, resource.format(upc=upc), correlation_id) response_content = json.loads(response.content.decode('utf-8')) retry -= 1 successful_request = response.status_code in [200, 404] if response.status_code not in [200, 404]: raise Exception( 'Request to ows_carveout microservice failed. ' 'Full Message: {full_message}, Message: {message}'.format( full_message=response_content, message=response_content.get('message'))) if (response.status_code == 200 and response_content and 'UK' in response_content.values()): return 'Y' else: return '' def transform_duration(length_minutes, **kwargs): """Transform Orchard track minutes/seconds to Proper format. Args: length_minutes (int): Track minutes length, from AR tracks. kwargs (dict): 'length_seconds' (int): Track seconds length, from AR tracks. Returns: str: Duration transformed to Proper format, 'MM:SS' or '' if 0 length. """ if length_minutes is None: length_minutes = 0 length_seconds = kwargs.get('length_seconds') if not length_minutes and not length_seconds: return '' duration = '{:02d}:{:02d}'.format(length_minutes, length_seconds) return duration def transform_price(price, **kwargs): """Transform Orchard buy price to price format. Args: price (int): buy price kwargs (dict): NOT USED - necessary for transform function contract Returns: str: If a string in float/int format is passed, convert to float with 2 decimal places. """ if re.match("^\d+?\.\d+?$", str(price)): # noqa price = '%0.2f' % price return price def transform_product_code_skip_if_null(product_code, **kwargs): """Skip release if product code is empty or NULL. Otherwise return product code as Proper catalogue number Args: product_code (str): Orchard product code kwargs (dict): 'release_id' (int): Orchard release ID Returns: str: Proper catalogue number Raises: TransformException: if product code is empty or NULL """ if not product_code: # raise exception with release_id information release_id = kwargs.get('release_id') error_msg = "Product code doesn't exist for release " \ '{release_id}'.format(release_id=release_id) raise exceptions.TransformException(error_msg) # return product code directly as catalogue number return product_code def transform_upc(display_upc, **kwargs): """Transform Orchard UPC into barcode conforming to Proper format. Args: display_upc (str): Orchard display UPC kwargs (dict): 'manufacturer_upc' (str): Orchard manufacturer UPC Returns: str: Proper barcode """ manufacturer_upc = kwargs.get('manufacturer_upc') # if manufacturer upc is unavailable, return display upc if manufacturer_upc is None: return display_upc # strip white spaces manufacturer_upc = manufacturer_upc.strip() # if manufacturer upc is invalid, return display upc if manufacturer_upc in ['0', '']: return display_upc # return valid manufacturer upc return manufacturer_upc def transform_description(description, **kwargs): """Add project description and value adds to obtain Proper Narrative. Args: description (str): Project description kwargs (dict): 'display_configuration' (str): Display configuration Returns: str: Proper Narrative """ display_configuration = kwargs.get('display_configuration', '') # Default narrative to description narrative = description if display_configuration: # Add value adds to description extra_line = 'The full display configuration for this product, ' \ 'including value adds, is: ' \ '{}'.format(display_configuration) if narrative: narrative += '. {}'.format(extra_line) else: narrative = extra_line return narrative