import datetime, pytz from typing import Dict, List, Optional, Set, Union, ClassVar 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 # from app.models.event_models import Event_Base # Causes an import loop # from app.models.event_abstract_models import Event_Abstract_Base from app.models.event_badge_models import Event_Badge_Base # from app.models.event_exhibit_tracking_models import Event_Exhibit_Tracking_Base # Causes an import loop from app.models.event_person_profile_models import Event_Person_Profile_Base from app.models.event_registration_models import Event_Registration_Base from app.models.person_models import Person_Base from app.models.user_models import User_Base, User_Out_Base # ### BEGIN ### API Event Person Models ### Event_Person_Base() ### class Event_Person_Base(BaseModel): log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) # --- Standardized Vision IDs (Strings) --- id: Optional[str] = Field(None, **base_fields['event_person_id_random']) event_person_id: Optional[str] = Field(None, **base_fields['event_person_id_random']) account_id: Optional[str] = Field(None, **base_fields['account_id_random']) event_id: Optional[str] = Field(None, **base_fields['event_id_random']) event_badge_id: Optional[str] = Field(None, **base_fields['event_badge_id_random']) event_badge_vendor_id: Optional[str] = Field(None, **base_fields['event_badge_id_random']) event_badge_vip_id: Optional[str] = Field(None, **base_fields['event_badge_id_random']) event_person_profile_id: Optional[str] = Field(None, **base_fields['event_person_profile_id_random']) event_registration_id: Optional[str] = Field(None, **base_fields['event_registration_id_random']) person_id: Optional[str] = Field(None, **base_fields['person_id_random']) user_id: Optional[str] = Field(None, **base_fields['user_id_random']) # --- Standardized Legacy / Internal IDs (Excluded) --- id_random: Optional[str] = Field(None, alias='event_person_id_random', exclude=True) account_id_random: Optional[str] = Field(None, exclude=True) event_id_random: Optional[str] = Field(None, exclude=True) event_badge_id_random: Optional[str] = Field(None, exclude=True) event_badge_vendor_id_random: Optional[str] = Field(None, exclude=True) event_badge_vip_id_random: Optional[str] = Field(None, exclude=True) event_person_profile_id_random: Optional[str] = Field(None, exclude=True) event_registration_id_random: Optional[str] = Field(None, exclude=True) person_id_random: Optional[str] = Field(None, exclude=True) user_id_random: Optional[str] = Field(None, exclude=True) 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) agree_to_tc: Optional[bool] # Agree to terms and conditions allow_tracking: Optional[bool] # Allow tracking for lead retrieval and other marketing passcode: Optional[str] # Passcode for accessing the event cfg_json: Optional[Union[Json, None]] # Store per person config options like theme, language, etc data_json: Optional[Union[Json, None]] # For key value data. Careful with overwriting existing fields! file_count: Optional[int] priority: Optional[bool] sort: Optional[int] group: Optional[str] enable: Optional[bool] hide: Optional[bool] notes: Optional[str] created_on: Optional[datetime.datetime] = None updated_on: Optional[datetime.datetime] = None # Including convenience data # This is only for convenience. Probably going to keep unless it causes a problem. # This block of person data should come from the event_person_profile table informal_name: Optional[str] given_name: Optional[str] middle_name: Optional[str] family_name: Optional[str] full_name_override: Optional[str] full_name: Optional[str] affiliations: Optional[str] email: Optional[str] website_url: Optional[str] # state_province_name: Optional[str] # Using extended_json for now for AACC abstracts extended_json: Optional[Union[Json, None]] event_badge_informal_name: Optional[str] event_badge_given_name: Optional[str] event_badge_middle_name: Optional[str] event_badge_family_name: Optional[str] event_badge_full_name: Optional[str] event_badge_full_name_override: Optional[str] event_badge_affiliations: Optional[str] event_badge_email: Optional[str] event_badge_city: Optional[str] event_badge_state_province: Optional[str] event_badge_country_alpha_2_code: Optional[str] event_badge_country: Optional[str] # This is the same as the other person data above event_person_informal_name: Optional[str] event_person_given_name: Optional[str] event_person_middle_name: Optional[str] event_person_family_name: Optional[str] event_person_name_override: Optional[str] event_person_full_name: Optional[str] event_person_affiliations: Optional[str] event_person_email: Optional[str] event_person_extended_json: Optional[Union[Json, None]] person_informal_name: Optional[str] person_given_name: Optional[str] person_middle_name: Optional[str] person_family_name: Optional[str] person_full_name: Optional[str] person_full_name_override: Optional[str] # person_display_name: Optional[str] person_affiliations: Optional[str] person_email: Optional[str] user_email: Optional[str] user_name: Optional[str] user_username: Optional[str] # Including other related objects # event: Optional[Event_Base] # Causes an import loop # event_abstract_list: Optional[list[Event_Abstract_Base]] # Use event_person_detail table. An event_person record can be linked to one or more abstracts event_badge: Optional[Event_Badge_Base] # Default attendee badge event_badge_vendor: Optional[Event_Badge_Base] # Additional vendor badge event_badge_vip: Optional[Event_Badge_Base] # Additional VIP badge event_exhibit_list: Optional[list] # Use event_person_detail table. An event_person record can be linked to one or more exhibits # event_exhibit_tracking_list: Optional[list[Event_Exhibit_Tracking_Base]] event_file_list: Optional[list] # Use event_person_detail table. An event_person record can be linked to one or more files event_location_list: Optional[list] # Use event_person_detail table. An event_person record can be linked to one or more locations (but unlikely?) #event_person_detail_list: Optional[list] # list of Event_Person_Detail event_person_profile: Optional[Event_Person_Profile_Base] event_presentation_list: Optional[list] # Use event_person_detail table. An event_person record can be linked to one or more presentations event_presenter_list: Optional[list] # Use event_person_detail table. An event_person record can be linked to one or more presenters (part of multiple presentations) event_registration: Optional[Event_Registration_Base] event_session: Optional[list] # Use event_person_detail table. An event_person record can be linked to one or more sessions event_track: Optional[list] # Use event_person_detail table. An event_person record can be linked to one or more tracks person: Optional[Person_Base] user: Optional[User_Base] _processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now) @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('event_person_id_random'): values['id'] = rid values['event_person_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 b_rid := values.get('event_badge_id_random'): values['event_badge_id'] = b_rid if bv_rid := values.get('event_badge_vendor_id_random'): values['event_badge_vendor_id'] = bv_rid if bvip_rid := values.get('event_badge_vip_id_random'): values['event_badge_vip_id'] = bvip_rid if ep_rid := values.get('event_person_profile_id_random'): values['event_person_profile_id'] = ep_rid if er_rid := values.get('event_registration_id_random'): values['event_registration_id'] = er_rid if p_rid := values.get('person_id_random'): values['person_id'] = p_rid if u_rid := values.get('user_id_random'): values['user_id'] = u_rid # 2. Prevent "Collision Population" for k in ['id', 'event_person_id', 'account_id', 'event_id', 'event_badge_id', 'event_badge_vendor_id', 'event_badge_vip_id', 'event_person_profile_id', 'event_registration_id', 'person_id', 'user_id']: if k in values and not isinstance(values[k], str) and values[k] is not None: del values[k] return values # Fields that are part of the model (for reading) but should not be saved to the DB table fields_to_exclude_from_db: ClassVar[list] = [ 'file_count', 'informal_name', 'given_name', 'middle_name', 'family_name', 'full_name_override', 'full_name', 'affiliations', 'email', 'website_url', 'event_badge_informal_name', 'event_badge_given_name', 'event_badge_middle_name', 'event_badge_family_name', 'event_badge_full_name', 'event_badge_full_name_override', 'event_badge_affiliations', 'event_badge_email', 'event_badge_city', 'event_badge_state_province', 'event_badge_country_alpha_2_code', 'event_badge_country', 'event_person_informal_name', 'event_person_given_name', 'event_person_middle_name', 'event_person_family_name', 'event_person_name_override', 'event_person_full_name', 'event_person_affiliations', 'event_person_email', 'event_person_extended_json', 'person_informal_name', 'person_given_name', 'person_middle_name', 'person_family_name', 'person_full_name', 'person_full_name_override', 'person_affiliations', 'person_email', 'user_email', 'user_name', 'user_username', 'event_badge', 'event_badge_vendor', 'event_badge_vip', 'event_exhibit_list', 'event_file_list', 'event_location_list', 'event_person_profile', 'event_presentation_list', 'event_presenter_list', 'event_registration', 'event_session', 'event_track', 'person', 'user' ] class Config: underscore_attrs_are_private = True allow_population_by_field_name = True fields = base_fields # ### END ### API Event Person Models ### Event_Person_Base() ### # ### BEGIN ### API Event Person Models ### Event_Person_New_Base() ### class Event_Person_New_Base(BaseModel): log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) # --- Standardized Vision IDs (Strings) --- id: Optional[str] = Field(None, **base_fields['event_person_id_random']) event_person_id: Optional[str] = Field(None, **base_fields['event_person_id_random']) account_id: Optional[str] = Field(None, **base_fields['account_id_random']) event_id: Optional[str] = Field(None, **base_fields['event_id_random']) # --- Standardized Legacy / Internal IDs (Excluded) --- id_random: Optional[str] = Field(None, alias='event_person_id_random', exclude=True) account_id_random: Optional[str] = Field(None, exclude=True) event_id_random: Optional[str] = Field(None, exclude=True) extended_json: Optional[Union[Json, None]] # Including convenience data # This is only for convenience. Probably going to keep unless it causes a problem. # This block of person data should come from the event_person_profile table informal_name: Optional[str] given_name: Optional[str] middle_name: Optional[str] family_name: Optional[str] full_name: Optional[str] full_name_override: Optional[str] affiliations: Optional[str] email: Optional[str] website_url: Optional[str] state_province_name: Optional[str] event_badge_informal_name: Optional[str] event_badge_given_name: Optional[str] event_badge_middle_name: Optional[str] event_badge_family_name: Optional[str] event_badge_full_name: Optional[str] event_badge_full_name_override: Optional[str] event_badge_affiliations: Optional[str] event_badge_email: Optional[str] event_badge_city: Optional[str] event_badge_state_province: Optional[str] event_badge_country_alpha_2_code: Optional[str] event_badge_country: Optional[str] # This is the same as the other person data above event_person_informal_name: Optional[str] event_person_given_name: Optional[str] event_person_middle_name: Optional[str] event_person_family_name: Optional[str] event_person_name_override: Optional[str] event_person_full_name: Optional[str] event_person_affiliations: Optional[str] event_person_email: Optional[str] person_given_name: Optional[str] person_middle_name: Optional[str] person_family_name: Optional[str] person_full_name: Optional[str] person_full_name_override: Optional[str] # person_display_name: Optional[str] # affiliations: Optional[str] # One or more affiliations with organizations, companies, and other groups # email: Optional[str] #new_password: str = Field(default_factory = lambda:secrets.token_urlsafe(default_num_bytes)) #password: Optional[str] new_password: Optional[str] notes: Optional[str] created_on: Optional[datetime.datetime] = None updated_on: Optional[datetime.datetime] = None _processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now) @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('event_person_id_random'): values['id'] = rid values['event_person_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 # 2. Prevent "Collision Population" for k in ['id', 'event_person_id', 'account_id', 'event_id']: if k in values and not isinstance(values[k], str) and values[k] is not None: del values[k] return values # Fields that are part of the model (for reading) but should not be saved to the DB table fields_to_exclude_from_db: ClassVar[list] = [ 'informal_name', 'given_name', 'middle_name', 'family_name', 'full_name', 'full_name_override', 'affiliations', 'email', 'website_url', 'state_province_name', 'event_badge_informal_name', 'event_badge_given_name', 'event_badge_middle_name', 'event_badge_family_name', 'event_badge_full_name', 'event_badge_full_name_override', 'event_badge_affiliations', 'event_badge_email', 'event_badge_city', 'event_badge_state_province', 'event_badge_country_alpha_2_code', 'event_badge_country', 'event_person_informal_name', 'event_person_given_name', 'event_person_middle_name', 'event_person_family_name', 'event_person_name_override', 'event_person_full_name', 'event_person_affiliations', 'event_person_email', 'person_given_name', 'person_middle_name', 'person_family_name', 'person_full_name', 'person_full_name_override', 'new_password' ] class Config: underscore_attrs_are_private = True allow_population_by_field_name = True fields = base_fields # ### END ### API Event Person Models ### Event_Person_New_Base() ###