- Rule tester (POST /api/filters/test): evaluates an unsaved rule
read-only against the newest matching mails in the source folder,
using an IMAP SEARCH on the first text condition to narrow candidates.
Shows matches, near misses with the failing condition, and whether
any candidate had an attachment at all (would have caught the
Klarmobil and it-moench misconfigurations immediately).
- Per-rule match_count / last_match_at (new migration), incremented by
the scheduler on every match and shown in the list.
- PATCH /api/filters/{id}/enabled toggles a rule without resetting its
processed state (a full PUT re-evaluates every mail for that rule).
- New filter list: cards with enable switch, priority, condition and
action chips, match stats, and warnings (display name in from/to,
"am (exakt)" date, duplicates, empty values, invalid regex, move
without folder). Quick filters for active/inactive/warnings/never
matched; copy-as-template; remembered account selection.
- Edit dialog: wider layout, live warnings, integrated test panel,
sensible defaults for new rules (from + date after Jan 1 + move).
- Light global refresh: nav with active item, page background, cards.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
155 lines
3.4 KiB
Python
155 lines
3.4 KiB
Python
from datetime import datetime
|
|
|
|
from pydantic import BaseModel
|
|
|
|
from app.models.db_models import ActionType, ConditionField, MatchType
|
|
|
|
|
|
# --- Filter Condition ---
|
|
|
|
|
|
class FilterConditionCreate(BaseModel):
|
|
field: ConditionField
|
|
match_type: MatchType
|
|
value: str
|
|
negate: bool = False
|
|
|
|
|
|
class FilterConditionResponse(FilterConditionCreate):
|
|
id: int
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
# --- Filter Action ---
|
|
|
|
|
|
class FilterActionCreate(BaseModel):
|
|
action_type: ActionType
|
|
parameter: str | None = None
|
|
|
|
|
|
class FilterActionResponse(FilterActionCreate):
|
|
id: int
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
# --- Filter Rule ---
|
|
|
|
|
|
class FilterRuleCreate(BaseModel):
|
|
name: str
|
|
account_id: int
|
|
priority: int | None = None # None → automatisch ans Ende einsortieren (max+10)
|
|
enabled: bool = True
|
|
stop_processing: bool = False
|
|
source_folder: str = "INBOX"
|
|
conditions: list[FilterConditionCreate] = []
|
|
actions: list[FilterActionCreate] = []
|
|
|
|
|
|
class FilterRuleUpdate(BaseModel):
|
|
name: str | None = None
|
|
priority: int | None = None
|
|
enabled: bool | None = None
|
|
stop_processing: bool | None = None
|
|
source_folder: str | None = None
|
|
conditions: list[FilterConditionCreate] | None = None
|
|
actions: list[FilterActionCreate] | None = None
|
|
|
|
|
|
class FilterRuleResponse(BaseModel):
|
|
id: int
|
|
account_id: int
|
|
name: str
|
|
priority: int
|
|
enabled: bool
|
|
stop_processing: bool
|
|
source_folder: str
|
|
match_count: int = 0
|
|
last_match_at: datetime | None = None
|
|
conditions: list[FilterConditionResponse]
|
|
actions: list[FilterActionResponse]
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class FilterRuleEnabled(BaseModel):
|
|
enabled: bool
|
|
|
|
|
|
class FilterRuleTest(BaseModel):
|
|
account_id: int
|
|
source_folder: str = "INBOX"
|
|
conditions: list[FilterConditionCreate]
|
|
limit: int = 40
|
|
|
|
|
|
# --- Account ---
|
|
|
|
|
|
class AccountCreate(BaseModel):
|
|
name: str
|
|
imap_host: str
|
|
imap_port: int = 993
|
|
use_ssl: bool = True
|
|
username: str
|
|
password: str
|
|
smtp_host: str | None = None
|
|
smtp_port: int | None = None
|
|
smtp_username: str | None = None
|
|
smtp_password: str | None = None
|
|
poll_interval_seconds: int = 120
|
|
enabled: bool = True
|
|
|
|
|
|
class AccountUpdate(BaseModel):
|
|
name: str | None = None
|
|
imap_host: str | None = None
|
|
imap_port: int | None = None
|
|
use_ssl: bool | None = None
|
|
username: str | None = None
|
|
password: str | None = None
|
|
smtp_host: str | None = None
|
|
smtp_port: int | None = None
|
|
smtp_username: str | None = None
|
|
smtp_password: str | None = None
|
|
poll_interval_seconds: int | None = None
|
|
enabled: bool | None = None
|
|
|
|
|
|
class AccountResponse(BaseModel):
|
|
id: int
|
|
name: str
|
|
imap_host: str
|
|
imap_port: int
|
|
use_ssl: bool
|
|
username: str
|
|
smtp_host: str | None
|
|
smtp_port: int | None
|
|
smtp_username: str | None
|
|
poll_interval_seconds: int
|
|
enabled: bool
|
|
last_poll_at: datetime | None
|
|
filter_rules: list[FilterRuleResponse] = []
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class AccountListResponse(BaseModel):
|
|
id: int
|
|
name: str
|
|
imap_host: str
|
|
username: str
|
|
enabled: bool
|
|
last_poll_at: datetime | None
|
|
poll_interval_seconds: int
|
|
filter_rule_count: int = 0
|
|
|
|
model_config = {"from_attributes": True}
|