"""DDEX marshmallow context schema.""" from ddex_ingester_common.helpers.xml_document_helper import ( get_conditional_text_value ) from ddex_ingester_common.models.purged_ddex.body import Body from ddex_ingester_common.models.purged_ddex.product import Product from marshmallow import (EXCLUDE, fields, post_load, pre_load, Schema, validates_schema, ValidationError) from marshmallow.utils import get_value class ReleaseIdSchema(Schema): """Release Id Schema.""" class Meta: """Marshmallow Meta class.""" unknown = EXCLUDE upc = fields.String(data_key='ICPN') isrc = fields.String(data_key='ISRC') grid = fields.String(data_key='GRid') catalog_number = fields.Dict(data_key='CatalogNumber') @validates_schema def validate_identifiers(self, data, **kwargs): """Enforces validation for the ReleaseId field.""" if not data.get('upc') and not data.get('isrc'): raise ValidationError( 'Neither UPC or ISRC is set in releaseId field') class ProductSchema(Schema): """Product Schema.""" class Meta: """Marshmallow Meta class.""" unknown = EXCLUDE release_id = fields.Nested( ReleaseIdSchema, data_key='ReleaseId', required=False, load_default=None ) product_name = fields.String( required=False, allow_none=True, load_default='' ) @pre_load def preprocess(self, data, **kwargs): """Ensure we have relevant fields available.""" if not data.get('ReleaseId'): data['ReleaseId'] = { 'ICPN': get_conditional_text_value( data['Release'][0]['ReleaseId']['ICPN']) } return data @post_load def make_product(self, data, **kwargs): """Make Product model object.""" formatted_data = { 'upc': get_value(data, 'release_id.upc', ''), 'grid': get_value(data, 'release_id.grid', ''), 'catalog_number': get_conditional_text_value( get_value( data, 'release_id.catalog_number', '' ) ), 'product_name': data.get('product_name', ''), } return Product(**formatted_data) class PurgedDDEXSchema(Schema): """DDEX Schema.""" class Meta: """Marshmallow Meta class.""" unknown = EXCLUDE product = fields.Nested(ProductSchema, required=False, load_default=None) bucket = fields.String(load_default=None) key = fields.String(load_default=None) message_id = fields.String( required=False, allow_none=True, load_default='' ) message_thread_id = fields.String( required=False, allow_none=True, load_default='') execution_name = fields.String(allow_none=True) state_machine_name = fields.String(allow_none=True) execution_start_time = fields.String(allow_none=True) @pre_load def preprocess(self, data, **kwargs): """Alter the data passed to ``load()`` before Marshmallow attempts deserialization.""" # noqa # Get Product and enrich with asset information prefix = 'ern:' if 'ern:PurgeReleaseMessage' in data.keys() else 'ernm:' # noqa thread_id = prefix + 'PurgeReleaseMessage.MessageHeader.MessageThreadId' # noqa message_id = prefix + 'PurgeReleaseMessage.MessageHeader.MessageId' title = None purged_schema = prefix + 'PurgeReleaseMessage.PurgedRelease' # If we can't find PurgeReleaseMessage, accept NewReleaseMessage. # These fields exist in different places to PurgeReleaseMessage. if not get_value(data, purged_schema): prefix = ( 'ern:' if 'ern:NewReleaseMessage' in data.keys() else 'ernm:') purged_schema = prefix + 'NewReleaseMessage.ReleaseList' thread_id = ( prefix + 'NewReleaseMessage.MessageHeader.MessageThreadId') message_id = ( prefix + 'NewReleaseMessage.MessageHeader.MessageId') release_details = get_value( get_value(data, purged_schema + '.Release')[0], 'ReleaseDetailsByTerritory' ) title = get_value( get_value(release_details[0], 'Title')[0], 'TitleText' ) release = get_value( data, purged_schema, [] ) product_name = title or get_value( get_value(release, 'Title')[0], 'TitleText' ) data['product'] = { **release, 'product_name': product_name, } data['message_id'] = get_value( data, message_id ) data['message_thread_id'] = get_value( data, thread_id ) return data @post_load def make_body(self, data, **kwargs): """Make Context model object.""" return Body(**data)