from datetime import datetime, timezone from enum import Enum from sqlalchemy import Column, String, ForeignKey, Enum as SQLEnum, Text, DateTime, Integer from sqlalchemy.dialects.postgresql import UUID, JSONB from sqlalchemy.orm import relationship from .base import Base, TimestampMixin, UUIDMixin class NotificationType(str, Enum): EMAIL = "email" SMS = "sms" PUSH = "push" IN_APP = "in_app" class NotificationStatus(str, Enum): QUEUED = "queued" SENT = "sent" DELIVERED = "delivered" FAILED = "failed" OPENED = "opened" CLICKED = "clicked" class NotificationLog(Base, UUIDMixin, TimestampMixin): __tablename__ = 'notification_logs' # Notification Details notification_type = Column(SQLEnum(NotificationType), nullable=False) status = Column(SQLEnum(NotificationStatus), nullable=False, default=NotificationStatus.QUEUED) # Content subject = Column(String) content_preview = Column(String(255)) # Short preview of content template_id = Column(UUID(as_uuid=True), ForeignKey('email_templates.id')) # Recipient Information event_id = Column(UUID(as_uuid=True), ForeignKey('events.id'), nullable=False) guest_id = Column(UUID(as_uuid=True), ForeignKey('guests.id')) # Optional, if sent to a specific guest recipient = Column(String, nullable=False) # Email address or phone number # Tracking sent_at = Column(DateTime(timezone=True)) delivered_at = Column(DateTime(timezone=True)) opened_at = Column(DateTime(timezone=True)) error_message = Column(Text) retry_count = Column(Integer, default=0) external_id = Column(String) # ID from external provider (SendGrid, Twilio, etc.) # Additional Data notification_metadata = Column(JSONB, default=dict) # Relationships event = relationship("Event") guest = relationship("Guest") template = relationship("EmailTemplate") def __repr__(self): return f"" def mark_sent(self): """Mark notification as sent""" self.status = NotificationStatus.SENT self.sent_at = datetime.now(timezone.utc) def mark_delivered(self): """Mark notification as delivered""" self.status = NotificationStatus.DELIVERED self.delivered_at = datetime.now(timezone.utc) def mark_opened(self): """Mark notification as opened""" self.status = NotificationStatus.OPENED self.opened_at = datetime.now(timezone.utc) def mark_failed(self, error: str): """Mark notification as failed with error message""" self.status = NotificationStatus.FAILED self.error_message = error self.retry_count += 1 @property def can_retry(self): """Check if notification can be retried (fewer than 3 attempts)""" return self.status == NotificationStatus.FAILED and self.retry_count < 3