Implementazioni varie

This commit is contained in:
2026-03-27 20:59:06 +01:00
parent 047990811f
commit 46784aca4c
40 changed files with 4090 additions and 34 deletions
+114
View File
@@ -0,0 +1,114 @@
"""
Modelli RoutingRule, RoutingRuleCondition, RoutingRuleAction.
Le regole di smistamento automatico vengono valutate a ogni messaggio in arrivo:
1. Si caricano tutte le regole attive del tenant, ordinate per priority ASC
2. Per ogni regola si valutano le condizioni (AND tra le condizioni della stessa regola)
3. Se tutte le condizioni sono soddisfatte, si eseguono le azioni
4. Se stop_processing=True, si ferma l'elaborazione (non vengono valutate regole successive)
"""
import uuid
from datetime import datetime
from sqlalchemy import Boolean, DateTime, ForeignKey, Index, Integer, String, Text, func
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database import Base
class RoutingRule(Base):
__tablename__ = "routing_rules"
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)
is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
priority: Mapped[int] = mapped_column(Integer, nullable=False, default=100)
# Se True, le regole successive non vengono valutate una volta che questa fa match
stop_processing: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
created_by: Mapped[uuid.UUID | None] = mapped_column(
UUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), 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
conditions: Mapped[list["RoutingRuleCondition"]] = relationship(
"RoutingRuleCondition", back_populates="rule", cascade="all, delete-orphan"
)
actions: Mapped[list["RoutingRuleAction"]] = relationship(
"RoutingRuleAction", back_populates="rule", cascade="all, delete-orphan"
)
__table_args__ = (
Index("idx_routing_rules_tenant", "tenant_id"),
Index("idx_routing_rules_active", "tenant_id", "priority"),
)
def __repr__(self) -> str:
return f"<RoutingRule {self.name!r} priority={self.priority}>"
class RoutingRuleCondition(Base):
"""
Singola condizione di una regola.
field : from_address | to_address | subject | mailbox_id | pec_type
operator : contains | equals | starts_with | ends_with | regex | not_contains
value : valore da confrontare (stringa)
"""
__tablename__ = "routing_rule_conditions"
id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
)
rule_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), ForeignKey("routing_rules.id", ondelete="CASCADE"), nullable=False
)
field: Mapped[str] = mapped_column(String(50), nullable=False)
operator: Mapped[str] = mapped_column(String(30), nullable=False, default="contains")
value: Mapped[str] = mapped_column(Text, nullable=False)
rule: Mapped["RoutingRule"] = relationship("RoutingRule", back_populates="conditions")
__table_args__ = (
Index("idx_routing_conditions_rule", "rule_id"),
)
class RoutingRuleAction(Base):
"""
Singola azione di una regola.
action_type : apply_label | assign_vbox | mark_read | mark_starred | notify_webhook
action_value : UUID della label/vbox, o URL del webhook
"""
__tablename__ = "routing_rule_actions"
id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
)
rule_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), ForeignKey("routing_rules.id", ondelete="CASCADE"), nullable=False
)
action_type: Mapped[str] = mapped_column(String(50), nullable=False)
action_value: Mapped[str | None] = mapped_column(Text, nullable=True)
rule: Mapped["RoutingRule"] = relationship("RoutingRule", back_populates="actions")
__table_args__ = (
Index("idx_routing_actions_rule", "rule_id"),
)