"""Logic for jobs (encoding_queue_detail).""" import itertools import operator from src.configs import config from src.connectors import logging from src.constants import fields from src.constants import maxwell from src.models import dms_delivery_spec from src.models import jobs from src.models import orders from src.models import products from src.utils import extra_data def update_from_datasources(records): """Update encoding_queue_detail records from various data sources. Args: records (list): A list of records as dictionaries. Returns: list: A list of updated records. """ logging.logger.debug( 'Starting fetching data by get_encoding_queue_bulk().') records = extra_data.get_and_update( records, [fields.ENCODING_QUEUE_ID], orders.get_encoding_queue_bulk, remove_not_found=True) logging.logger.debug( 'Starting fetching data by get_encoding_order_bulk().') records = extra_data.get_and_update( records, [fields.ORDER_ID], orders.get_encoding_order_bulk) logging.logger.debug( 'Starting fetching data by get_dms_delivery_spec_bulk().') records = extra_data.get_and_update( records, [fields.ORDER_TYPE, fields.DMS_MASTER_MASTER_ID], dms_delivery_spec.get_dms_delivery_spec_bulk) logging.logger.debug( 'Starting fetching data by get_release_bulk().') records = extra_data.get_and_update( records, [fields.UPC], products.get_release_bulk) return records def insert_details(records): """Insert encoding_queue_detail records. Args: records (list): A list of db records dictionaries. """ records = update_from_datasources(records) known_records = [ r for r in records if r[fields.ORDER_TYPE] not in config.IGNORE_ORDER_TYPES ] if not known_records: logging.logger.info('No known records to insert.') return # Create all records without setting is_duplicate flag. jobs.create_bulk(known_records) def update_details(records): """Process encoding_queue_detail update changes. Args: records (list): list of db records (dict) """ logging.logger.debug( 'Starting fetching data by get_encoding_queue_bulk().') records = extra_data.get_and_update( records, [fields.ENCODING_QUEUE_ID], orders.get_encoding_queue_bulk, remove_not_found=True) known_records = [ r for r in records if r[fields.ORDER_TYPE] not in config.IGNORE_ORDER_TYPES ] if not known_records: logging.logger.debug('No known records to update.') return logging.logger.debug('Starting updating details records.') successful_updates, not_found_records = jobs.update_bulk(known_records) if not_found_records: logging.logger.debug( f'Starting inserting {len(not_found_records)} details records ' 'not found during update') insert_details(not_found_records) def process_details(records): """Process encoding_queue_detail changes. Args: records (dict): A dictionary of Maxwell's daemon records: key - operation type as str, value - a list of db records as dicts. """ # Make sure inserts are handled first. The order is important not to # overwrite newer records! operations_map = ( (maxwell.TYPE_INSERT, insert_details), (maxwell.TYPE_UPDATE, update_details), ) for operation_type, processing_func in operations_map: operation_records = records.get(operation_type) if operation_records is None: continue logging.logger.debug( 'Starting processing operation: %s for encoding_queue_detail.', operation_type) if config.FETCH_FRESH_DD_RECORDS: logging.logger.debug( 'Starting fetching fresh encoding_queue_detail records.') pk_list = [ r[fields.ENCODING_QUEUE_DETAIL_ID] for r in operation_records] operation_records = list(jobs.get_encoding_queue_detail_bulk( pk_list).values()) logging.logger.debug( 'Finished fetching fresh encoding_queue_detail records.') processing_func(operation_records) logging.logger.debug( 'Finished processing operation %s for encoding_queue_detail.', operation_type) def get_duplicates_in_current_batch(records): """Get duplicates and non duplicates in a current batch of records. Duplicate items (with the same fields values) in the input are ignored. Args: records (list): A list of records as dictionaries (this should be a full list of possibly duplicate records). Records with meta_update = True are expected to be out already. Returns: tuple: Two lists with duplicate and non duplicate records. """ logging.logger.debug( 'Starting separating duplicates in the current batch of %d records.', len(records) ) key_fields = (fields.PRODUCT_ID, fields.STORE_ID, fields.ORDER_TYPE) key_kwarg = dict(key=operator.itemgetter(*key_fields)) # Make a unique list of records (removes the same records from the input). # We can not create a set of dicts directly, so we do it via intermediate # conversion of each record to a tuple. all_records_tuples = [tuple(r.items()) for r in records] unique_records_tuples = set(all_records_tuples) unique_records = [dict(t) for t in unique_records_tuples] # Group by keys. sorted_records = sorted(unique_records, **key_kwarg) grouped_records = itertools.groupby(sorted_records, **key_kwarg) # Separate duplicates form non duplicates in original records list. non_duplicates, duplicates = [], [] for group_key, group_records in grouped_records: group_records_list = list(group_records) if len(group_records_list) > 1: # A group of duplicates for the current key. group_duplicates_sorted = sorted( group_records_list, key=lambda r: r[fields.ENCODING_QUEUE_DETAIL_ID]) # The record with the largest EQD ID is not considered a duplicate. # Move it to non_duplicates list. duplicates.extend(group_duplicates_sorted[:-1]) non_duplicates.append(group_duplicates_sorted[-1]) else: non_duplicates.extend(group_records_list) logging.logger.debug( 'Finished separating duplicates in the current batch.') logging.logger.debug('Number of duplicates: %d', len(duplicates)) logging.logger.debug('Number of non duplicates: %d', len(non_duplicates)) return duplicates, non_duplicates