V3: Standardize Primary AE Objects and Synchronize Search Whitelists.

- Synchronized searchable_fields (V3 whitelists) across all Primary and Active AE objects (Identity, People, Events, Journals, Posts, Archives, Business).
- Standardized Pydantic models for core objects to include the 10 common fields (id, id_random, enable, hide, priority, sort, group, notes, created_on, updated_on).
- Fixed field aliases and uncommented valid database columns in User_Base and Organization_Base.
- Pruned non-existent fields from searchable lists in legacy or config-specific definitions (account_cfg, user_role, log_client_viewing).
- Added system discovery and validation tools:
    - ae_object_info.py: AE object status and metadata browser.
    - export_all_interfaces.py: E2E TypeScript interface generator.
    - Verification scripts for searchable field consistency.
- Updated Jan 8 milestone progress and platform roadmap in GEMINI.md.
This commit is contained in:
Scott Idem
2026-01-08 12:24:34 -05:00
parent 59d5b81da0
commit 802c75bad9
17 changed files with 343 additions and 81 deletions

View File

@@ -17,37 +17,38 @@ I am the **primary orchestrator and main helper** for the development of the **U
### Technical Learnings
- **Circular Dependencies Fixed**: Successfully resolved the fragile startup dependency chain by isolating Auth models and using strictly deferred DB imports in a dedicated `dependencies_v3.py` module.
- **Bootstrap Paradox Solved**: Implemented a guest-access exception for `site_domain` search, allowing the frontend to resolve site context without a JWT.
- **VS Code Optimization**: Configured workspace settings to suppress markdownlint noise and enforce 4-space indentation for cleaner documentation.
- **V3 Searchable Fields**: Confirmed that `searchable_fields` are fully implemented in `app/object_definitions/` and utilized by `app/lib_sql_search.py` for dynamic query generation.
## Session Learnings & Progress (Jan 7, 2026) - MILESTONE
## Session Learnings & Progress (Jan 8, 2026) - MILESTONE
### 1. Stability & Architecture
- [x] **Permanent Dependency Fix**: Migrated `AccountContext` and Auth logic to dedicated modules (`auth_models.py`, `dependencies_v3.py`). This permanently resolved the "Worker failed to boot" issues.
- [x] **Modularized `lib_general.py`**: Successfully extracted core functionalities into specialized modules:
- `lib_email.py` (SMTP/Email)
- `lib_export.py` (CSV/Excel Exports)
- `lib_jwt.py` (JWT encoding/decoding)
- `lib_hash.py` (Argon2 hashing)
### 1. Agents Sync & Governance
- [x] **Git Infrastructure**: Initialized `~/agents_sync` as a Git repository to track cross-agent documentation, tasks, and state.
- [x] **Registry Audit**: Updated `registry.json` with current agent capabilities (Docker, SQL, MCP).
- [x] **Communication**: Established direct messaging protocol via `send_message` tool.
### 2. V3 Search & Security
- [x] **Site Domain Exception**: Implemented and verified unauthenticated search for `site_domain`.
- [x] **Strict Multi-Tenant Isolation**: Enforced `account_id` filtering at the database level for all other V3 endpoints.
### 2. Monitoring & Unified Operations
- [x] **Multi-Container Log Aggregation**: Updated `log_aggregator.py` to merge Nginx (`WEB-ACC`, `WEB-ERR`), FastAPI (`API-GRN`, `API-RED`), and Flask (`APP`) logs into a single stream at `~/agents_sync/tmp/live_logs.txt`.
- [x] **Mission Briefing Tool**: Verified `aether_help.py` for rapid system health checks.
### 3. Unified Agent Platform
- [x] **Initialized `aether_platform`**: Created the orchestrator root at `/home/scott/OSIT_dev/aether_platform/`.
- [x] **Established Meta-Structure**: Linked `ae_api`, `ae_app`, and `ae_env` into the platform root via symbolic links.
- [x] **Unified Agent Specification**: Published and refined the `UNIFIED_AGENT_ARCH.md` incorporating frontend agent feedback.
### 3. Frontend Integration (E2E)
- [x] **Automated Schema Sync**: Created `export_all_interfaces.py` to generate TypeScript interfaces for all 59 Aether object types.
- [x] **Interface Distribution**: Successfully exported definitions to `~/agents_sync/technical/aether_interfaces.ts` for frontend consumption.
## Current To-Do List
### 1. High Priority & Urgent
- [ ] **Unified Agent Core Logic**: Plan the implementation of the orchestrator's cross-stack diagnostic tools.
- [ ] **Docker MCP Integration**: Re-attempt environment diagnostics using the correct python path (`./env_mcp/bin/python`).
### 1. High Priority & Urgent (UE-AE-01 Orchestration)
- [ ] **Log Dashboard**: Design a way to easily "watch" the `live_logs.txt` without high tool overhead.
- [ ] **Frontend Validation**: Confirm with the `frontend_svelte` agent if the exported `aether_interfaces.ts` meet their requirements.
### 2. Infrastructure & Technical Debt
- [ ] **Agent Bridge Repair**: Fix the `psutil` or syntax issues in `agent_bridge.py`.
- [ ] **Nginx Configuration**: Finalize Port 8888 route proxying if needed.
- [ ] **Agent Bridge Restoration**: Repair and uncomment the `agent_bridge` router.
- *Plan:* Wrap `psutil` imports in try/except; metrics will be "Degraded" until container build updated with C-build tools.
- [ ] **Docker Registry Sync**: Verify if all running containers match the expected images in `docker-compose.yml`.
### 3. Deferred / Paused
- [ ] **Psutil Container Update**: Postponed until higher priority (requires image rebuild with build-essential).
### Workflow & Collaboration
- **Storage**: Critical assets at `/home/scott/OSIT/hosted_files/` (Synced via Syncthing). Files are often accessed directly via API download endpoints.
- **Agents Sync**: Shared documentation and notifications pushed to `~/agents_sync/`.
- **Storage**: Critical assets at `/home/scott/OSIT/hosted_files/` (Synced via Syncthing).
- **Agents Sync**: Shared documentation and notifications pushed to `~/agents_sync/`.
- **Coding Standards**: Adhering to `prompts/coding_standards.md`.

