""" MRC Workflow. Ingest MRC data into MRC staging raw table in Snowflake. """ 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 get_aws_config from feed_ingestion.flows.mrc import config from feed_ingestion.flows.mrc import tasks from feed_ingestion.tasks import ftp_tasks from feed_ingestion.tasks import load_raw_table_tasks_sf from feed_ingestion.tasks import overall_status_tasks class Flow(base.FlowBase, base.FlowConfigMixin, base.FlowLoadRawMixinSF): """MRC Flow class. This flow downloads MRC data and loads to a main_market_share. """ def __init__(self): """Initialize an MRC workflow.""" super().__init__(config.feed_name, config.feed_version) def decider(self, schedule): """Orchestrate an MRC workflow. Args: schedule (callable): The scheduler method. """ bootstrap = schedule( 'bootstrap', self.bootstrap) requires_list = [bootstrap] if bootstrap.result.get('bootstrap.reload') == 'True': clear_s3_folders = schedule( 'clear_s3_folders', self.clear_s3_folders, requires=[bootstrap] ) requires_list = [clear_s3_folders] fetch_from_drop_location = schedule( 'fetch_from_drop_location', self.fetch_from_drop_location, requires=requires_list) if fetch_from_drop_location.result.get( 'fetch_from_drop_location.stop'): return fetch_from_drop_mapping_location = schedule( 'fetch_from_drop_mapping_location', self.fetch_from_drop_mapping_location, requires=[fetch_from_drop_location]) # Status updated to DOWNLOADED if files are present, else NOT_AVAILABLE set_status_to_downloaded = schedule( 'set_status_to_downloaded', self.set_status_to_downloaded, requires=[fetch_from_drop_mapping_location]) process_drop_files = schedule( 'process_drop_files', self.process_drop_files, requires=[set_status_to_downloaded]) create_temp_staging_raw_table = schedule( 'create_temp_staging_raw_table', self.create_temp_staging_raw_table, requires=[process_drop_files]) load_temp_staging_raw_table = schedule( 'load_temp_staging_raw_table', self.load_temp_staging_raw_table, requires=[create_temp_staging_raw_table]) load_staging_raw_table = schedule( 'load_staging_raw_table', self.load_staging_raw_table, requires=[load_temp_staging_raw_table]) set_ingested = schedule( 'set_overall_status_ingested', self.set_overall_status_ingested, requires=[load_staging_raw_table]) schedule( 'drop_remaining_temp_tables', self.drop_remaining_temp_tables, requires=[set_ingested]) @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'))) @property def clear_s3_folders(self): """Delete files from S3 folders if 'reload'.""" return self.create( name='clear_s3_folders', tasks=base.SyncRunner( tasks.clear_s3_folders.fill( namespace='clear_s3_folders', date='bootstrap.date', reload='bootstrap.reload' ) ) ) @property def fetch_from_drop_location(self): """Move files from FTP to S3 archive bucket.""" return self.create( name='fetch_from_drop_location', tasks=base.SyncRunner( ftp_tasks.fetch_from_drop_location.fill( namespace='fetch_from_drop_location', feed_name='bootstrap.feed_name', date='bootstrap.date', s3_archive_path='bootstrap.s3_archive_path', source_files_dict='bootstrap.file', ftp_creds=StaticParam(config.ftp), need_all_files=StaticParam(True)))) @property def fetch_from_drop_mapping_location(self): """Move mapping files from FTP to S3 archive bucket.""" return self.create( name='fetch_from_drop_mapping_location', tasks=base.SyncRunner( ftp_tasks.fetch_from_drop_location.fill( namespace='fetch_from_drop_mapping_location', feed_name='bootstrap.feed_name', date='bootstrap.date', s3_archive_path='bootstrap.s3_mapping_archive_path', ftp_creds=StaticParam(config.mapping_ftp), need_all_files=StaticParam(True), mapping=StaticParam(True)))) @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', processed_filename='bootstrap.processed_filename', file='fetch_from_drop_location.source_files_dict'))) @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_overall_status_ingested(self): """Set overall feed status INGESTED if all reports are completed.""" return self.create( name='set_overall_status_ingested', schedule_to_start=48000, 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_INGESTED)))) @property def create_temp_staging_raw_table(self): """Create temp staging raw table.""" return self.create( name='create_temp_staging_raw_table', generators=[temp_staging_tables_generator], tasks=base.SyncRunner( load_raw_table_tasks_sf.create_temp_staging_raw_table.fill( namespace='create_temp_staging_raw_table', date='{}.date'.format(self.bootstrap_namespace), sfdb_params='{}.sfdb_params'.format( self.bootstrap_namespace), feed_name='{}.feed_name'.format(self.bootstrap_namespace), secrets_path='{}.secrets_path'.format( self.bootstrap_namespace), temp_staging_raw_table='temp_staging_raw_table', kwargs='kwargs'))) @property def load_temp_staging_raw_table(self): """Load temp staging raw table.""" return self.create( name='load_temp_staging_raw_table', generators=[temp_staging_tables_generator], tasks=base.SyncRunner( load_raw_table_tasks_sf.load_temp_staging_raw_table.fill( namespace='load_temp_staging_raw_table', aws=StaticParam(get_aws_config()), feed_name='{}.feed_name'.format(self.bootstrap_namespace), secrets_path='{}.secrets_path'.format( self.bootstrap_namespace), date='{}.date'.format(self.bootstrap_namespace), sfdb_params='sfdb_params', temp_staging_raw_table='temp_staging_raw_table', key_dir='key_dir', kwargs='kwargs'))) @property def load_staging_raw_table(self): """Load permanent staging raw table.""" activity_name = 'load_staging_raw_table' return self.create( name=activity_name, generators=[temp_staging_tables_generator], tasks=base.SyncRunner( load_raw_table_tasks_sf.load_staging_raw_table.fill( namespace=activity_name, date='{}.date'.format(self.bootstrap_namespace), feed_name='{}.feed_name'.format(self.bootstrap_namespace), secrets_path='{}.secrets_path'.format( self.bootstrap_namespace), sfdb_params='sfdb_params', temp_staging_raw_table='temp_staging_raw_table', staging_raw_table='staging_raw_table', clean='clean', kwargs='kwargs'), overall_status_tasks.set_overall_status.fill( date='{}.date'.format(self.bootstrap_namespace), feed_name='{}.feed_name'.format(self.bootstrap_namespace), set_status_once=StaticParam(True), status=StaticParam( garcon_feed_status.STATUS_POPULATED_RAW_TABLE)))) @property def drop_remaining_temp_tables(self): """Drop remaining temp tables.""" activity_name = 'drop_remaining_temp_tables' return self.create( name=activity_name, tasks=base.SyncRunner( load_raw_table_tasks_sf. drop_remaining_temp_staging_raw_tables.fill( namespace=activity_name, feed_name='{}.feed_name'.format( self.bootstrap_namespace), temp_table_pattern='{}.temp_table_pattern'. format(self.bootstrap_namespace), date='{}.date'.format(self.bootstrap_namespace), sfdb_params='sfdb_params', secrets_path='{}.secrets_path'.format( self.bootstrap_namespace), ))) def temp_staging_tables_generator(context): """Generate parameters for temporary staging tables.""" date = context['bootstrap.date'] mrc_s3_file_path = context['bootstrap.s3_temp_staging_raw_bucket'] mapping_s3_file_path = \ context['bootstrap.s3_mapping_archive_path'] mrc_file = context['process_drop_files.' 'source_files_dict']['files'] mrc_mapping_files = context['fetch_from_drop_mapping_location.' 'source_files_dict']['files'] filenames = [f['file_name'] for f in [*mrc_file, *mrc_mapping_files]] mrc_temp_table = context['bootstrap.temp_staging_raw_table'] mrc_mapping_temp_tables = context[ 'bootstrap.mapping_temp_staging_raw_tables'] mrc_table = context['bootstrap.staging_raw_table'] mrc_mapping_tables = context['bootstrap.mapping_staging_raw_tables'] for file_name in filenames: name = file_name[:file_name.rfind('_')] temp_table = mrc_mapping_temp_tables.get(name) if \ mrc_mapping_temp_tables.get(name) is not None else mrc_temp_table s3_file_path = \ mrc_s3_file_path if file_name.replace( '.xlsx', '') in mrc_s3_file_path else mapping_s3_file_path.\ format(mapping=name, date=date) + '/' + file_name staging_table = mrc_mapping_tables.get(name) if \ mrc_mapping_tables.get(name) is not None else mrc_table yield dict( temp_staging_raw_table=temp_table, key_dir=s3_file_path, staging_raw_table=staging_table, kwargs=dict(download_date=date.replace('-', ''), file=file_name))