fix(v3-actions): implement from_hosted_file and harden vision IDs
1. Implemented specialized 'from_hosted_file' action for Event Files.\n2. Fixed ValueError in Pydantic models by removing default/default_factory conflict.\n3. Hardened integer stripping to strictly enforce Vision Standards.\n4. Updated documentation for the new action route.
This commit is contained in:
@@ -17,16 +17,16 @@ class Event_Badge_Base(BaseModel):
|
||||
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'])
|
||||
id: Optional[Union[int, str]] = Field(**base_fields['event_badge_id_random'])
|
||||
event_badge_id: Optional[Union[int, str]] = Field(**base_fields['event_badge_id_random'])
|
||||
event_id: Optional[Union[int, str]] = Field(**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_id_only: Optional[Union[int, str]] = Field(**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'])
|
||||
event_badge_template_id: Optional[Union[int, str]] = Field(**base_fields['event_badge_template_id_random'])
|
||||
event_person_id: Optional[Union[int, str]] = Field(**base_fields['event_person_id_random'])
|
||||
person_id: Optional[Union[int, str]] = Field(**base_fields['person_id_random'])
|
||||
|
||||
# --- Standardized Legacy / Internal IDs (Excluded) ---
|
||||
id_random: Optional[str] = Field(None, alias='event_badge_id_random', exclude=True)
|
||||
|
||||
@@ -16,14 +16,14 @@ class Event_Exhibit_Base(BaseModel):
|
||||
log.debug(locals())
|
||||
|
||||
# --- Standardized Vision IDs (Strings for API, Integers for DB) ---
|
||||
id: Optional[Union[int, str]] = Field(None, **base_fields['event_exhibit_id_random'])
|
||||
event_exhibit_id: Optional[Union[int, str]] = Field(None, **base_fields['event_exhibit_id_random'])
|
||||
account_id: Optional[Union[int, str]] = Field(None, **base_fields['account_id_random'])
|
||||
event_id: Optional[Union[int, str]] = Field(None, **base_fields['event_id_random'])
|
||||
organization_id: Optional[Union[int, str]] = Field(None, **base_fields['organization_id_random'])
|
||||
contact_id: Optional[Union[int, str]] = Field(None, **base_fields['contact_id_random'])
|
||||
person_id: Optional[Union[int, str]] = Field(None, **base_fields['person_id_random'])
|
||||
status_id: Optional[Union[int, str]] = Field(None, **base_fields['status_id_random'])
|
||||
id: Optional[Union[int, str]] = Field(**base_fields['event_exhibit_id_random'])
|
||||
event_exhibit_id: Optional[Union[int, str]] = Field(**base_fields['event_exhibit_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'])
|
||||
organization_id: Optional[Union[int, str]] = Field(**base_fields['organization_id_random'])
|
||||
contact_id: Optional[Union[int, str]] = Field(**base_fields['contact_id_random'])
|
||||
person_id: Optional[Union[int, str]] = Field(**base_fields['person_id_random'])
|
||||
status_id: Optional[Union[int, str]] = Field(**base_fields['status_id_random'])
|
||||
|
||||
# --- Standardized Legacy / Internal IDs (Excluded) ---
|
||||
id_random: Optional[str] = Field(None, alias='event_exhibit_id_random', exclude=True)
|
||||
|
||||
@@ -18,13 +18,13 @@ class Event_Exhibit_Tracking_Base(BaseModel):
|
||||
log.debug(locals())
|
||||
|
||||
# --- Standardized Vision IDs (Strings for API, Integers for DB) ---
|
||||
id: Optional[Union[int, str]] = Field(None, **base_fields['event_exhibit_tracking_id_random'])
|
||||
event_exhibit_tracking_id: Optional[Union[int, str]] = Field(None, **base_fields['event_exhibit_tracking_id_random'])
|
||||
account_id: Optional[Union[int, str]] = Field(None, **base_fields['account_id_random'])
|
||||
event_id: Optional[Union[int, str]] = Field(None, **base_fields['event_id_random'])
|
||||
event_exhibit_id: Optional[Union[int, str]] = Field(None, **base_fields['event_exhibit_id_random'])
|
||||
event_person_id: Optional[Union[int, str]] = Field(None, **base_fields['event_person_id_random'])
|
||||
event_badge_id: Optional[Union[int, str]] = Field(None, **base_fields['event_badge_id_random'])
|
||||
id: Optional[Union[int, str]] = Field(**base_fields['event_exhibit_tracking_id_random'])
|
||||
event_exhibit_tracking_id: Optional[Union[int, str]] = Field(**base_fields['event_exhibit_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_exhibit_id: Optional[Union[int, str]] = Field(**base_fields['event_exhibit_id_random'])
|
||||
event_person_id: Optional[Union[int, str]] = Field(**base_fields['event_person_id_random'])
|
||||
event_badge_id: Optional[Union[int, str]] = Field(**base_fields['event_badge_id_random'])
|
||||
|
||||
# --- Standardized Legacy / Internal IDs (Excluded) ---
|
||||
id_random: Optional[str] = Field(None, alias='event_exhibit_tracking_id_random', exclude=True)
|
||||
|
||||
@@ -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 get_id_random, redis_lookup_id_random
|
||||
# from app.lib_general import log, logging
|
||||
@@ -16,38 +16,71 @@ class Event_File_Base(BaseModel):
|
||||
log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||
log.debug(locals())
|
||||
|
||||
id_random: Optional[str] = Field(
|
||||
# **base_fields['event_file_id_random'],
|
||||
alias = 'event_file_id_random',
|
||||
)
|
||||
id: Optional[int] = Field(
|
||||
alias = 'event_file_id'
|
||||
)
|
||||
# --- Standardized Vision IDs (Strings for API, Integers for DB) ---
|
||||
id: Optional[Union[int, str]] = Field(**base_fields['event_file_id_random'])
|
||||
event_file_id: Optional[Union[int, str]] = Field(**base_fields['event_file_id_random'])
|
||||
hosted_file_id: Optional[Union[int, str]] = Field(**base_fields['hosted_file_id_random'])
|
||||
|
||||
# Generic Relational target
|
||||
for_id: Optional[Union[int, str]] = Field(**base_fields['obj_id_random'])
|
||||
|
||||
event_id: Optional[Union[int, str]] = Field(**base_fields['event_id_random'])
|
||||
event_exhibit_id: Optional[Union[int, str]] = Field(**base_fields['event_exhibit_id_random'])
|
||||
event_location_id: Optional[Union[int, str]] = Field(**base_fields['event_location_id_random'])
|
||||
event_presentation_id: Optional[Union[int, str]] = Field(**base_fields['event_presentation_id_random'])
|
||||
event_presenter_id: Optional[Union[int, str]] = Field(**base_fields['event_presenter_id_random'])
|
||||
event_session_id: Optional[Union[int, str]] = Field(**base_fields['event_session_id_random'])
|
||||
event_track_id: Optional[Union[int, str]] = Field(**base_fields['event_track_id_random'])
|
||||
|
||||
hosted_file_id_random: Optional[str]
|
||||
hosted_file_id: Optional[int]
|
||||
# --- Standardized Legacy / Internal IDs (Excluded) ---
|
||||
id_random: Optional[str] = Field(None, alias='event_file_id_random', exclude=True)
|
||||
hosted_file_id_random: Optional[str] = Field(None, exclude=True)
|
||||
for_id_random: Optional[str] = Field(None, exclude=True)
|
||||
event_id_random: Optional[str] = Field(None, exclude=True)
|
||||
event_exhibit_id_random: Optional[str] = Field(None, exclude=True)
|
||||
event_location_id_random: Optional[str] = Field(None, exclude=True)
|
||||
event_presentation_id_random: Optional[str] = Field(None, exclude=True)
|
||||
event_presenter_id_random: Optional[str] = Field(None, exclude=True)
|
||||
event_session_id_random: Optional[str] = Field(None, exclude=True)
|
||||
event_track_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_file_id_random')
|
||||
if rid and isinstance(rid, str):
|
||||
values['id'] = rid
|
||||
values['event_file_id'] = rid
|
||||
|
||||
if h_rid := values.get('hosted_file_id_random'): values['hosted_file_id'] = h_rid
|
||||
if f_rid := values.get('for_id_random'): values['for_id'] = f_rid
|
||||
if e_rid := values.get('event_id_random'): values['event_id'] = e_rid
|
||||
if ex_rid := values.get('event_exhibit_id_random'): values['event_exhibit_id'] = ex_rid
|
||||
if loc_rid := values.get('event_location_id_random'): values['event_location_id'] = loc_rid
|
||||
if pres_rid := values.get('event_presentation_id_random'): values['event_presentation_id'] = pres_rid
|
||||
if pr_rid := values.get('event_presenter_id_random'): values['event_presenter_id'] = pr_rid
|
||||
if s_rid := values.get('event_session_id_random'): values['event_session_id'] = s_rid
|
||||
if t_rid := values.get('event_track_id_random'): values['event_track_id'] = t_rid
|
||||
|
||||
# 2. Prevent leakage of integers during API responses (Vision Standard)
|
||||
id_fields = [
|
||||
'id', 'event_file_id', 'hosted_file_id', 'for_id', 'event_id',
|
||||
'event_exhibit_id', 'event_location_id', 'event_presentation_id',
|
||||
'event_presenter_id', 'event_session_id', 'event_track_id'
|
||||
]
|
||||
for k in id_fields:
|
||||
val = values.get(k)
|
||||
if val is not None and not isinstance(val, str):
|
||||
values[k] = None
|
||||
|
||||
return values
|
||||
|
||||
# NOTE: Handling this outside of the Pydantic model and model validation. See below as well. -STI 2021-09-10
|
||||
for_type: Optional[str]
|
||||
for_id: Optional[int] # NOTE: This is reversed with for_id_random
|
||||
for_id_random: Optional[str] # NOTE: This is reversed with for_id
|
||||
# for_id_random: Optional[str] = None # Need to override value from common_field_schema.py
|
||||
# for_id: Optional[int]
|
||||
|
||||
event_id_random: Optional[str]
|
||||
event_id: Optional[int]
|
||||
event_exhibit_id_random: Optional[str]
|
||||
event_exhibit_id: Optional[int]
|
||||
event_location_id_random: Optional[str]
|
||||
event_location_id: Optional[int]
|
||||
event_presentation_id_random: Optional[str]
|
||||
event_presentation_id: Optional[int]
|
||||
event_presenter_id_random: Optional[str]
|
||||
event_presenter_id: Optional[int]
|
||||
event_session_id_random: Optional[str]
|
||||
event_session_id: Optional[int]
|
||||
event_track_id_random: Optional[str]
|
||||
event_track_id: Optional[int]
|
||||
|
||||
filename: Optional[str]
|
||||
filename_no_ext: Optional[str] # Currently created with a view
|
||||
@@ -134,96 +167,6 @@ class Event_File_Base(BaseModel):
|
||||
|
||||
_processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now)
|
||||
|
||||
#@validator('event_file_id_random', always=True)
|
||||
def event_file_id_random_copy(cls, v, values, **kwargs):
|
||||
if values['id_random']:
|
||||
return values['id_random']
|
||||
return None
|
||||
|
||||
@validator('id', always=True)
|
||||
def event_file_id_lookup(cls, v, values, **kwargs):
|
||||
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='event_file')
|
||||
return None
|
||||
|
||||
@validator('hosted_file_id', always=True)
|
||||
def hosted_file_id_lookup(cls, v, values, **kwargs):
|
||||
if isinstance(v, int) and v > 0: return v
|
||||
elif id_random := values.get('hosted_file_id_random'):
|
||||
return redis_lookup_id_random(record_id_random=id_random, table_name='hosted_file')
|
||||
return None
|
||||
|
||||
@validator('event_id', always=True)
|
||||
def event_id_lookup(cls, v, values, **kwargs):
|
||||
if isinstance(v, int) and v > 0: return v
|
||||
elif id_random := values.get('event_id_random'):
|
||||
return redis_lookup_id_random(record_id_random=id_random, table_name='event')
|
||||
return None
|
||||
|
||||
@validator('event_exhibit_id', always=True)
|
||||
def event_exhibit_id_lookup(cls, v, values, **kwargs):
|
||||
if isinstance(v, int) and v > 0: return v
|
||||
elif id_random := values.get('event_exhibit_id_random'):
|
||||
return redis_lookup_id_random(record_id_random=id_random, table_name='event_exhibit')
|
||||
return None
|
||||
|
||||
@validator('event_location_id', always=True)
|
||||
def event_location_id_lookup(cls, v, values, **kwargs):
|
||||
if isinstance(v, int) and v > 0: return v
|
||||
elif id_random := values.get('event_location_id_random'):
|
||||
return redis_lookup_id_random(record_id_random=id_random, table_name='event_location')
|
||||
return None
|
||||
|
||||
@validator('event_presentation_id', always=True)
|
||||
def event_presentation_id_lookup(cls, v, values, **kwargs):
|
||||
if isinstance(v, int) and v > 0: return v
|
||||
elif id_random := values.get('event_presentation_id_random'):
|
||||
return redis_lookup_id_random(record_id_random=id_random, table_name='event_presentation')
|
||||
return None
|
||||
|
||||
@validator('event_presenter_id', always=True)
|
||||
def event_presenter_id_lookup(cls, v, values, **kwargs):
|
||||
if isinstance(v, int) and v > 0: return v
|
||||
elif id_random := values.get('event_presenter_id_random'):
|
||||
return redis_lookup_id_random(record_id_random=id_random, table_name='event_presenter')
|
||||
return None
|
||||
|
||||
@validator('event_session_id', always=True)
|
||||
def event_session_id_lookup(cls, v, values, **kwargs):
|
||||
if isinstance(v, int) and v > 0: return v
|
||||
elif id_random := values.get('event_session_id_random'):
|
||||
return redis_lookup_id_random(record_id_random=id_random, table_name='event_session')
|
||||
return None
|
||||
|
||||
@validator('event_track_id', always=True)
|
||||
def event_track_id_lookup(cls, v, values, **kwargs):
|
||||
if isinstance(v, int) and v > 0: return v
|
||||
elif id_random := values.get('event_track_id_random'):
|
||||
return redis_lookup_id_random(record_id_random=id_random, table_name='event_track')
|
||||
return None
|
||||
|
||||
# NOTE: I kind of give up on this. Handling this outside of Pydantic and before the data is even attempted to be loaded into the Event_File_Base model. -STI 2021-09-10
|
||||
# NOTE: This validator will try to find and "set" the for_id_random value. However, The value is not really "set" in Pydantic. To get this value, exclude_unset=True when returning a dict from the model.
|
||||
@validator('for_id_random', always=True)
|
||||
def for_id_random_lookup(cls, v, values, **kwargs):
|
||||
log.setLevel(logging.WARNING)
|
||||
log.debug(locals())
|
||||
if isinstance(v, str): return v
|
||||
elif values.get('for_id') and values['for_type']:
|
||||
return get_id_random(record_id=values['for_id'], table_name=values['for_type'])
|
||||
return None
|
||||
|
||||
# @validator('for_id', always=True)
|
||||
# def for_id_lookup(cls, v, values, **kwargs):
|
||||
# log.setLevel(logging.DEBUG)
|
||||
# log.debug(locals())
|
||||
|
||||
# if values.get('for_id_random', None) and values['for_type']:
|
||||
# return redis_lookup_id_random(record_id_random=values['for_id_random'], table_name=values['for_type'])
|
||||
# # return None
|
||||
# else: return v
|
||||
|
||||
class Config:
|
||||
underscore_attrs_are_private = True
|
||||
allow_population_by_field_name = True
|
||||
|
||||
Reference in New Issue
Block a user