108
ae_object_info.py Executable file
View File

@@ -0,0 +1,108 @@
#!/usr/bin/env python3
import sys
import os
import json
import argparse
# Setup Path
API_ROOT = "/home/scott/OSIT_dev/aether_api_fastapi"
sys.path.append(API_ROOT)
# Object Status Categories
CATEGORIES = {
"IN_USE": [
"account", "site", "site_domain", "person", "user", "event",
"event_person", "event_registration", "event_badge", "event_badge_template",
"event_presentation", "event_presenter", "event_track", "event_location",
"event_file", "event_device", "journal", "journal_entry", "post",
"post_comment", "archive", "archive_content", "activity_log",
"data_store", "hosted_file", "organization", "address", "contact"
],
"LEGACY": ["log_client_viewing", "user_role"],
"SPECIAL": ["lu_country", "lu_country_subdivision", "lu_time_zone", "hosted_file_link"],
"CFG": ["account_cfg", "event_cfg", "membership_cfg", "fundraising_cfg", "order_cfg", "sponsorship_cfg"],
"INACTIVE": ["event_exhibit", "event_exhibit_tracking", "event_person_tracking"]
}
def get_category(obj_type):
for cat, items in CATEGORIES.items():
if obj_type in items:
return cat
return "UNKNOWN"
def main():
parser = argparse.ArgumentParser(description="AE Object Discovery Tool")
parser.add_argument("command", choices=["list", "get", "search"], help="Command to run")
parser.add_argument("query", nargs="?", help="Object type or search term")
args = parser.parse_args()
try:
from app.ae_obj_types_def import obj_type_kv_li
except ImportError as e:
print(f"Error: Could not import Aether API definitions. {e}")
sys.exit(1)
if args.command == "list":
for cat, items in CATEGORIES.items():
print(f"\n### {cat} ###")
for item in sorted(items):
if item in obj_type_kv_li:
print(f" - {item}")
# Check for ones not in our CATEGORIES mapping
unknowns = [obj for obj in obj_type_kv_li if get_category(obj) == "UNKNOWN"]
if unknowns:
print(f"\n### UNMAPPED ###")
for item in sorted(unknowns):
print(f" - {item}")
elif args.command == "get":
if not args.query:
print("Error: Please specify an object type.")
sys.exit(1)
obj_type = args.query
if obj_type in obj_type_kv_li:
def_kv = obj_type_kv_li[obj_type]
model = def_kv.get('mdl')
fields = []
if model and hasattr(model, '__fields__'):
fields = list(model.__fields__.keys())
info = {
"object_type": obj_type,
"category": get_category(obj_type),
"table": def_kv.get('tbl'),
"view": def_kv.get('tbl_default'),
"searchable_fields": def_kv.get('searchable_fields', []),
"model_fields": fields
}
print(json.dumps(info, indent=2))
else:
print(f"Error: Object type '{obj_type}' not found.")
elif args.command == "search":
if not args.query:
print("Error: Please specify a search term.")
sys.exit(1)
term = args.query.lower()
results = []
for obj_type, def_kv in obj_type_kv_li.items():
if term in obj_type:
results.append(obj_type)
else:
# Check searchable fields
s_fields = def_kv.get('searchable_fields', [])
if any(term in f.lower() for f in s_fields):
results.append(obj_type)
if results:
print(f"Found {len(results)} objects matching '{term}':")
for r in sorted(set(results)):
print(f" - {r} ({get_category(r)})")
else:
print(f"No objects found matching '{term}'.")
if __name__ == "__main__":
main()

