Standardized Badge, Exhibit, and Tracking models to ID Vision standards. Included account_id support for exhibit tracking and removed legacy validators to ensure stable CRUD operations for the Tuesday demo.
334 lines
15 KiB
Python
334 lines
15 KiB
Python
import datetime, pytz
|
|
|
|
from typing import Dict, List, Optional, Set, Union
|
|
from pydantic import BaseModel, EmailStr, Field, Json, PrivateAttr, ValidationError, 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
|
|
from app.models.event_badge_template_models import Event_Badge_Template_Base
|
|
from app.models.order_models import Order_Base
|
|
|
|
|
|
# ### BEGIN ### API Event Badge Models ### Event_Badge_Base() ###
|
|
class Event_Badge_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(None, **base_fields['event_badge_id_random'])
|
|
event_badge_id: Optional[Union[int, str]] = Field(None, **base_fields['event_badge_id_random'])
|
|
event_id: Optional[Union[int, str]] = Field(None, **base_fields['event_id_random'])
|
|
|
|
# NOTE: This should only be used when the event_person record can not be created. And records before 2022.
|
|
event_id_only: Optional[Union[int, str]] = Field(None, **base_fields['event_id_random'])
|
|
|
|
event_badge_template_id: Optional[Union[int, str]] = Field(None, **base_fields['event_badge_template_id_random'])
|
|
event_person_id: Optional[Union[int, str]] = Field(None, **base_fields['event_person_id_random'])
|
|
person_id: Optional[Union[int, str]] = Field(None, **base_fields['person_id_random'])
|
|
|
|
# --- Standardized Legacy / Internal IDs (Excluded) ---
|
|
id_random: Optional[str] = Field(None, alias='event_badge_id_random', exclude=True)
|
|
event_id_random: Optional[str] = Field(None, exclude=True)
|
|
event_id_random_only: Optional[str] = Field(None, exclude=True)
|
|
event_badge_template_id_random: Optional[str] = Field(None, exclude=True)
|
|
event_person_id_random: Optional[str] = Field(None, exclude=True)
|
|
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.
|
|
During CREATE (POST) operations, we ensure resolved integers are preserved.
|
|
"""
|
|
# 1. Map Random Strings to Clean Names
|
|
rid = values.get('id_random') or values.get('event_badge_id_random')
|
|
if rid and isinstance(rid, str):
|
|
values['id'] = rid
|
|
values['event_badge_id'] = rid
|
|
|
|
if e_rid := values.get('event_id_random'): values['event_id'] = e_rid
|
|
if eo_rid := values.get('event_id_random_only'): values['event_id_only'] = eo_rid
|
|
if et_rid := values.get('event_badge_template_id_random'): values['event_badge_template_id'] = et_rid
|
|
if ep_rid := values.get('event_person_id_random'): values['event_person_id'] = ep_rid
|
|
if p_rid := values.get('person_id_random'): values['person_id'] = p_rid
|
|
|
|
# 2. Prevent "Collision Population" or leakage of integers during API responses
|
|
# WE MUST NOT DELETE these if they are already integers during a POST operation
|
|
# as they have been resolved by sanitize_payload.
|
|
for k in ['id', 'event_badge_id', 'event_id', 'event_id_only', 'event_badge_template_id', 'event_person_id', 'person_id']:
|
|
val = values.get(k)
|
|
if val is not None and not isinstance(val, str):
|
|
if values.get(f'{k}_random') or (k=='id' and values.get('id_random')) or (k=='event_id_only' and values.get('event_id_random_only')):
|
|
del values[k]
|
|
|
|
return values
|
|
|
|
external_id: Optional[str] # Generated internally or externally. Needs to be stable. It should not change.
|
|
external_event_id: Optional[str] # Event ID generated by external system. Needs to be stable. It should not change.
|
|
external_registration_id: Optional[str] # Registration ID generated by external system (should be stable and not change)
|
|
external_reg_id: Optional[str] # NOTE: Deprecated; Move to external_registration_id. Registration ID generated by external system (should be stable and not change)
|
|
external_person_id: Optional[str] # Person ID generated by external system (should be stable and not change)
|
|
external_sys_id: Optional[str] # NOTE: Deprecated; Move to external_person_id. Person ID generated by external system (should be stable and not change)
|
|
|
|
pronouns: Optional[str] # Preferred pronouns
|
|
pronouns_override: Optional[str] # Override pronouns
|
|
|
|
informal_name: Optional[str]
|
|
|
|
title_names: Optional[str] # Title for generation, official position, or professional or academic qualification, other honorific, or other name prefix
|
|
given_name: Optional[str]
|
|
middle_name: Optional[str]
|
|
family_name: Optional[str]
|
|
designations: Optional[str] # Temporary or long-term designations related to family, relationships, person differentiation (Junior/Senior), location, social status, professional qualifications, legal status, or other name suffix (degrees and credentials)
|
|
|
|
professional_title: Optional[str] # Professional title
|
|
display_professional_title: Optional[str] # NOTE: Deprecated! Phasing out! Use *full_name_override* instead.
|
|
professional_title_override: Optional[str] # Override professional title
|
|
# title: Optional[str] # NOTE: Phasing out! Use *professional_title* instead.
|
|
|
|
# BEGIN # Auto created name variations
|
|
full_name: Optional[str] # title_names given_name middle_name family_name designations
|
|
full_name_override: Optional[str] # # Override full_name; Actual name shown on badge and other "public" areas
|
|
|
|
affiliations: Optional[str] # One or more affiliations with organizations, companies, and other groups
|
|
affiliations_override: Optional[str] # Override affiliations
|
|
|
|
email: Optional[str]
|
|
email_override: Optional[str]
|
|
|
|
phone: Optional[str]
|
|
phone_override: Optional[str]
|
|
|
|
address_line_1: Optional[str]
|
|
address_line_2: Optional[str]
|
|
address_line_3: Optional[str]
|
|
|
|
city: Optional[str]
|
|
|
|
county: Optional[str] # NOTE: This is for a county within a state or province
|
|
|
|
country_subdivision_code: Optional[str]
|
|
state_province: Optional[str]
|
|
state_province_abb: Optional[str]
|
|
|
|
postal_code: Optional[str]
|
|
|
|
country_alpha_2_code: Optional[str]
|
|
country: Optional[str]
|
|
|
|
# full_address: Optional[str]
|
|
|
|
location: Optional[str] # Actual location name shown on badge and other "public" areas
|
|
location_override: Optional[str] # Override location
|
|
location_short: Optional[str] # Auto generated short version
|
|
location_long: Optional[str] # Auto generated long version
|
|
|
|
# This is updated using SQL triggers and a SQL function
|
|
# Combines informal, given, middle, family, email
|
|
query_str: Optional[str]
|
|
|
|
# NOTE: More badge fields need to be added here once things are cleaned up
|
|
badge_type_code_override: Optional[str]
|
|
badge_type_override: Optional[str]
|
|
badge_type_code: Optional[str]
|
|
badge_type: Optional[str]
|
|
member_type_code: Optional[str]
|
|
member_type: Optional[str]
|
|
member_status: Optional[str]
|
|
registration_type_code: Optional[str]
|
|
registration_type: Optional[str]
|
|
|
|
other_1: Optional[str]
|
|
other_2: Optional[str]
|
|
|
|
ticket_0_code: Optional[str]
|
|
ticket_1_code: Optional[str]
|
|
ticket_2_code: Optional[str]
|
|
ticket_3_code: Optional[str]
|
|
ticket_4_code: Optional[str]
|
|
ticket_5_code: Optional[str]
|
|
ticket_6_code: Optional[str]
|
|
ticket_7_code: Optional[str]
|
|
ticket_8_code: Optional[str]
|
|
ticket_9_code: Optional[str]
|
|
ticket_10_code: Optional[str]
|
|
|
|
agree_to_tc: Optional[bool] # Agree to terms and conditions
|
|
allow_tracking: Optional[bool] # Allow tracking for lead retrieval and other marketing
|
|
|
|
print_first_datetime: Optional[datetime.datetime] = None
|
|
print_last_datetime: Optional[datetime.datetime] = None
|
|
print_count: Optional[int]
|
|
|
|
# full_name_font_size: Optional[str] # Not currently used 2023-01-25
|
|
# professional_title_font_size: Optional[str] # Not currently used 2023-01-25
|
|
# affiliations_font_size: Optional[str] # Not currently used 2023-01-25
|
|
# location_font_size: Optional[str] # Not currently used 2023-01-25
|
|
# css: Optional[str] # Not currently used 2023-01-25
|
|
|
|
cfg_json: Optional[Union[Json, None]] # Store per badge config options like font size; Not currently used 2024-06-11
|
|
data_json: Optional[Union[Json, None]] # For key value data. Careful with overwriting existing fields! Not currently used 2024-06-11
|
|
|
|
default_qry_str: Optional[str] # Default query string used for searching and filtering badges. Updated using SQL triggers and a SQL function
|
|
|
|
hide: Optional[bool]
|
|
priority: Optional[bool]
|
|
sort: Optional[int]
|
|
group: Optional[str]
|
|
enable: Optional[bool]
|
|
|
|
notes: Optional[str]
|
|
created_on: Optional[datetime.datetime] = None
|
|
updated_on: Optional[datetime.datetime] = None
|
|
|
|
# Including other related objects
|
|
order: Optional[Union[Order_Base, None]]
|
|
ticket_list: Optional[list]
|
|
event_badge_template: Optional[Union[Event_Badge_Template_Base, None]]
|
|
|
|
_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
|
|
|
|
|
|
class Event_Badge_Basic_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(None, **base_fields['event_badge_id_random'])
|
|
event_badge_id: Optional[Union[int, str]] = Field(None, **base_fields['event_badge_id_random'])
|
|
event_badge_template_id: Optional[Union[int, str]] = Field(None, **base_fields['event_badge_template_id_random'])
|
|
event_person_id: Optional[Union[int, str]] = Field(None, **base_fields['event_person_id_random'])
|
|
|
|
# --- Standardized Legacy / Internal IDs (Excluded) ---
|
|
id_random: Optional[str] = Field(None, alias='event_badge_id_random', exclude=True)
|
|
event_badge_template_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.
|
|
During CREATE (POST) operations, we ensure resolved integers are preserved.
|
|
"""
|
|
# 1. Map Random Strings to Clean Names
|
|
rid = values.get('id_random') or values.get('event_badge_id_random')
|
|
if rid and isinstance(rid, str):
|
|
values['id'] = rid
|
|
values['event_badge_id'] = rid
|
|
|
|
if et_rid := values.get('event_badge_template_id_random'): values['event_badge_template_id'] = et_rid
|
|
if ep_rid := values.get('event_person_id_random'): values['event_person_id'] = ep_rid
|
|
|
|
# 2. Prevent "Collision Population" or leakage of integers during API responses
|
|
for k in ['id', 'event_badge_id', 'event_badge_template_id', 'event_person_id']:
|
|
val = values.get(k)
|
|
if val is not None and not isinstance(val, str):
|
|
if values.get(f'{k}_random') or (k=='id' and values.get('id_random')):
|
|
del values[k]
|
|
|
|
return values
|
|
|
|
external_id: Optional[str] # Generated internally or externally. Needs to be stable. It should not change.
|
|
# external_sys_id: Optional[str] # Person ID generated by external system (should be stable and not change)
|
|
# external_reg_id: Optional[str] # Registration ID generated by external system (should be stable and not change)
|
|
|
|
pronouns: Optional[str] # Preferred pronouns
|
|
pronouns_override: Optional[str] # Preferred pronouns
|
|
|
|
informal_name: Optional[str]
|
|
|
|
title_names: Optional[str] # Title for generation, official position, or professional or academic qualification, other honorific, or other name prefix
|
|
given_name: Optional[str]
|
|
middle_name: Optional[str]
|
|
family_name: Optional[str]
|
|
designations: Optional[str] # Temporary or long-term designations related to family, relationships, person differentiation (Junior/Senior), location, social status, professional qualifications, legal status, or other name suffix
|
|
|
|
professional_title: Optional[str] # Professional title
|
|
professional_title_override: Optional[str] # Override professional title
|
|
|
|
# BEGIN # Auto created name variations
|
|
full_name: Optional[str] # title_names given_name middle_name family_name designations
|
|
full_name_override: Optional[str] # Override full_name; Actual name shown on badge and other "public" areas
|
|
|
|
affiliations: Optional[str] # One or more affiliations with organizations, companies, and other groups
|
|
affiliations_override: Optional[str] # Override affiliations
|
|
|
|
email: Optional[str]
|
|
email_override: Optional[str]
|
|
|
|
phone: Optional[str]
|
|
phone_override: Optional[str]
|
|
|
|
# address_line_1: Optional[str]
|
|
# address_line_2: Optional[str]
|
|
# address_line_3: Optional[str]
|
|
|
|
# city: Optional[str]
|
|
|
|
# county: Optional[str] # NOTE: This is for a county within a state or province
|
|
|
|
# country_subdivision_code: Optional[str]
|
|
# state_province: Optional[str]
|
|
# state_province_abb: Optional[str]
|
|
|
|
# postal_code: Optional[str]
|
|
|
|
country_alpha_2_code: Optional[str]
|
|
country: Optional[str]
|
|
|
|
# full_address: Optional[str]
|
|
location: Optional[str] # Actual location name shown on badge and other "public" areas
|
|
location_override: Optional[str] # Override location
|
|
# location_short: Optional[str] # Auto generated short version
|
|
# location_long: Optional[str] # Auto generated long version
|
|
|
|
# NOTE: More badge fields need to be added here once things are cleaned up
|
|
# badge_type_code: Optional[str]
|
|
# badge_type: Optional[str]
|
|
# member_type_code: Optional[str]
|
|
# member_type: Optional[str]
|
|
# registration_type_code: Optional[str]
|
|
# registration_type: Optional[str]
|
|
|
|
# other_1: Optional[str]
|
|
# other_2: Optional[str]
|
|
|
|
allow_tracking: Optional[bool] # Allow tracking for lead retrieval and other marketing
|
|
# agree_to_tc: Optional[bool] # Agree to terms and conditions
|
|
|
|
print_first_datetime: Optional[datetime.datetime] = None
|
|
print_last_datetime: Optional[datetime.datetime] = None
|
|
print_count: Optional[int]
|
|
|
|
hide: Optional[bool]
|
|
priority: Optional[bool]
|
|
sort: Optional[int]
|
|
group: Optional[str]
|
|
enable: Optional[bool]
|
|
|
|
notes: Optional[str]
|
|
created_on: Optional[datetime.datetime] = None
|
|
updated_on: Optional[datetime.datetime] = None
|
|
|
|
# Including other related objects
|
|
# order: Optional[Union[Order_Base, None]]
|
|
# ticket_list: Optional[list]
|
|
event_badge_template: Optional[Union[Event_Badge_Template_Base, None]]
|
|
|
|
_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
|
|
|