From 45f6303219bf9779f1ba837b2f416c832166b5a3 Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Tue, 6 Jan 2026 15:54:31 -0500 Subject: [PATCH] feat(v3): robust search wildcards, smart status filtering, and fixed ID population --- app/db_sql.py | 38 +- app/models/account_models.py | 5 + app/models/address_models.py | 18 +- app/models/contact_models.py | 10 + app/models/event_badge_models.py | 26 +- app/models/membership_group_models.py | 10 + app/models/membership_person_group_models.py | 7 + app/models/membership_type_models.py | 6 + app/models/order_cfg_models.py | 15 + app/models/organization_models.py | 14 +- app/models/post_models.py | 1 + app/models/site_domain_models.py | 65 ++- app/models/site_models.py | 17 +- app/models/user_models.py | 479 ++++++++++-------- app/object_definitions/core.py | 2 +- app/object_definitions/events_general.py | 3 +- app/object_definitions/events_registration.py | 6 +- app/object_definitions/journals.py | 6 +- app/object_definitions/orders.py | 3 +- 19 files changed, 452 insertions(+), 279 deletions(-) diff --git a/app/db_sql.py b/app/db_sql.py index 0dd3eac..b9d8296 100644 --- a/app/db_sql.py +++ b/app/db_sql.py @@ -2034,6 +2034,7 @@ def sql_and_in_dict_li_part( # ### BEGIN ### API DB SQL Methods ### sql_enable_part() ### # Updated 2022-01-17 +# Updated 2026-01-06 to handle missing enable column gracefully. @logger_reset def sql_enable_part(table_name: str, enabled: str) -> bool|dict: log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL @@ -2043,19 +2044,25 @@ def sql_enable_part(table_name: str, enabled: str) -> bool|dict: if enabled in ['enabled', 'disabled', 'all']: log.info(f'Creating partial SQL string for "enabled" check. Enabled: {enabled}') + + if enabled == 'all': + return '', None + + # Check if column exists + try: + db.execute(text(f"SELECT enable FROM `{table_name}` LIMIT 0")) + except: + log.warning(f"Table/View '{table_name}' does not have an 'enable' column. Skipping filter.") + return '', None + if enabled == 'enabled': - # sql = f'AND `person`.enable = :enable' sql = f'AND `{table_name}`.enable = true' enable = True elif enabled == 'disabled': - # sql = f'AND `person`.enable = :enable' sql = f'AND `{table_name}`.enable = false' enable = False - elif enabled == 'all': - sql = '' - enable = None + log.debug(sql) - return sql, enable else: return False @@ -2064,6 +2071,7 @@ def sql_enable_part(table_name: str, enabled: str) -> bool|dict: # ### BEGIN ### API DB SQL Methods ### sql_hidden_part() ### # Updated 2022-01-17 +# Updated 2026-01-06 to handle missing hide column gracefully. @logger_reset def sql_hidden_part(table_name: str, hidden: str) -> bool|dict: log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL @@ -2073,17 +2081,25 @@ def sql_hidden_part(table_name: str, hidden: str) -> bool|dict: if hidden in ['hidden', 'not_hidden', 'all']: log.info(f'Creating partial SQL string for "hidden" check. Hide: {hidden}') + + if hidden == 'all': + return '', None + + # Check if column exists + try: + db.execute(text(f"SELECT hide FROM `{table_name}` LIMIT 0")) + except: + log.warning(f"Table/View '{table_name}' does not have a 'hide' column. Skipping filter.") + return '', None + if hidden == 'hidden': sql = f'AND `{table_name}`.hide = true' hide = True elif hidden == 'not_hidden': sql = f'AND (`{table_name}`.hide = false OR `{table_name}`.hide IS NULL)' hide = False - elif hidden == 'all': - sql = '' - hide = None + log.debug(sql) - return sql, hide else: return False @@ -2258,4 +2274,4 @@ def sql_search_qry_part( if sql_where: return f"AND ({sql_where})", data return "", {} -# ### END ### API DB SQL Methods ### sql_search_qry_part() ### +# ### END ### API DB SQL Methods ### sql_search_qry_part() ### \ No newline at end of file diff --git a/app/models/account_models.py b/app/models/account_models.py index a796c21..0d06d78 100644 --- a/app/models/account_models.py +++ b/app/models/account_models.py @@ -38,6 +38,11 @@ class Account_Base(BaseModel): short_name: Optional[str] description: Optional[str] + hide: Optional[bool] + priority: Optional[bool] + sort: Optional[int] + group: Optional[str] + enable: Optional[bool] enable_from: Optional[datetime.datetime] = None enable_to: Optional[datetime.datetime] = None diff --git a/app/models/address_models.py b/app/models/address_models.py index 5e5723d..e8dd272 100644 --- a/app/models/address_models.py +++ b/app/models/address_models.py @@ -60,10 +60,13 @@ class Address_Base(BaseModel): congressional_district: Optional[str] - #priority: Optional[int] - #sort: Optional[int] - #group: Optional[str] + enable: Optional[bool] + hide: 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 @@ -98,6 +101,13 @@ class Address_Base(BaseModel): return redis_lookup_id_random(record_id_random=id_random, table_name='account') return None + @validator('account_id_random', always=True) + def account_id_random_lookup(cls, v, values, **kwargs): + if isinstance(v, str) and len(v) >= 11: return v + elif account_id := values.get('account_id'): + return get_id_random(record_id=account_id, table_name='account') + return None + @validator('contact_id', always=True) def contact_id_lookup(cls, v, values, **kwargs): log.setLevel(logging.WARNING) @@ -121,4 +131,4 @@ class Address_Base(BaseModel): underscore_attrs_are_private = True allow_population_by_field_name = True fields = base_fields -# ### END ### API Address Models ### Address_Base() ### +# ### END ### API Address Models ### Address_Base() ### \ No newline at end of file diff --git a/app/models/contact_models.py b/app/models/contact_models.py index c3d0601..1eb5f12 100644 --- a/app/models/contact_models.py +++ b/app/models/contact_models.py @@ -72,10 +72,13 @@ class Contact_Base(BaseModel): other_text: Optional[str] other_json: Optional[Json] + enable: Optional[bool] + hide: 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 @@ -114,6 +117,13 @@ class Contact_Base(BaseModel): return redis_lookup_id_random(record_id_random=id_random, table_name='account') return None + @validator('account_id_random', always=True) + def account_id_random_lookup(cls, v, values, **kwargs): + if isinstance(v, str) and len(v) >= 11: return v + elif account_id := values.get('account_id'): + return get_id_random(record_id=account_id, table_name='account') + return None + @validator('address_id', always=True) def address_id_lookup(cls, v, values, **kwargs): log.setLevel(logging.WARNING) diff --git a/app/models/event_badge_models.py b/app/models/event_badge_models.py index 46b0ee8..61f6ada 100644 --- a/app/models/event_badge_models.py +++ b/app/models/event_badge_models.py @@ -220,6 +220,9 @@ class Event_Badge_Basic_Base(BaseModel): **base_fields['event_badge_id_random'], alias = 'event_badge_id_random', ) + id: Optional[int] = Field( + alias = 'event_badge_id' + ) event_badge_template_id_random: Optional[str] # event_badge_template_id: Optional[int] @@ -298,15 +301,15 @@ class Event_Badge_Basic_Base(BaseModel): 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] + 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 + 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]] @@ -315,6 +318,13 @@ class Event_Badge_Basic_Base(BaseModel): _processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now) + @validator('id', always=True) + def event_badge_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_badge') + return None + class Config: underscore_attrs_are_private = True allow_population_by_field_name = True diff --git a/app/models/membership_group_models.py b/app/models/membership_group_models.py index d05b829..a1509b3 100644 --- a/app/models/membership_group_models.py +++ b/app/models/membership_group_models.py @@ -42,6 +42,15 @@ class Membership_Group_Base(BaseModel): expire_in_days: Optional[int] + enable: Optional[bool] + enable_from: Optional[datetime.datetime] = None + enable_to: Optional[datetime.datetime] = None + + hide: 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 @@ -104,5 +113,6 @@ class Membership_Group_Base(BaseModel): class Config: underscore_attrs_are_private = True fields = base_fields + allow_population_by_field_name = True # Membership_Group_Base.update_forward_refs() diff --git a/app/models/membership_person_group_models.py b/app/models/membership_person_group_models.py index 8f5864b..b94e037 100644 --- a/app/models/membership_person_group_models.py +++ b/app/models/membership_person_group_models.py @@ -55,6 +55,12 @@ class Membership_Person_Group_Base(BaseModel): flag: Optional[bool] flag_message: Optional[str] + enable: Optional[bool] + hide: Optional[bool] + priority: Optional[bool] + sort: Optional[int] + group: Optional[str] + notes: Optional[str] created_on: Optional[datetime.datetime] = None @@ -117,5 +123,6 @@ class Membership_Person_Group_Base(BaseModel): class Config: underscore_attrs_are_private = True fields = base_fields + allow_population_by_field_name = True # Membership_Base.update_forward_refs() diff --git a/app/models/membership_type_models.py b/app/models/membership_type_models.py index 39c9ebc..729c22f 100644 --- a/app/models/membership_type_models.py +++ b/app/models/membership_type_models.py @@ -58,6 +58,7 @@ class Membership_Type_Base(BaseModel): enable_from: Optional[datetime.datetime] = None enable_to: Optional[datetime.datetime] = None + hide: Optional[bool] priority: Optional[bool] sort: Optional[int] = Field(0, ge=0, lt=100) # Essentially the membership level. Should there be a range limit? group: Optional[str] @@ -106,4 +107,9 @@ class Membership_Type_Base(BaseModel): # return redis_lookup_id_random(record_id_random=values['account_id_random'], table_name='account') # return None + class Config: + underscore_attrs_are_private = True + fields = base_fields + allow_population_by_field_name = True + # Membership_Type_Base.update_forward_refs() diff --git a/app/models/order_cfg_models.py b/app/models/order_cfg_models.py index 43371d4..515a88c 100644 --- a/app/models/order_cfg_models.py +++ b/app/models/order_cfg_models.py @@ -34,6 +34,16 @@ class Order_Cfg_Base(BaseModel): stripe_publishable_key: Optional[str] # Publish/Sharable stripe_account_id: Optional[str] # Connected Stripe Account ID + enable: Optional[bool] + hide: 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 + _processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now) @validator('account_id', always=True) @@ -44,3 +54,8 @@ class Order_Cfg_Base(BaseModel): if values['account_id_random']: return redis_lookup_id_random(record_id_random=values['account_id_random'], table_name='account') return None + + class Config: + underscore_attrs_are_private = True + fields = base_fields + allow_population_by_field_name = True diff --git a/app/models/organization_models.py b/app/models/organization_models.py index b639f3c..c4080cd 100644 --- a/app/models/organization_models.py +++ b/app/models/organization_models.py @@ -49,6 +49,8 @@ class Organization_Base(BaseModel): thumbnail_path: Optional[str] thumbnail_bg_color: Optional[str] + enable: Optional[bool] + hide: Optional[bool] priority: Optional[int] sort: Optional[int] group: Optional[str] @@ -89,8 +91,16 @@ class Organization_Base(BaseModel): log.setLevel(logging.WARNING) log.debug(locals()) - if values['account_id_random']: - return redis_lookup_id_random(record_id_random=values['account_id_random'], table_name='account') + if isinstance(v, int) and v > 0: return v + elif id_random := values.get('account_id_random'): + return redis_lookup_id_random(record_id_random=id_random, table_name='account') + return None + + @validator('account_id_random', always=True) + def account_id_random_lookup(cls, v, values, **kwargs): + if isinstance(v, str) and len(v) >= 11: return v + elif account_id := values.get('account_id'): + return get_id_random(record_id=account_id, table_name='account') return None @validator('contact_id', always=True) diff --git a/app/models/post_models.py b/app/models/post_models.py index 1117f82..858d2df 100644 --- a/app/models/post_models.py +++ b/app/models/post_models.py @@ -138,4 +138,5 @@ class Post_Base(BaseModel): class Config: underscore_attrs_are_private = True fields = base_fields + allow_population_by_field_name = True # ### END ### API Post Models ### Post_Base() ### diff --git a/app/models/site_domain_models.py b/app/models/site_domain_models.py index 1d2be9c..84a3e79 100644 --- a/app/models/site_domain_models.py +++ b/app/models/site_domain_models.py @@ -3,7 +3,7 @@ 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.db_sql import get_id_random, redis_lookup_id_random from app.lib_general import log, logging from app.models.common_field_schema import base_fields, default_num_bytes @@ -38,6 +38,12 @@ class Site_Domain_Base(BaseModel): cfg_json: Optional[Union[Json, None]] # In use 2024-03-04 + hide: 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 @@ -53,9 +59,23 @@ class Site_Domain_Base(BaseModel): log.setLevel(logging.WARNING) log.debug(locals()) - if values['id_random']: - log.debug(values['id_random']) - return redis_lookup_id_random(record_id_random=values['id_random'], table_name='site_domain') + 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='site_domain') + return None + + @validator('account_id', always=True) + def account_id_lookup(cls, v, values, **kwargs): + if isinstance(v, int) and v > 0: return v + elif id_random := values.get('account_id_random'): + return redis_lookup_id_random(record_id_random=id_random, table_name='account') + return None + + @validator('account_id_random', always=True) + def account_id_random_lookup(cls, v, values, **kwargs): + if isinstance(v, str) and len(v) >= 11: return v + elif account_id := values.get('account_id'): + return get_id_random(record_id=account_id, table_name='account') return None @validator('site_id', always=True) @@ -104,12 +124,18 @@ class Site_Domain_FQDN_ID_Base(BaseModel): valid_for: Optional[int] # number of hours enable: Optional[bool] + hide: 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. - # account_id: Optional[int] + account_id: Optional[int] account_id_random: Optional[str] account_code: Optional[str] # Useful for export file naming account_name: Optional[str] # Generally useful for display @@ -131,24 +157,19 @@ class Site_Domain_FQDN_ID_Base(BaseModel): _processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now) - # @validator('id', always=True) - # def site_domain_id_lookup(cls, v, values, **kwargs): - # log.setLevel(logging.WARNING) - # log.debug(locals()) + @validator('account_id', always=True) + def account_id_lookup(cls, v, values, **kwargs): + if isinstance(v, int) and v > 0: return v + elif id_random := values.get('account_id_random'): + return redis_lookup_id_random(record_id_random=id_random, table_name='account') + return None - # if values['id_random']: - # log.debug(values['id_random']) - # return redis_lookup_id_random(record_id_random=values['id_random'], table_name='site_domain') - # return None - - # @validator('account_id', always=True) - # def account_id_lookup(cls, v, values, **kwargs): - # log.setLevel(logging.WARNING) - # log.debug(locals()) - - # if values['account_id_random']: - # return redis_lookup_id_random(record_id_random=values['account_id_random'], table_name='account') - # return None + @validator('account_id_random', always=True) + def account_id_random_lookup(cls, v, values, **kwargs): + if isinstance(v, str) and len(v) >= 11: return v + elif account_id := values.get('account_id'): + return get_id_random(record_id=account_id, table_name='account') + return None @validator('site_id', always=True) def site_id_lookup(cls, v, values, **kwargs): diff --git a/app/models/site_models.py b/app/models/site_models.py index 726f2b8..56e90c1 100644 --- a/app/models/site_models.py +++ b/app/models/site_models.py @@ -87,6 +87,11 @@ class Site_Base(BaseModel): cfg_json: Optional[Union[Json, None]] # In use 2024-03-04 + hide: 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 @@ -111,8 +116,16 @@ class Site_Base(BaseModel): log.setLevel(logging.WARNING) log.debug(locals()) - if values['account_id_random']: - return redis_lookup_id_random(record_id_random=values['account_id_random'], table_name='account') + if isinstance(v, int) and v > 0: return v + elif id_random := values.get('account_id_random'): + return redis_lookup_id_random(record_id_random=id_random, table_name='account') + return None + + @validator('account_id_random', always=True) + def account_id_random_lookup(cls, v, values, **kwargs): + if isinstance(v, str) and len(v) >= 11: return v + elif account_id := values.get('account_id'): + return get_id_random(record_id=account_id, table_name='account') return None class Config: diff --git a/app/models/user_models.py b/app/models/user_models.py index ed62c86..55eb437 100644 --- a/app/models/user_models.py +++ b/app/models/user_models.py @@ -1,10 +1,9 @@ -import datetime, pytz, secrets -# import datetime, hashlib, logging, os, pytz, redis, secrets +import datetime, hashlib, logging, os, pytz, redis, secrets 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.db_sql import get_id_random, redis_lookup_id_random from app.lib_general import log, logging, secure_hash_string from app.models.common_field_schema import base_fields, default_num_bytes @@ -14,212 +13,6 @@ from app.models.organization_models import Organization_Base # from app.models.user_role_models import User_Role_Base -# ### BEGIN ### API User Models ### User_New_Base() ### -class User_New_Base(BaseModel): - log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL - log.debug(locals()) - - id_random: Optional[str] = Field( - **base_fields['user_id_random'], - alias = 'user_id_random', - # default_factory = lambda:secrets.token_urlsafe(default_num_bytes), - ) - id: Optional[int] = Field( - alias = 'user_id' - ) - account_id_random: Optional[str] - account_id: Optional[int] - account_name: Optional[str] - - contact_id_random: Optional[str] - contact_id: Optional[int] - - organization_id_random: Optional[str] - organization_id: Optional[int] - - person_id_random: Optional[str] - person_id: Optional[int] - - username: str - name: str - email: str - email_verified: bool = False - new_password: str = Field(default_factory = lambda:secrets.token_urlsafe(default_num_bytes)) - password: Optional[str] # If new_password is found then the validator below will create secure_hash_string() from the new password string. - - allow_auth_key: bool = False - - enable: bool = False - enable_from: Optional[datetime.datetime] = datetime.datetime.now(datetime.timezone.utc) - #enable_from: Optional[datetime.datetime] = datetime.datetime.now() - enable_to: Optional[datetime.datetime] = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=365) - #enable_to: Optional[datetime.datetime] = datetime.datetime.now() + datetime.timedelta(days=365) - - #super: Optional[bool] = False - #manager: Optional[bool] = False - administrator: bool = False - public: bool = False - verified: bool = False - - group: Optional[str] - - notes: Optional[str] - - # Including JSON data - other_json: Optional[Json] - - _processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now) - - #@validator('user_id_random', always=True) - def user_id_random_copy(cls, v, values, **kwargs): - log.setLevel(logging.WARNING) - log.debug(locals()) - - if values['id_random']: - return values['id_random'] - return None - - @validator('id', always=True) - def user_id_lookup(cls, v, values, **kwargs): - log.setLevel(logging.WARNING) - log.debug(locals()) - - if values['id_random']: - log.debug(values['id_random']) - return redis_lookup_id_random(record_id_random=values['id_random'], table_name='user') - return None - - @validator('account_id', always=True) - def account_id_lookup(cls, v, values, **kwargs): - if isinstance(v, int) and v > 0: return v - elif id_random := values.get('account_id_random'): - return redis_lookup_id_random(record_id_random=id_random, table_name='account') - return None - - @validator('contact_id', always=True) - def contact_id_lookup(cls, v, values, **kwargs): - if isinstance(v, int) and v > 0: return v - elif id_random := values.get('contact_id_random'): - return redis_lookup_id_random(record_id_random=id_random, table_name='contact') - return None - - @validator('organization_id', always=True) - def organization_id_lookup(cls, v, values, **kwargs): - if isinstance(v, int) and v > 0: return v - elif id_random := values.get('organization_id_random'): - return redis_lookup_id_random(record_id_random=id_random, table_name='organization') - return None - - @validator('person_id', always=True) - def person_id_lookup(cls, v, values, **kwargs): - if isinstance(v, int) and v > 0: return v - elif id_random := values.get('person_id_random'): - return redis_lookup_id_random(record_id_random=id_random, table_name='person') - return None - - @validator('password', always=True) - def hash_new_password(cls, v, values, **kwargs): - log.setLevel(logging.WARNING) - log.debug(locals()) - - if values.get('new_password'): - return secure_hash_string(string=values['new_password']) - return None - - class Config: - underscore_attrs_are_private = True - allow_population_by_field_name = True - fields = base_fields -# ### END ### API User Models ### User_New_Base() ### - - -# ### BEGIN ### API User Models ### User_Out_Base() ### -class User_Out_Base(BaseModel): - log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL - log.debug(locals()) - - id_random: Optional[str] = Field( - **base_fields['user_id_random'], - alias = 'user_id_random', - ) - id: Optional[int] = Field( - alias = 'user_id' - ) - - account_id_random: Optional[str] - #account_id: Optional[int] - account_name: Optional[str] - - contact_id_random: Optional[str] - #contact_id: Optional[int] - - organization_id_random: Optional[str] - #organization_id: Optional[int] - - person_id_random: Optional[str] - #person_id: Optional[int] - - username: Optional[str] - name: Optional[str] - email: Optional[str] - email_verified: Optional[bool] - password: Optional[str] - - allow_auth_key: Optional[int] - auth_key: Optional[str] - - enable: Optional[bool] - enable_from: Optional[datetime.datetime] - enable_to: Optional[datetime.datetime] - - super: Optional[bool] - manager: Optional[bool] - administrator: Optional[bool] - public: Optional[bool] - verified: Optional[bool] - status_id: Optional[int] - status_name: Optional[str] - - password_set_on: Optional[datetime.datetime] - password_reset_token: Optional[str] - password_reset_expire_on: Optional[datetime.datetime] - logged_in_on: Optional[datetime.datetime] - last_activity_on: Optional[datetime.datetime] - - group: Optional[str] - - notes: Optional[str] - created_on: Optional[datetime.datetime] - updated_on: Optional[datetime.datetime] - - # Including other related objects - # from app.models.person_models import Person_Base # Causes circular import - # archive_list: Optional[list] # Archive_Base() - # contact: Optional[Contact_Base] - event_list: Optional[list] # Event_Base() # Priority complete - hosted_file_list: Optional[list] # Hosted_File_Base() # Priority l3 - journal_list: Optional[list] # Journal_Base() # Priority l3 - # membership_person: Optional[Membership_Person_Base] # Priority l2 - # membership_person_list: Optional[list] # Membership_Base() ??? - order_list: Optional[list] # Order_Base() # Priority l2 - order_cart_list: Optional[list] # Order_Base() # Priority l2 - organization: Optional[Union[Organization_Base, None]] # Organization_Base() # Priority l3 - person: Optional[dict] # Person_Base() # Priority l2 - # person: Optional[Union[Person_Base, None]] - post_list: Optional[list] # Post_Base() # Priority l1 - user_role_list: Optional[list] = Field( - alias = 'role_list' - ) # User_Role_Base() - - _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 -# ### END ### API User Models ### User_Out_Base() ### - - # ### BEGIN ### API User Models ### User_Base() ### class User_Base(BaseModel): log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL @@ -271,12 +64,9 @@ class User_Base(BaseModel): status_id: Optional[int] status_name: Optional[str] - password_set_on: Optional[datetime.datetime] = None - password_reset_token: Optional[str] = None - password_reset_expire_on: Optional[datetime.datetime] = None - logged_in_on: Optional[datetime.datetime] = None - last_activity_on: Optional[datetime.datetime] = None - + hide: Optional[bool] + priority: Optional[bool] + sort: Optional[int] group: Optional[str] notes: Optional[str] @@ -317,18 +107,23 @@ class User_Base(BaseModel): log.setLevel(logging.WARNING) log.debug(locals()) - if values['id_random']: - log.debug(values['id_random']) - return redis_lookup_id_random(record_id_random=values['id_random'], table_name='user') + 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='user') return None @validator('account_id', always=True) def account_id_lookup(cls, v, values, **kwargs): - log.setLevel(logging.WARNING) - log.debug(locals()) + if isinstance(v, int) and v > 0: return v + elif id_random := values.get('account_id_random'): + return redis_lookup_id_random(record_id_random=id_random, table_name='account') + return None - if values['account_id_random']: - return redis_lookup_id_random(record_id_random=values['account_id_random'], table_name='account') + @validator('account_id_random', always=True) + def account_id_random_lookup(cls, v, values, **kwargs): + if isinstance(v, str) and len(v) >= 11: return v + elif account_id := values.get('account_id'): + return get_id_random(record_id=account_id, table_name='account') return None # @validator('contact_id', always=True) @@ -368,3 +163,243 @@ class User_Base(BaseModel): allow_population_by_field_name = True fields = base_fields # ### END ### API User Models ### User_Base() ### + + +# ### BEGIN ### API User Models ### User_New_Base() ### +class User_New_Base(BaseModel): + log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(locals()) + + id_random: Optional[str] = Field( + **base_fields['user_id_random'], + alias = 'user_id_random', + # default_factory = lambda:secrets.token_urlsafe(default_num_bytes), + ) + id: Optional[int] = Field( + alias = 'user_id' + ) + account_id_random: Optional[str] + account_id: Optional[int] + account_name: Optional[str] + + contact_id_random: Optional[str] + contact_id: Optional[int] + + organization_id_random: Optional[str] + organization_id: Optional[int] + + person_id_random: Optional[str] + person_id: Optional[int] + + username: str + name: str + email: str + email_verified: bool = False + new_password: str = Field(default_factory = lambda:secrets.token_urlsafe(default_num_bytes)) + password: Optional[str] # If new_password is found then the validator below will create secure_hash_string() from the new password string. + + allow_auth_key: bool = False + + enable: bool = False + enable_from: Optional[datetime.datetime] = datetime.datetime.now(datetime.timezone.utc) + #enable_from: Optional[datetime.datetime] = datetime.datetime.now() + enable_to: Optional[datetime.datetime] = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=365) + #enable_to: Optional[datetime.datetime] = datetime.datetime.now() + datetime.timedelta(days=365) + + #super: Optional[bool] = False + #manager: Optional[bool] = False + administrator: bool = False + public: bool = False + verified: bool = False + + hide: Optional[bool] + priority: Optional[bool] + sort: Optional[int] + group: Optional[str] + + notes: Optional[str] + + # Including JSON data + other_json: Optional[Json] + + _processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now) + + #@validator('user_id_random', always=True) + def user_id_random_copy(cls, v, values, **kwargs): + log.setLevel(logging.WARNING) + log.debug(locals()) + + if values['id_random']: + return values['id_random'] + return None + + @validator('id', always=True) + def user_id_lookup(cls, v, values, **kwargs): + log.setLevel(logging.WARNING) + log.debug(locals()) + + 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='user') + return None + + @validator('account_id', always=True) + def account_id_lookup(cls, v, values, **kwargs): + if isinstance(v, int) and v > 0: return v + elif id_random := values.get('account_id_random'): + return redis_lookup_id_random(record_id_random=id_random, table_name='account') + return None + + @validator('account_id_random', always=True) + def account_id_random_lookup(cls, v, values, **kwargs): + if isinstance(v, str) and len(v) >= 11: return v + elif account_id := values.get('account_id'): + return get_id_random(record_id=account_id, table_name='account') + return None + + @validator('contact_id', always=True) + def contact_id_lookup(cls, v, values, **kwargs): + if isinstance(v, int) and v > 0: return v + elif id_random := values.get('contact_id_random'): + return redis_lookup_id_random(record_id_random=id_random, table_name='contact') + return None + + @validator('organization_id', always=True) + def organization_id_lookup(cls, v, values, **kwargs): + if isinstance(v, int) and v > 0: return v + elif id_random := values.get('organization_id_random'): + return redis_lookup_id_random(record_id_random=id_random, table_name='organization') + return None + + @validator('person_id', always=True) + def person_id_lookup(cls, v, values, **kwargs): + if isinstance(v, int) and v > 0: return v + elif id_random := values.get('person_id_random'): + return redis_lookup_id_random(record_id_random=id_random, table_name='person') + return None + + @validator('password', always=True) + def hash_new_password(cls, v, values, **kwargs): + log.setLevel(logging.WARNING) + log.debug(locals()) + + if values.get('new_password'): + return secure_hash_string(string=values['new_password']) + return None + + class Config: + underscore_attrs_are_private = True + allow_population_by_field_name = True + fields = base_fields +# ### END ### API User Models ### User_New_Base() ### + + +# ### BEGIN ### API User Models ### User_Out_Base() ### +class User_Out_Base(BaseModel): + log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(locals()) + + id_random: Optional[str] = Field( + **base_fields['user_id_random'], + alias = 'user_id_random', + ) + id: Optional[int] = Field( + alias = 'user_id' + ) + + account_id_random: Optional[str] + account_id: Optional[int] + account_name: Optional[str] + + contact_id_random: Optional[str] + contact_id: Optional[int] + + organization_id_random: Optional[str] + organization_id: Optional[int] + + person_id_random: Optional[str] + person_id: Optional[int] + + username: Optional[str] + name: Optional[str] + email: Optional[str] + email_verified: Optional[bool] + password: Optional[str] + + allow_auth_key: Optional[int] + auth_key: Optional[str] + + enable: Optional[bool] + enable_from: Optional[datetime.datetime] + enable_to: Optional[datetime.datetime] + + super: Optional[bool] + manager: Optional[bool] + administrator: Optional[bool] + public: Optional[bool] + verified: Optional[bool] + status_id: Optional[int] + status_name: Optional[str] + + password_set_on: Optional[datetime.datetime] + password_reset_token: Optional[str] + password_reset_expire_on: Optional[datetime.datetime] + logged_in_on: Optional[datetime.datetime] + last_activity_on: Optional[datetime.datetime] + + hide: Optional[bool] + priority: Optional[bool] + sort: Optional[int] + group: Optional[str] + + notes: Optional[str] + created_on: Optional[datetime.datetime] + updated_on: Optional[datetime.datetime] + + # Including other related objects + # from app.models.person_models import Person_Base # Causes circular import + # archive_list: Optional[list] # Archive_Base() + # contact: Optional[Contact_Base] + event_list: Optional[list] # Event_Base() # Priority l1 + hosted_file_list: Optional[list] # Hosted_File_Base() # Priority l2 + journal_list: Optional[list] # Journal_Base() # Priority l3 + # membership_person: Optional[Membership_Person_Base] # Priority l2 + # membership_person_list: Optional[list] # Membership_Base() ??? + order_list: Optional[list] # Order_Base() # Priority l2 + order_cart_list: Optional[list] # Order_Base() # Priority l2 + organization: Optional[Union[Organization_Base, None]] # Organization_Base() # Priority l3 + person: Optional[dict] # Person_Base() # Priority l2 + # person: Optional[Union[Person_Base, None]] + post_list: Optional[list] # Post_Base() # Priority l1 + user_role_list: Optional[list] = Field( + alias = 'role_list' + ) # User_Role_Base() + + _processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now) + + @validator('account_id', always=True) + def account_id_lookup(cls, v, values, **kwargs): + if isinstance(v, int) and v > 0: return v + elif id_random := values.get('account_id_random'): + return redis_lookup_id_random(record_id_random=id_random, table_name='account') + return None + + @validator('account_id_random', always=True) + def account_id_random_lookup(cls, v, values, **kwargs): + if isinstance(v, str) and len(v) >= 11: return v + elif account_id := values.get('account_id'): + return get_id_random(record_id=account_id, table_name='account') + return None + + @validator('person_id', always=True) + def person_id_lookup(cls, v, values, **kwargs): + if isinstance(v, int) and v > 0: return v + elif id_random := values.get('person_id_random'): + return redis_lookup_id_random(record_id_random=id_random, table_name='person') + return None + + class Config: + underscore_attrs_are_private = True + allow_population_by_field_name = True + fields = base_fields +# ### END ### API User Models ### User_Out_Base() ### diff --git a/app/object_definitions/core.py b/app/object_definitions/core.py index fd690de..500d798 100644 --- a/app/object_definitions/core.py +++ b/app/object_definitions/core.py @@ -48,7 +48,7 @@ core_obj_li = { # V3 Search Security: 'searchable_fields': [ 'account_id_random', 'code', 'name', 'short_name', 'description', - 'enable', 'created_on', 'updated_on' + 'enable', 'hide', 'priority', 'sort', 'group', 'created_on', 'updated_on' ], }, 'account_cfg': { diff --git a/app/object_definitions/events_general.py b/app/object_definitions/events_general.py index abe9abf..94062e0 100644 --- a/app/object_definitions/events_general.py +++ b/app/object_definitions/events_general.py @@ -107,7 +107,8 @@ events_general_obj_li = { # V3 Search Security: 'searchable_fields': [ 'event_cfg_id_random', 'event_id_random', 'enable', 'conference', - 'status', 'hide', 'priority', 'group', 'notes' + 'status', 'hide', 'priority', 'sort', 'group', 'notes', 'created_on', + 'updated_on' ], }, } diff --git a/app/object_definitions/events_registration.py b/app/object_definitions/events_registration.py index 1c528bf..bab7674 100644 --- a/app/object_definitions/events_registration.py +++ b/app/object_definitions/events_registration.py @@ -25,7 +25,8 @@ events_registration_obj_li = { 'title_names', 'given_name', 'middle_name', 'family_name', 'designations', 'professional_title', 'full_name', 'affiliations', 'email', 'phone', 'location', 'allow_tracking', 'print_count', 'print_first_datetime', - 'print_last_datetime' + 'print_last_datetime', 'enable', 'hide', 'priority', 'sort', 'group', + 'notes', 'created_on', 'updated_on' ], }, 'event_badge_template': { @@ -43,7 +44,8 @@ events_registration_obj_li = { # V3 Search Security: 'searchable_fields': [ 'event_badge_template_id_random', 'event_id_random', 'name', - 'description', 'layout', 'notes' + 'description', 'layout', 'notes', 'enable', 'hide', 'priority', + 'sort', 'group', 'created_on', 'updated_on' ], }, 'event_person': { diff --git a/app/object_definitions/journals.py b/app/object_definitions/journals.py index 5f9e812..683f742 100644 --- a/app/object_definitions/journals.py +++ b/app/object_definitions/journals.py @@ -22,7 +22,7 @@ journal_obj_li = { 'searchable_fields': [ 'journal_id_random', 'name', 'short_name', 'summary', 'outline', 'description', 'type_code', 'tags', 'billable', 'enable', 'hide', - 'priority', 'group', 'created_on', 'updated_on' + 'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on' ], }, 'journal_entry': { @@ -44,8 +44,8 @@ journal_obj_li = { 'searchable_fields': [ 'journal_entry_id_random', 'journal_id_random', 'name', 'short_name', 'summary', 'content', 'type_code', 'topic_code', 'category_code', - 'tags', 'location', 'billable', 'enable', 'hide', 'priority', 'group', - 'created_on', 'updated_on' + 'tags', 'location', 'billable', 'enable', 'hide', 'priority', 'sort', + 'group', 'notes', 'created_on', 'updated_on' ], }, } diff --git a/app/object_definitions/orders.py b/app/object_definitions/orders.py index 4e6f9e8..2c5b46c 100644 --- a/app/object_definitions/orders.py +++ b/app/object_definitions/orders.py @@ -110,7 +110,8 @@ order_obj_li = { # V3 Search Security: 'searchable_fields': [ 'account_id_random', 'account_name', 'default_no_reply_email', - 'confirm_email' + 'confirm_email', 'enable', 'hide', 'priority', 'sort', 'group', + 'notes', 'created_on', 'updated_on' ], }, }