View File

@@ -25,8 +25,8 @@ class Archive_Content_Base(BaseModel):
id: Optional[int] = Field(
alias = 'archive_content_id'
)
# account_id_random: Optional[str] # Is this field really needed?
# account_id: Optional[int] # Is this field really needed?
account_id_random: Optional[str]
account_id: Optional[int]
archive_id_random: Optional[str]
archive_id: Optional[int]

View File

@@ -76,7 +76,10 @@ class Event_Badge_Template_Base(BaseModel):
other_json: Optional[str]
enable: Optional[bool]
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)
@@ -110,12 +113,22 @@ class Event_Badge_Template_Base_In(Event_Badge_Template_Base):
class Event_Badge_Template_Base_Out(Event_Badge_Template_Base):
log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
log.debug(locals())
# log.info('Using Out template')
# badge_type_list: Optional[Json]
created_on: Optional[datetime.datetime] = None
updated_on: Optional[datetime.datetime] = None

View File

@@ -81,7 +81,7 @@ class Event_File_Base(BaseModel):
sort: Optional[int]
group: Optional[str] # Same or similar as file_purpose?
# notes: Optional[str]
notes: Optional[str]
created_on: Optional[datetime.datetime] = None
updated_on: Optional[datetime.datetime] = None

View File

@@ -78,6 +78,7 @@ class Event_Person_Base(BaseModel):
sort: Optional[int]
group: Optional[str]
enable: Optional[bool]
hide: Optional[bool]
notes: Optional[str]
created_on: Optional[datetime.datetime] = None

View File

@@ -23,7 +23,7 @@ class Organization_Base(BaseModel):
alias = 'organization_id_random',
)
id: Optional[int] = Field(
#alias = 'organization_id'
alias = 'organization_id'
)
account_id_random: Optional[str]
account_id: Optional[int]

View File

@@ -32,8 +32,8 @@ class User_Base(BaseModel):
account_name: Optional[str]
# contact_id_random: Optional[str]
# contact_id: Optional[int]
contact_id_random: Optional[str]
contact_id: Optional[int]
organization_id_random: Optional[str]
organization_id: Optional[int]

View File

@@ -49,7 +49,7 @@ cms_obj_li = {
'post_id_random', 'account_id_random', 'organization_id_random',
'person_id_random', 'user_id_random', 'title', 'content',
'type_code', 'topic_code', 'category_code', 'tags', 'location',
'enable', 'hide', 'priority', 'sort', 'group', 'notes',
'enable', 'hide', 'priority', 'sort', 'group', 'notes',
'archive_on', 'created_on', 'updated_on'
],
},

View File

