"""Generate DDEX deal elements for sound recording delivery in v38.""" from datetime import datetime, timedelta, timezone from typing import List, Optional from soundrecording_utils.constants.ddex.constants import DEFAULT_TERRITORY_CODE, DealType from soundrecording_utils.metadata.consolidation_logic import consolidate_rights from soundrecording_utils.metadata.types import OrchardSoundRecording from .e import E def _generate_sound_recording_deals( sound_recording_metadata: OrchardSoundRecording, service: str, takedown: bool = False, ): """Generate SoundRecording deals from the consolidated rights.""" if not takedown: consolidated_rights = consolidate_rights(sound_recording_metadata, service) if consolidated_rights: deals = [] for monetize_deal in consolidated_rights.monetization_policies: territories = sorted(monetize_deal.territories) deals.append( _generate_sound_recording_deal( territories, start_date=monetize_deal.start_date, end_date=monetize_deal.end_date, deal_type=DealType.MONETIZE.value, ) ) for sixtyseconds_deal in consolidated_rights.sixtyseconds_policies: territories = sorted(sixtyseconds_deal.territories) deals.append( _generate_sound_recording_deal( territories, start_date=sixtyseconds_deal.start_date, end_date=sixtyseconds_deal.end_date, deal_type=DealType.MONETIZE.value, sixtyseconds=True, ) ) for block_deal in consolidated_rights.block_access_policies: territories = sorted(block_deal.territories) deals.append( _generate_sound_recording_deal( territories, start_date=block_deal.start_date, end_date=block_deal.end_date, deal_type=DealType.BLOCK_ACCESS.value, ) ) if deals: return deals # handle 'takedown' return [ _generate_sound_recording_deal( [DEFAULT_TERRITORY_CODE], end_date=(datetime.now(timezone.utc) - timedelta(days=2)).replace( hour=0, minute=0, second=0, microsecond=0 ), deal_type=DealType.MONETIZE.value, ) ] def _generate_sound_recording_deal( territories: List[str], deal_type: str, start_date: Optional[datetime] = None, end_date: Optional[datetime] = None, sixtyseconds: bool = False, ): """Generate Deal node by StartDateTime with Territories.""" periods = [] if start_date: periods.append(E('StartDateTime', start_date.replace(microsecond=0, tzinfo=None).isoformat())) if end_date: periods.append(E('EndDateTime', end_date.replace(microsecond=0, tzinfo=None).isoformat())) if not periods: raise ValueError('Deal must include start and/or end date') rights_claim_policies = ( _generate_rights_claim_policies() if sixtyseconds else (E('RightsClaimPolicy', E('RightsClaimPolicyType', deal_type)),) ) return E( 'Deal', E( 'DealTerms', E('CommercialModelType', 'RightsClaimModel'), E( 'Usage', E('UseType', 'UserMakeAvailableUserProvided'), ), *[E('TerritoryCode', territory) for territory in territories], E('ValidityPeriod', *periods), *rights_claim_policies, ), ) def _generate_rights_claim_policies(): """Generate the sixty-second conditional RightsClaimPolicy pair.""" return ( E( 'RightsClaimPolicy', _generate_rights_claim_policy_condition('LessThanOrEqualTo'), E('RightsClaimPolicyType', DealType.MONETIZE.value), ), E( 'RightsClaimPolicy', _generate_rights_claim_policy_condition('MoreThanOrEqualTo'), E('RightsClaimPolicyType', DealType.BLOCK_ACCESS.value), ), ) def _generate_rights_claim_policy_condition(relational_relator: str): """Generate the shared sixty-second policy condition.""" return E( 'Condition', E('Value', '60'), E('Unit', 'Seconds'), E('ReferenceCreation', 'ConsumerResource'), E('RelationalRelator', relational_relator), E('MeasurementType', 'Audio'), ) def generate_release_deal( release_reference: str, sound_recording_metadata: OrchardSoundRecording, service: str, takedown: bool = False, ): """Generate a ReleaseDeal node.""" return E( 'ReleaseDeal', E('DealReleaseReference', release_reference), *_generate_sound_recording_deals(sound_recording_metadata, service, takedown=takedown), )