diff --git a/app/models/event_exhibit_models.py b/app/models/event_exhibit_models.py index 3ec743d..0d5a850 100644 --- a/app/models/event_exhibit_models.py +++ b/app/models/event_exhibit_models.py @@ -39,21 +39,45 @@ class Event_Exhibit_Base(BaseModel): """ Vision Transformer: Map DB keys to clean API keys and strip internal integers during READ operations. + Falls back to Redis/DB lookups if random string IDs are missing from the view. """ - # 1. Map Random Strings to Clean Names + from app.db_sql import get_id_random + + # 1. Map Primary Object ID rid = values.get('id_random') or values.get('event_exhibit_id_random') if rid and isinstance(rid, str): values['id'] = rid values['event_exhibit_id'] = rid - - if a_rid := values.get('account_id_random'): values['account_id'] = a_rid - if e_rid := values.get('event_id_random'): values['event_id'] = e_rid - if o_rid := values.get('organization_id_random'): values['organization_id'] = o_rid - if c_rid := values.get('contact_id_random'): values['contact_id'] = c_rid - if p_rid := values.get('person_id_random'): values['person_id'] = p_rid - if s_rid := values.get('status_id_random'): values['status_id'] = s_rid - - # 2. Prevent leakage of integers during API responses (Vision Standard) + elif values.get('id') and isinstance(values.get('id'), int): + # Fallback for primary ID + resolved_rid = get_id_random(values['id'], 'event_exhibit') + if resolved_rid: + values['id'] = resolved_rid + values['event_exhibit_id'] = resolved_rid + values['id_random'] = resolved_rid + + # 2. Map & Resolve Relational IDs + id_map = [ + ('account_id', 'account'), + ('event_id', 'event'), + ('organization_id', 'organization'), + ('contact_id', 'contact'), + ('person_id', 'person'), + ('status_id', 'status'), + ] + + for field, table in id_map: + r_val = values.get(f'{field}_random') + if r_val and isinstance(r_val, str): + values[field] = r_val + elif values.get(field) and isinstance(values[field], int): + # Fallback: Resolve from Redis/DB if missing from view result + resolved_rid = get_id_random(values[field], table) + if resolved_rid: + values[field] = resolved_rid + values[f'{field}_random'] = resolved_rid + + # 3. Final Vision Enforcement: Strip internal integers for k in ['id', 'event_exhibit_id', 'account_id', 'event_id', 'organization_id', 'contact_id', 'person_id', 'status_id']: val = values.get(k) if val is not None and not isinstance(val, str): diff --git a/app/models/event_exhibit_tracking_models.py b/app/models/event_exhibit_tracking_models.py index 4376737..5c3ebe8 100644 --- a/app/models/event_exhibit_tracking_models.py +++ b/app/models/event_exhibit_tracking_models.py @@ -39,21 +39,44 @@ class Event_Exhibit_Tracking_Base(BaseModel): """ Vision Transformer: Map DB keys to clean API keys and strip internal integers during READ operations. - During CREATE (POST) operations, we ensure resolved integers are preserved. + Falls back to Redis/DB lookups if random string IDs are missing from the view. """ - # 1. Map Random Strings to Clean Names + from app.db_sql import get_id_random + + # 1. Map Primary Object ID rid = values.get('id_random') or values.get('event_exhibit_tracking_id_random') if rid and isinstance(rid, str): values['id'] = rid values['event_exhibit_tracking_id'] = rid - - if a_rid := values.get('account_id_random'): values['account_id'] = a_rid - if e_rid := values.get('event_id_random'): values['event_id'] = e_rid - if ex_rid := values.get('event_exhibit_id_random'): values['event_exhibit_id'] = ex_rid - if ep_rid := values.get('event_person_id_random'): values['event_person_id'] = ep_rid - if eb_rid := values.get('event_badge_id_random'): values['event_badge_id'] = eb_rid - - # 2. Prevent leakage of integers during API responses (Vision Standard) + elif values.get('id') and isinstance(values.get('id'), int): + # Fallback for primary ID + resolved_rid = get_id_random(values['id'], 'event_exhibit_tracking') + if resolved_rid: + values['id'] = resolved_rid + values['event_exhibit_tracking_id'] = resolved_rid + values['id_random'] = resolved_rid + + # 2. Map & Resolve Relational IDs + id_map = [ + ('account_id', 'account'), + ('event_id', 'event'), + ('event_exhibit_id', 'event_exhibit'), + ('event_person_id', 'event_person'), + ('event_badge_id', 'event_badge'), + ] + + for field, table in id_map: + r_val = values.get(f'{field}_random') + if r_val and isinstance(r_val, str): + values[field] = r_val + elif values.get(field) and isinstance(values[field], int): + # Fallback: Resolve from Redis/DB if missing from view result + resolved_rid = get_id_random(values[field], table) + if resolved_rid: + values[field] = resolved_rid + values[f'{field}_random'] = resolved_rid + + # 3. Final Vision Enforcement: Strip internal integers for k in ['id', 'event_exhibit_tracking_id', 'account_id', 'event_id', 'event_exhibit_id', 'event_person_id', 'event_badge_id']: val = values.get(k) if val is not None and not isinstance(val, str): diff --git a/app/models/event_person_tracking_models.py b/app/models/event_person_tracking_models.py index 28219f7..e346908 100644 --- a/app/models/event_person_tracking_models.py +++ b/app/models/event_person_tracking_models.py @@ -2,7 +2,7 @@ from __future__ import annotations import datetime, hashlib, logging, os, pytz, redis, secrets from typing import Dict, List, Optional, Set, Union -from pydantic import BaseModel, EmailStr, Field, Json, PrivateAttr, ValidationError, validator +from pydantic import BaseModel, EmailStr, Field, Json, PrivateAttr, ValidationError, validator, root_validator from app.db_sql import redis_lookup_id_random from app.lib_general import log, logging @@ -14,26 +14,69 @@ class Event_Person_Tracking_Base(BaseModel): log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) - id_random: Optional[str] = Field( - **base_fields['event_person_tracking_id_random'], - alias = 'event_person_tracking_id_random', - default_factory = lambda:secrets.token_urlsafe(default_num_bytes), - ) - id: Optional[int] = Field( - alias = 'event_person_tracking_id' - ) + # --- Standardized Vision IDs (Strings for API, Integers for DB) --- + id: Optional[Union[int, str]] = Field(**base_fields['event_person_tracking_id_random']) + event_person_tracking_id: Optional[Union[int, str]] = Field(**base_fields['event_person_tracking_id_random']) + account_id: Optional[Union[int, str]] = Field(**base_fields['account_id_random']) + event_id: Optional[Union[int, str]] = Field(**base_fields['event_id_random']) + event_session_id: Optional[Union[int, str]] = Field(**base_fields['event_session_id_random']) + event_person_id: Optional[Union[int, str]] = Field(**base_fields['event_person_id_random']) - # account_id_random: Optional[str] - # account_id: Optional[int] + # --- Standardized Legacy / Internal IDs (Excluded) --- + id_random: Optional[str] = Field(None, alias='event_person_tracking_id_random', exclude=True) + account_id_random: Optional[str] = Field(None, exclude=True) + event_id_random: Optional[str] = Field(None, exclude=True) + event_session_id_random: Optional[str] = Field(None, exclude=True) + event_person_id_random: Optional[str] = Field(None, exclude=True) - event_id_random: Optional[str] - event_id: Optional[int] + @root_validator(pre=True) + def map_v3_ids(cls, values): + """ + Vision Transformer: + Map DB keys to clean API keys and strip internal integers during READ operations. + Falls back to Redis/DB lookups if random string IDs are missing from the view. + """ + from app.db_sql import get_id_random - event_session_id_random: Optional[str] - event_session_id: Optional[int] + # 1. Map Primary Object ID + rid = values.get('id_random') or values.get('event_person_tracking_id_random') + if rid and isinstance(rid, str): + values['id'] = rid + values['event_person_tracking_id'] = rid + elif values.get('id') and isinstance(values.get('id'), int): + # Fallback for primary ID + resolved_rid = get_id_random(values['id'], 'event_person_tracking') + if resolved_rid: + values['id'] = resolved_rid + values['event_person_tracking_id'] = resolved_rid + values['id_random'] = resolved_rid - event_person_id_random: Optional[str] - event_person_id: Optional[int] + # 2. Map & Resolve Relational IDs + id_map = [ + ('account_id', 'account'), + ('event_id', 'event'), + ('event_session_id', 'event_session'), + ('event_person_id', 'event_person'), + ] + + for field, table in id_map: + r_val = values.get(f'{field}_random') + if r_val and isinstance(r_val, str): + values[field] = r_val + elif values.get(field) and isinstance(values[field], int): + # Fallback: Resolve from Redis/DB if missing from view result + resolved_rid = get_id_random(values[field], table) + if resolved_rid: + values[field] = resolved_rid + values[f'{field}_random'] = resolved_rid + + # 3. Final Vision Enforcement: Strip internal integers + for k in ['id', 'event_person_tracking_id', 'account_id', 'event_id', 'event_session_id', 'event_person_id']: + val = values.get(k) + if val is not None and not isinstance(val, str): + values[k] = None + + return values check_in_out: Optional[bool] break_in_out: Optional[bool] @@ -43,15 +86,11 @@ class Event_Person_Tracking_Base(BaseModel): in_datetime: Optional[datetime.datetime] # This should generally default to the created datetime and be overridden as needed out_datetime: Optional[datetime.datetime] # This should generally default to the updated datetime and be overridden as needed - # Maybe add minutes or hours? - # Maybe add timezone? - - - check_in: Optional[bool] # Does this make sense to use instead? - break_out: Optional[bool] # Does this make sense to use instead? - break_in: Optional[bool] # Does this make sense to use instead? - check_out: Optional[bool] # Does this make sense to use instead? - datetime: Optional[datetime.datetime] # This should generally default to the created datetime and be overridden as needed + check_in: Optional[bool] + break_out: Optional[bool] + break_in: Optional[bool] + check_out: Optional[bool] + datetime: Optional[datetime.datetime] enable: Optional[bool] @@ -60,14 +99,6 @@ class Event_Person_Tracking_Base(BaseModel): updated_on: Optional[datetime.datetime] = None # Including convenience data - # This is only for convenience. Probably going to keep unless it causes a problem. - # full_name: Optional[str] = Field( - # alias = 'event_person_full_name' - # ) - # display_name: Optional[str] = Field( - # alias = 'event_person_display_name' - # ) - event_person_informal_name: Optional[str] event_person_given_name: Optional[str] event_person_family_name: Optional[str] @@ -83,57 +114,10 @@ class Event_Person_Tracking_Base(BaseModel): track_name: Optional[str] = Field( alias = 'event_track_name' ) - # Maybe add timezone in the future? _processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now) - #@validator('event_person_tracking_id_random', always=True) - def event_person_tracking_id_random_copy(cls, v, values, **kwargs): - log.setLevel(logging.WARNING) - log.debug(locals()) - - if values['id_random']: - return values['id_random'] - return None - - @validator('id', always=True) - def event_person_tracking_id_lookup(cls, v, values, **kwargs): - log.setLevel(logging.WARNING) - log.debug(locals()) - - if values.get('id_random', None): - log.debug(values['id_random']) - return redis_lookup_id_random(record_id_random=values['id_random'], table_name='event_person_tracking') - return None - - @validator('event_id', always=True) - def event_id_lookup(cls, v, values, **kwargs): - log.setLevel(logging.WARNING) - log.debug(locals()) - - if values.get('event_id_random', None): - return redis_lookup_id_random(record_id_random=values['event_id_random'], table_name='event') - return None - - @validator('event_session_id', always=True) - def event_session_id_lookup(cls, v, values, **kwargs): - log.setLevel(logging.WARNING) - log.debug(locals()) - - if values.get('event_session_id_random', None): - return redis_lookup_id_random(record_id_random=values['event_session_id_random'], table_name='event_session') - return None - - @validator('event_person_id', always=True) - def event_person_id_lookup(cls, v, values, **kwargs): - log.setLevel(logging.WARNING) - log.debug(locals()) - - if values.get('event_person_id_random', None): - return redis_lookup_id_random(record_id_random=values['event_person_id_random'], table_name='event_person') - return None - class Config: underscore_attrs_are_private = True allow_population_by_field_name = True - fields = base_fields + fields = base_fields \ No newline at end of file diff --git a/app/object_definitions/events_registration.py b/app/object_definitions/events_registration.py index b2db4b2..f72a998 100644 --- a/app/object_definitions/events_registration.py +++ b/app/object_definitions/events_registration.py @@ -113,8 +113,8 @@ events_registration_obj_li = { 'base_name': Event_Person_Tracking_Base, # V3 Search Security: 'searchable_fields': [ - 'id', 'event_person_tracking_id', 'event_id', 'event_session_id', 'event_person_id', - 'id_random', 'event_person_tracking_id_random', 'event_id_random', + 'id', 'event_person_tracking_id', 'account_id', 'event_id', 'event_session_id', 'event_person_id', + 'id_random', 'event_person_tracking_id_random', 'account_id_random', 'event_id_random', 'event_session_id_random', 'event_person_id_random', 'check_in_out', 'in_datetime', 'out_datetime', 'enable', 'notes', 'created_on', 'updated_on'