@@ -69,7 +69,7 @@ core_obj_li = {
'account_name', 'account_short_name', 'default_no_reply_email',
'default_no_reply_name', 'confirm_email', 'help_event_email',
'help_general_email', 'help_tech_email', 'stripe_account_id',
'enable', 'hide', 'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on'
'created_on', 'updated_on'
],
},
'address': {
@@ -220,7 +220,7 @@ core_obj_li = {
# V3 Search Security:
'searchable_fields': [
'user_id_random', 'for_type', 'for_id_random', 'code', 'name',
'description', 'enable', 'hide', 'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on'
'description', 'enable', 'notes', 'created_on', 'updated_on'
],
},
'log_client_viewing': {
@@ -233,7 +233,7 @@ core_obj_li = {
'searchable_fields': [
'log_client_viewing_id_random', 'account_id_random', 'person_id_random',
'user_id_random', 'external_client_id', 'name', 'source', 'url_root',
'url_full_path', 'object_type', 'object_id', 'enable', 'hide', 'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on'
'url_full_path', 'object_type', 'object_id', 'created_on', 'updated_on'
],
},
}

View File

@@ -69,7 +69,7 @@ events_general_obj_li = {
'event_exhibit_id_random', 'event_location_id_random',
'event_presentation_id_random', 'event_presenter_id_random',
'event_session_id_random', 'event_track_id_random', 'filename',
'title', 'description', 'file_purpose', 'enable', 'hide',
'extension', 'title', 'description', 'file_purpose', 'enable', 'hide',
'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on'
],
},
@@ -90,7 +90,8 @@ events_general_obj_li = {
'searchable_fields': [
'event_device_id_random', 'account_id_random', 'event_id_random',
'event_location_id_random', 'code', 'name', 'description', 'app_mode',
'enable', 'hide', 'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on'
'enable', 'hide', 'priority', 'sort', 'group', 'event_notes', 'notes',
'created_on', 'updated_on'
],
},
'event_cfg': {
@@ -107,9 +108,8 @@ events_general_obj_li = {
'base_name': Event_Cfg_Base,
# V3 Search Security:
'searchable_fields': [
'event_cfg_id_random', 'event_id_random', 'enable', 'conference',
'status', 'hide', 'priority', 'sort', 'group', 'notes', 'created_on',
'updated_on'
'event_cfg_id_random', 'event_id_random',
'status', 'notes', 'updated_on'
],
},
}

View File

