Files
OSIT-AE-API-FastAPI/app/methods/data_store_methods.py
Scott Idem 9dd941eb36 docs: overhaul Data Store cascading guide for frontend agents
- Replaced Section 8 with 'The Hierarchy of Truth' examples.
- Added explicit rules for Vision IDs and automatic JSON parsing.
- Clarified dynamic return behavior based on the 'limit' parameter.
- Cleaned up formatting and synced to agents_sync documentation path.
2026-01-28 17:36:33 -05:00

273 lines
11 KiB
Python

import datetime
from typing import Dict, List, Optional, Set, Union
from pydantic import BaseModel, EmailStr, Field, PrivateAttr, ValidationError, validator
from app.db_sql import redis_lookup_id_random, sql_enable_part, sql_insert, sql_limit_offset_part, sql_select, sql_update
from app.lib_general import log, logging, logger_reset
from app.methods.event_cfg_methods import load_event_cfg_obj
from app.methods.event_location_methods import load_event_location_obj
from app.models.common_field_schema import default_num_bytes
from app.models.data_store_models import Data_Store_Base
# ### BEGIN ### API Data Store Methods ### load_data_store_obj() ###
# Updated 2022-03-11
@logger_reset
def load_data_store_obj(
data_store_id: int,
enabled: str = 'enabled', # enabled, disabled, all
limit: int = 10,
offset: int = 0,
by_alias: bool = True,
exclude_unset: bool = True,
model_as_dict: bool = False,
) -> Data_Store_Base|dict|bool:
log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
log.debug(locals())
if data_store_rec := sql_select(table_name='v_data_store', record_id=data_store_id): pass
else: return False
log.debug(data_store_rec)
try:
data_store_obj = Data_Store_Base(**data_store_rec)
except ValidationError as e:
log.error(e.json())
return False
log.debug(data_store_obj)
if model_as_dict:
return data_store_obj.dict(by_alias=by_alias, exclude_unset=exclude_unset) # pylint: disable=no-member
else:
return data_store_obj
# ### END ### API Data Store Methods ### load_data_store_obj() ###
# ### BEGIN ### API Data Store Methods ### load_data_store_obj_w_code() ###
# NOTE: This is customized to look for records with or without an account_id and some code. By default only the first sorted result will be returned.
# NOTE: By default the first sorted result should be the one for a specific account with some code. If a result with a null account_id and some code is found it will be returned if no account_id specific results are found.
# Updated 2022-03-11
@logger_reset
def load_data_store_obj_w_code(
account_id: int,
code: str,
for_type: str = None,
for_id: int = None,
enabled: str = 'enabled', # enabled, disabled, all
limit: int = 1,
offset: int = 0,
by_alias: bool = True, # NOTE: For now this is ignored
exclude_unset: bool = True, # NOTE: For now this is ignored
model_as_dict: bool = False, # NOTE: For now this is ignored
) -> Data_Store_Base|dict|bool:
log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
log.debug(locals())
log.info(f'Getting Data Store record with code: {code} for Account ID: {account_id} and For Type: {for_type} and For ID: {for_id}')
data = {}
data['account_id'] = account_id
data['code'] = code
data['for_type'] = for_type
data['for_id'] = for_id
log.debug(data)
# log.warning(f'Can we get past this?????????? {code}')
# if for_id := redis_lookup_id_random(record_id_random=for_id, table_name=for_type): pass
# else: return False
sql_enabled, data['enable'] = sql_enable_part(table_name='data_store', enabled=enabled) # Reasonably safe return str and bool
sql_limit = sql_limit_offset_part(limit=limit, offset=offset) # Reasonably safe return str
sql = f"""
SELECT *
FROM `v_data_store` AS `data_store`
WHERE
(
`data_store`.account_id = :account_id
OR `data_store`.account_id IS NULL
OR (`data_store`.for_type = :for_type AND `data_store`.for_id = :for_id)
)
AND `data_store`.code = :code
{sql_enabled}
ORDER BY `data_store`.for_id DESC, `data_store`.account_id DESC, `data_store`.created_on DESC, `data_store`.updated_on DESC
{sql_limit};
"""
# log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
log.debug(sql)
if data_store_rec_li_result := sql_select(data=data, sql=sql, as_list=True):
data_store_rec_li = data_store_rec_li_result
else: # [] or False
data_store_rec_li = data_store_rec_li_result
log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
log.info(f'No Data Store records found with code: {code} for Account ID: {account_id} and For Type: {for_type} and For ID: {for_id}')
log.debug(data_store_rec_li_result)
data_store_obj_li = []
if data_store_rec_li:
for data_store_rec in data_store_rec_li:
try:
data_store_obj = Data_Store_Base(**data_store_rec)
data_store_obj_li.append(data_store_obj)
log.debug(data_store_obj)
except ValidationError as e:
log.error(e.json())
data_store_obj_li.append(None)
# return False
else: pass
log.info(f'Found {len(data_store_obj_li)} Data Store records with code: {code} for Account ID: {account_id} and For Type: {for_type} and For ID: {for_id}')
log.debug(data_store_obj_li)
return data_store_obj_li
# ### END ### API Data Store Methods ### load_data_store_obj_w_code() ###
# ### BEGIN ### API Data Store Methods ### get_data_store_rec_list() ###
# Updated 2022-03-11
@logger_reset
def get_data_store_rec_list(
account_id: int,
for_type: str, # 'account' is a special case
for_id: int,
person_id: int = None,
user_id: int = None,
code: str = None,
enabled: str = 'enabled', # enabled, disabled, all
limit: int = 100,
offset: int = 0,
) -> list|bool:
log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
log.debug(locals())
# if for_type and for_id:
# if for_id := redis_lookup_id_random(record_id_random=for_id, table_name=for_type): pass
# else: return False
if account_id:
sql_account_id = f'`data_store`.account_id = :account_id'
# if code:
# sql_code = f'`data_store`.code = :code'
data = {}
data['account_id'] = account_id
data['code'] = code
# data['for_type'] = for_type
# data['for_id'] = for_id
sql_enabled, data['enable'] = sql_enable_part(table_name='data_store', enabled=enabled) # Reasonably safe return str and bool
sql_limit = sql_limit_offset_part(limit=limit, offset=offset) # Reasonably safe return str
sql = f"""
SELECT `data_store`.id AS 'data_store_id', `data_store`.id_random AS 'data_store_id_random'
FROM `v_data_store` AS `data_store`
WHERE
{sql_account_id}
{sql_enabled}
ORDER BY `data_store`.created_on DESC, `data_store`.updated_on DESC
{sql_limit};
"""
log.debug(sql)
if data_store_rec_li_result := sql_select(data=data, sql=sql, as_list=True):
data_store_rec_li = data_store_rec_li_result
else: # [] or False
data_store_rec_li = data_store_rec_li_result
log.debug(data_store_rec_li_result)
return data_store_rec_li
# ### END ### API Data Store Methods ### get_data_store_rec_list() ###
# ### BEGIN ### API Data Store Methods ### create_update_data_store_obj() ###
# Updated 2022-03-11
def create_update_data_store_obj(
data_store_dict_obj: Data_Store_Base|dict,
data_store_id: int|str = None,
create_sub_obj: bool = False,
fail_any: bool = False, # Fail if any thing goes wrong for sub objects
return_outline: bool = False,
) -> int|bool:
log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
log.debug(locals())
# ### SECTION ### Secondary data validation
log.info('Checking requirements...')
if data_store_id:
log.info(f'Data Store ID passed. Update existing Data Store. Data Store ID: {data_store_id}')
if data_store_id := redis_lookup_id_random(record_id_random=data_store_id, table_name='data_store'): pass
else:
log.error('Data Store ID passed but is invalid. Failed requirement.')
return False
else:
log.info('No Data Store ID passed. Create new Data Store. Required: None; Optional: Account ID, For Type, For ID, Person ID, User ID')
if account_id := redis_lookup_id_random(record_id_random=account_id, table_name='account'): pass
elif account_id is None: pass
else:
log.warning('Missing or invalid Account ID passed. Failed requirement.')
log.info(f'Account ID: {account_id}')
return False
log.info('Create dictionary or Pydantic object')
log.debug(type(data_store_dict_obj))
if isinstance(data_store_dict_obj, dict):
data_store_dict = data_store_dict_obj
if data_store_id:
data_store_dict['data_store_id'] = data_store_id
if account_id:
account_dict['account_id'] = account_id
try:
data_store_obj = Data_Store_Base(**data_store_dict)
except ValidationError as e:
log.error(e.json())
return False
else:
data_store_obj = data_store_dict_obj
if data_store_id:
# NOTE: Can't update the ID alias if it was never set.
data_store_obj.id = data_store_id
if account_id:
data_store_obj.account_id = account_id
log.debug(data_store_obj)
data_store_dict = data_store_obj.dict(by_alias=False, exclude_defaults=False, exclude_unset=True, exclude={'account', 'created_on', 'updated_on'})
# ### SECTION ### Process data
if data_store_id:
if data_store_dict_up_result := sql_update(data=data_store_dict, table_name='data_store', rm_id_random=True): pass
else:
log.warning(f'Data Store not updated. Data Store ID: {data_store_id}')
log.debug(data_store_dict_up_result)
return False
log.debug(data_store_dict_up_result)
else:
if data_store_dict_in_result := sql_insert(data=data_store_dict, table_name='data_store', rm_id_random=True, id_random_length=8): pass
else:
log.warning(f'Data Store not created.')
log.debug(data_store_dict_in_result)
return False
log.debug(data_store_dict_in_result)
data_store_id = data_store_dict_in_result
data_store_outline = {}
data_store_outline['data_store_id'] = data_store_id
if return_outline:
log.debug(f'Returning the Data Store Outline: {data_store_outline}')
return data_store_outline
else:
log.debug(f'Returning the Data Store ID: {data_store_id}')
return data_store_id
# ### END ### API Data Store Methods ### create_update_data_store_obj() ###