""" iTunes data ingestion workflow. Extract, transform and load iTunes raw data to fact_analytics table. """ from garcon.param import StaticParam from garcon_contrib.dynamo_feed_status import garcon_feed_status from feed_ingestion.flows import base from feed_ingestion.flows.base import FlowConfigMixin from feed_ingestion.flows.base import FlowLicensor from feed_ingestion.flows.itunes import config from feed_ingestion.flows.itunes import generators from feed_ingestion.flows.itunes import load_fact_table_tasks from feed_ingestion.flows.itunes import load_raw_table_tasks from feed_ingestion.flows.itunes import tasks from feed_ingestion.flows.itunes import update_mapping_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(FlowLicensor, FlowConfigMixin): """Class representing a workflow.""" def __init__(self): """Initialize flow object.""" super().__init__('itunes', '2.0') self.timeout = 3600 # override default to 1 hour (60 * 60 * 1) def decider(self, schedule): """Activity decider. Args: schedule (callable): The scheduler method. """ check_concurrent_status = schedule( 'check_concurrent_status', self.check_concurrent_status) if check_concurrent_status.result.get( 'check_concurrent_status.stop'): return bootstrap = schedule( 'bootstrap', self.bootstrap, requires=[check_concurrent_status]) if bootstrap.result.get('bootstrap.stop'): return # Download report files and upload them to s3 if bootstrap.result.get('bootstrap.licensor') == 'awal'\ and bootstrap.result.get('bootstrap.use_s3'): grab_drop_files_from_s3_awal = schedule( 'grab_drop_files_from_s3_awal', self.grab_drop_files_from_s3_awal, requires=[bootstrap]) file_to_archive_bucket = grab_drop_files_from_s3_awal else: reporter_to_s3 = schedule( 'reporter_to_s3', self.reporter_to_s3, requires=[bootstrap]) file_to_archive_bucket = reporter_to_s3 # Check if all files were downloaded update_feed_file_status = schedule( 'update_feed_file_status', self.update_feed_file_status, requires=[file_to_archive_bucket]) check_available_reports = schedule( 'check_available_reports', self.check_available_reports, requires=[update_feed_file_status]) if check_available_reports.result.get( 'check_available_reports.stop') is True: return set_status_to_downloaded = schedule( 'set_status_to_downloaded', self.set_status_to_downloaded, requires=[check_available_reports]) load_temp_table = schedule( 'load_temp_table', self.load_temp_table, requires=[set_status_to_downloaded]) clear_staging_raw_itunes = schedule( 'clear_staging_raw_itunes', self.clear_staging_raw_itunes, requires=[load_temp_table]) load_staging_raw_itunes = schedule( 'load_staging_raw_itunes', self.load_staging_raw_itunes, requires=[clear_staging_raw_itunes]) set_populated_status = schedule( 'set_populated_status', self.set_populated_status, requires=[load_staging_raw_itunes]) # Stop the flow if only populating raw tables needed if bootstrap.result.get('bootstrap.populate_only'): return insert_new_zipcode = schedule( 'insert_new_zipcode', self.insert_new_zipcode, requires=[set_populated_status]) update_apple_id_mapping = schedule( 'update_apple_id_mapping', self.update_apple_id_mapping, requires=[insert_new_zipcode]) update_upc_isrc_on_staging_raw = schedule( 'update_upc_isrc_on_staging_raw', self.update_upc_isrc_on_staging_raw, requires=[update_apple_id_mapping]) load_staging_fact = schedule( 'load_staging_fact', self.load_staging_fact, requires=[update_upc_isrc_on_staging_raw]) schedule( 'load_fact_tables', self.load_fact_tables, requires=[load_staging_fact]) @property def check_concurrent_status(self): """Check and reset feed status if it is needed.""" return self.create( name='check_concurrent_status', tasks=base.SyncRunner( tasks.check_concurrent_status.fill( namespace='check_concurrent_status', date='context_date', reload='reload', domain=StaticParam(self.domain), licensor='licensor'))) @property def bootstrap(self): """Bootstrap initial configuration.""" return self.create( name='bootstrap', tasks=base.SyncRunner( tasks.bootstrap.fill( namespace='bootstrap', date='context_date', reload='reload', licensor='licensor', snowflake_error_limit='snowflake_error_limit', snowflake_error_on_column_count_mismatch=( 'snowflake_error_on_column_count_mismatch'), populate_only='populate_only', use_s3='use_s3'))) @property def grab_drop_files_from_s3_awal(self): """Copy files from drop location and archive files on s3.""" return self.create( name='grab_drop_files_from_s3_awal', schedule_to_start=48000, tasks=base.SyncRunner( tasks.grab_drop_files_from_s3_awal.fill( namespace='grab_drop_files_from_s3_awal', feed_name='bootstrap.feed_name', date='bootstrap.date'))) @property def reporter_to_s3(self): """Load Reporter files to iTunes S3 archives bucket.""" return self.create( name='reporter_to_s3', generators=[generators.itunes_reports_generator], schedule_to_start=48000, tasks=base.SyncRunner( reporter_tasks.extract_reporter_file_to_s3.fill( namespace='reporter_to_s3', reporter_account='reporter_account', report_type=StaticParam(config.report_name), report_role=StaticParam('sales'), date='bootstrap.date', destination_s3_path='bootstrap.s3_archive_bucket', feed_name='bootstrap.feed_name', licensor='bootstrap.licensor', vendors='vendors_config'))) @property def update_feed_file_status(self): """Update Feed Status whether files were downloaded. (Or not available). """ return self.create( name='update_feed_file_status', tasks=base.SyncRunner( feed_status_tasks.update_feed_file_status.fill( namespace='feed_file_status', feed_name='bootstrap.feed_name', date='bootstrap.date', s3_path='bootstrap.s3_archive_bucket', files='bootstrap.expected_download_files', redownload='redownload', report_type=StaticParam('default'), contexts_config='bootstrap.contexts', licensor='licensor'))) @property def load_temp_table(self): """Populate temp table.""" return self.create( name='populate_temp_table', generators=[generators.available_vendors_generator], tasks=base.SyncRunner( load_raw_table_tasks.populate_temp_tables.fill( namespace='populate_temp_table', date='bootstrap.date', feed_name='bootstrap.feed_name', sfdb_params='sfdb_params', report_data='report_data', secrets_path=StaticParam(config.secrets_path), snowflake_error_limit='snowflake_error_limit', snowflake_error_on_column_count_mismatch=( 'bootstrap.' 'snowflake_error_on_column_count_mismatch'), use_s3='use_s3'))) @property def clear_staging_raw_itunes(self): """Clear staging raw table.""" return self.create( name='clear_staging_raw_itunes', tasks=base.SyncRunner( load_raw_table_tasks.clear_staging_raw_itunes.fill( date='bootstrap.date', feed_name='bootstrap.feed_name', secrets_path=StaticParam(config.secrets_path), sfdb_params='sfdb_params', licensor='licensor'))) @property def load_staging_raw_itunes(self): """Load staging raw table.""" return self.create( name='load_staging_raw_itunes', generators=[generators.available_vendors_generator], tasks=base.SyncRunner( load_raw_table_tasks.load_staging_raw_itunes.fill( date='bootstrap.date', secrets_path=StaticParam(config.secrets_path), feed_name='bootstrap.feed_name', report_data='report_data', sfdb_params='sfdb_params', use_s3='use_s3'))) @property def set_populated_status(self): """Set status populated staging raw table.""" return self.create( name='set_populated_status', tasks=base.SyncRunner( load_raw_table_tasks.set_populated_status.fill( date='bootstrap.date', feed_name='bootstrap.feed_name'))) @property def insert_new_zipcode(self): """Insert into dim_zip table with new zip code.""" return self.create( name='insert_new_zipcode', tasks=base.SyncRunner( update_mapping_tasks.insert_new_zipcode.fill( secrets_path=StaticParam(config.secrets_path), sfdb_params='sfdb_params', date='bootstrap.date', feed_name='bootstrap.feed_name', licensor='licensor'))) @property def update_apple_id_mapping(self): """Update apple_id_mapping table in Snowflake.""" return self.create( name='update_apple_id_mapping', tasks=base.SyncRunner( update_mapping_tasks.update_apple_id_mapping.fill( secrets_path=StaticParam(config.secrets_path), sfdb_params='sfdb_params', date='bootstrap.date', licensor='licensor', skip_mapping='skip_mapping'))) @property def update_upc_isrc_on_staging_raw(self): """Update upc and isrc on staging_raw_itunes from apple_id_mapping.""" return self.create( name='update_upc_isrc_on_staging_raw', tasks=base.SyncRunner( load_raw_table_tasks.update_upc_isrc_on_staging_raw.fill( secrets_path=StaticParam(config.secrets_path), sfdb_params='sfdb_params', licensor='licensor', date='bootstrap.date', skip_mapping='skip_mapping'))) @property def load_staging_fact(self): """Update ingestion status in raw_file_ingestion_history table.""" return self.create( name='load_staging_fact', tasks=base.SyncRunner( load_fact_table_tasks.load_staging_fact.fill( date='bootstrap.date', secrets_path=StaticParam(config.secrets_path), sfdb_params='sfdb_params', licensor='licensor'))) @property def load_fact_tables(self): """Load data into fact_analytics tables.""" return self.create( name='load_fact_tables', tasks=base.SyncRunner( load_fact_table_tasks.delete_fact_data.fill( date='bootstrap.date', feed_name='bootstrap.feed_name', licensor='licensor', secrets_path=StaticParam(config.secrets_path), sfdb_params='sfdb_params'), load_fact_table_tasks.load_fact_tables.fill( namespace='load_fact_tables', date='bootstrap.date', feed_name='bootstrap.feed_name', secrets_path=StaticParam(config.secrets_path), sfdb_params='sfdb_params', licensor='licensor'), load_fact_table_tasks.drop_staging_fact.fill( date='bootstrap.date', secrets_path=StaticParam(config.secrets_path), sfdb_params='sfdb_params', licensor='licensor', ), load_fact_table_tasks.set_overall_status_to_ingested.fill( feed_name='bootstrap.feed_name', secrets_path=StaticParam(config.secrets_path), date='bootstrap.date'))) @property def set_status_to_downloaded(self): """Set overall feed status to DOWNLOADED.""" return self.create( name='set_status_to_downloaded', tasks=base.SyncRunner( overall_status_tasks.set_overall_status.fill( namespace='set_status_to_downloaded', feed_name='bootstrap.feed_name', date='bootstrap.date', status=StaticParam( garcon_feed_status.STATUS_DOWNLOADED)))) @property def set_status_to_populated_raw_table(self): """Set overall feed status to POPULATED_RAW_TABLE.""" return self.create( name='set_status_to_populated_raw_table', tasks=base.SyncRunner( overall_status_tasks.set_overall_status.fill( namespace='set_status_to_populated_raw_table', feed_name='bootstrap.feed_name', date='bootstrap.date', status=StaticParam( garcon_feed_status.STATUS_POPULATED_RAW_TABLE)))) @property def check_available_reports(self): """Check status of reports whether we could continue execution.""" return self.create( name='check_available_reports', schedule_to_start=48000, tasks=base.SyncRunner( tasks.check_available_reports.fill( namespace='check_available_reports', date='bootstrap.date', s3_path='bootstrap.s3_archive_bucket', licensor='licensor')))