241 lines
8.8 KiB
Python
241 lines
8.8 KiB
Python
from django.contrib import admin
|
|
from igny8_core.admin.base import SiteSectorAdminMixin
|
|
from .models import Tasks, Images, Content
|
|
from igny8_core.business.content.models import ContentTaxonomy, ContentAttribute, ContentTaxonomyRelation, ContentClusterMap
|
|
|
|
|
|
class ContentTaxonomyInline(admin.TabularInline):
|
|
"""Inline admin for managing content taxonomy relationships"""
|
|
model = ContentTaxonomyRelation
|
|
extra = 1
|
|
autocomplete_fields = ['taxonomy']
|
|
verbose_name = 'Taxonomy Term'
|
|
verbose_name_plural = 'Taxonomy Terms (Tags & Categories)'
|
|
|
|
|
|
@admin.register(Tasks)
|
|
class TasksAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
|
|
list_display = ['title', 'content_type', 'content_structure', 'site', 'sector', 'status', 'cluster', 'created_at']
|
|
list_filter = ['status', 'content_type', 'content_structure', 'site', 'sector', 'cluster']
|
|
search_fields = ['title', 'description']
|
|
ordering = ['-created_at']
|
|
readonly_fields = ['created_at', 'updated_at']
|
|
|
|
fieldsets = (
|
|
('Basic Info', {
|
|
'fields': ('title', 'description', 'status', 'site', 'sector')
|
|
}),
|
|
('Content Classification', {
|
|
'fields': ('content_type', 'content_structure', 'taxonomy_term')
|
|
}),
|
|
('Planning', {
|
|
'fields': ('cluster', 'keywords')
|
|
}),
|
|
('Timestamps', {
|
|
'fields': ('created_at', 'updated_at'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
def get_site_display(self, obj):
|
|
"""Safely get site name"""
|
|
try:
|
|
return obj.site.name if obj.site else '-'
|
|
except:
|
|
return '-'
|
|
get_site_display.short_description = 'Site'
|
|
|
|
def get_sector_display(self, obj):
|
|
"""Safely get sector name"""
|
|
try:
|
|
return obj.sector.name if obj.sector else '-'
|
|
except:
|
|
return '-'
|
|
|
|
def get_cluster_display(self, obj):
|
|
"""Safely get cluster name"""
|
|
try:
|
|
return obj.cluster.name if obj.cluster else '-'
|
|
except:
|
|
return '-'
|
|
get_cluster_display.short_description = 'Cluster'
|
|
|
|
|
|
@admin.register(Images)
|
|
class ImagesAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
|
|
list_display = ['get_content_title', 'site', 'sector', 'image_type', 'status', 'position', 'created_at']
|
|
list_filter = ['image_type', 'status', 'site', 'sector']
|
|
search_fields = ['content__title']
|
|
ordering = ['-id'] # Sort by ID descending (newest first)
|
|
|
|
def get_content_title(self, obj):
|
|
"""Get content title, fallback to task title if no content"""
|
|
if obj.content:
|
|
return obj.content.title or obj.content.meta_title or f"Content #{obj.content.id}"
|
|
elif obj.task:
|
|
return obj.task.title or f"Task #{obj.task.id}"
|
|
return '-'
|
|
get_content_title.short_description = 'Content Title'
|
|
|
|
def get_site_display(self, obj):
|
|
"""Safely get site name"""
|
|
try:
|
|
return obj.site.name if obj.site else '-'
|
|
except:
|
|
return '-'
|
|
get_site_display.short_description = 'Site'
|
|
|
|
def get_sector_display(self, obj):
|
|
"""Safely get sector name"""
|
|
try:
|
|
return obj.sector.name if obj.sector else '-'
|
|
except:
|
|
return '-'
|
|
|
|
|
|
@admin.register(Content)
|
|
class ContentAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
|
|
list_display = ['title', 'content_type', 'content_structure', 'site', 'sector', 'source', 'status', 'get_taxonomy_count', 'created_at']
|
|
list_filter = ['content_type', 'content_structure', 'source', 'status', 'site', 'sector', 'created_at']
|
|
search_fields = ['title', 'content_html', 'external_url']
|
|
ordering = ['-created_at']
|
|
readonly_fields = ['created_at', 'updated_at', 'word_count', 'get_tags_display', 'get_categories_display']
|
|
inlines = [ContentTaxonomyInline]
|
|
|
|
fieldsets = (
|
|
('Basic Info', {
|
|
'fields': ('title', 'site', 'sector', 'cluster', 'status')
|
|
}),
|
|
('Content Classification', {
|
|
'fields': ('content_type', 'content_structure', 'source')
|
|
}),
|
|
('Taxonomies (Read-only - manage below)', {
|
|
'fields': ('get_tags_display', 'get_categories_display'),
|
|
'classes': ('collapse',),
|
|
'description': 'View tags and categories. To add/remove, use the Taxonomy Terms section below.'
|
|
}),
|
|
('Content', {
|
|
'fields': ('content_html', 'word_count')
|
|
}),
|
|
('SEO', {
|
|
'fields': ('meta_title', 'meta_description', 'primary_keyword', 'secondary_keywords'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
('WordPress Sync', {
|
|
'fields': ('external_id', 'external_url'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
('Timestamps', {
|
|
'fields': ('created_at', 'updated_at'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
def get_taxonomy_count(self, obj):
|
|
"""Display count of associated taxonomy terms"""
|
|
return obj.taxonomy_terms.count()
|
|
get_taxonomy_count.short_description = 'Taxonomy Count'
|
|
|
|
def get_tags_display(self, obj):
|
|
"""Display tags"""
|
|
tags = obj.taxonomy_terms.filter(taxonomy_type='tag')
|
|
if tags.exists():
|
|
return ', '.join([tag.name for tag in tags])
|
|
return 'No tags'
|
|
get_tags_display.short_description = 'Tags'
|
|
|
|
def get_categories_display(self, obj):
|
|
"""Display categories"""
|
|
categories = obj.taxonomy_terms.filter(taxonomy_type='category')
|
|
if categories.exists():
|
|
return ', '.join([cat.name for cat in categories])
|
|
return 'No categories'
|
|
get_categories_display.short_description = 'Categories'
|
|
|
|
def get_site_display(self, obj):
|
|
"""Safely get site name"""
|
|
try:
|
|
return obj.site.name if obj.site else '-'
|
|
except:
|
|
return '-'
|
|
get_site_display.short_description = 'Site'
|
|
|
|
def get_sector_display(self, obj):
|
|
"""Safely get sector name"""
|
|
try:
|
|
return obj.sector.name if obj.sector else '-'
|
|
except:
|
|
return '-'
|
|
|
|
|
|
@admin.register(ContentTaxonomy)
|
|
class ContentTaxonomyAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
|
|
list_display = ['name', 'taxonomy_type', 'slug', 'count', 'external_id', 'external_taxonomy', 'site', 'sector']
|
|
list_filter = ['taxonomy_type', 'site', 'sector']
|
|
search_fields = ['name', 'slug', 'external_taxonomy']
|
|
ordering = ['taxonomy_type', 'name']
|
|
readonly_fields = ['count', 'created_at', 'updated_at']
|
|
|
|
fieldsets = (
|
|
('Basic Info', {
|
|
'fields': ('name', 'slug', 'taxonomy_type', 'description', 'site', 'sector')
|
|
}),
|
|
('Usage', {
|
|
'fields': ('count',),
|
|
'description': 'Number of content items using this term'
|
|
}),
|
|
('WordPress Sync', {
|
|
'fields': ('external_id', 'external_taxonomy', 'metadata'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
('Timestamps', {
|
|
'fields': ('created_at', 'updated_at'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
def get_queryset(self, request):
|
|
qs = super().get_queryset(request)
|
|
return qs.select_related('site', 'sector')
|
|
|
|
|
|
@admin.register(ContentAttribute)
|
|
class ContentAttributeAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
|
|
list_display = ['name', 'value', 'attribute_type', 'content', 'cluster', 'external_id', 'source', 'site', 'sector']
|
|
list_filter = ['attribute_type', 'source', 'site', 'sector']
|
|
search_fields = ['name', 'value', 'external_attribute_name', 'content__title']
|
|
ordering = ['attribute_type', 'name']
|
|
|
|
fieldsets = (
|
|
('Basic Info', {
|
|
'fields': ('attribute_type', 'name', 'value', 'site', 'sector')
|
|
}),
|
|
('Relationships', {
|
|
'fields': ('content', 'cluster'),
|
|
'description': 'Link to content (products/services) or cluster (semantic attributes).'
|
|
}),
|
|
('WordPress/WooCommerce Sync', {
|
|
'fields': ('external_id', 'external_attribute_name', 'source', 'metadata')
|
|
}),
|
|
)
|
|
|
|
def get_queryset(self, request):
|
|
qs = super().get_queryset(request)
|
|
return qs.select_related('content', 'cluster', 'site', 'sector')
|
|
|
|
|
|
@admin.register(ContentTaxonomyRelation)
|
|
class ContentTaxonomyRelationAdmin(admin.ModelAdmin):
|
|
list_display = ['content', 'taxonomy', 'created_at']
|
|
search_fields = ['content__title', 'taxonomy__name']
|
|
readonly_fields = ['created_at', 'updated_at']
|
|
|
|
|
|
@admin.register(ContentClusterMap)
|
|
class ContentClusterMapAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
|
|
list_display = ['content', 'task', 'cluster', 'role', 'source', 'site', 'sector', 'created_at']
|
|
list_filter = ['role', 'source', 'site', 'sector']
|
|
search_fields = ['content__title', 'task__title', 'cluster__name']
|
|
readonly_fields = ['created_at', 'updated_at']
|
|
|