gateway/modules/features/trustee/accounting/accountingRegistry.py
2026-04-26 08:31:35 +02:00

83 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
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"] = f.label
fields.append(fd)
result.append({
"connectorType": connectorType,
"label": 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