291 lines
8.9 KiB
Python
291 lines
8.9 KiB
Python
|
|
# ================================================================
|
||
|
|
# services/notification/app/schemas/notifications.py
|
||
|
|
# ================================================================
|
||
|
|
"""
|
||
|
|
Notification schemas for API validation and serialization
|
||
|
|
"""
|
||
|
|
|
||
|
|
from pydantic import BaseModel, EmailStr, Field, validator
|
||
|
|
from typing import Optional, Dict, Any, List
|
||
|
|
from datetime import datetime
|
||
|
|
from enum import Enum
|
||
|
|
|
||
|
|
# Reuse enums from models
|
||
|
|
class NotificationType(str, Enum):
|
||
|
|
EMAIL = "email"
|
||
|
|
WHATSAPP = "whatsapp"
|
||
|
|
PUSH = "push"
|
||
|
|
SMS = "sms"
|
||
|
|
|
||
|
|
class NotificationStatus(str, Enum):
|
||
|
|
PENDING = "pending"
|
||
|
|
SENT = "sent"
|
||
|
|
DELIVERED = "delivered"
|
||
|
|
FAILED = "failed"
|
||
|
|
CANCELLED = "cancelled"
|
||
|
|
|
||
|
|
class NotificationPriority(str, Enum):
|
||
|
|
LOW = "low"
|
||
|
|
NORMAL = "normal"
|
||
|
|
HIGH = "high"
|
||
|
|
URGENT = "urgent"
|
||
|
|
|
||
|
|
# ================================================================
|
||
|
|
# REQUEST SCHEMAS
|
||
|
|
# ================================================================
|
||
|
|
|
||
|
|
class NotificationCreate(BaseModel):
|
||
|
|
"""Schema for creating a new notification"""
|
||
|
|
type: NotificationType
|
||
|
|
recipient_id: Optional[str] = None # For individual notifications
|
||
|
|
recipient_email: Optional[EmailStr] = None
|
||
|
|
recipient_phone: Optional[str] = None
|
||
|
|
|
||
|
|
# Content
|
||
|
|
subject: Optional[str] = None
|
||
|
|
message: str = Field(..., min_length=1, max_length=5000)
|
||
|
|
html_content: Optional[str] = None
|
||
|
|
|
||
|
|
# Template-based content
|
||
|
|
template_id: Optional[str] = None
|
||
|
|
template_data: Optional[Dict[str, Any]] = None
|
||
|
|
|
||
|
|
# Configuration
|
||
|
|
priority: NotificationPriority = NotificationPriority.NORMAL
|
||
|
|
scheduled_at: Optional[datetime] = None
|
||
|
|
broadcast: bool = False
|
||
|
|
|
||
|
|
# Internal fields (set by service)
|
||
|
|
tenant_id: Optional[str] = None
|
||
|
|
sender_id: Optional[str] = None
|
||
|
|
|
||
|
|
@validator('recipient_phone')
|
||
|
|
def validate_phone(cls, v):
|
||
|
|
"""Validate Spanish phone number format"""
|
||
|
|
if v and not v.startswith(('+34', '6', '7', '9')):
|
||
|
|
raise ValueError('Invalid Spanish phone number format')
|
||
|
|
return v
|
||
|
|
|
||
|
|
@validator('scheduled_at')
|
||
|
|
def validate_scheduled_at(cls, v):
|
||
|
|
"""Ensure scheduled time is in the future"""
|
||
|
|
if v and v <= datetime.utcnow():
|
||
|
|
raise ValueError('Scheduled time must be in the future')
|
||
|
|
return v
|
||
|
|
|
||
|
|
class NotificationUpdate(BaseModel):
|
||
|
|
"""Schema for updating notification status"""
|
||
|
|
status: Optional[NotificationStatus] = None
|
||
|
|
error_message: Optional[str] = None
|
||
|
|
delivered_at: Optional[datetime] = None
|
||
|
|
read: Optional[bool] = None
|
||
|
|
read_at: Optional[datetime] = None
|
||
|
|
|
||
|
|
class BulkNotificationCreate(BaseModel):
|
||
|
|
"""Schema for creating bulk notifications"""
|
||
|
|
type: NotificationType
|
||
|
|
recipients: List[str] = Field(..., min_items=1, max_items=1000) # User IDs or emails
|
||
|
|
|
||
|
|
# Content
|
||
|
|
subject: Optional[str] = None
|
||
|
|
message: str = Field(..., min_length=1, max_length=5000)
|
||
|
|
html_content: Optional[str] = None
|
||
|
|
|
||
|
|
# Template-based content
|
||
|
|
template_id: Optional[str] = None
|
||
|
|
template_data: Optional[Dict[str, Any]] = None
|
||
|
|
|
||
|
|
# Configuration
|
||
|
|
priority: NotificationPriority = NotificationPriority.NORMAL
|
||
|
|
scheduled_at: Optional[datetime] = None
|
||
|
|
|
||
|
|
# ================================================================
|
||
|
|
# RESPONSE SCHEMAS
|
||
|
|
# ================================================================
|
||
|
|
|
||
|
|
class NotificationResponse(BaseModel):
|
||
|
|
"""Schema for notification response"""
|
||
|
|
id: str
|
||
|
|
tenant_id: str
|
||
|
|
sender_id: str
|
||
|
|
recipient_id: Optional[str]
|
||
|
|
|
||
|
|
type: NotificationType
|
||
|
|
status: NotificationStatus
|
||
|
|
priority: NotificationPriority
|
||
|
|
|
||
|
|
subject: Optional[str]
|
||
|
|
message: str
|
||
|
|
recipient_email: Optional[str]
|
||
|
|
recipient_phone: Optional[str]
|
||
|
|
|
||
|
|
scheduled_at: Optional[datetime]
|
||
|
|
sent_at: Optional[datetime]
|
||
|
|
delivered_at: Optional[datetime]
|
||
|
|
|
||
|
|
broadcast: bool
|
||
|
|
read: bool
|
||
|
|
read_at: Optional[datetime]
|
||
|
|
|
||
|
|
retry_count: int
|
||
|
|
error_message: Optional[str]
|
||
|
|
|
||
|
|
created_at: datetime
|
||
|
|
updated_at: datetime
|
||
|
|
|
||
|
|
class Config:
|
||
|
|
from_attributes = True
|
||
|
|
|
||
|
|
class NotificationHistory(BaseModel):
|
||
|
|
"""Schema for notification history"""
|
||
|
|
notifications: List[NotificationResponse]
|
||
|
|
total: int
|
||
|
|
page: int
|
||
|
|
per_page: int
|
||
|
|
has_next: bool
|
||
|
|
has_prev: bool
|
||
|
|
|
||
|
|
class NotificationStats(BaseModel):
|
||
|
|
"""Schema for notification statistics"""
|
||
|
|
total_sent: int
|
||
|
|
total_delivered: int
|
||
|
|
total_failed: int
|
||
|
|
delivery_rate: float
|
||
|
|
avg_delivery_time_minutes: Optional[float]
|
||
|
|
by_type: Dict[str, int]
|
||
|
|
by_status: Dict[str, int]
|
||
|
|
recent_activity: List[Dict[str, Any]]
|
||
|
|
|
||
|
|
# ================================================================
|
||
|
|
# PREFERENCE SCHEMAS
|
||
|
|
# ================================================================
|
||
|
|
|
||
|
|
class NotificationPreferences(BaseModel):
|
||
|
|
"""Schema for user notification preferences"""
|
||
|
|
user_id: str
|
||
|
|
tenant_id: str
|
||
|
|
|
||
|
|
# Email preferences
|
||
|
|
email_enabled: bool = True
|
||
|
|
email_alerts: bool = True
|
||
|
|
email_marketing: bool = False
|
||
|
|
email_reports: bool = True
|
||
|
|
|
||
|
|
# WhatsApp preferences
|
||
|
|
whatsapp_enabled: bool = False
|
||
|
|
whatsapp_alerts: bool = False
|
||
|
|
whatsapp_reports: bool = False
|
||
|
|
|
||
|
|
# Push notification preferences
|
||
|
|
push_enabled: bool = True
|
||
|
|
push_alerts: bool = True
|
||
|
|
push_reports: bool = False
|
||
|
|
|
||
|
|
# Timing preferences
|
||
|
|
quiet_hours_start: str = Field(default="22:00", pattern=r"^([01]?[0-9]|2[0-3]):[0-5][0-9]$")
|
||
|
|
quiet_hours_end: str = Field(default="08:00", pattern=r"^([01]?[0-9]|2[0-3]):[0-5][0-9]$")
|
||
|
|
timezone: str = "Europe/Madrid"
|
||
|
|
|
||
|
|
# Frequency preferences
|
||
|
|
digest_frequency: str = Field(default="daily", pattern=r"^(none|daily|weekly)$")
|
||
|
|
max_emails_per_day: int = Field(default=10, ge=1, le=100)
|
||
|
|
|
||
|
|
# Language preference
|
||
|
|
language: str = Field(default="es", pattern=r"^(es|en)$")
|
||
|
|
|
||
|
|
created_at: datetime
|
||
|
|
updated_at: datetime
|
||
|
|
|
||
|
|
class Config:
|
||
|
|
from_attributes = True
|
||
|
|
|
||
|
|
class PreferencesUpdate(BaseModel):
|
||
|
|
"""Schema for updating notification preferences"""
|
||
|
|
email_enabled: Optional[bool] = None
|
||
|
|
email_alerts: Optional[bool] = None
|
||
|
|
email_marketing: Optional[bool] = None
|
||
|
|
email_reports: Optional[bool] = None
|
||
|
|
|
||
|
|
whatsapp_enabled: Optional[bool] = None
|
||
|
|
whatsapp_alerts: Optional[bool] = None
|
||
|
|
whatsapp_reports: Optional[bool] = None
|
||
|
|
|
||
|
|
push_enabled: Optional[bool] = None
|
||
|
|
push_alerts: Optional[bool] = None
|
||
|
|
push_reports: Optional[bool] = None
|
||
|
|
|
||
|
|
quiet_hours_start: Optional[str] = Field(None, pattern=r"^([01]?[0-9]|2[0-3]):[0-5][0-9]$")
|
||
|
|
quiet_hours_end: Optional[str] = Field(None, pattern=r"^([01]?[0-9]|2[0-3]):[0-5][0-9]$")
|
||
|
|
timezone: Optional[str] = None
|
||
|
|
|
||
|
|
digest_frequency: Optional[str] = Field(None, pattern=r"^(none|daily|weekly)$")
|
||
|
|
max_emails_per_day: Optional[int] = Field(None, ge=1, le=100)
|
||
|
|
language: Optional[str] = Field(None, pattern=r"^(es|en)$")
|
||
|
|
|
||
|
|
# ================================================================
|
||
|
|
# TEMPLATE SCHEMAS
|
||
|
|
# ================================================================
|
||
|
|
|
||
|
|
class TemplateCreate(BaseModel):
|
||
|
|
"""Schema for creating notification templates"""
|
||
|
|
template_key: str = Field(..., min_length=3, max_length=100)
|
||
|
|
name: str = Field(..., min_length=3, max_length=255)
|
||
|
|
description: Optional[str] = None
|
||
|
|
category: str = Field(..., pattern=r"^(alert|marketing|transactional)$")
|
||
|
|
|
||
|
|
type: NotificationType
|
||
|
|
subject_template: Optional[str] = None
|
||
|
|
body_template: str = Field(..., min_length=10)
|
||
|
|
html_template: Optional[str] = None
|
||
|
|
|
||
|
|
language: str = Field(default="es", pattern=r"^(es|en)$")
|
||
|
|
default_priority: NotificationPriority = NotificationPriority.NORMAL
|
||
|
|
required_variables: Optional[List[str]] = None
|
||
|
|
|
||
|
|
class TemplateResponse(BaseModel):
|
||
|
|
"""Schema for template response"""
|
||
|
|
id: str
|
||
|
|
tenant_id: Optional[str]
|
||
|
|
template_key: str
|
||
|
|
name: str
|
||
|
|
description: Optional[str]
|
||
|
|
category: str
|
||
|
|
|
||
|
|
type: NotificationType
|
||
|
|
subject_template: Optional[str]
|
||
|
|
body_template: str
|
||
|
|
html_template: Optional[str]
|
||
|
|
|
||
|
|
language: str
|
||
|
|
is_active: bool
|
||
|
|
is_system: bool
|
||
|
|
default_priority: NotificationPriority
|
||
|
|
required_variables: Optional[List[str]]
|
||
|
|
|
||
|
|
created_at: datetime
|
||
|
|
updated_at: datetime
|
||
|
|
|
||
|
|
class Config:
|
||
|
|
from_attributes = True
|
||
|
|
|
||
|
|
# ================================================================
|
||
|
|
# WEBHOOK SCHEMAS
|
||
|
|
# ================================================================
|
||
|
|
|
||
|
|
class DeliveryWebhook(BaseModel):
|
||
|
|
"""Schema for delivery status webhooks"""
|
||
|
|
notification_id: str
|
||
|
|
status: NotificationStatus
|
||
|
|
provider: str
|
||
|
|
provider_message_id: Optional[str] = None
|
||
|
|
delivered_at: Optional[datetime] = None
|
||
|
|
error_code: Optional[str] = None
|
||
|
|
error_message: Optional[str] = None
|
||
|
|
metadata: Optional[Dict[str, Any]] = None
|
||
|
|
|
||
|
|
class ReadReceiptWebhook(BaseModel):
|
||
|
|
"""Schema for read receipt webhooks"""
|
||
|
|
notification_id: str
|
||
|
|
read_at: datetime
|
||
|
|
user_agent: Optional[str] = None
|
||
|
|
ip_address: Optional[str] = None
|