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:
47
GEMINI.md
47
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/`.
|
||||
- **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
108
ae_object_info.py
Executable 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()
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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'
|
||||
],
|
||||
},
|
||||
|
||||
@@ -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'
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
],
|
||||
},
|
||||
|
||||
@@ -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
55
export_all_interfaces.py
Executable 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()
|
||||
60
verify_searchable_fields.py
Normal file
60
verify_searchable_fields.py
Normal 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()
|
||||
40
verify_searchable_fields_db.py
Normal file
40
verify_searchable_fields_db.py
Normal 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()
|
||||
Reference in New Issue
Block a user