""" Line Ingestion Workflow. Ingest data from Line feed. Files downloaded from theorchard s3. """ 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.line import config from feed_ingestion.flows.line import tasks from feed_ingestion.tasks import load_fact_tables_tasks_sf from feed_ingestion.tasks import load_raw_table_tasks_sf from feed_ingestion.tasks import overall_status_tasks class Flow(base.FlowBase, base.FlowConfigMixin): """Class representing the workflow.""" def __init__(self): """Initialize flow object.""" super(Flow, self).__init__(config.feed_name, version='1.0') def workflow_id(self, initial_context): """Generate workflow id. Args: initial_context (dict): The initial context for the flow. Returns: str: A unique identifier for a workflow being executed. """ licensor = initial_context['licensor'] flow_name = self.name date = initial_context['context_date'] return '{flow_name}-{licensor}-{date}'.format( flow_name=flow_name, licensor=licensor, date=date) def contextified_feed_name(self, context): """Get feed_name in context. Args: context (dict): The context of the flow. Returns: str: Contextified feed name. """ assert 'licensor' in context, 'There is no licensor in context' licensor = context['licensor'] assert licensor in config.licensors, \ f'Unknown licensor "{licensor}"' return '_'.join([self.feed_name, licensor]) def decider(self, schedule): """Activity decider. Args: schedule (callable): The scheduler method. """ bootstrap = schedule('bootstrap', self.bootstrap_activity) if bootstrap.result.get('bootstrap.stop'): return grab_drop_files = schedule( 'grab_drop_files', self.grab_drop_files, requires=[bootstrap] ) # if files unavailable, let's stop here if grab_drop_files.result.get('grab_drop_files.stop'): return set_status_to_downloaded = schedule( 'set_status_to_downloaded', self.set_status_to_downloaded, requires=[grab_drop_files]) unzip_files = schedule( 'unzip_files', self.unzip_files, requires=[set_status_to_downloaded]) if unzip_files.result.get('unzip_files.stop'): return create_temp_staging_raw_tables = schedule( 'create_temp_staging_raw_tables', self.create_temp_staging_raw_tables, requires=[unzip_files]) load_temp_staging_raw_tables = schedule( 'load_temp_staging_raw_tables', self.load_temp_staging_raw_tables, requires=[create_temp_staging_raw_tables]) load_staging_raw_table = schedule( 'load_staging_raw_table', self.load_staging_raw_table, requires=[load_temp_staging_raw_tables]) mark_staging_raw_table_tasks_complete = schedule( 'mark_staging_raw_table_tasks_complete', self.mark_staging_raw_table_tasks_complete, requires=[load_staging_raw_table]) set_status_populated_raw_table = schedule( 'set_status_populated_raw_table', self.set_status_populated_raw_table, requires=[mark_staging_raw_table_tasks_complete]) load_staging_fact = schedule( 'load_staging_fact', self.load_staging_fact, requires=[set_status_populated_raw_table]) load_fact_table = schedule( 'load_fact_table', self.load_fact_table, requires=[load_staging_fact]) delete_tmp_tables = schedule( 'delete_tmp_tables', self.delete_tmp_tables, requires=[load_fact_table]) schedule( 'set_overall_status_ingested', self.set_overall_status_ingested, requires=[delete_tmp_tables] ) @property def bootstrap_activity(self): """First activity for the workflow.""" return self.create( name='bootstrap', tasks=base.SyncRunner( tasks.bootstrap.fill( namespace='bootstrap', date='context_date', licensor='licensor', reload='reload'))) @property def grab_drop_files(self): """Grab files and upload to archive bucket on S3.""" return self.create( name='grab_drop_files', tasks=base.SyncRunner( tasks.grab_drop_files.fill( namespace='grab_drop_files', feed_name='bootstrap.feed_name', licensor='bootstrap.licensor', date='bootstrap.date', zipped_path='bootstrap.zipped_path', zipped_filename='bootstrap.zipped_filename'))) @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 unzip_files(self): """Unzip files and upload them on s3.""" return self.create( name='unzip_files', tasks=base.SyncRunner( tasks.unzip_files.fill( namespace='unzip_files', feed_name='bootstrap.feed_name', date='bootstrap.date', archive_path='bootstrap.archive_path', zipped_key_name='bootstrap.zipped_key_name', zipped_filename='bootstrap.zipped_filename'))) @property def create_temp_staging_raw_tables(self): """Create temp staging raw table.""" return self.create( name='create_temp_staging_raw_tables', generators=[self.temp_staging_tables_generator], tasks=base.AsyncRunner( tasks.create_temp_staging_raw_table.fill( namespace='create_temp_staging_raw_tables', feed_name='bootstrap.feed_name', date='bootstrap.date', report='report', temp_staging_raw_table='temp_staging_raw_table', kwargs='kwargs'), max_workers=4)) @property def load_temp_staging_raw_tables(self): """Load temp staging raw table.""" return self.create( name='load_temp_staging_raw_tables', generators=[self.temp_staging_tables_generator], tasks=base.SyncRunner( tasks.load_temp_staging_raw_table.fill( namespace='load_temp_staging_raw_tables', feed_name='bootstrap.feed_name', date='bootstrap.date', temp_staging_raw_table='temp_staging_raw_table', s3_dir_path='bootstrap.s3_archive_path', source_files_dict='bootstrap.source_files_dict', report='report', kwargs='kwargs'))) @property def load_staging_raw_table(self): """Load staging raw table.""" return self.create( name='load_staging_raw_table', generators=[self.temp_staging_tables_generator], tasks=base.SyncRunner( tasks.load_staging_raw_table.fill( namespace='load_staging_raw_table', feed_name='bootstrap.feed_name', date='bootstrap.date', report='report', staging_raw_table='staging_raw_table', temp_staging_raw_table='temp_staging_raw_table', kwargs='kwargs'))) @property def mark_staging_raw_table_tasks_complete(self): """Set the complete staging_raw_table_tasks to feed.""" return self.create( name='mark_staging_raw_table_tasks_complete', tasks=base.SyncRunner( (load_raw_table_tasks_sf.mark_staging_raw_table_tasks_complete. fill( namespace='mark_staging_raw_table_tasks_complete', date='bootstrap.date', feed_name='bootstrap.feed_name')))) @property def set_status_populated_raw_table(self): """Set the overall feed status to populated_raw_table.""" return self.create( name='set_status_populated_raw_table', tasks=base.SyncRunner( overall_status_tasks.set_overall_status.fill( namespace='set_status_populated_raw_table', feed_name='bootstrap.feed_name', date='bootstrap.date', status=StaticParam( garcon_feed_status.STATUS_POPULATED_RAW_TABLE)))) @property def load_staging_fact(self): """Load staging raw table.""" return self.create( name='load_staging_fact', tasks=base.SyncRunner( load_fact_tables_tasks_sf.create_staging_fact.fill( namespace='create_staging_fact', feed_name='bootstrap.feed_name', date='bootstrap.date'), load_fact_tables_tasks_sf.load_staging_fact.fill( namespace='load_staging_fact', feed_name='bootstrap.feed_name', date='bootstrap.date', kwargs='bootstrap.kwargs'))) @property def load_fact_table(self): """Load staging raw table.""" return self.create( name='load_fact_table', tasks=base.SyncRunner( load_fact_tables_tasks_sf.load_fact_data.fill( namespace='load_fact_table', feed_name='bootstrap.feed_name', date='bootstrap.date', kwargs='bootstrap.kwargs'))) @property def delete_tmp_tables(self): """Load staging raw table.""" return self.create( name='delete_tmp_tables', generators=[self.temp_staging_tables_generator], tasks=base.AsyncRunner( tasks.delete_temp_staging_raw_table.fill( namespace='delete_tmp_tables', feed_name='bootstrap.feed_name', report='report', temp_staging_raw_table='temp_staging_raw_table', date='bootstrap.date'))) @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', 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)))) def temp_staging_tables_generator(self, context): """Generate parameters for ingestion activities. Args: context (dict): The current context. Yields: dict: Dictionary with a report name. """ context_date = context['bootstrap.date'].replace('-', '') licensor = context['bootstrap.licensor'] for report in context['bootstrap.reports']: temp_table = (f'temp_staging_raw_line' f'_{licensor}_{report}_{context_date}') staging_raw_table = config.staging_raw_table.format(report) yield dict( temp_staging_raw_table=temp_table, staging_raw_table=staging_raw_table, report=report, kwargs=dict( licensor=licensor, ), )