"""Tasks for Ingestion Workflow.""" import datetime from garcon import task from garcon_contrib.dynamo_feed_status import garcon_feed_status from feed_ingestion.conf.config import merge_configs from feed_ingestion.flows.helpers import get_sf_config from feed_ingestion.flows.seated import config from feed_ingestion.flows.seated.snowflake_executor \ import SeatedSnowflakeExecutor from feed_ingestion.tasks.feed_status_tasks \ import check_files_on_s3 as common_check_files_on_s3 @task.decorate(timeout=700) def bootstrap(activity, date, report, reload): """Bootstrap workflow by getting the correct configurations. Args: activity (ActivityWorker): The activity worker. date (str): Reporting date (YYYY-MM-DD). report (str): one of config.report reload (str or None): If 'True' reload status. Returns: dict: Context. """ assert report in config.reports report_config = config.reports[report] feed_name = f'{config.feed_name}_{report}' date_obj = datetime.datetime.strptime(date, '%Y-%m-%d').date() date = date_obj.strftime('%Y-%m-%d') is_set_overall_ingested = ( date_obj + datetime.timedelta(days=1) < datetime.date.today() ) if reload == 'True': activity.logger.info('Delete status for feed: {} {} '.format( feed_name, date)) garcon_feed_status.delete_status(feed_name, date) elif garcon_feed_status.get_overall_status( feed_name, date) == garcon_feed_status.STATUS_INGESTED: activity.logger.info('Feed already ingested for {}'.format(date)) return { 'stop': True, 'message': '{feed_name} is already ingested for {date}'.format( feed_name=feed_name, date=date)} activity.logger.info('Bootstrap flow for {} date: {}'.format(report, date)) s3_dir_path = f's3://{config.drop_bucket}/{config.drop_path}' filename = report_config['filename_template'].format( date=date_obj ) file_pattern = rf'.*\/{filename}' return dict( feed_name=feed_name, date=date, report_name=report, secrets_path=config.secrets_path, drop_bucket=config.drop_bucket, drop_path=config.drop_path, s3_dir_path=s3_dir_path, file_pattern=file_pattern, snowflake_pattern=filename, staging_raw_table=report_config['staging_raw_table'], staging_raw_table_errors=report_config['staging_raw_table_errors'], is_set_overall_ingested=is_set_overall_ingested, pii_columns=report_config['pii_columns'], ) @task.decorate(timeout=700) def check_files_on_s3( activity, feed_name, date, s3_download_path, file_pattern, list_output=False): """Wrap feed_status_task to exclude a list of files due to size limit.""" result_dict = common_check_files_on_s3( activity, feed_name, date, s3_download_path, file_pattern, list_output ) if 'stop' in result_dict: return result_dict else: return { 'source_files_dict': { 'files': 'excluded due to swf limitations on returned size' } } @task.decorate(timeout=3600 * 5) def load_staging_raw_table_reports( activity, feed_name, date, report_name, s3_dir_path, staging_raw_table_name, staging_raw_errors_table_name, file_names, snowflake_pattern, pii_columns ): """Save data to staging raw table(s) for given report. Args: activity (ActivityWorker): The Garcon activity worker. feed_name (str): Feed name of workflow execution for status updates. date (str): Reporting date (YYYY-MM-DD). report_name (str): Reporting name s3_dir_path (str): S3 directory staging_raw_table_name (str): The staging table name for report. staging_raw_errors_table_name (str): The staging errors table name. file_names (dict[str, list[str]]): File names to process (excluded). snowflake_pattern (str): Snowflake pattern for file names. pii_columns (list[str]): List of pii column names. """ temp_table_name = 'temp_{staging_raw_table_name}_{date}'.format( staging_raw_table_name=staging_raw_table_name, date=date.replace('-', '')) sf_config = merge_configs(get_sf_config(config.secrets_path), {}) with SeatedSnowflakeExecutor(sf_config) as executor: # Create a temp table executor.create_temp_staging_raw_table( report_name=report_name, temp_staging_raw_table=temp_table_name ) activity.logger.info(f'{temp_table_name} was created') executor.assign_masking_on_temp_staging_raw_table( temp_staging_raw_table=temp_table_name, pii_columns=pii_columns ) activity.logger.info(f'{temp_table_name} default masking was set') activity.logger.info(f' {file_names}') stage_name = f"stage_seated_{date.replace('-', '')}" executor.create_stage( s3_dir_path=s3_dir_path, stage_name=stage_name ) activity.logger.info( f'{stage_name} was created') executor.load_temp_staging_raw_table( temp_staging_raw_table=temp_table_name, file_name=snowflake_pattern, stage_name=stage_name, ) activity.logger.info( f'{temp_table_name} was loaded for pattern {snowflake_pattern}') # Clear for the given report data/file executor.clean_staging_raw_table( staging_raw_table=staging_raw_table_name, staging_raw_errors_table=staging_raw_errors_table_name, file_name=snowflake_pattern, date=date, ) activity.logger.info( f'{staging_raw_table_name} and {staging_raw_errors_table_name} ' f'were cleared for pattern {snowflake_pattern}' ) # Load staging raw table executor.load_staging_raw_table( staging_raw_table=staging_raw_table_name, staging_raw_errors_table=staging_raw_errors_table_name, temp_staging_raw_table=temp_table_name, report_name=report_name, date=date, file_name=snowflake_pattern, ) activity.logger.info( f'{staging_raw_table_name} and {staging_raw_errors_table_name} ' f'were loaded by pattern {snowflake_pattern}' ) # Drop the temp table executor.drop_table(table=temp_table_name) activity.logger.info(f'{temp_table_name} was dropped for {date}') # Drop Snowflake stage executor.drop_stage(stage_name=stage_name) activity.logger.info(f'{stage_name} stage was dropped')