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
+56
View File
@@ -0,0 +1,56 @@
"""
Schemi Pydantic per PecContact (Feature 6 Rubrica indirizzi PEC).
"""
import uuid
from datetime import datetime
from pydantic import BaseModel, EmailStr, field_validator
class PecContactCreate(BaseModel):
email: EmailStr
name: str | None = None
organization: str | None = None
notes: str | None = None
is_favorite: bool = False
@field_validator("email")
@classmethod
def email_lowercase(cls, v: str) -> str:
return v.lower().strip()
class PecContactUpdate(BaseModel):
name: str | None = None
organization: str | None = None
notes: str | None = None
is_favorite: bool | None = None
class PecContactResponse(BaseModel):
model_config = {"from_attributes": True}
id: uuid.UUID
tenant_id: uuid.UUID
email: str
name: str | None = None
organization: str | None = None
notes: str | None = None
is_favorite: bool
auto_saved: bool
created_by: uuid.UUID | None = None
created_at: datetime
updated_at: datetime
class PecContactListResponse(BaseModel):
items: list[PecContactResponse]
total: int
class PecContactImportResult(BaseModel):
created: int
updated: int
skipped: int
errors: list[str] = []
+113
View File
@@ -0,0 +1,113 @@
"""
Schemi Pydantic per RoutingRule (Feature 2 Regole di smistamento automatico).
"""
import uuid
from datetime import datetime
from typing import Literal
from pydantic import BaseModel, field_validator
# Valori validi per field nelle condizioni
CONDITION_FIELDS = Literal[
"from_address", "to_address", "subject", "mailbox_id", "pec_type"
]
# Operatori supportati
CONDITION_OPERATORS = Literal[
"contains", "equals", "starts_with", "ends_with", "regex", "not_contains"
]
# Tipi di azione
ACTION_TYPES = Literal[
"apply_label", "assign_vbox", "mark_read", "mark_starred", "notify_webhook"
]
class RoutingRuleConditionCreate(BaseModel):
field: CONDITION_FIELDS
operator: CONDITION_OPERATORS = "contains"
value: str
@field_validator("value")
@classmethod
def value_not_empty(cls, v: str) -> str:
if not v.strip():
raise ValueError("Il valore della condizione non puo' essere vuoto")
return v.strip()
class RoutingRuleActionCreate(BaseModel):
action_type: ACTION_TYPES
action_value: str | None = None
class RoutingRuleCreate(BaseModel):
name: str
description: str | None = None
is_active: bool = True
priority: int = 100
stop_processing: bool = True
conditions: list[RoutingRuleConditionCreate] = []
actions: list[RoutingRuleActionCreate] = []
@field_validator("name")
@classmethod
def name_not_empty(cls, v: str) -> str:
if not v.strip():
raise ValueError("Il nome della regola non puo' essere vuoto")
return v.strip()
@field_validator("priority")
@classmethod
def priority_positive(cls, v: int) -> int:
if v < 1:
raise ValueError("La priorita' deve essere >= 1")
return v
class RoutingRuleUpdate(BaseModel):
name: str | None = None
description: str | None = None
is_active: bool | None = None
priority: int | None = None
stop_processing: bool | None = None
conditions: list[RoutingRuleConditionCreate] | None = None
actions: list[RoutingRuleActionCreate] | None = None
class RoutingRuleConditionResponse(BaseModel):
model_config = {"from_attributes": True}
id: uuid.UUID
field: str
operator: str
value: str
class RoutingRuleActionResponse(BaseModel):
model_config = {"from_attributes": True}
id: uuid.UUID
action_type: str
action_value: str | None = None
class RoutingRuleResponse(BaseModel):
model_config = {"from_attributes": True}
id: uuid.UUID
tenant_id: uuid.UUID
name: str
description: str | None = None
is_active: bool
priority: int
stop_processing: bool
conditions: list[RoutingRuleConditionResponse] = []
actions: list[RoutingRuleActionResponse] = []
created_by: uuid.UUID | None = None
created_at: datetime
updated_at: datetime
class RoutingRuleListResponse(BaseModel):
items: list[RoutingRuleResponse]
total: int
+4
View File
@@ -37,6 +37,9 @@ class SendPecRequest(BaseModel):
reply_to_message_id: uuid.UUID | None = None
"""UUID del messaggio a cui si risponde (per threading, opzionale)."""
scheduled_at: datetime | None = None
"""Data/ora di invio differito (Feature 5). Se None, invio immediato."""
@field_validator("to_addresses")
@classmethod
def at_least_one_recipient(cls, v: list[EmailStr]) -> list[EmailStr]:
@@ -67,6 +70,7 @@ class SendJobResponse(BaseModel):
max_attempts: int
next_retry_at: datetime | None = None
last_error: str | None = None
scheduled_at: datetime | None = None
queued_at: datetime
sent_at: datetime | None = None
created_by: uuid.UUID | None = None
+51
View File
@@ -0,0 +1,51 @@
"""
Schemi Pydantic per MessageTemplate (Feature 1 Template messaggi).
"""
import uuid
from datetime import datetime
from pydantic import BaseModel, field_validator
class TemplateCreate(BaseModel):
name: str
description: str | None = None
subject: str = ""
body_text: str | None = None
body_html: str | None = None
@field_validator("name")
@classmethod
def name_not_empty(cls, v: str) -> str:
if not v.strip():
raise ValueError("Il nome del template non puo' essere vuoto")
return v.strip()
class TemplateUpdate(BaseModel):
name: str | None = None
description: str | None = None
subject: str | None = None
body_text: str | None = None
body_html: str | None = None
class TemplateResponse(BaseModel):
model_config = {"from_attributes": True}
id: uuid.UUID
tenant_id: uuid.UUID
name: str
description: str | None = None
subject: str
body_text: str | None = None
body_html: str | None = None
created_by: uuid.UUID | None = None
created_at: datetime
updated_at: datetime
class TemplateListResponse(BaseModel):
items: list[TemplateResponse]
total: int