API and persisted records use PowerOnModel system fields: - sysCreatedAt, sysCreatedBy, sysModifiedAt, sysModifiedBy Removed legacy JSON/DB field names: - _createdAt, _createdBy, _modifiedAt, _modifiedBy Frontend (frontend_nyla) and gateway call sites were updated accordingly. Database: - Bootstrap runs idempotent backfill (_migrateSystemFieldColumns) from old underscore columns and selected business duplicates into sys* where sys* IS NULL. - Re-run app bootstrap against each PostgreSQL database after deploy. - Optional: DROP INDEX IF EXISTS "idx_invitation_createdby" if an old index remains; new index: idx_invitation_syscreatedby on Invitation(sysCreatedBy). Tests: - RBAC integration tests aligned with current GROUP mandate filter and UserMandate-based UserConnection GROUP clause; buildRbacWhereClause(..., mandateId=...) must be passed explicitly (same as production request context).
97 lines
6.7 KiB
Python
97 lines
6.7 KiB
Python
# Copyright (c) 2025 Patrick Motsch
|
|
# All rights reserved.
|
|
"""Automation models: AutomationDefinition, AutomationTemplate."""
|
|
|
|
from typing import List, Dict, Any, Optional
|
|
from pydantic import BaseModel, Field
|
|
from modules.datamodels.datamodelBase import PowerOnModel
|
|
from modules.shared.attributeUtils import registerModelLabels
|
|
from modules.datamodels.datamodelUtils import TextMultilingual
|
|
import uuid
|
|
|
|
|
|
class AutomationDefinition(BaseModel):
|
|
id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Primary key", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
|
|
mandateId: str = Field(description="Mandate ID", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
|
|
featureInstanceId: str = Field(description="ID of the feature instance this automation belongs to", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
|
|
label: str = Field(description="User-friendly name", json_schema_extra={"frontend_type": "text", "frontend_required": True})
|
|
schedule: str = Field(description="Cron schedule pattern", json_schema_extra={"frontend_type": "select", "frontend_required": True, "frontend_options": [
|
|
{"value": "0 */4 * * *", "label": {"en": "Every 4 hours", "fr": "Toutes les 4 heures"}},
|
|
{"value": "0 22 * * *", "label": {"en": "Daily at 22:00", "fr": "Quotidien à 22:00"}},
|
|
{"value": "0 10 * * 1", "label": {"en": "Weekly Monday 10:00", "fr": "Hebdomadaire lundi 10:00"}}
|
|
]})
|
|
template: str = Field(description="JSON template with placeholders (format: {{KEY:PLACEHOLDER_NAME}})", json_schema_extra={"frontend_type": "textarea", "frontend_required": True})
|
|
placeholders: Dict[str, str] = Field(default_factory=dict, description="Dictionary of placeholder key/value pairs (e.g., {'connectionName': 'MyConnection', 'sharepointFolderNameSource': '/folder/path', 'webResearchUrl': 'https://...', 'webResearchPrompt': '...', 'documentPrompt': '...'})", json_schema_extra={"frontend_type": "textarea"})
|
|
active: bool = Field(default=False, description="Whether automation should be launched in event handler", json_schema_extra={"frontend_type": "checkbox", "frontend_required": False})
|
|
eventId: Optional[str] = Field(None, description="Event ID from event management (None if not registered)", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
|
|
status: Optional[str] = Field(None, description="Status: 'active' if event is registered, 'inactive' if not (computed, readonly)", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
|
|
executionLogs: List[Dict[str, Any]] = Field(default_factory=list, description="List of execution logs, each containing timestamp, workflowId, status, and messages", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
|
|
allowedProviders: List[str] = Field(default_factory=list, description="List of allowed AICore providers (e.g., 'anthropic', 'openai'). Empty means all RBAC-permitted providers are allowed.", json_schema_extra={"frontend_type": "multiselect", "frontend_readonly": False, "frontend_required": False})
|
|
|
|
|
|
registerModelLabels(
|
|
"AutomationDefinition",
|
|
{"en": "Automation Definition", "ge": "Automatisierungs-Definition", "fr": "Définition d'automatisation"},
|
|
{
|
|
"id": {"en": "ID", "ge": "ID", "fr": "ID"},
|
|
"mandateId": {"en": "Mandate ID", "ge": "Mandanten-ID", "fr": "ID du mandat"},
|
|
"featureInstanceId": {"en": "Feature Instance ID", "ge": "Feature-Instanz-ID", "fr": "ID de l'instance de fonctionnalité"},
|
|
"label": {"en": "Label", "ge": "Bezeichnung", "fr": "Libellé"},
|
|
"schedule": {"en": "Schedule", "ge": "Zeitplan", "fr": "Planification"},
|
|
"template": {"en": "Template", "ge": "Vorlage", "fr": "Modèle"},
|
|
"placeholders": {"en": "Placeholders", "ge": "Platzhalter", "fr": "Espaces réservés"},
|
|
"active": {"en": "Active", "ge": "Aktiv", "fr": "Actif"},
|
|
"eventId": {"en": "Event ID", "ge": "Event-ID", "fr": "ID de l'événement"},
|
|
"status": {"en": "Status", "ge": "Status", "fr": "Statut"},
|
|
"executionLogs": {"en": "Execution Logs", "ge": "Ausführungsprotokolle", "fr": "Journaux d'exécution"},
|
|
"allowedProviders": {"en": "Allowed Providers", "ge": "Erlaubte Provider", "fr": "Fournisseurs autorisés"},
|
|
},
|
|
)
|
|
|
|
|
|
class AutomationTemplate(PowerOnModel):
|
|
"""Automation-Vorlage ohne scharfe Placeholder-Werte (DB-persistiert).
|
|
|
|
System-Templates (isSystem=True): Nur durch SysAdmin aenderbar. Alle User koennen lesen.
|
|
Instance-Templates (isSystem=False, featureInstanceId gesetzt): CRUD durch Instance-Admin/Editor.
|
|
"""
|
|
id: str = Field(
|
|
default_factory=lambda: str(uuid.uuid4()),
|
|
description="Primary key",
|
|
json_schema_extra={"frontend_type": "text", "frontend_readonly": True}
|
|
)
|
|
label: TextMultilingual = Field(
|
|
description="Template name (multilingual)",
|
|
json_schema_extra={"frontend_type": "multilingual", "frontend_required": True}
|
|
)
|
|
overview: Optional[TextMultilingual] = Field(
|
|
None,
|
|
description="Short description (multilingual)",
|
|
json_schema_extra={"frontend_type": "multilingual", "frontend_required": False}
|
|
)
|
|
template: str = Field(
|
|
description="JSON workflow structure with {{KEY:...}} placeholders",
|
|
json_schema_extra={"frontend_type": "textarea", "frontend_required": True}
|
|
)
|
|
isSystem: bool = Field(
|
|
default=False,
|
|
description="System template (only SysAdmin can modify, all users can read)",
|
|
json_schema_extra={"frontend_type": "checkbox", "frontend_readonly": True, "frontend_required": False}
|
|
)
|
|
featureInstanceId: Optional[str] = Field(
|
|
None,
|
|
description="Feature instance ID (null for system templates, set for instance-scoped templates)",
|
|
json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}
|
|
)
|
|
registerModelLabels(
|
|
"AutomationTemplate",
|
|
{"en": "Automation Template", "ge": "Automation-Vorlage", "fr": "Modèle d'automatisation"},
|
|
{
|
|
"id": {"en": "ID", "ge": "ID", "fr": "ID"},
|
|
"label": {"en": "Label", "ge": "Bezeichnung", "fr": "Libellé"},
|
|
"overview": {"en": "Overview", "ge": "Übersicht", "fr": "Aperçu"},
|
|
"template": {"en": "Template", "ge": "Vorlage", "fr": "Modèle"},
|
|
"isSystem": {"en": "System Template", "ge": "System-Vorlage", "fr": "Modèle système"},
|
|
"featureInstanceId": {"en": "Feature Instance", "ge": "Feature-Instanz", "fr": "Instance de fonctionnalité"},
|
|
},
|
|
)
|