Auto slug, auto summary/SEO, and deterministic tag colours
All checks were successful
CI / nightly-e2e (pull_request) Has been skipped
CI / deploy (pull_request) Has been skipped
CI / ci (pull_request) Successful in 1m23s
CI / pr-e2e (pull_request) Successful in 1m21s

Issue #61: Strengthen auto-generation for slug, summary, and SEO fields.
- ArticlePage.save() now auto-generates slug from title when empty
- ArticlePage.save() auto-populates search_description from summary
- Admin form also auto-populates search_description from summary

Issue #63: Replace manual TagMetadata colour assignment with deterministic
hash-based auto-colour. Tags get a consistent colour from a 12-entry
palette without needing a TagMetadata snippet. TagMetadata still works
as an explicit override.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-19 00:35:39 +00:00
parent 0e35fb0ad3
commit 0dc997d2cf
7 changed files with 306 additions and 29 deletions

View File

@@ -4,7 +4,7 @@ from django import template
from django.utils.safestring import mark_safe
from wagtail.models import Site
from apps.blog.models import ArticleIndexPage, Category, TagMetadata
from apps.blog.models import ArticleIndexPage, Category, TagMetadata, get_auto_tag_colour_css
from apps.core.models import SiteSettings
from apps.legal.models import LegalPage
@@ -70,20 +70,24 @@ def get_categories_nav(context):
]
@register.simple_tag
@register.filter
def get_tag_css(tag):
def _resolve_tag_css(tag) -> dict[str, str]:
"""Return CSS classes for a tag, using TagMetadata if set, else auto-colour."""
meta = getattr(tag, "metadata", None)
if meta is None:
meta = TagMetadata.objects.filter(tag=tag).first()
classes = meta.get_css_classes() if meta else TagMetadata.get_fallback_css()
if meta:
return meta.get_css_classes()
return get_auto_tag_colour_css(tag.name)
@register.simple_tag
@register.filter
def get_tag_css(tag):
classes = _resolve_tag_css(tag)
return mark_safe(f"{classes['bg']} {classes['text']}")
@register.filter
def get_tag_border_css(tag):
meta = getattr(tag, "metadata", None)
if meta is None:
meta = TagMetadata.objects.filter(tag=tag).first()
classes = meta.get_css_classes() if meta else TagMetadata.get_fallback_css()
classes = _resolve_tag_css(tag)
return mark_safe(classes.get("border", ""))

View File

@@ -21,17 +21,20 @@ def test_context_processor_returns_sitesettings(home_page):
@pytest.mark.django_db
def test_get_tag_css_fallback():
def test_get_tag_css_auto_colour():
"""Tags without metadata get a deterministic auto-assigned colour."""
tag = Tag.objects.create(name="x", slug="x")
value = core_tags.get_tag_css(tag)
assert "bg-zinc" in value
assert "bg-" in value
assert "text-" in value
@pytest.mark.django_db
def test_get_tag_border_css_fallback():
def test_get_tag_border_css_auto_colour():
"""Tags without metadata get a deterministic auto-assigned border colour."""
tag = Tag.objects.create(name="y", slug="y")
value = core_tags.get_tag_border_css(tag)
assert "border-zinc" in value
assert "border-" in value
@pytest.mark.django_db