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
+152
View File
@@ -0,0 +1,152 @@
"""
Router scadenzario e tracking deadlines (Feature 4).
Endpoint:
GET /deadlines messaggi con scadenze imminenti
POST /messages/{id}/deadline imposta/modifica/rimuove scadenza
"""
import uuid
from datetime import datetime, timedelta, timezone
from fastapi import APIRouter, Query
from pydantic import BaseModel
from sqlalchemy import and_, select
from app.dependencies import CurrentUser, DB
from app.models.message import Message
from app.schemas.message import MessageResponse
from app.core.exceptions import NotFoundError
router = APIRouter(tags=["Deadlines"])
class DeadlineSetRequest(BaseModel):
deadline_at: datetime | None = None
"""Imposta a null per rimuovere la scadenza."""
deadline_note: str | None = None
class DeadlineMessageResponse(BaseModel):
model_config = {"from_attributes": True}
id: uuid.UUID
subject: str | None
from_address: str | None
to_addresses: list[str] | None = None
direction: str
pec_type: str
state: str
mailbox_id: uuid.UUID
deadline_at: datetime | None = None
deadline_note: str | None = None
is_overdue: bool = False
received_at: datetime | None = None
sent_at: datetime | None = None
created_at: datetime
@router.get("/deadlines", response_model=list[DeadlineMessageResponse])
async def list_deadlines(
current_user: CurrentUser,
db: DB,
days_ahead: int = Query(30, ge=1, le=365, description="Giorni da considerare in avanti"),
include_overdue: bool = Query(True, description="Includi scadenze gia' passate"),
) -> list[DeadlineMessageResponse]:
"""
Restituisce i messaggi con scadenze nel range specificato.
Ordinati per: scaduti prima, poi per deadline_at ASC.
"""
now = datetime.now(timezone.utc)
future_limit = now + timedelta(days=days_ahead)
conditions = [
Message.tenant_id == current_user.tenant_id,
Message.deadline_at.is_not(None),
Message.is_trashed == False, # noqa: E712
]
if include_overdue:
# Include scaduti e futuri fino al limite
conditions.append(Message.deadline_at <= future_limit)
else:
# Solo scadenze future
conditions.append(and_(Message.deadline_at > now, Message.deadline_at <= future_limit))
result = await db.execute(
select(Message)
.where(and_(*conditions))
.order_by(Message.deadline_at)
.limit(200)
)
messages = list(result.scalars().all())
items = []
for msg in messages:
is_overdue = msg.deadline_at < now if msg.deadline_at else False
items.append(DeadlineMessageResponse(
id=msg.id,
subject=msg.subject,
from_address=msg.from_address,
to_addresses=msg.to_addresses,
direction=msg.direction,
pec_type=msg.pec_type,
state=msg.state,
mailbox_id=msg.mailbox_id,
deadline_at=msg.deadline_at,
deadline_note=msg.deadline_note,
is_overdue=is_overdue,
received_at=msg.received_at,
sent_at=msg.sent_at,
created_at=msg.created_at,
))
return items
@router.post("/messages/{message_id}/deadline", response_model=DeadlineMessageResponse)
async def set_deadline(
message_id: uuid.UUID,
data: DeadlineSetRequest,
current_user: CurrentUser,
db: DB,
) -> DeadlineMessageResponse:
"""
Imposta, modifica o rimuove la scadenza di un messaggio.
Passa deadline_at=null per rimuovere la scadenza.
"""
result = await db.execute(
select(Message).where(
Message.id == message_id,
Message.tenant_id == current_user.tenant_id,
)
)
msg = result.scalar_one_or_none()
if not msg:
raise NotFoundError(f"Messaggio {message_id} non trovato")
msg.deadline_at = data.deadline_at
msg.deadline_note = data.deadline_note
await db.commit()
await db.refresh(msg)
now = datetime.now(timezone.utc)
is_overdue = msg.deadline_at < now if msg.deadline_at else False
return DeadlineMessageResponse(
id=msg.id,
subject=msg.subject,
from_address=msg.from_address,
to_addresses=msg.to_addresses,
direction=msg.direction,
pec_type=msg.pec_type,
state=msg.state,
mailbox_id=msg.mailbox_id,
deadline_at=msg.deadline_at,
deadline_note=msg.deadline_note,
is_overdue=is_overdue,
received_at=msg.received_at,
sent_at=msg.sent_at,
created_at=msg.created_at,
)