""" YouTubeMonthly Ingestion Workflow. Ingest data from YouTubeMonthly Feed (IODA, TheOrchardMusic, DMGI). """ from garcon.param import StaticParam from garcon_contrib.aws import garcon_s3 from garcon_contrib.dynamo_feed_status import garcon_feed_status from snowflake_connector.etl_connector import SQLLoader from feed_ingestion.flows import base from feed_ingestion.flows.youtube_monthly import config from feed_ingestion.flows.youtube_monthly import tasks from feed_ingestion.flows.youtube_monthly.stage_loader import YouTubeMonthlySL from feed_ingestion.tasks import feed_status_tasks from feed_ingestion.tasks import overall_status_tasks class Flow(base.FlowBase, base.FlowConfigMixin): """YouTube Monthly Flow class. This flow retrieves the YouTube monthly reports from IODA, The ORCHARD and DMGI accounts. This flow maps the new YouTube column names to the old YouTube column names and returns the old-style YT columns. No data is loaded into fact_analytics. """ def __init__(self): """Initialize flow object.""" super(Flow, self).__init__(feed_name=config.feed_name, version='3.0') self.timeout = 60 * 60 * 6 def decider(self, schedule, context): """Activity decider. Args: schedule (callable): The scheduler method. context (dict): Initial context of the flow. """ bootstrap = schedule('bootstrap', self.bootstrap) # Stop flow if feed already ingested. if bootstrap.result.get('bootstrap.stop'): return check_files_on_s3 = schedule( 'check_files_on_s3', self.check_files_on_s3, requires=[bootstrap]) if check_files_on_s3.result.get('check_files_on_s3.stop'): return # Remove s3 staging data that correspond to the # current workflow's report date. remove_files_from_path = schedule( 'remove_files_from_path', self.remove_files_from_path, requires=[check_files_on_s3] ) # Move files from archives to temp_raw folder # Extract zip files and make gz format move_and_extract_files = schedule( 'move_and_extract_files', self.move_and_extract_files, requires=[remove_files_from_path] ) if bootstrap.result['bootstrap.report_name'] == 'legacy_monthly': load_staging_raw_table = schedule( 'load_staging_raw_table', self.load_staging_raw_table, requires=[move_and_extract_files] ) else: load_staging_raw_table = schedule( 'load_staging_raw_table', self.load_staging_raw_table_reports, requires=[move_and_extract_files] ) schedule( 'set_status_to_ingested', self.set_status_to_ingested, requires=[load_staging_raw_table] ) def workflow_id(self, initial_context): """Generate workflow id. It only works for one-per-report invocation. So you cannot have the same report run for two different dates. Args: initial_context (dict): The initial context for the flow. Returns: str: A unique identifier for a workflow being executed. """ report_name = initial_context['report_name'] date = initial_context['context_date'] flow_name = '_'.join([self.name, report_name, date]) return flow_name 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 'report_name' in context, 'There is no report_name in context' return '_'.join([self.feed_name, context['report_name']]) @property def bootstrap(self): """Bootstrap initial configuration.""" return self.create( name='bootstrap', tasks=base.SyncRunner( tasks.bootstrap.fill( namespace='bootstrap', date='context_date', report_name='report_name', reload='reload', accounts='accounts', ) ) ) @property def check_files_on_s3(self): """Check if there are some new files in s3_download_path.""" return self.create( name='check_files_on_s3', tasks=base.SyncRunner( feed_status_tasks.check_files_on_s3.fill( namespace='check_files_on_s3', feed_name='bootstrap.feed_name', date='bootstrap.date', file_pattern='bootstrap.file_pattern_regexp', s3_download_path='bootstrap.s3_drop_path'))) @property def remove_files_from_path(self): """Delete existing data from staging S3 path.""" return self.create( name='remove_files_from_path', tasks=base.SyncRunner( garcon_s3.remove_files_from_path.fill( namespace='remove_files_from_path', path='bootstrap.s3_archive_path', return_deleted_files=StaticParam(True) ) ) ) @property def move_and_extract_files(self): """Extract files from archives folder and move to temp_raw folder.""" return self.create( name='move_and_extract_files', tasks=base.SyncRunner( tasks.move_and_extract_files.fill( namespace='move_and_extract_files', source_files_dict='bootstrap.source_files_dict', old_s3_path='bootstrap.s3_drop_path', new_s3_path='bootstrap.s3_archive_path', ) ) ) @property def set_status_to_ingested(self): """Set overall feed status to INGESTED.""" return self.create( name='set_status_to_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) ) ) ) load_staging_raw_table = YouTubeMonthlySL.load_activity( feed_name=config.feed_name, secrets_path=config.secrets_path, sql_loader=SQLLoader(__file__), requirements=dict( date='bootstrap.date', source_files_dict='move_and_extract_files.source_files_dict', s3_dir_path='bootstrap.s3_archive_path', staging_raw_table_name='bootstrap.staging_raw_table', ), task_timeout=10800 ) @property def load_staging_raw_table_reports(self): """Create temp staging raw table.""" return self.create( name='load_staging_raw_table_reports', schedule_to_start=10000, tasks=base.SyncRunner( tasks.load_staging_raw_table_reports.fill( namespace='load_staging_raw_table_reports', date='bootstrap.date', feed_name='bootstrap.feed_name', report_name='bootstrap.report_name', staging_raw_table_name='bootstrap.staging_raw_table', s3_dir_path='bootstrap.s3_archive_path', source_files_dict=( 'move_and_extract_files.source_files_dict'), )))