"""Schemata für Benachrichtigungsregeln, Protokoll und Testversand.""" from datetime import date, datetime from pydantic import Field from app.models.enums import ( NotificationChannel, NotificationStatus, NotificationType, ) from app.schemas.common import ApiModel, InputModel TYPE_HINTS = { "due_soon": "Fälligkeiten innerhalb der Vorlaufzeit.", "notice_deadline": "Kündigungsfristen, die in 30, 14 oder 7 Tagen ablaufen.", "budget_exceeded": "Überschrittene Budgets, einmal je Monat und Kategorie.", "contract_renewal": "Verträge, die sich im kommenden Monat verlängern.", } class NotificationRuleCreate(InputModel): type: NotificationType = Field( description="; ".join(f"{k}: {v}" for k, v in TYPE_HINTS.items()) ) channel: NotificationChannel lead_days: int = Field( default=7, ge=0, le=365, description="Vorlauf in Tagen; nur für `due_soon` maßgeblich." ) target: str | None = Field( default=None, description="Mailadresse bzw. Apprise-URL. Ohne Angabe gelten SMTP_FROM " "beziehungsweise APPRISE_URLS.", ) is_active: bool = True class NotificationRuleUpdate(InputModel): lead_days: int | None = Field(default=None, ge=0, le=365) channel: NotificationChannel | None = None target: str | None = None is_active: bool | None = None class NotificationRuleOut(ApiModel): id: int type: NotificationType channel: NotificationChannel lead_days: int target: str | None is_active: bool created_at: datetime class NotificationLogOut(ApiModel): """Ein Eintrag des Versandprotokolls.""" id: int rule_id: int ref_type: str ref_id: str dedupe_day: date = Field( description="Zieltag des Ereignisses – Grundlage des Duplikatsschutzes." ) sent_at: datetime status: NotificationStatus error: str | None class ChannelStatusOut(ApiModel): """Einrichtungsstand eines Kanals.""" channel: NotificationChannel configured: bool detail: str class NotificationSettingsOut(ApiModel): """Überblick über Kanäle und Zeitplan.""" enabled: bool scheduler_enabled: bool run_at: str = Field(description="Uhrzeit des täglichen Laufs, etwa '07:00'.") timezone: str next_run_at: datetime | None channels: list[ChannelStatusOut] class TestSendRequest(InputModel): target: str | None = Field( default=None, description="Abweichendes Ziel; ohne Angabe gelten die Einstellungen." ) class TestResultOut(ApiModel): channel: NotificationChannel configured: bool sent: bool error: str | None = None class TestSendResponse(ApiModel): results: list[TestResultOut] any_sent: bool = Field(description="True, wenn mindestens ein Kanal zugestellt hat.") class RunResponse(ApiModel): """Ergebnis eines manuell ausgelösten Laufs.""" checked: int sent: int skipped: int = Field(description="Bereits gemeldete Ereignisse.") failed: int