"""iTunes Marketshare Workflow.""" from garcon import param from garcon.param import StaticParam from garcon_contrib.dynamo_feed_status import garcon_feed_status from snowflake_connector.etl_connector import SQLLoader from feed_ingestion.flows import base from feed_ingestion.flows.base import FlowBase from feed_ingestion.flows.base import FlowConfigMixin from feed_ingestion.flows.base import FlowLoadMarketshareMixinSF from feed_ingestion.flows.itunes_marketshare import config from feed_ingestion.flows.itunes_marketshare import tasks from feed_ingestion.flows.itunes_marketshare.stage_loader import \ ITunesMarketshareSL from feed_ingestion.tasks import date_tasks from feed_ingestion.tasks import feed_status_tasks from feed_ingestion.tasks import overall_status_tasks from feed_ingestion.tasks import reporter_tasks class Flow(FlowBase, FlowConfigMixin, FlowLoadMarketshareMixinSF): """iTunes Marketshare Flow class. This flow downloads iTunes Marketshare data and loads to a main_market_share. """ def __init__(self): """Initialize a iTunes Marketshare workflow.""" super(Flow, self).__init__(config.feed_name, config.feed_version) def decider(self, schedule, context): """Orchestrate a iTunes Marketshare workflow. Args: schedule (callable): The scheduler method. """ skip_reporter_tasks = context.get('skip_reporter') == 'True' get_first_day_of_month = schedule( 'get_first_day_of_month', self.get_first_day_of_month) bootstrap = schedule( 'bootstrap', self.bootstrap, requires=[get_first_day_of_month]) get_vendors_and_regions = schedule( 'get_vendors_and_regions', self.get_vendors_and_regions, requires=[bootstrap]) next_task = bootstrap if not skip_reporter_tasks: reporter_to_s3 = schedule( 'reporter_to_s3', self.reporter_to_s3, requires=[get_vendors_and_regions]) next_task = reporter_to_s3 check_files_on_s3 = schedule( 'check_files_on_s3', self.check_files_on_s3, requires=[next_task]) if check_files_on_s3.result.get('check_files_on_s3.stop') is True: return reset_dynamo_db_status = schedule( 'reset_dynamo_db_status', self.reset_dynamo_db_status, requires=[check_files_on_s3]) process_drop_files = schedule( 'process_drop_files', self.process_drop_files, requires=[reset_dynamo_db_status]) load_staging_raw_table = schedule( 'load_staging_raw_table', self.load_staging_raw_table, requires=[process_drop_files]) load_marketshare_table = schedule( 'load_market_share_table', self.load_marketshare_table, requires=[load_staging_raw_table]) mark_ingested_files = schedule( 'mark_ingested_files', self.mark_ingested_files, requires=[load_marketshare_table] ) schedule( 'set_status_to_ingested', self.set_status_to_ingested, requires=[mark_ingested_files], ) @property def get_first_day_of_month(self): """Get the first day of the month of date.""" return self.create( name='get_first_day_of_month', tasks=base.SyncRunner( date_tasks.get_first_day_of_month.fill( namespace='get_first_day_of_month', date='context_date'))) @property def bootstrap(self): """Bootstrap initial configuration.""" return self.create( name='bootstrap', tasks=base.SyncRunner( tasks.bootstrap.fill( namespace='bootstrap', date='get_first_day_of_month.date', reload='reload'))) @property def get_vendors_and_regions(self): """Check if there are some new files in s3_download_path.""" return self.create( name='get_vendors_and_regions', generators=[vendor_generator], tasks=base.SyncRunner( tasks.get_vendors_and_regions.fill( namespace='get_vendors_regions', vendor='generator.vendor', report_type='generator.report_type'))) @property def check_files_on_s3(self): """Check if there are some new files in s3_download_path.""" return self.create( name='check_files_on_s3', tasks=base.SyncRunner( feed_status_tasks.check_files_on_s3.fill( namespace='check_files_on_s3', feed_name='bootstrap.feed_name', date='bootstrap.date', file_pattern='bootstrap.file_pattern', s3_download_path='bootstrap.s3_archive_path'))) @property def reporter_to_s3(self): """Load Reporter files to Apple Music Streams' S3 archives bucket.""" return self.create( name='reporter_to_s3', generators=[itunes_reports_generator], schedule_to_start=14400, tasks=base.SyncRunner( reporter_tasks.extract_reporter_file_to_s3.fill( namespace='reporter_to_s3', reporter_account='generator.reporter_account', report_type='generator.report_type', report_country='generator.report_country', report_role=param.StaticParam('finance'), date='bootstrap.date', destination_s3_path='bootstrap.s3_archive_path', feed_name=param.StaticParam(self.feed_name)))) @property def mark_ingested_files(self): """Put list of ingested files in DynamoDB.""" return self.create( name='mark_ingested_files', tasks=base.SyncRunner( feed_status_tasks.mark_ingested_files.fill( namespace='mark_ingested_files', feed_name='bootstrap.feed_name', date='bootstrap.date', source_files_dict='check_files_on_s3.source_files_dict'))) @property def process_drop_files(self): """Process and archive drop files on S3.""" return self.create( name='process_drop_files', tasks=base.SyncRunner( tasks.process_drop_files.fill( namespace='process_drop_files', date='bootstrap.date', s3_archive_path='bootstrap.s3_archive_path', s3_preprocessed_path='bootstrap.s3_preprocessed_path', preprocessed_filename='bootstrap.preprocessed_filename', source_files_dict='check_files_on_s3.source_files_dict' ))) @property def set_status_to_ingested(self): """Set overall feed status to INGESTED.""" return self.create( name='set_status_to_ingested', tasks=base.SyncRunner( overall_status_tasks.set_overall_status.fill( namespace='set_status_to_ingested', feed_name='bootstrap.feed_name', date='bootstrap.date', status=StaticParam( garcon_feed_status.STATUS_INGESTED, ), ), ), ) load_staging_raw_table = ITunesMarketshareSL.load_activity( feed_name=config.feed_name, secrets_path=config.secrets_path, sql_loader=SQLLoader(__file__), requirements=dict( date='bootstrap.date', s3_dir_path='bootstrap.s3_preprocessed_path', staging_raw_table_name='bootstrap.staging_raw_table', source_files_dict='process_drop_files.source_files_dict', sfdb_params='sfdb_params')) def itunes_reports_generator(context): """Generate reporter_account, report_type params that we want to process. Used by reporter_to_s3 activity. Args: context (dict): The current context. Must have a 'bootstrap.itunes_reports' key that has a list of all (reporter_account, report_type) tuples to download and process. Yields: (dict): Dictionary for each reporter account / report we want to download and process. """ for (reporter_account, report_type) in context['bootstrap.itunes_reports']: available_countries = context.get( f'get_vendors_regions.{reporter_account}') for report_country in available_countries: gen = { 'generator.reporter_account': reporter_account, 'generator.report_type': report_type, 'generator.report_country': report_country} yield gen def vendor_generator(context): """Generate vendor names that we want to process. Used by get_vendors_regions activity. Yields: str: vendor we want to process. """ for vendor, report_type in config.itunes_reports: gen = { 'generator.vendor': vendor, 'generator.report_type': report_type } yield gen