bulk actions & some next audits docs

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-20 02:46:00 +00:00
parent c17b22e927
commit ab0d6469d4
16 changed files with 2987 additions and 305 deletions

View File

@@ -9,21 +9,35 @@ from unfold.contrib.filters.admin import (
)
from igny8_core.admin.base import SiteSectorAdminMixin, Igny8ModelAdmin
from .models import Keywords, Clusters, ContentIdeas
from import_export.admin import ExportMixin
from import_export.admin import ExportMixin, ImportExportMixin
from import_export import resources
class KeywordsResource(resources.ModelResource):
"""Resource class for exporting Keywords"""
"""Resource class for importing/exporting Keywords"""
class Meta:
model = Keywords
fields = ('id', 'keyword', 'seed_keyword__keyword', 'site__name', 'sector__name',
'cluster__name', 'volume', 'difficulty', 'country', 'status', 'created_at')
export_order = fields
import_id_fields = ('id',)
skip_unchanged = True
class ClustersResource(resources.ModelResource):
"""Resource class for importing/exporting Clusters"""
class Meta:
model = Clusters
fields = ('id', 'name', 'site__name', 'sector__name', 'keywords_count', 'volume',
'status', 'created_at')
export_order = fields
import_id_fields = ('id',)
skip_unchanged = True
@admin.register(Clusters)
class ClustersAdmin(SiteSectorAdminMixin, Igny8ModelAdmin):
class ClustersAdmin(ImportExportMixin, SiteSectorAdminMixin, Igny8ModelAdmin):
resource_class = ClustersResource
list_display = ['name', 'site', 'sector', 'keywords_count', 'volume', 'status', 'created_at']
list_filter = [
('status', ChoicesDropdownFilter),
@@ -35,6 +49,11 @@ class ClustersAdmin(SiteSectorAdminMixin, Igny8ModelAdmin):
search_fields = ['name']
ordering = ['name']
autocomplete_fields = ['site', 'sector']
actions = [
'bulk_set_status_active',
'bulk_set_status_inactive',
'bulk_soft_delete',
]
def get_site_display(self, obj):
"""Safely get site name"""
@@ -50,10 +69,31 @@ class ClustersAdmin(SiteSectorAdminMixin, Igny8ModelAdmin):
return obj.sector.name if obj.sector else '-'
except:
return '-'
def bulk_set_status_active(self, request, queryset):
"""Set selected clusters to active status"""
updated = queryset.update(status='active')
self.message_user(request, f'{updated} cluster(s) set to active.', messages.SUCCESS)
bulk_set_status_active.short_description = 'Set status to Active'
def bulk_set_status_inactive(self, request, queryset):
"""Set selected clusters to inactive status"""
updated = queryset.update(status='inactive')
self.message_user(request, f'{updated} cluster(s) set to inactive.', messages.SUCCESS)
bulk_set_status_inactive.short_description = 'Set status to Inactive'
def bulk_soft_delete(self, request, queryset):
"""Soft delete selected clusters"""
count = 0
for cluster in queryset:
cluster.delete() # Soft delete via SoftDeletableModel
count += 1
self.message_user(request, f'{count} cluster(s) soft deleted.', messages.SUCCESS)
bulk_soft_delete.short_description = 'Soft delete selected clusters'
@admin.register(Keywords)
class KeywordsAdmin(ExportMixin, SiteSectorAdminMixin, Igny8ModelAdmin):
class KeywordsAdmin(ImportExportMixin, SiteSectorAdminMixin, Igny8ModelAdmin):
resource_class = KeywordsResource
list_display = ['keyword', 'seed_keyword', 'site', 'sector', 'cluster', 'volume', 'difficulty', 'country', 'status', 'created_at']
list_editable = ['status'] # Enable inline editing for status
@@ -74,6 +114,7 @@ class KeywordsAdmin(ExportMixin, SiteSectorAdminMixin, Igny8ModelAdmin):
'bulk_assign_cluster',
'bulk_set_status_active',
'bulk_set_status_inactive',
'bulk_soft_delete',
]
def get_site_display(self, obj):
@@ -150,10 +191,32 @@ class KeywordsAdmin(ExportMixin, SiteSectorAdminMixin, Igny8ModelAdmin):
updated = queryset.update(status='inactive')
self.message_user(request, f'{updated} keyword(s) set to inactive.', messages.SUCCESS)
bulk_set_status_inactive.short_description = 'Set status to Inactive'
def bulk_soft_delete(self, request, queryset):
"""Soft delete selected keywords"""
count = 0
for keyword in queryset:
keyword.delete() # Soft delete via SoftDeletableModel
count += 1
self.message_user(request, f'{count} keyword(s) soft deleted.', messages.SUCCESS)
bulk_soft_delete.short_description = 'Soft delete selected keywords'
class ContentIdeasResource(resources.ModelResource):
"""Resource class for importing/exporting Content Ideas"""
class Meta:
model = ContentIdeas
fields = ('id', 'idea_title', 'description', 'site__name', 'sector__name',
'content_type', 'content_structure', 'status', 'keyword_cluster__name',
'target_keywords', 'estimated_word_count', 'created_at')
export_order = fields
import_id_fields = ('id',)
skip_unchanged = True
@admin.register(ContentIdeas)
class ContentIdeasAdmin(SiteSectorAdminMixin, Igny8ModelAdmin):
class ContentIdeasAdmin(ImportExportMixin, SiteSectorAdminMixin, Igny8ModelAdmin):
resource_class = ContentIdeasResource
list_display = ['idea_title', 'site', 'sector', 'description_preview', 'content_type', 'content_structure', 'status', 'keyword_cluster', 'estimated_word_count', 'created_at']
list_filter = [
('status', ChoicesDropdownFilter),
@@ -168,6 +231,15 @@ class ContentIdeasAdmin(SiteSectorAdminMixin, Igny8ModelAdmin):
search_fields = ['idea_title', 'target_keywords', 'description']
ordering = ['-created_at']
readonly_fields = ['created_at', 'updated_at']
actions = [
'bulk_set_status_draft',
'bulk_set_status_approved',
'bulk_set_status_rejected',
'bulk_set_status_completed',
'bulk_assign_cluster',
'bulk_update_content_type',
'bulk_soft_delete',
]
fieldsets = (
('Basic Info', {
@@ -218,4 +290,109 @@ class ContentIdeasAdmin(SiteSectorAdminMixin, Igny8ModelAdmin):
except:
return '-'
get_keyword_cluster_display.short_description = 'Cluster'
def bulk_set_status_draft(self, request, queryset):
"""Set selected content ideas to draft status"""
updated = queryset.update(status='draft')
self.message_user(request, f'{updated} content idea(s) set to draft.', messages.SUCCESS)
bulk_set_status_draft.short_description = 'Set status to Draft'
def bulk_set_status_approved(self, request, queryset):
"""Set selected content ideas to approved status"""
updated = queryset.update(status='approved')
self.message_user(request, f'{updated} content idea(s) set to approved.', messages.SUCCESS)
bulk_set_status_approved.short_description = 'Set status to Approved'
def bulk_set_status_rejected(self, request, queryset):
"""Set selected content ideas to rejected status"""
updated = queryset.update(status='rejected')
self.message_user(request, f'{updated} content idea(s) set to rejected.', messages.SUCCESS)
bulk_set_status_rejected.short_description = 'Set status to Rejected'
def bulk_set_status_completed(self, request, queryset):
"""Set selected content ideas to completed status"""
updated = queryset.update(status='completed')
self.message_user(request, f'{updated} content idea(s) set to completed.', messages.SUCCESS)
bulk_set_status_completed.short_description = 'Set status to Completed'
def bulk_assign_cluster(self, request, queryset):
"""Assign selected content ideas to a cluster"""
from django import forms
if 'apply' in request.POST:
cluster_id = request.POST.get('cluster')
if cluster_id:
cluster = Clusters.objects.get(pk=cluster_id)
updated = queryset.update(keyword_cluster=cluster)
self.message_user(request, f'{updated} content idea(s) assigned to cluster: {cluster.name}', messages.SUCCESS)
return
first_idea = queryset.first()
if first_idea:
clusters = Clusters.objects.filter(site=first_idea.site, sector=first_idea.sector)
else:
clusters = Clusters.objects.all()
class ClusterForm(forms.Form):
cluster = forms.ModelChoiceField(
queryset=clusters,
label="Select Cluster",
help_text=f"Assign {queryset.count()} selected content idea(s) to:"
)
if clusters.exists():
from django.shortcuts import render
return render(request, 'admin/bulk_action_form.html', {
'title': 'Assign Content Ideas to Cluster',
'queryset': queryset,
'form': ClusterForm(),
'action': 'bulk_assign_cluster',
})
else:
self.message_user(request, 'No clusters available for the selected content ideas.', messages.WARNING)
bulk_assign_cluster.short_description = 'Assign to Cluster'
def bulk_update_content_type(self, request, queryset):
"""Update content type for selected content ideas"""
from django import forms
if 'apply' in request.POST:
content_type = request.POST.get('content_type')
if content_type:
updated = queryset.update(content_type=content_type)
self.message_user(request, f'{updated} content idea(s) updated to content type: {content_type}', messages.SUCCESS)
return
CONTENT_TYPE_CHOICES = [
('blog_post', 'Blog Post'),
('article', 'Article'),
('product', 'Product'),
('service', 'Service'),
('page', 'Page'),
('landing_page', 'Landing Page'),
]
class ContentTypeForm(forms.Form):
content_type = forms.ChoiceField(
choices=CONTENT_TYPE_CHOICES,
label="Select Content Type",
help_text=f"Update content type for {queryset.count()} selected content idea(s)"
)
from django.shortcuts import render
return render(request, 'admin/bulk_action_form.html', {
'title': 'Update Content Type',
'queryset': queryset,
'form': ContentTypeForm(),
'action': 'bulk_update_content_type',
})
bulk_update_content_type.short_description = 'Update content type'
def bulk_soft_delete(self, request, queryset):
"""Soft delete selected content ideas"""
count = 0
for idea in queryset:
idea.delete() # Soft delete via SoftDeletableModel
count += 1
self.message_user(request, f'{count} content idea(s) soft deleted.', messages.SUCCESS)
bulk_soft_delete.short_description = 'Soft delete selected content ideas'