""" YouTube Asset Conflict Ingestion Workflow. Ingest data from the YouTube Asset Conflict report. """ from garcon.param import StaticParam from garcon_contrib.aws.utils import garcon_s3 from garcon_contrib.dynamo_feed_status import garcon_feed_status from feed_ingestion.flows import base from feed_ingestion.flows.youtube_asset_conflict import config from feed_ingestion.flows.youtube_asset_conflict import tasks from feed_ingestion.tasks import overall_status_tasks from feed_ingestion.tasks import s3_tasks class Flow(base.FlowBase, base.FlowLoadRawMixinSF, base.FlowLoadFactMixinSF, base.FlowYouTubeMixin): """Class representing the workflow.""" def __init__(self): """Initialize flow object.""" super(Flow, self).__init__(feed_name=config.feed_name, version='1.0') def decider(self, schedule, context): """Activity decider. Args: schedule (callable): The scheduler method. """ bootstrap = schedule('bootstrap', self.bootstrap) # Stop if the requested date is older than the latest INGESTED date # (non-sequential / past-date load — see bootstrap guard). if bootstrap.result.get('bootstrap.stop') is True: return skip_grab_reports_files = context.get('skip_grab_reports_files') if skip_grab_reports_files == 'True': next_task_requires = bootstrap else: # download files and upload to s3 grab_reports_files = schedule( 'grab_reports_files', self.grab_reports_files, requires=[bootstrap]) next_task_requires = grab_reports_files source_files = schedule( 'source_files', self.source_files, requires=[next_task_requires]) move_files_to_staging_raw_temp_bucket = schedule( 'move_files_to_staging_raw_temp_bucket', self.move_files_to_staging_raw_temp_bucket, requires=[source_files] ) create_staging_raw_temp_table = schedule( 'create_temp_staging_tables', self.create_temp_staging_tables, requires=[move_files_to_staging_raw_temp_bucket] ) load_staging_raw_temp_table = schedule( 'load_temp_staging_table', self.load_temp_staging_tables, requires=[create_staging_raw_temp_table]) truncate_staging_raw_table = schedule( 'truncate_staging_raw_table', self.truncate_staging_raw_table, requires=[load_staging_raw_temp_table] ) insert_into_staging_raw_table = schedule( 'insert_into_staging_raw_table', self.insert_into_staging_table, requires=[truncate_staging_raw_table] ) create_fact_conflict_temp_table = schedule( 'create_fact_conflict_temp_table', self.create_fact_conflict_temp_table, requires=[insert_into_staging_raw_table] ) drop_create_territories_temp_table = schedule( 'drop_create_territories_temp_table', self.drop_create_territories_temp_table, requires=[create_fact_conflict_temp_table] ) fill_territories_temp_table = schedule( 'fill_territories_temp_table', self.fill_territories_temp_table, requires=[drop_create_territories_temp_table] ) # TODO: schedule the task in the correct order create_yact_table = schedule( 'create_youtube_asset_conflict_by_territory_table', self.create_staging_raw_yact_table, requires=[fill_territories_temp_table] ) fill_youtube_asset_conflict_by_territory_table = schedule( 'fill_youtube_asset_conflict_by_territory_table', self.fill_staging_raw_yact_table, requires=[create_yact_table] ) create_fact_conflict_temp_table = schedule( 'create_fact_conflict_temp_table', self.create_fact_conflict_temp_table, requires=[fill_youtube_asset_conflict_by_territory_table] ) insert_overwrite_into_fact_conflict_temp_table = schedule( 'insert_overwrite_into_fact_conflict_temp_table', self.insert_overwrite_into_fact_conflict_temp_table, requires=[create_fact_conflict_temp_table] ) check_size_of_fact_conflict_temp_table = schedule( 'check_size_of_fact_conflict_temp_table', self.check_size_of_fact_conflict_temp_table, requires=[insert_overwrite_into_fact_conflict_temp_table] ) check_number_of_unresolved_conflicts = schedule( 'check_number_of_unresolved_conflicts', self.check_number_of_unresolved_conflicts, requires=[check_size_of_fact_conflict_temp_table] ) insert_into_fact_conflict_table = schedule( 'insert_into_fact_conflict_table', self.insert_into_fact_conflict_table, requires=[check_number_of_unresolved_conflicts] ) update_fact_conflict_resolved_datetime = schedule( 'update_fact_conflict_resolved_datetime', self.update_fact_conflict_resolved_datetime, requires=[insert_into_fact_conflict_table] ) reset_es_indexed_for_partially_resolved_conflicts = \ schedule( 'reset_es_indexed_for_partially_resolved_conflicts', self.reset_es_indexed_for_partially_resolved_conflicts, requires=[update_fact_conflict_resolved_datetime]) update_fact_conflict_yt_recent_daily_average = schedule( 'update_fact_conflict_yt_recent_daily_average', self.update_fact_conflict_yt_recent_daily_average, requires=[reset_es_indexed_for_partially_resolved_conflicts] ) update_fact_conflict_views_in_conflict = schedule( 'update_fact_conflict_views_in_conflict', self.update_fact_conflict_views_in_conflict, requires=[update_fact_conflict_yt_recent_daily_average] ) drop_staging_raw_temp_tables = schedule( 'drop_staging_raw_temp_tables', self.drop_staging_raw_temp_tables, requires=[update_fact_conflict_views_in_conflict]) drop_yact_table = schedule( 'drop_yact_table', self.drop_yact_table, requires=[drop_staging_raw_temp_tables]) drop_fact_conflict_temp_table = schedule( 'drop_fact_conflict_temp_table', self.drop_fact_conflict_temp_table, requires=[drop_yact_table]) schedule( 'set_overall_status_ingested', self.set_status_to_ingested, requires=[drop_fact_conflict_temp_table]) @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 move_files_to_staging_raw_temp_bucket(self): """Move files from archive to staging_raw_temp bucket.""" return self.create( name='move_files_to_staging_raw_temp_bucket', generators=[self.staging_raw_temp_files_generator], tasks=base.AsyncRunner( s3_tasks.copy_file.fill( namespace='copy_file', source_bucket_name=StaticParam(config.s3_archive_bucket), source_key_name='source_key_name', destination_bucket_name=StaticParam( config.s3_archive_bucket), destination_key_name='destination_key_name', replace='bootstrap.replace_archive_files'), max_workers=4)) @property def create_temp_staging_tables(self): """Create temp staging tables.""" return self.create( name='create_temp_staging_tables', generators=[self.staging_raw_temp_tables_relationships_generator], tasks=base.AsyncRunner( tasks.create_temp_staging_table.fill( namespace='create_temp_staging_table', date='bootstrap.date', staging_raw_temp_table='staging_raw_temp_table' ) ) ) @property def load_temp_staging_tables(self): """Load temp staging tables.""" return self.create( name='load_temp_staging_tables', generators=[self.staging_raw_temp_tables_relationships_generator], tasks=base.AsyncRunner( tasks.load_temp_staging_table.fill( namespace='load_temp_staging_table', date='bootstrap.date', staging_raw_temp_table='staging_raw_temp_table', key_dir='bootstrap.s3_staging_raw_temp', drop_file='drop_file' ) ) ) @property def truncate_staging_raw_table(self): """Truncate staging raw table.""" return self.create( name='truncate_staging_table', tasks=base.AsyncRunner( tasks.truncate_staging_raw_table.fill( namespace='truncate_staging_table', date='bootstrap.date', staging_raw_table='bootstrap.staging_raw_table', ) ) ) @property def insert_into_staging_table(self): """Insert into staging raw table.""" return self.create( name='insert_into_staging_table', generators=[self.staging_raw_temp_tables_relationships_generator], tasks=base.AsyncRunner( tasks.insert_into_staging_raw_table.fill( namespace='insert_into_staging_table', date='bootstrap.date', staging_raw_temp_table='staging_raw_temp_table', drop_file='drop_file', s3_staging_raw_temp='bootstrap.s3_staging_raw_temp', staging_raw_table='bootstrap.staging_raw_table', content_owner='content_owner' ) ) ) @property def create_fact_conflict_temp_table(self): """Create fact_conflict_temp table.""" fact_conflict_temp_table = 'bootstrap.fact_conflict_temp_table' return self.create( name='create_fact_conflict_temp_table', tasks=base.AsyncRunner( tasks.create_fact_conflict_temp_table.fill( namespace='create_fact_conflict_temp_table', date='bootstrap.date', fact_conflict_temp_table=fact_conflict_temp_table ) ) ) def staging_raw_temp_files_generator(self, context): """Generate source and destination keys for staging_raw_temp_files. Used by the move_files_to_staging_raw_temp_bucket activity. Args: context (dict): The current context. Yields: Dictionary of files that maps between archive to staging_raw_temp destination. """ for file in context['bootstrap.drop_file_names']: staging_raw_temp_file = '{dir}{file}'.format( dir=context['bootstrap.s3_staging_raw_temp'], file=file) archive_file = '{dir}{file}'.format( dir=context['bootstrap.s3_archive_path'], file=file) source_key_name = ( garcon_s3.extract_bucket_path(archive_file)[1]) destination_key_name = ( garcon_s3.extract_bucket_path(staging_raw_temp_file)[1]) yield dict( source_key_name=source_key_name, destination_key_name=destination_key_name) def staging_raw_temp_tables_relationships_generator(self, context): """Generate parameters for temporary staging tables and related params. Args: context (dict): The current context. Yields: Dictionary of temporary staging table params and relationships. """ temp_table_to_drop_file = context[ 'bootstrap.staging_raw_temp_table_relationships'] for table_name, drop_file, content_owner in temp_table_to_drop_file: yield dict( staging_raw_temp_table=table_name, drop_file=drop_file, content_owner=content_owner ) @property def drop_create_territories_temp_table(self): """Drop and create temp territories table.""" return self.create( name='drop_create_territories_temp_table', tasks=base.SyncRunner( tasks.drop_create_territories_temp_table.fill( date='bootstrap.date' ) ) ) @property def fill_territories_temp_table(self): """Fill temp territories table.""" return self.create( name='fill_territories_temp_table', tasks=base.SyncRunner( tasks.fill_territories_temp_table.fill( date='bootstrap.date', standard='bootstrap.territory_standard' ) ) ) @property def create_staging_raw_yact_table(self): """Create youtube_asset_conflict_by_territory table.""" yact_table_name = ( 'bootstrap.youtube_asset_conflict_by_territory_temp_table') return self.create( name='create_youtube_asset_conflict_by_territory_table', tasks=base.SyncRunner( tasks.create_youtube_asset_conflict_by_territory_table.fill( date='bootstrap.date', yact_table=yact_table_name ) ) ) # TODO: schedule this task as a final step of the flow @property def set_overall_status_to_ingested(self): """Set overall status to INGESTED.""" return self.create( name='set_overall_status_to_ingested', tasks=base.SyncRunner( overall_status_tasks.set_overall_status.fill( namespace='set_overall_status_to_ingested', feed_name='bootstrap.feed_name', date='bootstrap.date', status=StaticParam(garcon_feed_status.STATUS_INGESTED)))) @property def fill_staging_raw_yact_table(self): """Fill youtube_asset_conflict_by_territory table.""" yact_table_name = ( 'bootstrap.youtube_asset_conflict_by_territory_temp_table') return self.create( name='fill_youtube_asset_conflict_by_territory_table', tasks=base.SyncRunner( tasks.fill_youtube_asset_conflict_by_territory_table.fill( date='bootstrap.date', yact_table=yact_table_name, staging_raw_table='bootstrap.staging_raw_table' ) ) ) @property def insert_overwrite_into_fact_conflict_temp_table(self): """Insert overwrite into the fact_conflict_temp table.""" fact_conflict_temp_table_name = ( 'bootstrap.fact_conflict_temp_table') yact_table_name = ( 'bootstrap.youtube_asset_conflict_by_territory_temp_table') yar_table_name = ( 'bootstrap.staging_raw_youtube_asset_report_table') return self.create( name='insert_overwrite_into_fact_conflict_temp_table', tasks=base.SyncRunner( tasks.insert_overwrite_into_fact_conflict_temp_table.fill( date='bootstrap.date', art_relations_db='bootstrap.art_relations_db', art_relations_schema='bootstrap.art_relations_schema', asset_report_schema='bootstrap.asset_report_schema', registry_schema='bootstrap.registry_schema', fact_conflict_temp_table=fact_conflict_temp_table_name, yact_table=yact_table_name, staging_raw_youtube_asset_report_table=yar_table_name, registry_table='bootstrap.registry_table', track_table='bootstrap.track_table', releases_table='bootstrap.releases_table', artist_info_table='bootstrap.artist_info_table', territory_standard='bootstrap.territory_standard', asset_type='bootstrap.sound_recording_asset_type', time_zone='bootstrap.time_zone', orchard_account='bootstrap.orchard_account' ) ) ) @property def check_size_of_fact_conflict_temp_table(self): """Check if the size of the fact conflict temp table seems correct.""" fact_conflict_temp_table_name = ( 'bootstrap.fact_conflict_temp_table') return self.create( name='check_size_of_fact_conflict_temp_table', tasks=base.SyncRunner( tasks.check_size_of_fact_conflict_temp_table.fill( date='bootstrap.date', fact_conflict_temp_table=fact_conflict_temp_table_name ) ) ) @property def check_number_of_unresolved_conflicts(self): """Check if the number of unresolved conflicts seems correct.""" fact_conflict_table_name = ('bootstrap.fact_conflict_table') return self.create( name='check_number_of_unresolved_conflicts', tasks=base.SyncRunner( tasks.check_number_of_unresolved_conflicts.fill( date='bootstrap.date', fact_conflict_table=fact_conflict_table_name ) ) ) @property def insert_into_fact_conflict_table(self): """Insert into fact_conflict table.""" fact_conflict_temp_table_name = ( 'bootstrap.fact_conflict_temp_table') return self.create( name='insert_into_fact_conflict_table', tasks=base.SyncRunner( tasks.insert_into_fact_conflict_table.fill( date='bootstrap.date', fact_conflict_temp_table=fact_conflict_temp_table_name, fact_conflict_table='bootstrap.fact_conflict_table' ) ) ) @property def update_fact_conflict_resolved_datetime(self): """Update fact conflict resolved datetime.""" fact_conflict_temp_table_name = ( 'bootstrap.fact_conflict_temp_table') return self.create( name='update_fact_conflict_resolved_datetime', tasks=base.SyncRunner( tasks.update_fact_conflict_resolved_datetime.fill( date='bootstrap.date', fact_conflict_temp_table=fact_conflict_temp_table_name, fact_conflict_table='bootstrap.fact_conflict_table', time_zone='bootstrap.time_zone' ) ) ) @property def update_fact_conflict_yt_recent_daily_average(self): """Update fact conflict yt recent daily average.""" fact_conflict_temp_table_name = ( 'bootstrap.fact_conflict_temp_table') return self.create( name='update_fact_conflict_yt_recent_daily_average', tasks=base.SyncRunner( tasks.update_fact_conflict_yt_recent_daily_average.fill( date='bootstrap.date', fact_conflict_temp_table=fact_conflict_temp_table_name, fact_conflict_table='bootstrap.fact_conflict_table' ) ) ) @property def update_fact_conflict_views_in_conflict(self): """Update fact conflict views in conflict.""" fact_conflict_temp_table_name = ( 'bootstrap.fact_conflict_temp_table') return self.create( name='update_fact_conflict_views_in_conflict', tasks=base.SyncRunner( tasks.update_fact_conflict_views_in_conflict.fill( date='bootstrap.date', fact_conflict_temp_table=fact_conflict_temp_table_name, fact_conflict_table='bootstrap.fact_conflict_table' ) ) ) @property def reset_es_indexed_for_partially_resolved_conflicts(self): """Update fact conflict es_indexed.""" return self.create( name='reset_es_indexed_for_partially_resolved_conflicts', tasks=base.SyncRunner( tasks.reset_es_indexed_for_partially_resolved_conflicts.fill( date='bootstrap.date', fact_conflict_table='bootstrap.fact_conflict_table', time_zone='bootstrap.time_zone' ) ) ) @property def drop_fact_conflict_temp_table(self): """Drop fact conflict temp table.""" return self.create( name='drop_fact_conflict_temp_table', tasks=base.SyncRunner( tasks.drop_temp_table.fill( namespace='drop_fact_conflict_temp_table', date='bootstrap.date', table_name='bootstrap.fact_conflict_temp_table' ) ) ) @property def drop_staging_raw_temp_tables(self): """Drop drop_staging_raw_temp_tables.""" return self.create( name='drop_staging_raw_temp_tables', generators=[self.staging_raw_temp_tables_relationships_generator], tasks=base.SyncRunner( tasks.drop_temp_table.fill( namespace='drop_staging_raw_temp_tables', date='bootstrap.date', table_name='staging_raw_temp_table' ) ) ) @property def drop_yact_table(self): """Drop yact temp table.""" yact_table_name = ( 'bootstrap.youtube_asset_conflict_by_territory_temp_table') return self.create( name='drop_yact_table', tasks=base.SyncRunner( tasks.drop_temp_table.fill( namespace='drop_yact_table', date='bootstrap.date', table_name=yact_table_name ) ) ) @property def source_files(self): """Get list of source files.""" return self.create( name='source_files', tasks=base.SyncRunner( s3_tasks.source_files.fill( namespace='source_files', s3_bucket=StaticParam(config.s3_archive_bucket), s3_path='bootstrap.s3_dir_path', file_pattern=StaticParam(config.source_file_pattern))))