# Copyright (c) 2025 Patrick Motsch # All rights reserved. """ Notification model for in-app notifications. Supports actionable notifications (e.g., invitation accept/decline). """ import uuid from typing import Optional, List from enum import Enum from pydantic import BaseModel, Field, ConfigDict from modules.shared.attributeUtils import registerModelLabels from modules.shared.timeUtils import getUtcTimestamp class NotificationType(str, Enum): """Types of notifications""" INVITATION = "invitation" # Einladung zu Mandat/Feature SYSTEM = "system" # System-Nachrichten WORKFLOW = "workflow" # Workflow-Status Updates MENTION = "mention" # Erwähnung in Chat/Kommentar class NotificationStatus(str, Enum): """Status of a notification""" UNREAD = "unread" # Noch nicht gelesen READ = "read" # Gelesen ACTIONED = "actioned" # Aktion wurde durchgeführt DISMISSED = "dismissed" # Verworfen/Geschlossen class NotificationAction(BaseModel): """Possible action for a notification""" actionId: str = Field( description="Unique identifier for the action (e.g., 'accept', 'decline')" ) label: str = Field( description="Display label for the action button" ) style: str = Field( default="default", description="Button style: 'primary', 'danger', 'default'" ) class UserNotification(BaseModel): """ In-app notification for a user. Supports actionable notifications with accept/decline buttons. """ id: str = Field( default_factory=lambda: str(uuid.uuid4()), description="Unique ID of the notification", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False} ) userId: str = Field( description="Target user ID for this notification", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": True} ) # Notification type and status type: NotificationType = Field( default=NotificationType.SYSTEM, description="Type of notification", json_schema_extra={ "frontend_type": "select", "frontend_readonly": True, "frontend_required": True, "frontend_options": [ {"value": "invitation", "label": {"en": "Invitation", "de": "Einladung"}}, {"value": "system", "label": {"en": "System", "de": "System"}}, {"value": "workflow", "label": {"en": "Workflow", "de": "Workflow"}}, {"value": "mention", "label": {"en": "Mention", "de": "Erwähnung"}} ] } ) status: NotificationStatus = Field( default=NotificationStatus.UNREAD, description="Current status of the notification", json_schema_extra={ "frontend_type": "select", "frontend_readonly": True, "frontend_required": False, "frontend_options": [ {"value": "unread", "label": {"en": "Unread", "de": "Ungelesen"}}, {"value": "read", "label": {"en": "Read", "de": "Gelesen"}}, {"value": "actioned", "label": {"en": "Actioned", "de": "Bearbeitet"}}, {"value": "dismissed", "label": {"en": "Dismissed", "de": "Verworfen"}} ] } ) # Content title: str = Field( description="Notification title", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": True} ) message: str = Field( description="Notification message/body", json_schema_extra={"frontend_type": "textarea", "frontend_readonly": True, "frontend_required": True} ) icon: Optional[str] = Field( default=None, description="Optional icon identifier (e.g., 'mail', 'warning', 'info')", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False} ) # Reference to triggering object (for actionable notifications) referenceType: Optional[str] = Field( default=None, description="Type of referenced object (e.g., 'Invitation', 'Workflow')", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False} ) referenceId: Optional[str] = Field( default=None, description="ID of referenced object", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False} ) # Actions (for actionable notifications like invitations) actions: Optional[List[NotificationAction]] = Field( default=None, description="List of possible actions for this notification", json_schema_extra={"frontend_type": "json", "frontend_readonly": True, "frontend_required": False} ) # Action result (when user takes action) actionTaken: Optional[str] = Field( default=None, description="Which action was taken (actionId)", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False} ) actionResult: Optional[str] = Field( default=None, description="Result message from the action", json_schema_extra={"frontend_type": "textarea", "frontend_readonly": True, "frontend_required": False} ) # Timestamps createdAt: float = Field( default_factory=getUtcTimestamp, description="When the notification was created (UTC timestamp)", json_schema_extra={"frontend_type": "timestamp", "frontend_readonly": True, "frontend_required": False} ) readAt: Optional[float] = Field( default=None, description="When the notification was read (UTC timestamp)", json_schema_extra={"frontend_type": "timestamp", "frontend_readonly": True, "frontend_required": False} ) actionedAt: Optional[float] = Field( default=None, description="When action was taken (UTC timestamp)", json_schema_extra={"frontend_type": "timestamp", "frontend_readonly": True, "frontend_required": False} ) expiresAt: Optional[float] = Field( default=None, description="When the notification expires (optional, UTC timestamp)", json_schema_extra={"frontend_type": "timestamp", "frontend_readonly": True, "frontend_required": False} ) model_config = ConfigDict(use_enum_values=True) registerModelLabels( "UserNotification", {"en": "Notification", "de": "Benachrichtigung", "fr": "Notification"}, { "id": {"en": "ID", "de": "ID", "fr": "ID"}, "userId": {"en": "User", "de": "Benutzer", "fr": "Utilisateur"}, "type": {"en": "Type", "de": "Typ", "fr": "Type"}, "status": {"en": "Status", "de": "Status", "fr": "Statut"}, "title": {"en": "Title", "de": "Titel", "fr": "Titre"}, "message": {"en": "Message", "de": "Nachricht", "fr": "Message"}, "icon": {"en": "Icon", "de": "Symbol", "fr": "Icône"}, "referenceType": {"en": "Reference Type", "de": "Referenz-Typ", "fr": "Type de référence"}, "referenceId": {"en": "Reference ID", "de": "Referenz-ID", "fr": "ID de référence"}, "actions": {"en": "Actions", "de": "Aktionen", "fr": "Actions"}, "actionTaken": {"en": "Action Taken", "de": "Durchgeführte Aktion", "fr": "Action effectuée"}, "actionResult": {"en": "Action Result", "de": "Aktions-Ergebnis", "fr": "Résultat de l'action"}, "createdAt": {"en": "Created At", "de": "Erstellt am", "fr": "Créé le"}, "readAt": {"en": "Read At", "de": "Gelesen am", "fr": "Lu le"}, "actionedAt": {"en": "Actioned At", "de": "Bearbeitet am", "fr": "Traité le"}, "expiresAt": {"en": "Expires At", "de": "Gültig bis", "fr": "Expire le"}, }, ) registerModelLabels( "NotificationType", {"en": "Notification Type", "de": "Benachrichtigungs-Typ", "fr": "Type de notification"}, { "invitation": {"en": "Invitation", "de": "Einladung", "fr": "Invitation"}, "system": {"en": "System", "de": "System", "fr": "Système"}, "workflow": {"en": "Workflow", "de": "Workflow", "fr": "Workflow"}, "mention": {"en": "Mention", "de": "Erwähnung", "fr": "Mention"}, }, ) registerModelLabels( "NotificationStatus", {"en": "Notification Status", "de": "Benachrichtigungs-Status", "fr": "Statut de notification"}, { "unread": {"en": "Unread", "de": "Ungelesen", "fr": "Non lu"}, "read": {"en": "Read", "de": "Gelesen", "fr": "Lu"}, "actioned": {"en": "Actioned", "de": "Bearbeitet", "fr": "Traité"}, "dismissed": {"en": "Dismissed", "de": "Verworfen", "fr": "Rejeté"}, }, )