ID Vision Phase 2: Standardize Page, Post, Person, Organization, and Hosted File objects

This commit is contained in:
Scott Idem
2026-01-19 18:04:17 -05:00
parent ab8afb72d2
commit 817bb80f87
11 changed files with 324 additions and 457 deletions

View File

@@ -1,7 +1,7 @@
import datetime, pytz
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
@@ -23,31 +23,17 @@ class Person_Base(BaseModel):
log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
log.debug(locals())
id_random: Optional[str] = Field(
**base_fields['person_id_random'],
alias = 'person_id_random',
# default_factory = lambda:secrets.token_urlsafe(default_num_bytes),
)
id: Optional[int] = Field(
alias = 'person_id'
)
account_id_random: Optional[str]
account_id: Optional[int]
# --- Standardized Vision IDs (Strings) ---
id: Optional[str] = Field(None, **base_fields['person_id_random'])
person_id: Optional[str] = Field(None, **base_fields['person_id_random'])
account_id: Optional[str] = Field(None, **base_fields['account_id_random'])
contact_id: Optional[str] = Field(None, **base_fields['contact_id_random'])
organization_id: Optional[str] = Field(None, **base_fields['organization_id_random'])
user_id: Optional[str] = Field(None, **base_fields['user_id_random'])
membership_person_id: Optional[str] = Field(None, **base_fields['membership_person_id_random'])
contact_id_random: Optional[str]
contact_id: Optional[int]
organization_id_random: Optional[str]
organization_id: Optional[int]
user_id_random: Optional[str]
user_id: Optional[int]
membership_person_id_random: Optional[str] # Linked from membership_person using the v_person view
membership_person_id: Optional[int] # Linked from membership_person using the v_person view
pronouns: Optional[str] # Preferred pronouns
informal_name: Optional[str] # Informal or nick name they commonly go by
# pronouns: Optional[str] # MISSING in physical table
# informal_name: Optional[str] # MISSING in physical table
title_names: Optional[str] # Title for generation, official position, or professional or academic qualification, other honorific, or other name prefix
prefix: Optional[str] # NOTE: Phasing out! Use *title_names* instead.
@@ -59,11 +45,7 @@ class Person_Base(BaseModel):
professional_title: Optional[str] # Professional title
# display_name: Optional[str] # NOTE: This will be changed to full_name_override to match event_badge, event_presenter, and event_profile
# informal_display_name: Optional[str] # Custom what they want for informal public display
# professional_display_name: Optional[str] # Custom what they want for professional public display. This should include professional title.
preferred_display_name: Optional[str] # Which name variant to display? '', 'informal', 'professional', etc
# preferred_display_name: Optional[str] # MISSING in physical table
# BEGIN # Auto created name variations
first_last_name: Optional[str] # With SQL view?
@@ -161,64 +143,34 @@ class Person_Base(BaseModel):
_processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now)
#@validator('person_id_random', always=True)
def person_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 person_id_lookup(cls, v, values, **kwargs):
log.setLevel(logging.WARNING)
log.debug(locals())
if isinstance(v, int) and v > 0: return v
elif id_random := values.get('id_random'):
return redis_lookup_id_random(record_id_random=id_random, table_name='person')
return None
@validator('account_id', always=True)
def account_id_lookup(cls, v, values, **kwargs):
log.setLevel(logging.WARNING)
log.debug(locals())
if isinstance(v, int) and v > 0: return v
elif id_random := values.get('account_id_random'):
return redis_lookup_id_random(record_id_random=id_random, table_name='account')
return None
@validator('contact_id', always=True)
def contact_id_lookup(cls, v, values, **kwargs):
log.setLevel(logging.WARNING)
log.debug(locals())
if isinstance(v, int) and v > 0: return v
elif id_random := values.get('contact_id_random'):
return redis_lookup_id_random(record_id_random=id_random, table_name='contact')
return None
@validator('organization_id', always=True)
def organization_id_lookup(cls, v, values, **kwargs):
log.setLevel(logging.WARNING)
log.debug(locals())
if isinstance(v, int) and v > 0: return v
elif id_random := values.get('organization_id_random'):
return redis_lookup_id_random(record_id_random=id_random, table_name='organization')
return None
@validator('user_id', always=True)
def user_id_lookup(cls, v, values, **kwargs):
log.setLevel(logging.WARNING)
log.debug(locals())
if isinstance(v, int) and v > 0: return v
elif id_random := values.get('id_random'):
return redis_lookup_id_random(record_id_random=id_random, table_name='user')
return None
@root_validator(pre=True)
def map_v3_ids(cls, values):
"""
Vision Transformer:
Map DB keys to clean API keys and strip internal integers.
"""
# 1. Map Random Strings to Clean Names
if rid := values.get('id_random') or values.get('person_id_random'):
values['id'] = rid
values['person_id'] = rid
if a_rid := values.get('account_id_random'):
values['account_id'] = a_rid
if c_rid := values.get('contact_id_random'):
values['contact_id'] = c_rid
if o_rid := values.get('organization_id_random'):
values['organization_id'] = o_rid
if u_rid := values.get('user_id_random'):
values['user_id'] = u_rid
if mp_rid := values.get('membership_person_id_random'):
values['membership_person_id'] = mp_rid
# 2. Prevent "Collision Population"
for k in ['id', 'account_id', 'contact_id', 'organization_id', 'user_id', 'membership_person_id']:
if k in values and not isinstance(values[k], str):
del values[k]
return values
@validator('given_name', always=True)
def given_name_validator(cls, v):
@@ -234,6 +186,6 @@ class Person_Base(BaseModel):
class Config:
underscore_attrs_are_private = True
allow_population_by_field_name = True
allow_population_by_field_name = False
fields = base_fields
# ### END ### API Person Models ### Person_Base() ###