This commit refactors numerous Pydantic models to align with the V3 ID Vision standard, ensuring that primary and foreign key fields are represented as clean string IDs in the API. It also introduces and populates the ClassVar in each model to prevent view-only fields and linked objects from being inadvertently written to the database during PATCH/POST operations. Specifically, this includes: - Adding to exclude view-derived or joined fields such as , , nested objects (e.g., , ), and convenience fields (e.g., ). - Adjusting root validators to correctly map string IDs and strip internal integer IDs for API responses. - Resolving a KeyError by adding to . These changes are crucial for maintaining data integrity and consistency with the V3 API architecture.
237 lines
10 KiB
Python
237 lines
10 KiB
Python
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_location_models import Event_Location_Base
|
|
from app.models.event_person_models import Event_Person_Base # NOTE: Using this will probably create an import loop
|
|
from app.models.event_presentation_models import Event_Presentation_Base
|
|
from app.models.event_track_models import Event_Track_Base
|
|
from app.models.person_models import Person_Base
|
|
|
|
|
|
# ### BEGIN ### API Event Session Models ### Event_Session_Base() ###
|
|
class Event_Session_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_session_id_random'])
|
|
event_session_id: Optional[str] = Field(None, **base_fields['event_session_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_location_id: Optional[str] = Field(None, **base_fields['event_location_id_random'])
|
|
event_track_id: Optional[str] = Field(None, **base_fields['event_track_id_random'])
|
|
poc_event_person_id: Optional[str] = Field(None, **base_fields['event_person_id_random'])
|
|
poc_person_id: Optional[str] = Field(None, **base_fields['person_id_random'])
|
|
|
|
# --- Standardized Legacy / Internal IDs (Excluded) ---
|
|
id_random: Optional[str] = Field(None, alias='event_session_id_random', exclude=True)
|
|
account_id_random: Optional[str] = Field(None, exclude=True)
|
|
event_id_random: Optional[str] = Field(None, exclude=True)
|
|
event_location_id_random: Optional[str] = Field(None, exclude=True)
|
|
event_track_id_random: Optional[str] = Field(None, exclude=True)
|
|
poc_event_person_id_random: Optional[str] = Field(None, exclude=True)
|
|
poc_person_id_random: Optional[str] = Field(None, exclude=True)
|
|
|
|
external_id: Optional[str] = Field(
|
|
# alias = 'event_session_external_id'
|
|
)
|
|
|
|
code: Optional[str] = Field(
|
|
# alias = 'event_session_code'
|
|
)
|
|
|
|
# General catchall for agreement or consent
|
|
poc_agree: Optional[bool]
|
|
|
|
poc_kv_json: Optional[Union[Json, None]]
|
|
|
|
# type_id_random: Optional[str] # Not used or needed?
|
|
# type_id: Optional[int] # Not used or needed?
|
|
type_code: Optional[str] # From client; max 25 characters for now; This is a bug with MariaDB?
|
|
|
|
name: Optional[str]
|
|
description: Optional[str]
|
|
|
|
proposal_json: Optional[Union[Json, None]] # Is this still used or needed? 2024-09-12
|
|
|
|
start_datetime: Optional[datetime.datetime]
|
|
end_datetime: Optional[datetime.datetime]
|
|
|
|
# Need to redo this using a JSON field
|
|
# attend_url: Optional[str]
|
|
# attend_url_text: Optional[str]
|
|
# attend_url_passcode: Optional[str]
|
|
# attend_phone: Optional[str]
|
|
# attend_phone_passcode: Optional[str]
|
|
# attend_text: Optional[str]
|
|
attend_json: Optional[Union[Json, None]]
|
|
|
|
# Need to redo this using a JSON field
|
|
# rehearsal_start_datetime: Optional[datetime.datetime]
|
|
# rehearsal_end_datetime: Optional[datetime.datetime]
|
|
# rehearsal_url: Optional[str]
|
|
# rehearsal_url_passcode: Optional[str]
|
|
# rehearsal_phone: Optional[str]
|
|
# rehearsal_phone_passcode: Optional[str]
|
|
# rehearsal_text: Optional[str]
|
|
rehearsal_json: Optional[Union[Json, None]]
|
|
|
|
image_path: Optional[str] # Not currently in use. For a banner or logo
|
|
# presentation_file_path: Optional[str] # No longer used 2022-09-15
|
|
# presentation_file_size: Optional[str] # No longer used 2022-09-15
|
|
|
|
enable_event_file_approval_option: Optional[bool]
|
|
# enable_event_file_approval_option: Optional[bool] = Field(default=False)
|
|
# enable_event_file_approval_option: Optional[bool] = False # Setting a default to try and prevent client side null value issues.
|
|
|
|
# New internal use fields to help with logistics and planning 2022-09-15
|
|
internal_use: Optional[bool] # Will hide from moderators, presenters, non support people, etc
|
|
record_audio: Optional[bool] # Record the sessions in this location
|
|
record_video: Optional[bool] # Record the sessions in this location
|
|
internal_notes: Optional[str] # general notes
|
|
internal_notes_access: Optional[str] # accessibility
|
|
internal_notes_av: Optional[str] # audio video
|
|
internal_notes_fb: Optional[str] # food and beverage
|
|
internal_notes_it: Optional[str] # IT and networking
|
|
internal_notes_staff: Optional[str] # staffing and labor
|
|
|
|
passcode: Optional[str]
|
|
|
|
file_count: Optional[int] # Only files directly under the session
|
|
internal_use_count: Optional[int] # Should be renamed to "internal_use_file_count"???
|
|
event_file_id_li_json: Optional[Union[Json, None]] # List of file IDs (actually id_random)
|
|
file_count_all: Optional[int] # Of all files under a session
|
|
|
|
status: Optional[int]
|
|
review: Optional[bool]
|
|
approve: Optional[bool]
|
|
ready: Optional[bool]
|
|
|
|
alert: Optional[bool]
|
|
alert_msg: Optional[str]
|
|
|
|
# Options: 'colloquium', 'lecture', 'panel', 'poster', 'symposium', 'workshop'
|
|
# This is mainly reflected in the Launcher.
|
|
ux_mode: Optional[str]
|
|
# Other options??? None, poster (image, video), assume presentation (PPT, Key, PDF, etc)
|
|
|
|
enable: Optional[bool]
|
|
enable_from: Optional[datetime.datetime] = None
|
|
enable_to: Optional[datetime.datetime] = None
|
|
|
|
hide: Optional[bool]
|
|
public: Optional[bool]
|
|
public_hide: Optional[bool]
|
|
hide_event_launcher: Optional[bool]
|
|
|
|
priority: Optional[bool]
|
|
sort: Optional[int]
|
|
group: Optional[str]
|
|
|
|
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.
|
|
# event_code: Optional[str]
|
|
event_name: Optional[str]
|
|
event_start_datetime: Optional[datetime.datetime]
|
|
event_end_datetime: Optional[datetime.datetime]
|
|
# event_location_code: Optional[str]
|
|
event_location_name: Optional[str]
|
|
# event_track_code: Optional[str]
|
|
event_track_name: Optional[str]
|
|
# location_name: Optional[str] = Field(
|
|
# alias = 'event_location_name'
|
|
# )
|
|
# track_name: Optional[str] = Field(
|
|
# alias = 'event_track_name'
|
|
# )
|
|
|
|
# Including other related objects
|
|
# event: Optional[Event_Base]
|
|
event_abstract_list: Optional[list] # Optional[Event_Abstract_Base]
|
|
event_badge_list: Optional[list] # Optional[Event_Abstract_Base]
|
|
event_device_list: Optional[list] # Optional[Event_Device_Base]
|
|
event_file_list: Optional[list] # Optional[Event_File_Base]
|
|
event_file_internal_use_list: Optional[list] # Optional[Event_File_Base]
|
|
event_location: Optional[Union[Event_Location_Base, None]]
|
|
event_location_list: Optional[list[Event_Location_Base]] # Optional[Event_Location_Base]
|
|
event_person_list: Optional[list]
|
|
event_presenter_cat: Optional[Union[str, None]]
|
|
event_presentation_list: Optional[list[Event_Presentation_Base]] # Optional[Event_Presentation_Base]
|
|
event_presenter_list: Optional[list] # Optional[Event_Presenter_Base]
|
|
event_track: Optional[Event_Track_Base]
|
|
|
|
poc_event_person: Optional[Event_Person_Base] # NOTE: Using thi will probably create an import loop
|
|
|
|
poc_person: Optional[Person_Base]
|
|
poc_person_external_id: Optional[str]
|
|
poc_person_given_name: Optional[str]
|
|
poc_person_family_name: Optional[str]
|
|
poc_person_full_name: Optional[str]
|
|
poc_person_primary_email: Optional[str]
|
|
poc_person_passcode: Optional[str]
|
|
|
|
_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_session_id_random'):
|
|
values['id'] = rid
|
|
values['event_session_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 el_rid := values.get('event_location_id_random'):
|
|
values['event_location_id'] = el_rid
|
|
if et_rid := values.get('event_track_id_random'):
|
|
values['event_track_id'] = et_rid
|
|
if pep_rid := values.get('poc_event_person_id_random'):
|
|
values['poc_event_person_id'] = pep_rid
|
|
if pp_rid := values.get('poc_person_id_random'):
|
|
values['poc_person_id'] = pp_rid
|
|
|
|
# 2. Prevent "Collision Population"
|
|
for k in ['id', 'event_session_id', 'account_id', 'event_id', 'event_location_id', 'event_track_id', 'poc_event_person_id', 'poc_person_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] = [
|
|
'account_id',
|
|
'file_count', 'internal_use_count', 'event_file_id_li_json', 'file_count_all',
|
|
'event_name', 'event_start_datetime', 'event_end_datetime',
|
|
'event_location_name', 'event_track_name',
|
|
'event_abstract_list', 'event_badge_list', 'event_device_list',
|
|
'event_file_list', 'event_file_internal_use_list', 'event_location',
|
|
'event_location_list', 'event_person_list', 'event_presenter_cat',
|
|
'event_presentation_list', 'event_presenter_list', 'event_track',
|
|
'poc_event_person', 'poc_person',
|
|
'poc_person_external_id', 'poc_person_given_name', 'poc_person_family_name',
|
|
'poc_person_full_name', 'poc_person_primary_email', 'poc_person_passcode'
|
|
]
|
|
|
|
class Config:
|
|
underscore_attrs_are_private = True
|
|
allow_population_by_field_name = False
|
|
fields = base_fields
|
|
# ### END ### API Event Session Models ### Event_Session_Base() ###
|