"""Cache packing strategy for various datatypes.""" from accounting.adapters.cache import CacheAdapter cache_adapter = CacheAdapter() CACHE_INNER_DELIMITER = ':' CACHE_FIELD_DELIMITER = '\t' PREFIXES = { 'appended_fields': 'af_', 'data': 'dt_', 'exchange_rates': 'er_', 'statement': 'st_', 'label_contract': 'lc_', 'owner_contract': 'oc_', 'release_tracks': 'rc_' } def cache_item(item_type, key, value): """Save a basic data type under a unique key. Args: item_type (str): the item type, used for key namespacing. key (str|int): item key. Should be unique per item_type group. value (str|int): The value to cache. Returns: bool: true on success. """ unique_key = '{}{}'.format(get_cache_prefix(item_type), key) adapter = get_cache_adapter() return adapter.cache_item(unique_key, value) def cache_string_list(item_type, key, value): """Save a dict under a unique key. Convert a dict to a string and cache it normally. @see cache_item Args: item_type (str): Item type. key (str|int): Cache key. value (dict): Dict to cache. Returns: bool """ return cache_item(item_type, key, pack_sting_list_for_cache(value)) def cache_key_val(item_type, key, value): """Cache key-val datatype.""" return cache_item(item_type, key, pack_key_value_list_for_cache(value)) def pack_key_value_list_for_cache(data): """Pack a keyed list for the cache. Args: data (dict): A dict containing integers, floats, strings, and booleans. Returns: str: The polyglot cache representation of the data. """ if not isinstance(data, dict): raise Exception('Expected type "dict", given {}'.format(type(data))) cache_representation = '' for key in data.keys(): value = data[key] if isinstance(value, float): value = '{:.6f}'.format(value) elif isinstance(value, bool): value = '1' if value else '0' cache_representation += '{}{}{}{}'.format( key, CACHE_INNER_DELIMITER, value, CACHE_FIELD_DELIMITER) return cache_representation[:-1] def pack_sting_list_for_cache(data): """Pack for caching. Args: data (dict): Dict to format into a cacheable string. Returns: str """ """Pack for caching.""" if not isinstance(data, dict): raise Exception('Expected type "dict", given {}'.format(type(data))) cache_representation = '' for value in data.values(): if isinstance(value, bool): value = '1' if value else '0' cache_representation += '{}{}'.format(value, CACHE_FIELD_DELIMITER) return cache_representation[:-1] def get_cached_item(item_type, key): """Get an item by unique key. Args: item_type (str): Item type. key (str|int): Cache key. Returns: str if item, else None """ unique_key = '{}{}'.format(get_cache_prefix(item_type), key) adapter = get_cache_adapter() item = adapter.get_item(unique_key) return item.decode() if item else None def get_cache_prefix(item_type): """Get a different string depending upon the item_type. This string is used to namespace the items in the cache by type. Args: item_type (str): Item type. Returns: str: item_type-specific prefix for cache namespacing. Raises: ValueError: Unknown item_type. """ if item_type in PREFIXES: return PREFIXES[item_type] raise ValueError(( 'expected one of appended_fields, statement, or data' ' got: {}').format(item_type)) def get_cache_adapter(): """Mockable getter.""" return cache_adapter