Files
PecHub/backend/app/models/virtual_box.py
T
2026-03-19 11:41:10 +01:00

183 lines
5.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Modelli Virtual Box filtri nominati assegnabili agli utenti.
Struttura:
VirtualBox → definisce il filtro (nome, label, mailbox scope)
VirtualBoxRule → singola regola (field + pattern) dentro una VBox
VirtualBoxAssignment → assegnazione VBox → User
Logica:
- Le regole nella stessa VBox si combinano in AND.
- Più VBox assegnate allo stesso utente si uniscono in OR.
- Il filtro si applica automaticamente a inbox e ricerca.
"""
import uuid
from datetime import datetime
from sqlalchemy import (
Boolean,
Column,
DateTime,
ForeignKey,
Index,
String,
Table,
Text,
UniqueConstraint,
func,
)
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database import Base
# ─── Tabella di associazione VirtualBox ↔ Mailbox ─────────────────────────────
virtual_box_mailboxes = Table(
"virtual_box_mailboxes",
Base.metadata,
Column(
"virtual_box_id",
UUID(as_uuid=True),
ForeignKey("virtual_boxes.id", ondelete="CASCADE"),
primary_key=True,
),
Column(
"mailbox_id",
UUID(as_uuid=True),
ForeignKey("mailboxes.id", ondelete="CASCADE"),
primary_key=True,
),
)
class VirtualBox(Base):
"""Casella virtuale: contenitore di regole + etichetta opzionale."""
__tablename__ = "virtual_boxes"
id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
)
tenant_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False
)
name: Mapped[str] = mapped_column(String(255), nullable=False)
description: Mapped[str | None] = mapped_column(Text, nullable=True)
label: Mapped[str | None] = mapped_column(String(100), nullable=True)
is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
created_by: Mapped[uuid.UUID | None] = mapped_column(
UUID(as_uuid=True), ForeignKey("users.id"), nullable=True
)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now()
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now()
)
# Relazioni
rules: Mapped[list["VirtualBoxRule"]] = relationship(
"VirtualBoxRule", back_populates="virtual_box", cascade="all, delete-orphan",
order_by="VirtualBoxRule.created_at"
)
assignments: Mapped[list["VirtualBoxAssignment"]] = relationship(
"VirtualBoxAssignment", back_populates="virtual_box", cascade="all, delete-orphan"
)
mailboxes: Mapped[list["Mailbox"]] = relationship( # noqa: F821
"Mailbox",
secondary=virtual_box_mailboxes,
lazy="selectin",
)
__table_args__ = (
UniqueConstraint("tenant_id", "name", name="uq_vbox_name_tenant"),
Index("idx_vbox_tenant", "tenant_id"),
)
def __repr__(self) -> str:
return f"<VirtualBox {self.name!r}>"
class VirtualBoxRule(Base):
"""
Singola regola di filtro all'interno di una VBox.
field : mailbox_id | imap_folder | subject | from_address | to_address
operator : contains | equals | starts_with | ends_with | regex
value : valore da confrontare
Può anche specificare un date_from / date_to per filtrare per periodo.
"""
__tablename__ = "virtual_box_rules"
id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
)
virtual_box_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True),
ForeignKey("virtual_boxes.id", ondelete="CASCADE"),
nullable=False,
)
field: Mapped[str] = mapped_column(String(50), nullable=False)
operator: Mapped[str] = mapped_column(String(20), nullable=False, default="contains")
value: Mapped[str] = mapped_column(Text, nullable=False)
date_from: Mapped[str | None] = mapped_column(String(20), nullable=True) # ISO date
date_to: Mapped[str | None] = mapped_column(String(20), nullable=True) # ISO date
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now()
)
# Relazioni
virtual_box: Mapped["VirtualBox"] = relationship(
"VirtualBox", back_populates="rules"
)
__table_args__ = (
Index("idx_vbox_rule_vbox", "virtual_box_id"),
)
def __repr__(self) -> str:
return f"<VirtualBoxRule {self.field!r} {self.operator!r} {self.value!r}>"
class VirtualBoxAssignment(Base):
"""Assegnazione di una VBox a un utente."""
__tablename__ = "virtual_box_assignments"
id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
)
virtual_box_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True),
ForeignKey("virtual_boxes.id", ondelete="CASCADE"),
nullable=False,
)
user_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), nullable=False
)
assigned_by: Mapped[uuid.UUID | None] = mapped_column(
UUID(as_uuid=True), ForeignKey("users.id"), nullable=True
)
assigned_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now()
)
# Relazioni
virtual_box: Mapped["VirtualBox"] = relationship(
"VirtualBox", back_populates="assignments"
)
__table_args__ = (
UniqueConstraint("virtual_box_id", "user_id", name="uq_vbox_assignment"),
Index("idx_vbox_assign_user", "user_id"),
Index("idx_vbox_assign_vbox", "virtual_box_id"),
)
def __repr__(self) -> str:
return f"<VirtualBoxAssignment vbox={self.virtual_box_id} user={self.user_id}>"