Restructure data model: TrackingConfig + TemplateConfig entities
Some checks failed
Validate / Hassfest (push) Has been cancelled
Some checks failed
Validate / Hassfest (push) Has been cancelled
Major model restructuring for clean separation of concerns: New entities: - TrackingConfig: What to react to (event types, asset filters, periodic/scheduled/memory mode config) - reusable across targets - TemplateConfig: All ~15 template slots from blueprint (event messages, asset formatting, date/location, scheduled messages) with full defaults - separate entities per locale Changed entities: - AlbumTracker: Simplified to album selection + polling + target_ids (removed event_types, template_id, all filter fields) - NotificationTarget: Extended with tracking_config_id and template_config_id FKs (many-to-one, reusable configs) Removed entities: - MessageTemplate (replaced by TemplateConfig) - ScheduledJob (absorbed into TrackingConfig) Updated services: - watcher.py: Each target checked against its own tracking_config for event filtering before sending notification - notifier.py: Uses target's template_config to select the right template slot based on event type New API routes: - /api/tracking-configs/* (CRUD) - /api/template-configs/* (CRUD + per-slot preview) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,9 +6,9 @@ from .models import (
|
||||
AlbumTracker,
|
||||
EventLog,
|
||||
ImmichServer,
|
||||
MessageTemplate,
|
||||
NotificationTarget,
|
||||
ScheduledJob,
|
||||
TemplateConfig,
|
||||
TrackingConfig,
|
||||
User,
|
||||
)
|
||||
|
||||
@@ -19,7 +19,8 @@ __all__ = [
|
||||
"AlbumTracker",
|
||||
"EventLog",
|
||||
"ImmichServer",
|
||||
"MessageTemplate",
|
||||
"NotificationTarget",
|
||||
"TemplateConfig",
|
||||
"TrackingConfig",
|
||||
"User",
|
||||
]
|
||||
|
||||
@@ -36,8 +36,121 @@ class ImmichServer(SQLModel, table=True):
|
||||
created_at: datetime = Field(default_factory=_utcnow)
|
||||
|
||||
|
||||
class TrackingConfig(SQLModel, table=True):
|
||||
"""Tracking configuration: what events/assets to react to and scheduled modes."""
|
||||
|
||||
__tablename__ = "tracking_config"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
user_id: int = Field(foreign_key="user.id")
|
||||
name: str
|
||||
|
||||
# Event-driven tracking
|
||||
track_assets_added: bool = Field(default=True)
|
||||
track_assets_removed: bool = Field(default=False)
|
||||
track_album_renamed: bool = Field(default=True)
|
||||
track_album_deleted: bool = Field(default=True)
|
||||
track_images: bool = Field(default=True)
|
||||
track_videos: bool = Field(default=True)
|
||||
notify_favorites_only: bool = Field(default=False)
|
||||
|
||||
# Asset display in notifications
|
||||
include_people: bool = Field(default=True)
|
||||
include_asset_details: bool = Field(default=False)
|
||||
max_assets_to_show: int = Field(default=5)
|
||||
assets_order_by: str = Field(default="none") # none/date/rating/name
|
||||
assets_order: str = Field(default="descending")
|
||||
|
||||
# Periodic summary
|
||||
periodic_enabled: bool = Field(default=False)
|
||||
periodic_interval_days: int = Field(default=1)
|
||||
periodic_start_date: str = Field(default="2025-01-01")
|
||||
periodic_times: str = Field(default="12:00")
|
||||
|
||||
# Scheduled assets
|
||||
scheduled_enabled: bool = Field(default=False)
|
||||
scheduled_times: str = Field(default="09:00")
|
||||
scheduled_album_mode: str = Field(default="per_album") # per_album/combined/random
|
||||
scheduled_limit: int = Field(default=10)
|
||||
scheduled_favorite_only: bool = Field(default=False)
|
||||
scheduled_asset_type: str = Field(default="all") # all/photo/video
|
||||
scheduled_min_rating: int = Field(default=0)
|
||||
scheduled_order_by: str = Field(default="random")
|
||||
scheduled_order: str = Field(default="descending")
|
||||
|
||||
# Memory mode (On This Day)
|
||||
memory_enabled: bool = Field(default=False)
|
||||
memory_times: str = Field(default="09:00")
|
||||
memory_album_mode: str = Field(default="combined")
|
||||
memory_limit: int = Field(default=10)
|
||||
memory_favorite_only: bool = Field(default=False)
|
||||
memory_asset_type: str = Field(default="all")
|
||||
memory_min_rating: int = Field(default=0)
|
||||
|
||||
created_at: datetime = Field(default_factory=_utcnow)
|
||||
|
||||
|
||||
class TemplateConfig(SQLModel, table=True):
|
||||
"""Message template configuration: all template slots from the blueprint."""
|
||||
|
||||
__tablename__ = "template_config"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
user_id: int = Field(foreign_key="user.id")
|
||||
name: str # e.g. "Default EN", "Default RU"
|
||||
|
||||
# Event messages
|
||||
message_assets_added: str = Field(
|
||||
default='📷 {added_count} new photo(s) added to album "{album_name}"{common_date}{common_location}.{people}{assets}{video_warning}'
|
||||
)
|
||||
message_assets_removed: str = Field(
|
||||
default='🗑️ {removed_count} photo(s) removed from album "{album_name}".'
|
||||
)
|
||||
message_album_renamed: str = Field(
|
||||
default='✏️ Album "{old_name}" renamed to "{new_name}".'
|
||||
)
|
||||
message_album_deleted: str = Field(
|
||||
default='🗑️ Album "{album_name}" was deleted.'
|
||||
)
|
||||
|
||||
# Asset item formatting
|
||||
message_asset_image: str = Field(default="\n • 🖼️ {filename}")
|
||||
message_asset_video: str = Field(default="\n • 🎬 {filename}")
|
||||
message_assets_format: str = Field(default="\nAssets:{assets}")
|
||||
message_assets_more: str = Field(default="\n • ...and {more_count} more")
|
||||
message_people_format: str = Field(default=" People: {people}.")
|
||||
|
||||
# Date/location formatting
|
||||
date_format: str = Field(default="%d.%m.%Y, %H:%M UTC")
|
||||
common_date_template: str = Field(default=" from {date}")
|
||||
date_if_unique_template: str = Field(default=" ({date})")
|
||||
location_format: str = Field(default="{city}, {country}")
|
||||
common_location_template: str = Field(default=" in {location}")
|
||||
location_if_unique_template: str = Field(default=" 📍 {location}")
|
||||
favorite_indicator: str = Field(default="❤️")
|
||||
|
||||
# Scheduled notification templates
|
||||
periodic_summary_message: str = Field(
|
||||
default="📋 Tracked Albums Summary ({album_count} albums):{albums}"
|
||||
)
|
||||
periodic_album_template: str = Field(
|
||||
default="\n • {album_name}: {album_url}"
|
||||
)
|
||||
scheduled_assets_message: str = Field(
|
||||
default='📸 Here are some photos from album "{album_name}":{assets}'
|
||||
)
|
||||
memory_mode_message: str = Field(default="📅 On this day:{assets}")
|
||||
|
||||
# Telegram-specific
|
||||
video_warning: str = Field(
|
||||
default="\n\n⚠️ Note: Videos may not be sent due to Telegram's 50 MB file size limit."
|
||||
)
|
||||
|
||||
created_at: datetime = Field(default_factory=_utcnow)
|
||||
|
||||
|
||||
class NotificationTarget(SQLModel, table=True):
|
||||
"""Notification destination (Telegram chat, webhook URL)."""
|
||||
"""Notification destination with tracking and template config references."""
|
||||
|
||||
__tablename__ = "notification_target"
|
||||
|
||||
@@ -46,26 +159,13 @@ class NotificationTarget(SQLModel, table=True):
|
||||
type: str # "telegram" or "webhook"
|
||||
name: str
|
||||
config: dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
|
||||
created_at: datetime = Field(default_factory=_utcnow)
|
||||
|
||||
|
||||
class MessageTemplate(SQLModel, table=True):
|
||||
"""Jinja2 message template."""
|
||||
|
||||
__tablename__ = "message_template"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
user_id: int = Field(foreign_key="user.id")
|
||||
name: str
|
||||
body: str = Field(default="")
|
||||
body_ru: str = Field(default="") # Russian locale variant (empty = use body)
|
||||
event_type: str = Field(default="") # "" = all events, or specific type
|
||||
is_default: bool = Field(default=False)
|
||||
tracking_config_id: int | None = Field(default=None, foreign_key="tracking_config.id")
|
||||
template_config_id: int | None = Field(default=None, foreign_key="template_config.id")
|
||||
created_at: datetime = Field(default_factory=_utcnow)
|
||||
|
||||
|
||||
class AlbumTracker(SQLModel, table=True):
|
||||
"""Album change tracker configuration."""
|
||||
"""Album change tracker: which albums to poll and who to notify."""
|
||||
|
||||
__tablename__ = "album_tracker"
|
||||
|
||||
@@ -74,53 +174,11 @@ class AlbumTracker(SQLModel, table=True):
|
||||
server_id: int = Field(foreign_key="immich_server.id")
|
||||
name: str
|
||||
album_ids: list[str] = Field(default_factory=list, sa_column=Column(JSON))
|
||||
event_types: list[str] = Field(
|
||||
default_factory=lambda: ["assets_added"],
|
||||
sa_column=Column(JSON),
|
||||
)
|
||||
target_ids: list[int] = Field(default_factory=list, sa_column=Column(JSON))
|
||||
template_id: int | None = Field(default=None, foreign_key="message_template.id")
|
||||
scan_interval: int = Field(default=60) # seconds
|
||||
scan_interval: int = Field(default=60)
|
||||
enabled: bool = Field(default=True)
|
||||
quiet_hours_start: str | None = None # "HH:MM"
|
||||
quiet_hours_end: str | None = None # "HH:MM"
|
||||
# Enhanced filtering (matching HAOS blueprint)
|
||||
track_images: bool = Field(default=True)
|
||||
track_videos: bool = Field(default=True)
|
||||
notify_favorites_only: bool = Field(default=False)
|
||||
include_people: bool = Field(default=True)
|
||||
include_asset_details: bool = Field(default=False)
|
||||
max_assets_to_show: int = Field(default=5)
|
||||
assets_order_by: str = Field(default="none") # none/date/rating/name/random
|
||||
assets_order: str = Field(default="descending")
|
||||
created_at: datetime = Field(default_factory=_utcnow)
|
||||
|
||||
|
||||
class ScheduledJob(SQLModel, table=True):
|
||||
"""Scheduled notification job (periodic summary, scheduled assets, memory mode)."""
|
||||
|
||||
__tablename__ = "scheduled_job"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
tracker_id: int = Field(foreign_key="album_tracker.id")
|
||||
job_type: str # "periodic_summary", "scheduled_assets", "memory"
|
||||
enabled: bool = Field(default=True)
|
||||
# Timing
|
||||
times: str = Field(default="09:00") # "HH:MM, HH:MM"
|
||||
interval_days: int = Field(default=1) # For periodic: 1=daily, 7=weekly
|
||||
start_date: str = Field(default="2025-01-01") # For periodic interval anchor
|
||||
# Asset fetching config (scheduled_assets + memory modes)
|
||||
album_mode: str = Field(default="per_album") # per_album/combined/random
|
||||
limit: int = Field(default=10)
|
||||
favorite_only: bool = Field(default=False)
|
||||
asset_type: str = Field(default="all") # all/photo/video
|
||||
min_rating: int = Field(default=0) # 0=no filter, 1-5
|
||||
order_by: str = Field(default="random")
|
||||
order: str = Field(default="descending")
|
||||
min_date: str | None = None
|
||||
max_date: str | None = None
|
||||
# Template
|
||||
message_template: str = Field(default="") # Custom Jinja2 template for this job
|
||||
quiet_hours_start: str | None = None
|
||||
quiet_hours_end: str | None = None
|
||||
created_at: datetime = Field(default_factory=_utcnow)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user