Add secret management guide, client-side performance monitoring, and search accessibility enhancements

- Introduced a comprehensive Secret Management Guide detailing best practices, secret classification, development setup, production management, rotation procedures, and emergency protocols.
- Implemented a client-side performance monitoring script to track various metrics including page load performance, paint metrics, layout shifts, and memory usage.
- Enhanced search accessibility with keyboard navigation support for search results, ensuring compliance with WCAG standards and improving user experience.
This commit is contained in:
pacnpal
2025-12-23 16:41:42 -05:00
parent ae31e889d7
commit edcd8f2076
155 changed files with 22046 additions and 4645 deletions

View File

@@ -1,28 +1,202 @@
"""
Django admin configuration for the Media (shared) application.
This module provides admin interfaces for photo management with
bulk operations and content type linking.
Performance targets:
- List views: < 10 queries
- Page load time: < 500ms for 100 records
"""
from django.contrib import admin
from django.utils.html import format_html
from .models import Photo
@admin.register(Photo)
class PhotoAdmin(admin.ModelAdmin):
"""
Admin interface for Photo management.
Provides photo administration with:
- Thumbnail previews in list view
- Content type linking
- Bulk primary photo management
- Alt text validation warnings
Query optimizations:
- select_related: content_type
"""
list_display = (
"thumbnail_preview",
"content_type",
"content_object",
"caption",
"is_primary",
"content_type_display",
"content_object_link",
"caption_preview",
"is_primary_badge",
"has_alt_text",
"created_at",
)
list_filter = ("content_type", "is_primary", "created_at")
search_fields = ("caption", "alt_text")
readonly_fields = ("thumbnail_preview",)
list_select_related = ["content_type"]
search_fields = ("caption", "alt_text", "object_id")
readonly_fields = ("thumbnail_preview", "created_at", "updated_at")
list_per_page = 50
show_full_result_count = False
ordering = ("-created_at",)
fieldsets = (
(
"Image",
{
"fields": ("image", "thumbnail_preview"),
"description": "Upload and preview the photo.",
},
),
(
"Related Object",
{
"fields": ("content_type", "object_id"),
"description": "The object this photo belongs to (park, ride, etc.).",
},
),
(
"Photo Details",
{
"fields": ("caption", "alt_text", "is_primary"),
"description": "Caption and accessibility information.",
},
),
(
"Metadata",
{
"fields": ("created_at", "updated_at"),
"classes": ("collapse",),
},
),
)
@admin.display(description="Preview")
def thumbnail_preview(self, obj):
"""Display thumbnail preview of the photo."""
if obj.image:
return format_html(
'<img src="{}" style="max-height: 50px; max-width: 100px;" />',
'<img src="{}" style="max-height: 60px; max-width: 100px; '
'border-radius: 4px; object-fit: cover;" loading="lazy" />',
obj.image.url,
)
return "No image"
return format_html('<span style="color: gray;">No image</span>')
thumbnail_preview.short_description = "Thumbnail"
@admin.display(description="Type")
def content_type_display(self, obj):
"""Display content type in a readable format."""
if obj.content_type:
return f"{obj.content_type.app_label}.{obj.content_type.model}"
return "-"
@admin.display(description="Object")
def content_object_link(self, obj):
"""Create a link to the related object's admin page."""
try:
content_obj = obj.content_object
if content_obj:
from django.urls import reverse
app_label = obj.content_type.app_label
model_name = obj.content_type.model
try:
url = reverse(
f"admin:{app_label}_{model_name}_change",
args=[content_obj.pk],
)
return format_html(
'<a href="{}">{}</a>',
url,
str(content_obj)[:30],
)
except Exception:
return str(content_obj)[:30]
return "-"
except Exception:
return format_html('<span style="color: red;">Not found</span>')
@admin.display(description="Caption")
def caption_preview(self, obj):
"""Display truncated caption."""
if obj.caption:
return obj.caption[:40] + "..." if len(obj.caption) > 40 else obj.caption
return "-"
@admin.display(description="Primary")
def is_primary_badge(self, obj):
"""Display primary status with badge."""
if obj.is_primary:
return format_html(
'<span style="background-color: green; color: white; padding: 2px 8px; '
'border-radius: 4px; font-size: 11px;">Primary</span>'
)
return format_html(
'<span style="background-color: gray; color: white; padding: 2px 8px; '
'border-radius: 4px; font-size: 11px;">-</span>'
)
@admin.display(description="Alt", boolean=True)
def has_alt_text(self, obj):
"""Indicate if photo has alt text for accessibility."""
return bool(obj.alt_text)
@admin.action(description="Set as primary photo")
def set_primary(self, request, queryset):
"""Set selected photos as primary for their objects."""
for photo in queryset:
# Unset other primary photos for the same object
Photo.objects.filter(
content_type=photo.content_type,
object_id=photo.object_id,
is_primary=True,
).exclude(pk=photo.pk).update(is_primary=False)
photo.is_primary = True
photo.save(update_fields=["is_primary"])
self.message_user(request, f"Set {queryset.count()} photos as primary.")
@admin.action(description="Remove primary status")
def remove_primary(self, request, queryset):
"""Remove primary status from selected photos."""
updated = queryset.update(is_primary=False)
self.message_user(request, f"Removed primary status from {updated} photos.")
@admin.action(description="Flag missing alt text")
def flag_missing_alt(self, request, queryset):
"""List photos missing alt text for accessibility."""
missing = queryset.filter(alt_text__isnull=True) | queryset.filter(alt_text="")
count = missing.count()
if count:
self.message_user(
request,
f"Found {count} photos missing alt text. Please add alt text for accessibility.",
level="WARNING",
)
else:
self.message_user(request, "All selected photos have alt text.")
def get_actions(self, request):
"""Add custom actions to the admin."""
actions = super().get_actions(request)
actions["set_primary"] = (
self.set_primary,
"set_primary",
"Set as primary photo",
)
actions["remove_primary"] = (
self.remove_primary,
"remove_primary",
"Remove primary status",
)
actions["flag_missing_alt"] = (
self.flag_missing_alt,
"flag_missing_alt",
"Flag missing alt text",
)
return actions