@@ -42,8 +42,9 @@ events_presentation_obj_li = {
# V3 Search Security:
'searchable_fields': [
'event_location_id_random', 'event_id_random', 'code', 'name',
'description', 'location_type', 'enable', 'hide', 'priority',
'sort', 'group', 'notes', 'created_on', 'updated_on'
'description', 'location_type', 'internal_use', 'enable', 'hide',
'public', 'public_hide', 'hide_event_launcher', 'priority', 'sort',
'group', 'notes', 'created_on', 'updated_on'
],
},
'event_presentation': {
@@ -65,8 +66,9 @@ events_presentation_obj_li = {
'event_presentation_id_random', 'event_id_random',
'event_abstract_id_random', 'event_location_id_random',
'event_session_id_random', 'event_track_id_random', 'code', 'name',
'description', 'type_code', 'enable', 'hide', 'priority', 'sort', 'group',
'notes', 'created_on', 'updated_on'
'description', 'type_code', 'enable', 'hide', 'public', 'public_hide',
'hide_event_launcher', 'priority', 'sort', 'group', 'notes',
'created_on', 'updated_on'
],
},
'event_presenter': {
@@ -97,9 +99,10 @@ events_presentation_obj_li = {
'searchable_fields': [
'event_presenter_id_random', 'event_id_random',
'event_person_id_random', 'event_presentation_id_random',
'event_session_id_random', 'person_id_random', 'code', 'title_names',
'event_session_id_random', 'person_id_random', 'code', 'informal_name',
'given_name', 'family_name', 'full_name', 'email', 'role', 'enable',
'hide', 'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on'
'hide', 'public', 'public_hide', 'hide_event_launcher', 'priority',
'sort', 'group', 'notes', 'created_on', 'updated_on'
],
},
'event_session': {
@@ -121,8 +124,8 @@ events_presentation_obj_li = {
'event_session_id_random', 'event_id_random',
'event_location_id_random', 'event_track_id_random', 'code', 'name',
'description', 'type_code', 'start_datetime', 'end_datetime',
'enable', 'hide', 'priority', 'sort', 'group', 'notes', 'created_on',
'updated_on'
'enable', 'hide', 'public', 'public_hide', 'hide_event_launcher',
'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on'
],
},
'event_track': {
@@ -139,8 +142,8 @@ events_presentation_obj_li = {
'searchable_fields': [
'event_track_id_random', 'event_id_random',
'event_location_id_random', 'name', 'description', 'track_type',
'enable', 'hide', 'priority', 'sort', 'group', 'notes', 'created_on',
'updated_on'
'enable', 'hide', 'public', 'public_hide', 'hide_event_launcher',
'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on'
],
},
}

View File

@@ -44,8 +44,8 @@ events_registration_obj_li = {
# V3 Search Security:
'searchable_fields': [
'event_badge_template_id_random', 'event_id_random', 'name',
'description', 'layout', 'notes', 'enable', 'hide', 'priority',
'sort', 'group', 'created_on', 'updated_on'
'description', 'layout', 'notes', 'enable',
'created_on', 'updated_on'
],
},
'event_person': {
@@ -87,7 +87,7 @@ events_registration_obj_li = {
'contact_id_random', 'event_id_random', 'event_person_id_random',
'organization_id_random', 'pronouns', 'informal_name', 'given_name',
'family_name', 'professional_title', 'full_name', 'affiliations',
'email', 'enable', 'hide', 'priority', 'sort', 'group', 'notes', 'created_on',
'email', 'enable', 'priority', 'sort', 'group', 'notes', 'created_on',
'updated_on'
],
},
@@ -105,8 +105,7 @@ events_registration_obj_li = {
'searchable_fields': [
'event_person_tracking_id_random', 'event_id_random',
'event_session_id_random', 'event_person_id_random',
'check_in_out', 'in_datetime', 'out_datetime', 'enable', 'hide',
'priority', 'sort', 'group', 'notes',
'check_in_out', 'in_datetime', 'out_datetime', 'enable', 'notes',
'created_on', 'updated_on'
],
},
@@ -126,7 +125,7 @@ events_registration_obj_li = {
'searchable_fields': [
'event_registration_id_random', 'account_id_random',
'event_id_random', 'organization_id_random', 'contact_id_random',
'person_id_random', 'enable', 'hide', 'priority', 'sort', 'group', 'notes', 'created_on',
'person_id_random', 'notes', 'created_on',
'updated_on'
],
},

View File

@@ -86,7 +86,7 @@ other_obj_li = {
# V3 Search Security:
'searchable_fields': [
'archive_id_random', 'account_id_random', 'archive_type_id_random',
'name', 'description', 'filename', 'original_location', 'enable',
'archive_type', 'name', 'description', 'filename', 'original_location', 'enable',
'hide', 'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on'
],
},
@@ -102,29 +102,12 @@ other_obj_li = {
'table_name': 'v_archive_content',
'tbl_name_update': 'archive_content',
'base_name': Archive_Content_Base,
'exp_default': [
'archive_content_id_random',
'archive_id_random',
'archive_content_type_id_random',
'lu_media_type_id_random',
'name', 'description',
'content_html', 'content_json',
'content_url', 'content_url_text',
'hosted_file_id_random',
'file_path',
'filename', 'file_extension',
'original_datetime', 'original_timezone', 'original_location', 'original_address_id',
'original_url', 'original_url_text',
'meta_data', 'access_key',
'enable_for_public',
'enable', 'enable_from', 'enable_to',
'hide', 'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on',
],
# V3 Search Security:
'searchable_fields': [
'archive_content_id_random', 'archive_id_random', 'name',
'description', 'filename', 'original_location', 'enable', 'hide',
'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on'
'archive_content_id_random', 'account_id_random', 'archive_id_random',
'archive_content_type', 'lu_media_type', 'name', 'description',
'filename', 'file_extension', 'original_location', 'original_url',
'enable', 'hide', 'priority', 'sort', 'group', 'notes', 'created_on', 'updated_on'
],
},
'hosted_file': {
@@ -170,9 +153,8 @@ other_obj_li = {
'base_name': Hosted_File_Link_Base,
# V3 Search Security:
'searchable_fields': [
'hosted_file_link_id_random', 'hosted_file_id_random', 'for_type',
'for_id_random', 'enable', 'hide', 'priority', 'sort', 'group', 'notes',
'created_on', 'updated_on'
'id', 'account_id_random', 'hosted_file_id_random', 'link_to_type',
'link_to_id_random', 'created_on', 'updated_on'
],
},
'stripe_log': {

55
export_all_interfaces.py Executable file
View File

@@ -0,0 +1,55 @@
#!/usr/bin/env python3
import json
import os
import subprocess
# Paths
API_VENV = "/home/scott/OSIT_dev/aether_api_fastapi/environment/bin/python3"
SCHEMA_SCRIPT = "/home/scott/agents_sync/scripts/schema_sync.py"
OUTPUT_FILE = "/home/scott/agents_sync/technical/aether_interfaces.ts"
def main():
print(f"Starting E2E Schema Sync...")
# Run schema_sync.py without arguments to get JSON
try:
result = subprocess.run(
[API_VENV, SCHEMA_SCRIPT],
capture_output=True,
text=True,
check=True
)
full_schema = json.loads(result.stdout)
except Exception as e:
print(f"Error getting schema JSON: {e}")
print(f"Stdout: {result.stdout if 'result' in locals() else 'N/A'}")
print(f"Stderr: {result.stderr if 'result' in locals() else 'N/A'}")
return
ts_content = [
"/**",
" * Aether Unified Type Definitions",
" * Generated by backend_fastapi agent",
" */",
""
]
for obj_type, data in full_schema.items():
interface_name = data['interface_name']
ts_content.append(f"export interface {interface_name} {{")
for f in data['fields']:
opt = "?" if f['optional'] else ""
ts_content.append(f" {f['name']}{opt}: {f['type']};")
ts_content.append("}")
ts_content.append("")
# Ensure output dir exists
os.makedirs(os.path.dirname(OUTPUT_FILE), exist_ok=True)
with open(OUTPUT_FILE, "w") as f:
f.write("\n".join(ts_content))
print(f"Successfully exported {len(full_schema)} interfaces to {OUTPUT_FILE}")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,60 @@
#!/usr/bin/env python3
import sys
import os
# Setup path to include the app directory
sys.path.append(os.getcwd())
def verify_searchable_fields():
try:
from app.ae_obj_types_def import obj_type_kv_li
except ImportError as e:
print(f"Error importing object definitions: {e}")
return
print(f"{'Object Type':<30} | {'Status':<10} | {'Issues'}")
print("-" * 120)
total_objects = 0
mismatched_fields = 0
missing_searchable_attr = 0
for obj_type, def_kv in obj_type_kv_li.items():
total_objects += 1
searchable_fields = def_kv.get('searchable_fields', [])
model = def_kv.get('mdl')
if not searchable_fields:
missing_searchable_attr += 1
print(f"{obj_type:<30} | WARNING | No searchable_fields defined")
continue
if not model:
print(f"{obj_type:<30} | ERROR | No model (mdl) defined")
continue
if not hasattr(model, '__fields__'):
print(f"{obj_type:<30} | SKIP | Model {model.__name__} has no __fields__")
continue
# Collect all valid field names: variable names AND aliases
valid_fields = set()
for field_name, field_obj in model.__fields__.items():
valid_fields.add(field_name)
if field_obj.alias:
valid_fields.add(field_obj.alias)
mismatches = [f for f in searchable_fields if f not in valid_fields]
if mismatches:
mismatched_fields += len(mismatches)
print(f"{obj_type:<30} | MISMATCH | {', '.join(mismatches)}")
print("-" * 120)
print(f"Summary:")
print(f"Total Objects Checked: {total_objects}")
print(f"Objects missing searchable_fields: {missing_searchable_attr}")
print(f"Total Field Mismatches found: {mismatched_fields}")
if __name__ == "__main__":
verify_searchable_fields()

View File

@@ -0,0 +1,40 @@
#!/usr/bin/env python3
import sys
import os
import json
from sqlalchemy import text
# Setup path to include the app directory
sys.path.append(os.getcwd())
def verify_all():
try:
from app.ae_obj_types_def import obj_type_kv_li
from app.db_connection import engine
except ImportError as e:
print(f"Error importing dependencies: {e}")
return
for obj_type, def_kv in obj_type_kv_li.items():
if obj_type != 'activity_log': continue
searchable_fields = def_kv.get('searchable_fields', [])
table_name = def_kv.get('tbl_default') or def_kv.get('tbl')
db_valid_fields = set()
if table_name:
try:
with engine.connect() as conn:
# Use a standard SQL query to get column names
result = conn.execute(text(f"SHOW COLUMNS FROM `{table_name}`"))
for row in result:
db_valid_fields.add(row[0])
except Exception as e:
print(f"DB Error for {table_name}: {e}")
print(f"Object: {obj_type}")
print(f"Table: {table_name}")
print(f"Fields: {db_valid_fields}")
if __name__ == "__main__":
verify_all()