""" Centralized ID random to integer ID resolution. """ import logging import datetime import random import redis from app.config import settings from app.db_connection import db log = logging.getLogger(__name__) def redis_lookup_id_random( record_id_random: int|str, table_name: str, check_int_id: bool = False, log_lvl: int = logging.WARNING, minutes: int = 30, reset_rate: int = 10, ) -> str|int|bool|None: """ Looks up a record ID in Redis, falling back to SQL if not found. Resolves 'id_random' (URL-safe string) to internal integer 'id'. """ from app.db_sql import sql_select, get_id_random log.setLevel(log_lvl) if isinstance(record_id_random, str) and 11 <= len(record_id_random) <= 22: pass elif isinstance(record_id_random, int): if check_int_id: if get_id_random(record_id=record_id_random, table_name=table_name): return record_id_random return False return record_id_random elif record_id_random is None: return None else: log.error(f'Unexpected data type: {type(record_id_random)}. Expected string (11-22 chars) or int.') return False if not table_name: log.error(f'Missing table_name for id_random lookup: {record_id_random}') return False r = redis.Redis(host=settings.REDIS['server'], port=settings.REDIS['port'], db=7, password=None, decode_responses=True) key_name = f'{table_name}:{record_id_random}' record_id = r.get(key_name) # Periodic cache refresh if record_id and random.randint(1, reset_rate) == 1: log.warning(f'Redis: Randomly (1/{reset_rate}) refreshing cache for Key="{key_name}"') record_id = None if record_id: r.setex(key_name, datetime.timedelta(minutes=minutes), value=record_id) return int(record_id) else: data = { 'id_random': record_id_random } sql = f"SELECT id FROM `{table_name}` WHERE id_random = :id_random;" if select_results := sql_select(sql=sql, data=data): if isinstance(select_results, dict): if rid := select_results.get('id'): r.setex(key_name, datetime.timedelta(minutes=minutes), value=rid) return int(rid) log.error('SQL result missing ID field.') return False else: log.error(f'SQL: Duplicate id_random found in "{table_name}". Retrying...') return redis_lookup_id_random(record_id_random=record_id_random, table_name=table_name) else: log.warning(f'SQL: ID Random "{record_id_random}" not found in "{table_name}".') return None def lookup_id_random_pop( obj_data: dict, log_lvl: int = logging.WARNING, ): """ Resolves any *_id_random fields in a dict to their integer IDs and removes the random keys. """ log.setLevel(log_lvl) # List of common prefix patterns to resolve id_patterns = [ 'account', 'activity_log', 'address', 'archive', 'contact', 'cont_edu_cert', 'cont_edu_cert_person', 'event', 'event_abstract', 'event_badge', 'event_badge_template', 'event_exhibit', 'event_file', 'event_location', 'event_person', 'event_person_profile', 'event_presentation', 'event_presenter', 'event_registration', 'event_session', 'event_track', 'grant', 'hosted_file', 'journal', 'journal_entry', 'membership_group', 'membership_person_group', 'membership_person', 'membership_type', 'membership_person_type', 'order', 'order_line', 'order_cart', 'order_cart_line', 'organization', 'page', 'person', 'post', 'product', 'sponsorship', 'sponsorship_cfg', 'site', 'user' ] for prefix in id_patterns: key = f'{prefix}_id_random' if key in obj_data: table = prefix if prefix == 'address_location': table = 'address' if prefix in ['contact_1', 'contact_2']: table = 'contact' if prefix == 'event_id_random_only': table = 'event' resolved_id = redis_lookup_id_random(record_id_random=obj_data[key], table_name=table) obj_data[f'{prefix}_id'] = resolved_id obj_data.pop(key) # Handle polymorphic link fields polymorphic = [ ('for_type', 'for_id_random', 'for_id'), ('link_to_type', 'link_to_id_random', 'link_to_id'), ('object_type', 'object_id_random', 'object_id'), ('to_object_type', 'to_object_id_random', 'to_object_id'), ('from_object_type', 'from_object_id_random', 'from_object_id') ] for type_key, rand_key, id_key in polymorphic: if type_key in obj_data and rand_key in obj_data: obj_data[id_key] = redis_lookup_id_random( record_id_random=obj_data[rand_key], table_name=obj_data[type_key] ) obj_data.pop(rand_key) return obj_data