gateway/modules/features/trustee/accounting/accountingRegistry.py
2026-04-11 22:23:41 +02:00

84 lines
3.2 KiB
Python

# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""Plugin-discovery registry for accounting connectors (analogous to aicoreModelRegistry)."""
import glob
import importlib
import inspect
import logging
import os
from typing import Dict, List, Optional
from .accountingConnectorBase import BaseAccountingConnector
from modules.shared.i18nRegistry import resolveText
logger = logging.getLogger(__name__)
class AccountingRegistry:
"""Discovers and manages accounting connector plugins."""
_connectors: Dict[str, BaseAccountingConnector] = {}
_discovered: bool = False
def discoverConnectors(self) -> int:
"""Scan connectors/ for accountingConnector*.py files and register them."""
if self._discovered:
return len(self._connectors)
connectorsDir = os.path.join(os.path.dirname(__file__), "connectors")
pattern = os.path.join(connectorsDir, "accountingConnector*.py")
for filePath in glob.glob(pattern):
moduleName = os.path.splitext(os.path.basename(filePath))[0]
fullModuleName = f"modules.features.trustee.accounting.connectors.{moduleName}"
try:
module = importlib.import_module(fullModuleName)
for _name, obj in inspect.getmembers(module, inspect.isclass):
if issubclass(obj, BaseAccountingConnector) and obj is not BaseAccountingConnector:
instance = obj()
connectorType = instance.getConnectorType()
self._connectors[connectorType] = instance
logger.info(f"Registered accounting connector: {connectorType}")
except Exception as e:
logger.error(f"Failed to load accounting connector from {moduleName}: {e}")
self._discovered = True
logger.info(f"Discovered {len(self._connectors)} accounting connector(s)")
return len(self._connectors)
def getConnector(self, connectorType: str) -> Optional[BaseAccountingConnector]:
"""Return the connector instance for the given type."""
if not self._discovered:
self.discoverConnectors()
return self._connectors.get(connectorType)
def getAvailableConnectors(self) -> List[Dict]:
"""Return metadata for all available connectors (for frontend dropdown)."""
if not self._discovered:
self.discoverConnectors()
result = []
for connectorType, connector in self._connectors.items():
fields = []
for f in connector.getRequiredConfigFields():
fd = f.model_dump()
fd["label"] = resolveText(f.label)
fields.append(fd)
result.append({
"connectorType": connectorType,
"label": resolveText(connector.getConnectorLabel()),
"configFields": fields,
})
return result
_registryInstance: Optional[AccountingRegistry] = None
def _getAccountingRegistry() -> AccountingRegistry:
"""Singleton access to the accounting registry."""
global _registryInstance
if _registryInstance is None:
_registryInstance = AccountingRegistry()
_registryInstance.discoverConnectors()
return _registryInstance