Simplify templates to pure Jinja2 + CodeMirror editor + variable reference
Some checks failed
Validate / Hassfest (push) Has been cancelled

Major template system overhaul:
- TemplateConfig simplified from 21 fields to 9: removed all sub-templates
  (asset_image, asset_video, assets_format, people_format, etc.)
  Users write full Jinja2 with {% for %}, {% if %} inline.
- Default EN/RU templates seeded on first startup (user_id=0, system-owned)
  with proper Jinja2 loops over added_assets, people, albums.
- build_full_context() simplified: passes raw data directly to Jinja2
  instead of pre-rendering sub-templates.
- CodeMirror editor for template slots (HTML syntax highlighting,
  line wrapping, dark theme support via oneDark).
- Variable reference API: GET /api/template-configs/variables returns
  per-slot variable descriptions + asset_fields for loop contexts.
- Variable reference modal in UI: click "{{ }} Variables" next to any
  slot to see available variables with Jinja2 syntax examples.
- Route ordering fix: /variables registered before /{config_id}.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 18:57:51 +03:00
parent bc8fda5984
commit 0bb4d8a949
9 changed files with 791 additions and 181 deletions

View File

@@ -108,7 +108,11 @@ class TrackingConfig(SQLModel, table=True):
class TemplateConfig(SQLModel, table=True):
"""Message template configuration: all template slots from the blueprint."""
"""Message template configuration with full Jinja2 templates.
Each slot is a complete Jinja2 template with access to loops, conditionals,
and filters. No sub-templates needed -- use {% for %}, {% if %} inline.
"""
__tablename__ = "template_config"
@@ -117,49 +121,19 @@ class TemplateConfig(SQLModel, table=True):
name: str # e.g. "Default EN", "Default RU"
icon: str = Field(default="")
# 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.'
)
# Event-driven notification templates (full Jinja2)
message_assets_added: str = Field(default="")
message_assets_removed: str = Field(default="")
message_album_renamed: str = Field(default="")
message_album_deleted: str = Field(default="")
# 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}.")
# Scheduled notification templates (full Jinja2)
periodic_summary_message: str = Field(default="")
scheduled_assets_message: str = Field(default="")
memory_mode_message: str = Field(default="")
# Date/location formatting
# Settings
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."
)
@@ -167,6 +141,81 @@ class TemplateConfig(SQLModel, table=True):
created_at: datetime = Field(default_factory=_utcnow)
# --- Default template content (EN) ---
DEFAULT_TEMPLATE_EN = {
"message_assets_added": """📷 {{ added_count }} new photo(s) added to album "{{ album_name }}".\
{% if people %}
👤 {{ people | join(", ") }}{% endif %}\
{% if added_assets %}
{% for asset in added_assets %}\
{% if asset.type == "VIDEO" %}🎬{% else %}🖼️{% endif %} {{ asset.filename }}\
{% if asset.city %} 📍 {{ asset.city }}{% if asset.country %}, {{ asset.country }}{% endif %}{% endif %}\
{% if asset.is_favorite %} ❤️{% endif %}
{% endfor %}\
{% endif %}\
{{ video_warning }}""",
"message_assets_removed": '🗑️ {{ removed_count }} photo(s) removed from album "{{ album_name }}".',
"message_album_renamed": '✏️ Album "{{ old_name }}" renamed to "{{ new_name }}".',
"message_album_deleted": '🗑️ Album "{{ album_name }}" was deleted.',
"periodic_summary_message": """📋 Tracked Albums Summary ({{ albums | length }} albums):\
{% for album in albums %}
{{ album.name }}: {{ album.asset_count }} assets{% if album.url %} — {{ album.url }}{% endif %}\
{% endfor %}""",
"scheduled_assets_message": """📸 Photos from "{{ album_name }}":\
{% for asset in assets %}
{% if asset.type == "VIDEO" %}🎬{% else %}🖼️{% endif %} {{ asset.filename }}\
{% endfor %}""",
"memory_mode_message": """📅 On this day:\
{% for asset in assets %}
{% if asset.type == "VIDEO" %}🎬{% else %}🖼️{% endif %} {{ asset.filename }} ({{ asset.created_at[:4] }})\
{% endfor %}""",
}
# --- Default template content (RU) ---
DEFAULT_TEMPLATE_RU = {
"message_assets_added": """📷 {{ added_count }} новых фото добавлено в альбом "{{ album_name }}".\
{% if people %}
👤 {{ people | join(", ") }}{% endif %}\
{% if added_assets %}
{% for asset in added_assets %}\
{% if asset.type == "VIDEO" %}🎬{% else %}🖼️{% endif %} {{ asset.filename }}\
{% if asset.city %} 📍 {{ asset.city }}{% if asset.country %}, {{ asset.country }}{% endif %}{% endif %}\
{% if asset.is_favorite %} ❤️{% endif %}
{% endfor %}\
{% endif %}\
{{ video_warning }}""",
"message_assets_removed": '🗑️ {{ removed_count }} фото удалено из альбома "{{ album_name }}".',
"message_album_renamed": '✏️ Альбом "{{ old_name }}" переименован в "{{ new_name }}".',
"message_album_deleted": '🗑️ Альбом "{{ album_name }}" был удалён.',
"periodic_summary_message": """📋 Сводка альбомов ({{ albums | length }}):\
{% for album in albums %}
{{ album.name }}: {{ album.asset_count }} файлов{% if album.url %} — {{ album.url }}{% endif %}\
{% endfor %}""",
"scheduled_assets_message": """📸 Фото из "{{ album_name }}":\
{% for asset in assets %}
{% if asset.type == "VIDEO" %}🎬{% else %}🖼️{% endif %} {{ asset.filename }}\
{% endfor %}""",
"memory_mode_message": """📅 В этот день:\
{% for asset in assets %}
{% if asset.type == "VIDEO" %}🎬{% else %}🖼️{% endif %} {{ asset.filename }} ({{ asset.created_at[:4] }})\
{% endfor %}""",
}
class NotificationTarget(SQLModel, table=True):
"""Notification destination with tracking and template config references."""