Files
multi-seal-mail/server/app/mailer/messages/models.py

143 lines
4.3 KiB
Python

from __future__ import annotations
from enum import StrEnum
from typing import Literal
from pydantic import BaseModel, ConfigDict, Field
from app.mailer.campaign.models import BuildStatus, SendStatus
class MessageValidationStatus(StrEnum):
READY = "ready"
WARNING = "warning"
NEEDS_REVIEW = "needs_review"
BLOCKED = "blocked"
EXCLUDED = "excluded"
INACTIVE = "inactive"
class ImapStatus(StrEnum):
NOT_REQUESTED = "not_requested"
PENDING = "pending"
APPENDED = "appended"
FAILED = "failed"
SKIPPED = "skipped"
class MessageIssue(BaseModel):
model_config = ConfigDict(extra="forbid")
severity: Literal["info", "warning", "error"]
code: str
message: str
behavior: str | None = None
source: str | None = None
class MessageAddress(BaseModel):
model_config = ConfigDict(extra="forbid")
email: str
name: str | None = None
class MessageAttachmentSummary(BaseModel):
model_config = ConfigDict(extra="forbid")
attachment_id: str | None = None
label: str | None = None
status: str
behavior: str | None = None
required: bool
allow_multiple: bool
zip_enabled: bool
base_path_name: str | None = None
base_path: str | None = None
file_filter: str
directory: str
matches: list[str] = Field(default_factory=list)
managed_matches: list[dict[str, object]] = Field(default_factory=list)
class MessageDraft(BaseModel):
model_config = ConfigDict(extra="forbid", populate_by_name=True)
entry_index: int
entry_id: str | None = None
active: bool
build_status: BuildStatus
validation_status: MessageValidationStatus
send_status: SendStatus
imap_status: ImapStatus
subject: str | None = None
from_: MessageAddress | None = Field(default=None, alias="from")
to: list[MessageAddress] = Field(default_factory=list)
cc: list[MessageAddress] = Field(default_factory=list)
bcc: list[MessageAddress] = Field(default_factory=list)
reply_to: list[MessageAddress] = Field(default_factory=list)
bounce_to: list[MessageAddress] = Field(default_factory=list)
disposition_notification_to: list[MessageAddress] = Field(default_factory=list)
attachment_count: int = 0
attachments: list[MessageAttachmentSummary] = Field(default_factory=list)
issues: list[MessageIssue] = Field(default_factory=list)
eml_path: str | None = None
eml_size_bytes: int | None = None
@property
def is_queueable(self) -> bool:
return self.active and self.build_status == BuildStatus.BUILT and self.validation_status in {
MessageValidationStatus.READY,
MessageValidationStatus.WARNING,
}
class CampaignBuildReport(BaseModel):
model_config = ConfigDict(extra="forbid")
campaign_id: str
campaign_name: str
campaign_file: str
entries_count: int
messages: list[MessageDraft] = Field(default_factory=list)
@property
def built_count(self) -> int:
return sum(1 for message in self.messages if message.build_status == BuildStatus.BUILT)
@property
def build_failed_count(self) -> int:
return sum(1 for message in self.messages if message.build_status == BuildStatus.BUILD_FAILED)
@property
def ready_count(self) -> int:
return sum(1 for message in self.messages if message.validation_status == MessageValidationStatus.READY)
@property
def warning_count(self) -> int:
return sum(1 for message in self.messages if message.validation_status == MessageValidationStatus.WARNING)
@property
def needs_review_count(self) -> int:
return sum(1 for message in self.messages if message.validation_status == MessageValidationStatus.NEEDS_REVIEW)
@property
def blocked_count(self) -> int:
return sum(1 for message in self.messages if message.validation_status == MessageValidationStatus.BLOCKED)
@property
def excluded_count(self) -> int:
return sum(1 for message in self.messages if message.validation_status == MessageValidationStatus.EXCLUDED)
@property
def inactive_count(self) -> int:
return sum(1 for message in self.messages if message.validation_status == MessageValidationStatus.INACTIVE)
@property
def queueable_count(self) -> int:
return sum(1 for message in self.messages if message.is_queueable)