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, root_validator from app.db_sql import redis_lookup_id_random from app.lib_general import log, logging from app.models.common_field_schema import base_fields, default_num_bytes class Event_Person_Tracking_Base(BaseModel): log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) # --- 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']) # --- 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) @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 # 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 # 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] other_in_out: Optional[bool] other_type: Optional[str] # For unknown client specific tracking (lunch, medical, pause, etc) 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 check_in: Optional[bool] break_out: Optional[bool] break_in: Optional[bool] check_out: Optional[bool] datetime: Optional[datetime.datetime] enable: Optional[bool] notes: Optional[str] created_on: Optional[datetime.datetime] = None updated_on: Optional[datetime.datetime] = None # Including convenience data event_person_informal_name: Optional[str] event_person_given_name: Optional[str] event_person_family_name: Optional[str] event_person_full_name: Optional[str] event_person_full_name_override: Optional[str] event_person_affiliations: Optional[str] event_person_email: Optional[str] event_session_name: Optional[str] location_name: Optional[str] = Field( alias = 'event_location_name' ) track_name: Optional[str] = Field( alias = 'event_track_name' ) _processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now) class Config: underscore_attrs_are_private = True allow_population_by_field_name = True fields = base_fields