"""Tasks of the Sme latam Ingestion Workflow.""" import datetime import os from garcon import task from garcon_contrib.dynamo_feed_status import garcon_feed_status import pandas as pd from feed_ingestion.flows.sme_latam import config from feed_ingestion.tasks import check_status from feed_ingestion.tasks import s3_tasks STOP_RESPONSE = {'stop': True} @task.decorate(timeout=700) def bootstrap(activity, date, reload): """Bootstrap workflow by getting the correct configurations. Args: activity (ActivityWorker): The activity worker. date (str): Reporting date (YYYY-MM-DD). reload (str or None): If 'True' reload status. Returns: dict: Context. """ date = datetime.datetime.strptime( date, '%Y-%m-%d').date() if date else datetime.date.today() # date for DynamoDB status date_obj = date + datetime.timedelta(days=-date.weekday()) date = date_obj.strftime('%Y-%m-%d') if reload == 'True': activity.logger.info('Delete status for feed: {} {} '.format( config.feed_name, date)) garcon_feed_status.delete_status(config.feed_name, date) elif garcon_feed_status.get_overall_status( config.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:%Y-%m-%d}'.format( feed_name=config.feed_name, date=date_obj)} activity.logger.info('Bootstrap flow for {}'.format(date)) week_number = date_obj.isocalendar()[1] week_year = date_obj.isocalendar()[0] activity.logger.info(f'Week number {week_number} Year: {week_year}') format_args = dict( week_number=week_number, week_year=week_year ) archive_path = config.s3['archive_path'].format(**format_args) drop_path = config.s3['drop_path'].format(**format_args) filename = config.file_template.format(**format_args) processed_path = config.s3['processed_path'].format(**format_args) processed_filename = filename.replace('.xlsx', '.csv') return dict( feed_name=config.feed_name, date=date, secrets_path=config.secrets_path, archive_path=f'{archive_path}{filename}', drop_path=f'{drop_path}{filename}', processed_path=f'{processed_path}{processed_filename}', staging_raw_table=config.staging_raw_table) @task.decorate(timeout=7000) @check_status() def fetch_from_drop_location( activity, feed_name, date, drop_path, archive_path): """Download a feed file from the drop location into the archive folder. Args: activity (ActivityWorker): The activity worker. feed_name (str): The name of the feed. date (str): Reporting date (YYYY-MM-DD). drop_path (str): Source S3 path to the archive location. archive_path (str): Destination S3 path to the archive location. """ # copy zip archive result = s3_tasks.copy_file( activity, source_bucket_name=config.drop_bucket, source_key_name=drop_path, destination_bucket_name=config.data_bucket, destination_key_name=archive_path, replace=True) # file is not downloaded if not result.get(os.path.basename(drop_path)): garcon_feed_status.set_overall_status( feed_name, date, garcon_feed_status.STATUS_NOT_AVAILABLE) return {'stop': True, 'message': f'Missing file {drop_path}'} else: garcon_feed_status.set_overall_status( feed_name, date, garcon_feed_status.STATUS_DOWNLOADED) return result @task.decorate(timeout=1000) def process_drop_files( activity, feed_name, date, archive_path, processed_path): """Process xlsx file to csv. Args: activity (ActivityWorker): The activity worker. feed_name (str): The name of the feed. date (str): Reporting date (YYYY-MM-DD). archive_path (str): Destination S3 path to the archive location. processed_path (str): S3 path to save csv files. Returns: dict: s3_dir_path & source_files_dict for loading data into snowflake. """ df = pd.read_excel( f's3://{config.data_bucket}/{archive_path}', engine='openpyxl', skiprows=4) activity.logger.info(f'Loading initial file {archive_path}') s3_processed_path = f's3://{config.data_bucket}/{processed_path}' df.to_csv(s3_processed_path, index=None, header=False) activity.logger.info('xlsx file has been processed to csv.') source_files_dict = {'files': [{ 'file_name': os.path.basename(s3_processed_path), 'file_size': 1, 'found': True }]} return dict( s3_dir_path=s3_processed_path, source_files_dict=source_files_dict )