Files
PecHub/backend/app/schemas/message.py
T
2026-06-04 20:54:49 +02:00

134 lines
4.1 KiB
Python

"""
Schemi Pydantic per Message, Attachment e operazioni correlate.
"""
import uuid
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, model_validator
from app.schemas.label import LabelResponse
# ─── Search match info ────────────────────────────────────────────────────────
class AttachmentMatchInfo(BaseModel):
"""Allegato in cui e' stato trovato il termine cercato."""
id: uuid.UUID
filename: str
model_config = {"from_attributes": True}
class SearchMatchInfo(BaseModel):
"""Indica dove e' stato trovato il termine di ricerca in un messaggio."""
in_subject: bool = False
in_body: bool = False
in_attachments: list[AttachmentMatchInfo] = []
model_config = {"from_attributes": True}
# ─── Attachment ───────────────────────────────────────────────────────────────
class AttachmentResponse(BaseModel):
id: uuid.UUID
message_id: uuid.UUID
filename: str
content_type: Optional[str] = None
size_bytes: Optional[int] = None
storage_path: str
checksum_sha256: Optional[str] = None
created_at: datetime
model_config = {"from_attributes": True}
class MessageResponse(BaseModel):
id: uuid.UUID
tenant_id: uuid.UUID
mailbox_id: uuid.UUID
message_id_header: Optional[str] = None
imap_uid: Optional[int] = None
imap_folder: str
direction: str
pec_type: str
state: str
subject: Optional[str] = None
from_address: Optional[str] = None
to_addresses: list[str] = []
cc_addresses: list[str] = []
sent_at: Optional[datetime] = None
received_at: Optional[datetime] = None
size_bytes: Optional[int] = None
body_text: Optional[str] = None
body_html: Optional[str] = None
has_attachments: bool = False
parent_message_id: Optional[uuid.UUID] = None
is_read: bool = False
is_starred: bool = False
is_archived: bool = False
archived_at: Optional[datetime] = None
is_trashed: bool = False
trashed_at: Optional[datetime] = None
is_pending_conservation: bool = False
pending_conservation_at: Optional[datetime] = None
is_conserved: bool = False
conserved_at: Optional[datetime] = None
raw_eml_path: Optional[str] = None
created_at: datetime
updated_at: datetime
labels: list[LabelResponse] = []
# Popolato solo nelle risposte di ricerca full-text
search_match: Optional[SearchMatchInfo] = None
@model_validator(mode="before")
@classmethod
def coerce_arrays(cls, data: object) -> object:
"""Normalizza i campi array a liste vuote se None (da ORM)."""
if hasattr(data, "__dict__"):
# ORM object
for field in ("to_addresses", "cc_addresses"):
val = getattr(data, field, None)
if val is None:
object.__setattr__(data, field, [])
elif isinstance(data, dict):
for field in ("to_addresses", "cc_addresses"):
if data.get(field) is None:
data[field] = []
return data
model_config = {"from_attributes": True}
class MessageListResponse(BaseModel):
items: list[MessageResponse]
total: int
page: int
page_size: int
class MessageUpdateRequest(BaseModel):
is_read: Optional[bool] = None
is_starred: Optional[bool] = None
is_archived: Optional[bool] = None
is_trashed: Optional[bool] = None
is_pending_conservation: Optional[bool] = None
is_conserved: Optional[bool] = None
class MessageBulkUpdateRequest(BaseModel):
ids: list[uuid.UUID]
is_read: Optional[bool] = None
is_starred: Optional[bool] = None
is_archived: Optional[bool] = None
is_trashed: Optional[bool] = None
is_pending_conservation: Optional[bool] = None
is_conserved: Optional[bool] = None
class MessageBulkUpdateResponse(BaseModel):
updated: int
items: list[MessageResponse]