""" TikTok and Douyin Ingestion Workflow. Ingest data from TikTok Daily feed. """ from garcon import param 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.tiktok import config from feed_ingestion.flows.tiktok import tasks from feed_ingestion.tasks import load_raw_table_tasks_sf from feed_ingestion.tasks import overall_status_tasks from feed_ingestion.tasks import validate_raw_data_tasks_sf from feed_ingestion.util.jenkins.tasks import build_jenkins_dbt class Flow(base.FlowLicensor, base.FlowConfigMixin, base.FlowLoadRawMixinSF): """Class representing the workflow.""" def __init__(self): """Initialize flow object.""" super(Flow, self).__init__(config.feed_name, version='1.0') def decider(self, schedule): """Activity decider. Args: schedule (callable): The scheduler method. """ check_feed_status = schedule( 'check_feed_status', self.check_feed_status) if check_feed_status.result.get('check_feed_status.stop'): return bootstrap = schedule( 'bootstrap', self.bootstrap, requires=[check_feed_status]) grab_drop_files = schedule( 'grab_drop_files', self.grab_drop_files, requires=[bootstrap] ) check_available_reports = schedule( 'check_available_reports', self.check_available_reports, requires=[grab_drop_files]) # if files unavailable, let's stop here if check_available_reports.result.get( 'check_available_reports.stop') is True: return create_temp_staging_raw_table = schedule( 'create_temp_staging_raw_table', self.create_temp_staging_raw_table, requires=[check_available_reports]) load_temp_staging_raw_table = schedule( 'load_temp_staging_raw_table', self.load_temp_staging_raw_table, requires=[create_temp_staging_raw_table]) load_staging_raw_table = schedule( 'load_staging_raw_table', self.load_staging_raw_table, requires=[load_temp_staging_raw_table]) remap_fingerprint_isrc_product_code = schedule( 'remap_fingerprint_isrc_product_code', self.remap_fingerprint_isrc_product_code, requires=[load_staging_raw_table]) set_status_to_ingested = schedule( 'set_status_to_ingested', self.set_status_to_ingested, requires=[remap_fingerprint_isrc_product_code]) schedule( 'build_jenkins_dbt', self.build_jenkins_dbt, requires=[set_status_to_ingested]) @property def check_feed_status(self): """Check and reset feed status if it is needed.""" return self.create( name='check_feed_status', tasks=base.SyncRunner( tasks.check_feed_status.fill( namespace='check_feed_status', date='context_date', licensor='licensor', reload='reload'))) @property def bootstrap(self): """First activity for the workflow.""" return self.create( name='bootstrap', tasks=base.SyncRunner( tasks.bootstrap.fill( namespace='bootstrap', feed_name='check_feed_status.feed_name', licensor='licensor', date='context_date', reports='reports', reload='reload', use_sme_s3='use_sme_s3'))) @property def grab_drop_files(self): """Grab files and upload to archive bucket on S3.""" return self.create( name='grab_drop_files', generators=[self.grab_drop_files_generator], tasks=base.SyncRunner( tasks.grab_drop_files.fill( namespace='grab_drop_files', feed_name='feed_name', date='bootstrap.date', licensor='licensor', drop_bucket='drop_bucket', drop_path='drop_path', archive_path='archive_path', processed_path='processed_path'))) @property def check_available_reports(self): """Check reports which were uploaded on s3.""" return self.create( name='check_available_reports', schedule_to_start=48000, tasks=base.SyncRunner( tasks.check_available_reports.fill( namespace='check_available_reports', date='bootstrap.date', feed_name='bootstrap.feed_name', reports_info='bootstrap.reports_info'))) @property def create_temp_staging_raw_table(self): """Create temp staging raw table.""" return self.create( name='create_temp_staging_raw_table', generators=[self.temp_staging_tables_generator], schedule_to_start=48000, tasks=base.SyncRunner( load_raw_table_tasks_sf.create_temp_staging_raw_table.fill( namespace='create_temp_staging_raw_table', date='bootstrap.date', sfdb_params='sfdb_params', feed_name='feed_name', secrets_path=StaticParam(config.secrets_path), temp_staging_raw_table='temp_staging_raw_table', kwargs='kwargs'))) @property def load_temp_staging_raw_table(self): """Load temp staging raw table.""" return self.create( name='load_temp_staging_raw_table', generators=[self.temp_staging_tables_generator], schedule_to_start=48000, tasks=base.SyncRunner( validate_raw_data_tasks_sf.load_temp_staging_raw_table.fill( namespace='load_temp_staging_raw_table', date='bootstrap.date', sfdb_params='sfdb_params', feed_name='feed_name', secrets_path=StaticParam(config.secrets_path), key_dir='key_dir', temp_staging_raw_table='temp_staging_raw_table', error_limit=param.StaticParam( config.snowflake_error_limit), kwargs='kwargs'))) @property def load_staging_raw_table(self): """Load permanent staging raw table.""" activity_name = 'load_staging_raw_table' return self.create( name=activity_name, generators=[self.temp_staging_tables_generator], schedule_to_start=48000, tasks=base.SyncRunner( load_raw_table_tasks_sf.load_staging_raw_table.fill( namespace=activity_name, date='bootstrap.date', feed_name='feed_name', secrets_path=StaticParam(config.secrets_path), temp_staging_raw_table='temp_staging_raw_table', sfdb_params='sfdb_params', staging_raw_table=StaticParam(config.staging_raw_table), kwargs='kwargs'), overall_status_tasks.set_overall_status.fill( date='bootstrap.date', feed_name='feed_name', set_status_once=param.StaticParam(True), status=param.StaticParam( garcon_feed_status.STATUS_POPULATED_RAW_TABLE)))) @property def remap_fingerprint_isrc_product_code(self): """Update isrc and product_code using three-tier lookup strategy.""" activity_name = 'remap_fingerprint_isrc_product_code' return self.create( name=activity_name, schedule_to_start=48000, tasks=base.SyncRunner( tasks.remap_fingerprint_isrc_product_code.fill( namespace=activity_name, date='bootstrap.date', feed_name='bootstrap.feed_name', sfdb_params='sfdb_params', licensor='licensor'))) @property def set_status_to_ingested(self): """Set overall feed status to to_ingested.""" return self.create( name='set_status_to_ingested', tasks=base.SyncRunner( tasks.set_status_to_ingested.fill( namespace='set_status_to_ingested', licensor='licensor', date='bootstrap.date', feed_name='bootstrap.feed_name', reports='reports'))) @property def build_jenkins_dbt(self): """Build Jenkins DBT job.""" return self.create( name='build_jenkins_dbt', schedule_to_start=48000, tasks=base.SyncRunner( build_jenkins_dbt.fill( namespace='build_jenkins_dbt', date='bootstrap.date', feed_name='bootstrap.feed_name', licensor='licensor', build_dbt='build_dbt', config=StaticParam(config.jenkins_config)))) def grab_drop_files_generator(self, context): """Generate parameters for grab_drop_files activity. Args: context (dict): The current context. Yields: dict: Dictionary with report_name and feed name, which will be used for writing statuses in DynamoDB. """ reports_info = context['bootstrap.reports_info'] for report_name, info in reports_info.items(): yield dict( feed_name=info['feed_name'], drop_bucket=info['drop_bucket'], drop_path=info['drop_path'], archive_path=info['archive_path'], processed_path=info['processed_path'], ) def temp_staging_tables_generator(self, context): """Generate parameters for staging_raw_table activities. Args: context (dict): The current context. Yields: dict: Dictionary with report_name and feed name, which will be used for writing statuses in DynamoDB. """ downloaded_reports = context[ 'check_available_reports.downloaded_reports'] for report_name, info in downloaded_reports.items(): processed_path = info['processed_path'] yield dict( feed_name=info['feed_name'], temp_staging_raw_table=info['temp_staging_raw_table'], key_dir=f's3://{config.data_bucket}/{processed_path}', kwargs=dict( report_name=report_name, download_date=context['bootstrap.date'] ) )