From 802c75bad9d8930954da1a92cf8893e774ebb88b Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Thu, 8 Jan 2026 12:24:34 -0500 Subject: [PATCH] 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. --- GEMINI.md | 47 ++++---- ae_object_info.py | 108 ++++++++++++++++++ app/models/archive_content_models.py | 4 +- app/models/event_badge_template_models.py | 17 ++- app/models/event_file_models.py | 2 +- app/models/event_person_models.py | 1 + app/models/organization_models.py | 2 +- app/models/user_models.py | 4 +- app/object_definitions/cms.py | 2 +- app/object_definitions/core.py | 6 +- app/object_definitions/events_general.py | 10 +- app/object_definitions/events_presentation.py | 23 ++-- app/object_definitions/events_registration.py | 11 +- app/object_definitions/other.py | 32 ++---- export_all_interfaces.py | 55 +++++++++ verify_searchable_fields.py | 60 ++++++++++ verify_searchable_fields_db.py | 40 +++++++ 17 files changed, 343 insertions(+), 81 deletions(-) create mode 100755 ae_object_info.py create mode 100755 export_all_interfaces.py create mode 100644 verify_searchable_fields.py create mode 100644 verify_searchable_fields_db.py diff --git a/GEMINI.md b/GEMINI.md index b0182d2..2524499 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -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/`. \ No newline at end of file +- **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`. diff --git a/ae_object_info.py b/ae_object_info.py new file mode 100755 index 0000000..494e75e --- /dev/null +++ b/ae_object_info.py @@ -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() diff --git a/app/models/archive_content_models.py b/app/models/archive_content_models.py index 38b7ebd..a3a4547 100644 --- a/app/models/archive_content_models.py +++ b/app/models/archive_content_models.py @@ -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] diff --git a/app/models/event_badge_template_models.py b/app/models/event_badge_template_models.py index 1153a1c..c9113ac 100644 --- a/app/models/event_badge_template_models.py +++ b/app/models/event_badge_template_models.py @@ -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 \ No newline at end of file diff --git a/app/models/event_file_models.py b/app/models/event_file_models.py index da0b33d..6df0428 100644 --- a/app/models/event_file_models.py +++ b/app/models/event_file_models.py @@ -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 diff --git a/app/models/event_person_models.py b/app/models/event_person_models.py index 3e2e444..52a4737 100644 --- a/app/models/event_person_models.py +++ b/app/models/event_person_models.py @@ -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 diff --git a/app/models/organization_models.py b/app/models/organization_models.py index c4080cd..ceb4099 100644 --- a/app/models/organization_models.py +++ b/app/models/organization_models.py @@ -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] diff --git a/app/models/user_models.py b/app/models/user_models.py index 55eb437..c302288 100644 --- a/app/models/user_models.py +++ b/app/models/user_models.py @@ -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] diff --git a/app/object_definitions/cms.py b/app/object_definitions/cms.py index 691a51e..5916ed2 100644 --- a/app/object_definitions/cms.py +++ b/app/object_definitions/cms.py @@ -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' ], }, diff --git a/app/object_definitions/core.py b/app/object_definitions/core.py index aa7019e..10f87fc 100644 --- a/app/object_definitions/core.py +++ b/app/object_definitions/core.py @@ -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' ], }, } diff --git a/app/object_definitions/events_general.py b/app/object_definitions/events_general.py index 5260ca1..fa5b793 100644 --- a/app/object_definitions/events_general.py +++ b/app/object_definitions/events_general.py @@ -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' ], }, } diff --git a/app/object_definitions/events_presentation.py b/app/object_definitions/events_presentation.py index 50433e2..8ad0d10 100644 --- a/app/object_definitions/events_presentation.py +++ b/app/object_definitions/events_presentation.py @@ -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' ], }, } diff --git a/app/object_definitions/events_registration.py b/app/object_definitions/events_registration.py index a53d944..1a5920f 100644 --- a/app/object_definitions/events_registration.py +++ b/app/object_definitions/events_registration.py @@ -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' ], }, diff --git a/app/object_definitions/other.py b/app/object_definitions/other.py index 1b0f0c8..d7e7408 100644 --- a/app/object_definitions/other.py +++ b/app/object_definitions/other.py @@ -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': { diff --git a/export_all_interfaces.py b/export_all_interfaces.py new file mode 100755 index 0000000..ca63117 --- /dev/null +++ b/export_all_interfaces.py @@ -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() diff --git a/verify_searchable_fields.py b/verify_searchable_fields.py new file mode 100644 index 0000000..7f7dce0 --- /dev/null +++ b/verify_searchable_fields.py @@ -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() \ No newline at end of file diff --git a/verify_searchable_fields_db.py b/verify_searchable_fields_db.py new file mode 100644 index 0000000..3775be9 --- /dev/null +++ b/verify_searchable_fields_db.py @@ -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()