199 lines
8.5 KiB
Python
199 lines
8.5 KiB
Python
# Copyright (c) 2025 Patrick Motsch
|
|
# All rights reserved.
|
|
"""
|
|
View models for the /api/attributes/ endpoint.
|
|
|
|
These extend base DB models with computed / enriched fields that the gateway
|
|
adds at response time (JOINs, aggregations, synthetics). They are NEVER used
|
|
for DB operations — only for ``getModelAttributeDefinitions()`` so the frontend
|
|
can resolve column types via ``resolveColumnTypes`` without hardcoding.
|
|
|
|
Naming convention: ``{BaseModel}View``.
|
|
|
|
``getModelClasses()`` in ``attributeUtils.py`` auto-discovers every
|
|
``datamodel*.py`` under ``modules/datamodels/`` — so placing them here is
|
|
sufficient for registration.
|
|
"""
|
|
|
|
from typing import Optional, List
|
|
from pydantic import Field
|
|
|
|
from modules.datamodels.datamodelBase import MODEL_REGISTRY, PowerOnModel
|
|
from modules.datamodels.datamodelMembership import UserMandate, FeatureAccess
|
|
from modules.datamodels.datamodelBilling import BillingTransaction
|
|
from modules.datamodels.datamodelSubscription import MandateSubscription
|
|
from modules.datamodels.datamodelUiLanguage import UiLanguageSet
|
|
from modules.features.neutralization.datamodelFeatureNeutralizer import DataNeutralizerAttributes
|
|
from modules.shared.i18nRegistry import i18nModel
|
|
|
|
|
|
# ============================================================================
|
|
# Punkt 1a: UserMandate + enriched user fields
|
|
# ============================================================================
|
|
|
|
@i18nModel("Benutzer-Mandant (Ansicht)")
|
|
class UserMandateView(UserMandate):
|
|
"""UserMandate erweitert um aufgeloeste Benutzerfelder und Rollenlabels."""
|
|
|
|
username: Optional[str] = Field(
|
|
default=None,
|
|
description="Username (resolved from userId)",
|
|
json_schema_extra={"label": "Benutzername", "frontend_type": "text", "frontend_readonly": True},
|
|
)
|
|
email: Optional[str] = Field(
|
|
default=None,
|
|
description="E-Mail address (resolved from userId)",
|
|
json_schema_extra={"label": "E-Mail", "frontend_type": "text", "frontend_readonly": True},
|
|
)
|
|
fullName: Optional[str] = Field(
|
|
default=None,
|
|
description="Full name (resolved from userId)",
|
|
json_schema_extra={"label": "Vollstaendiger Name", "frontend_type": "text", "frontend_readonly": True},
|
|
)
|
|
roleLabels: Optional[List[str]] = Field(
|
|
default=None,
|
|
description="Role labels (resolved from junction table)",
|
|
json_schema_extra={"label": "Rollen", "frontend_type": "text", "frontend_readonly": True},
|
|
)
|
|
|
|
|
|
# ============================================================================
|
|
# Punkt 1b: FeatureAccess + enriched user fields
|
|
# ============================================================================
|
|
|
|
@i18nModel("Feature-Zugang (Ansicht)")
|
|
class FeatureAccessView(FeatureAccess):
|
|
"""FeatureAccess erweitert um aufgeloeste Benutzerfelder und Rollenlabels."""
|
|
|
|
username: Optional[str] = Field(
|
|
default=None,
|
|
description="Username (resolved from userId)",
|
|
json_schema_extra={"label": "Benutzername", "frontend_type": "text", "frontend_readonly": True},
|
|
)
|
|
email: Optional[str] = Field(
|
|
default=None,
|
|
description="E-Mail address (resolved from userId)",
|
|
json_schema_extra={"label": "E-Mail", "frontend_type": "text", "frontend_readonly": True},
|
|
)
|
|
fullName: Optional[str] = Field(
|
|
default=None,
|
|
description="Full name (resolved from userId)",
|
|
json_schema_extra={"label": "Vollstaendiger Name", "frontend_type": "text", "frontend_readonly": True},
|
|
)
|
|
roleLabels: Optional[List[str]] = Field(
|
|
default=None,
|
|
description="Role labels (resolved from junction table)",
|
|
json_schema_extra={"label": "Rollen", "frontend_type": "text", "frontend_readonly": True},
|
|
)
|
|
|
|
|
|
# ============================================================================
|
|
# Punkt 1d: BillingTransaction + enriched mandate/user names
|
|
# ============================================================================
|
|
|
|
@i18nModel("Transaktion (Ansicht)")
|
|
class BillingTransactionView(BillingTransaction):
|
|
"""BillingTransaction erweitert um aufgeloeste Mandanten-/Benutzernamen."""
|
|
|
|
mandateName: Optional[str] = Field(
|
|
default=None,
|
|
description="Mandate name (resolved from accountId/mandateId)",
|
|
json_schema_extra={"label": "Mandant", "frontend_type": "text", "frontend_readonly": True},
|
|
)
|
|
userName: Optional[str] = Field(
|
|
default=None,
|
|
description="User name (resolved from createdByUserId)",
|
|
json_schema_extra={"label": "Benutzer", "frontend_type": "text", "frontend_readonly": True},
|
|
)
|
|
|
|
|
|
# ============================================================================
|
|
# Punkt 3a: MandateSubscription + aggregated fields
|
|
# ============================================================================
|
|
|
|
@i18nModel("Abonnement (Ansicht)")
|
|
class MandateSubscriptionView(MandateSubscription):
|
|
"""MandateSubscription erweitert um aggregierte Laufzeitwerte."""
|
|
|
|
mandateName: Optional[str] = Field(
|
|
default=None,
|
|
description="Mandate name (resolved from mandateId)",
|
|
json_schema_extra={"label": "Mandant", "frontend_type": "text", "frontend_readonly": True},
|
|
)
|
|
planTitle: Optional[str] = Field(
|
|
default=None,
|
|
description="Plan title (resolved from planKey)",
|
|
json_schema_extra={"label": "Plan", "frontend_type": "text", "frontend_readonly": True},
|
|
)
|
|
activeUsers: Optional[int] = Field(
|
|
default=None,
|
|
description="Number of active users in the mandate",
|
|
json_schema_extra={"label": "Benutzer", "frontend_type": "number", "frontend_readonly": True},
|
|
)
|
|
activeInstances: Optional[int] = Field(
|
|
default=None,
|
|
description="Number of active feature instances in the mandate",
|
|
json_schema_extra={"label": "Module", "frontend_type": "number", "frontend_readonly": True},
|
|
)
|
|
monthlyRevenueCHF: Optional[float] = Field(
|
|
default=None,
|
|
description="Calculated monthly revenue in CHF",
|
|
json_schema_extra={"label": "Umsatz pro Monat", "frontend_type": "number", "frontend_readonly": True},
|
|
)
|
|
|
|
|
|
# ============================================================================
|
|
# Punkt 3b: UiLanguageSet + computed counts
|
|
# ============================================================================
|
|
|
|
@i18nModel("Sprachset (Ansicht)")
|
|
class UiLanguageSetView(UiLanguageSet):
|
|
"""UiLanguageSet erweitert um berechnete Uebersetzungszaehler."""
|
|
|
|
uiCount: Optional[int] = Field(
|
|
default=None,
|
|
description="Number of UI translation entries",
|
|
json_schema_extra={"label": "UI", "frontend_type": "number", "frontend_readonly": True},
|
|
)
|
|
gatewayCount: Optional[int] = Field(
|
|
default=None,
|
|
description="Number of gateway/API translation entries",
|
|
json_schema_extra={"label": "API", "frontend_type": "number", "frontend_readonly": True},
|
|
)
|
|
entriesCount: Optional[int] = Field(
|
|
default=None,
|
|
description="Total number of translation entries",
|
|
json_schema_extra={"label": "Gesamt", "frontend_type": "number", "frontend_readonly": True},
|
|
)
|
|
|
|
|
|
# ============================================================================
|
|
# Punkt 1c: DataNeutralizerAttributes + enriched fields
|
|
#
|
|
# DataNeutralizerAttributes extends BaseModel (not PowerOnModel), so its
|
|
# subclass does NOT auto-register in MODEL_REGISTRY. We register manually.
|
|
# ============================================================================
|
|
|
|
@i18nModel("Neutralisierungs-Zuordnung (Ansicht)")
|
|
class DataNeutralizerAttributesView(DataNeutralizerAttributes):
|
|
"""DataNeutralizerAttributes erweitert um synthetische/aufgeloeste Felder."""
|
|
|
|
placeholder: Optional[str] = Field(
|
|
default=None,
|
|
description="Synthetic placeholder string [patternType.id]",
|
|
json_schema_extra={"label": "Platzhalter", "frontend_type": "text", "frontend_readonly": True},
|
|
)
|
|
username: Optional[str] = Field(
|
|
default=None,
|
|
description="Username (resolved from userId)",
|
|
json_schema_extra={"label": "Benutzer", "frontend_type": "text", "frontend_readonly": True},
|
|
)
|
|
instanceLabel: Optional[str] = Field(
|
|
default=None,
|
|
description="Feature instance label (resolved from featureInstanceId)",
|
|
json_schema_extra={"label": "Feature-Instanz", "frontend_type": "text", "frontend_readonly": True},
|
|
)
|
|
|
|
|
|
# Manual registration for non-PowerOnModel view
|
|
MODEL_REGISTRY["DataNeutralizerAttributesView"] = DataNeutralizerAttributesView # type: ignore[assignment]
|