"""Megaphone inventory ingester. run this with docker compose run --build megaphone-inventory-ingester --api_key=megaphone_token --env=dev --mp_org_id=org_id """ import argparse from owslogger import logger import requests from podcast import config from podcast.logic import campaign as campaign_logic from podcast.models import ad_action as ad_action_model from podcast.models import advertisement as advertisement_model from podcast.models import user as user_model from podcast.models.api_campaign import ApiCampaign from podcast.models.ows_asset_transcoder import get_simple_asset_dict from podcast.utils import api_utils from podcast.utils.exc import OwsError podcast_admin_users = { 'dev': '1298', 'qa': '1298', 'prod': '88' } mp_inventory_logger = logger.setup( config.ENVIRONMENT, 'megaphone-inventory-importer', config.LOGGER_LEVEL, config.SERVICE_NAME, config.SERVICE_VERSION) def get_matching_ad(mp_ads, expected_type): """Get mp ad of the expected type or None.""" for ad in mp_ads: if ad['insertionPoint'] == expected_type: return ad return None def get_ad_assets(ad_id, asset_transcoder_api_url): """Return assets for an advertisement that are already in assets_final.""" request_headers = { 'Grass-Account-Type': 'vendor', 'Grass-Account-Id': '1', 'Correlation-Id': '12', 'Orchard-Identity-UUID': 'podcast-admin', 'Content-Type': 'application/json' } oat_response = requests.get( '{}/assets-by-ids-and-types'.format(asset_transcoder_api_url), params={ 'object_ids': [ad_id], 'object_types': ['adupload'] }, headers=request_headers ) if oat_response.status_code > 399: raise OwsError.response_error(oat_response) return get_simple_asset_dict(oat_response.json()['items']) def update_megaphone_audio(ad_action, asset_transcoder_api_url): """Update audio in megaphone for an advertisement.""" audio_url = get_ad_assets(ad_action['id'], asset_transcoder_api_url)['audio_url'] megaphone_api = ApiCampaign(is_org=True) megaphone_api.update_advertisement( ad_action['campaign_id'], ad_action['order_id'], ad_action['advertisement_id'], {'backgroundAudioFileUrl': audio_url} ) success_message = 'Re-pushed audio for ad read: {}'.format(ad_action['id']) mp_inventory_logger.info(success_message) print(success_message) def sync_outdated_ad_actions(ad_actions, new_advertisement, asset_transcoder_api_url): """Sync the ad action to new advertisement by re-linking and re-pushing latest audio. Re-linking all outdated ad actions and re-push audio in following 2 cases: 1) Check if the new megaphone ad is used by any ad action, if yes don't re-push audio else step 2. 2) Sort the ad actions in desc order based on due-date and ad action id and only re-push 1st ad action audio. """ is_audio_repushed = False new_ad_action_has_audio = False new_ad_actions = ad_action_model.get_ad_actions_by_advertisement_ids([new_advertisement['id']])['items'] for new_ad_action in new_ad_actions: if new_ad_action['status'] == 'completed': new_ad_action_has_audio = True break sorted_ad_actions = sorted( ad_actions, key=lambda ad_action: (ad_action['due_date'], ad_action['created_date']), reverse=True) for ad_action in sorted_ad_actions: db_has_audio = ad_action['status'] == 'completed' # re-link ad action updated_ad_action = ad_action_model.update_ad_action( ad_action['id'], {'advertisement_id': new_advertisement['id']}) re_link_success_message = 'Re-linked ad action: {} by updating advertisement id to: {}'.format( ad_action['id'], new_advertisement['id']) mp_inventory_logger.info(re_link_success_message) print(re_link_success_message) # re-push audio to megaphone if db_has_audio and new_ad_action_has_audio is False and is_audio_repushed is False: update_megaphone_audio(updated_ad_action, asset_transcoder_api_url) is_audio_repushed = True else: no_re_push_audio_message = 'Audio was not re-pushed for ad action: {} because of following reasons:' \ '\n Does old ad action has audio and status completed? : {}, expected: {}.' \ '\n Does new ad action having new megaphone advertisement id has audio ' \ 'and status completed? : {}, expected: {}.\n Is audio already repushed ' \ 'for the new advertisement? : {}, expected: {}.'.format( ad_action['id'], db_has_audio, True, new_ad_action_has_audio, False, is_audio_repushed, False) mp_inventory_logger.info(no_re_push_audio_message) print(no_re_push_audio_message) def update_outdated_ad_actions(megaphone_ads, order_id, asset_transcoder_api_url): """Get ad actions belonging to now deleted advertisements and relink them.""" megaphone_ad_ids = [ad['id'] for ad in megaphone_ads] db_ads = advertisement_model.get_advertisements_by_order_ids([order_id])['items'] # ads that exist in our db but not in mp outdated_ads = [ad['id'] for ad in db_ads if ad['id'] not in megaphone_ad_ids] ad_actions = ad_action_model.get_ad_actions_by_advertisement_ids(outdated_ads)['items'] # group ad actions by advertisement id ad_actions_by_type = {} for ad_action in ad_actions: roll_type = ad_action['roll_type'] if ad_actions_by_type.get(roll_type) is None: ad_actions_by_type[roll_type] = [] ad_actions_by_type[roll_type].append(ad_action) for roll_type, old_ad_actions in ad_actions_by_type.items(): new_ad = get_matching_ad(megaphone_ads, roll_type) if new_ad is None: # no matching advertisement currently continue sync_outdated_ad_actions(old_ad_actions, new_ad, asset_transcoder_api_url) def main(): """Ingest inventory.""" argparser = argparse.ArgumentParser(prog='megaphone inventory ingester') argparser.add_argument('--mp_org_id', required=True, help='Megaphone Org ID') argparser.add_argument('--env', required=True, help='env', default='dev') argparser.add_argument('--api_key', required=True, help='API Key') argparser.add_argument( '--asset_transcoder_api_url', required=False, help='asset_transcoder_api_url, https://qa-ows-asset-transcoder.theorchard.io' ) args = argparser.parse_args() asset_transcoder_api_url = args.asset_transcoder_api_url or 'http://localhost:5001' env = args.env def get_current_user(): return user_model.get_user_by_id(podcast_admin_users[env]) api_utils.get_current_user = get_current_user config.MEGAPHONE_API_TOKEN = args.api_key config.MEGAPHONE_ORG_ID = args.mp_org_id campaigns = campaign_logic.campaigns() for campaign in campaigns: campaign_id = campaign['id'] orders = campaign_logic.campaign_orders(campaign_id) for order in orders: order_id = order['id'] # Inserts new ads into db megaphone_ads = campaign_logic.advertisements(campaign_id, order_id) update_outdated_ad_actions(megaphone_ads, order_id, asset_transcoder_api_url) if __name__ == '__main__': main()