Add read-only admin functionality and enhance billing models in admin interface

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-07 02:05:06 +00:00
parent 7a2b424237
commit 31c06d032c
9 changed files with 311 additions and 3 deletions

View File

@@ -1,8 +1,43 @@
from django.contrib import admin
from django.contrib.admin.apps import AdminConfig from django.contrib.admin.apps import AdminConfig
class ReadOnlyAdmin(admin.ModelAdmin):
"""Generic read-only admin for system tables."""
def has_add_permission(self, request):
return False
def has_change_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
def _safe_register(model, model_admin):
try:
admin.site.register(model, model_admin)
except admin.sites.AlreadyRegistered:
pass
class Igny8AdminConfig(AdminConfig): class Igny8AdminConfig(AdminConfig):
default_site = 'igny8_core.admin.site.Igny8AdminSite' default_site = 'igny8_core.admin.site.Igny8AdminSite'
name = 'django.contrib.admin' name = 'django.contrib.admin'
def ready(self):
super().ready()
# Register Django internals in admin (read-only where appropriate)
from django.contrib.admin.models import LogEntry
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from django.contrib.sessions.models import Session
_safe_register(LogEntry, ReadOnlyAdmin)
_safe_register(Permission, admin.ModelAdmin)
_safe_register(Group, admin.ModelAdmin)
_safe_register(ContentType, ReadOnlyAdmin)
_safe_register(Session, ReadOnlyAdmin)

View File

@@ -37,6 +37,12 @@ class Igny8AdminSite(admin.AdminSite):
('igny8_core_auth', 'Subscription'), ('igny8_core_auth', 'Subscription'),
('billing', 'CreditTransaction'), ('billing', 'CreditTransaction'),
('billing', 'CreditUsageLog'), ('billing', 'CreditUsageLog'),
('billing', 'Invoice'),
('billing', 'Payment'),
('billing', 'CreditPackage'),
('billing', 'PaymentMethodConfig'),
('billing', 'AccountPaymentMethod'),
('billing', 'CreditCostConfig'),
], ],
}, },
'Sites & Users': { 'Sites & Users': {
@@ -45,6 +51,7 @@ class Igny8AdminSite(admin.AdminSite):
('igny8_core_auth', 'User'), ('igny8_core_auth', 'User'),
('igny8_core_auth', 'SiteUserAccess'), ('igny8_core_auth', 'SiteUserAccess'),
('igny8_core_auth', 'PasswordResetToken'), ('igny8_core_auth', 'PasswordResetToken'),
('igny8_core_auth', 'Sector'),
], ],
}, },
'Global Reference Data': { 'Global Reference Data': {
@@ -70,6 +77,10 @@ class Igny8AdminSite(admin.AdminSite):
('writer', 'Tasks'), ('writer', 'Tasks'),
('writer', 'Content'), ('writer', 'Content'),
('writer', 'Images'), ('writer', 'Images'),
('writer', 'ContentTaxonomy'),
('writer', 'ContentAttribute'),
('writer', 'ContentTaxonomyRelation'),
('writer', 'ContentClusterMap'),
], ],
}, },
'Thinker Module': { 'Thinker Module': {
@@ -77,6 +88,7 @@ class Igny8AdminSite(admin.AdminSite):
('system', 'AIPrompt'), ('system', 'AIPrompt'),
('system', 'AuthorProfile'), ('system', 'AuthorProfile'),
('system', 'Strategy'), ('system', 'Strategy'),
('ai', 'AITaskLog'),
], ],
}, },
'System Configuration': { 'System Configuration': {
@@ -89,11 +101,38 @@ class Igny8AdminSite(admin.AdminSite):
('system', 'UserSettings'), ('system', 'UserSettings'),
('system', 'ModuleSettings'), ('system', 'ModuleSettings'),
('system', 'AISettings'), ('system', 'AISettings'),
('system', 'ModuleEnableSettings'),
# Automation config lives under the automation app - include here # Automation config lives under the automation app - include here
('automation', 'AutomationConfig'), ('automation', 'AutomationConfig'),
('automation', 'AutomationRun'), ('automation', 'AutomationRun'),
], ],
}, },
'Integrations & Sync': {
'models': [
('integration', 'SiteIntegration'),
('integration', 'SyncEvent'),
],
},
'Publishing': {
'models': [
('publishing', 'PublishingRecord'),
('publishing', 'DeploymentRecord'),
],
},
'Optimization': {
'models': [
('optimization', 'OptimizationTask'),
],
},
'Django Internals': {
'models': [
('admin', 'LogEntry'),
('auth', 'Group'),
('auth', 'Permission'),
('contenttypes', 'ContentType'),
('sessions', 'Session'),
],
},
} }
# Build the custom app list # Build the custom app list
@@ -131,6 +170,10 @@ class Igny8AdminSite(admin.AdminSite):
'Writer Module', 'Writer Module',
'Thinker Module', 'Thinker Module',
'System Configuration', 'System Configuration',
'Integrations & Sync',
'Publishing',
'Optimization',
'Django Internals',
] ]
app_list.sort(key=lambda x: order.index(x['name']) if x['name'] in order else 999) app_list.sort(key=lambda x: order.index(x['name']) if x['name'] in order else 999)

View File

@@ -3,7 +3,15 @@ Billing Business Logic Admin
""" """
from django.contrib import admin from django.contrib import admin
from django.utils.html import format_html from django.utils.html import format_html
from .models import CreditCostConfig, AccountPaymentMethod from igny8_core.admin.base import AccountAdminMixin
from .models import (
CreditCostConfig,
AccountPaymentMethod,
Invoice,
Payment,
CreditPackage,
PaymentMethodConfig,
)
@admin.register(CreditCostConfig) @admin.register(CreditCostConfig)
@@ -81,6 +89,56 @@ class CreditCostConfigAdmin(admin.ModelAdmin):
super().save_model(request, obj, form, change) super().save_model(request, obj, form, change)
@admin.register(Invoice)
class InvoiceAdmin(AccountAdminMixin, admin.ModelAdmin):
list_display = [
'invoice_number',
'account',
'status',
'total',
'currency',
'invoice_date',
'due_date',
'subscription',
]
list_filter = ['status', 'currency', 'invoice_date', 'account']
search_fields = ['invoice_number', 'account__name', 'subscription__id']
readonly_fields = ['created_at', 'updated_at']
@admin.register(Payment)
class PaymentAdmin(AccountAdminMixin, admin.ModelAdmin):
list_display = [
'id',
'invoice',
'account',
'payment_method',
'status',
'amount',
'currency',
'processed_at',
]
list_filter = ['status', 'payment_method', 'currency', 'created_at']
search_fields = ['invoice__invoice_number', 'account__name', 'stripe_payment_intent_id', 'paypal_order_id']
readonly_fields = ['created_at', 'updated_at']
@admin.register(CreditPackage)
class CreditPackageAdmin(admin.ModelAdmin):
list_display = ['name', 'slug', 'credits', 'price', 'discount_percentage', 'is_active', 'is_featured', 'sort_order']
list_filter = ['is_active', 'is_featured']
search_fields = ['name', 'slug']
readonly_fields = ['created_at', 'updated_at']
@admin.register(PaymentMethodConfig)
class PaymentMethodConfigAdmin(admin.ModelAdmin):
list_display = ['country_code', 'payment_method', 'is_enabled', 'display_name', 'sort_order']
list_filter = ['payment_method', 'is_enabled', 'country_code']
search_fields = ['country_code', 'display_name', 'payment_method']
readonly_fields = ['created_at', 'updated_at']
@admin.register(AccountPaymentMethod) @admin.register(AccountPaymentMethod)
class AccountPaymentMethodAdmin(admin.ModelAdmin): class AccountPaymentMethodAdmin(admin.ModelAdmin):
list_display = [ list_display = [

View File

@@ -0,0 +1,36 @@
from django.contrib import admin
from igny8_core.admin.base import AccountAdminMixin
from .models import SiteIntegration, SyncEvent
@admin.register(SiteIntegration)
class SiteIntegrationAdmin(AccountAdminMixin, admin.ModelAdmin):
list_display = [
'site',
'platform',
'platform_type',
'is_active',
'sync_enabled',
'sync_status',
'last_sync_at',
]
list_filter = ['platform', 'platform_type', 'is_active', 'sync_enabled', 'sync_status']
search_fields = ['site__name', 'site__domain', 'platform']
readonly_fields = ['created_at', 'updated_at']
@admin.register(SyncEvent)
class SyncEventAdmin(AccountAdminMixin, admin.ModelAdmin):
list_display = [
'integration',
'site',
'event_type',
'action',
'success',
'external_id',
'created_at',
]
list_filter = ['event_type', 'action', 'success', 'created_at']
search_fields = ['integration__site__name', 'site__name', 'description', 'external_id']
readonly_fields = ['created_at']

View File

@@ -0,0 +1,12 @@
from django.contrib import admin
from igny8_core.admin.base import AccountAdminMixin
from .models import OptimizationTask
@admin.register(OptimizationTask)
class OptimizationTaskAdmin(AccountAdminMixin, admin.ModelAdmin):
list_display = ['content', 'account', 'status', 'credits_used', 'created_at']
list_filter = ['status', 'created_at']
search_fields = ['content__title', 'account__name']
readonly_fields = ['created_at', 'updated_at']

View File

@@ -0,0 +1,36 @@
from django.contrib import admin
from igny8_core.admin.base import SiteSectorAdminMixin
from .models import PublishingRecord, DeploymentRecord
@admin.register(PublishingRecord)
class PublishingRecordAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
list_display = [
'content',
'site',
'sector',
'destination',
'status',
'destination_url',
'published_at',
]
list_filter = ['destination', 'status', 'site']
search_fields = ['content__title', 'destination', 'destination_url']
readonly_fields = ['created_at', 'updated_at']
@admin.register(DeploymentRecord)
class DeploymentRecordAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
list_display = [
'site',
'sector',
'version',
'deployed_version',
'status',
'deployment_url',
'deployed_at',
]
list_filter = ['status', 'site']
search_fields = ['site__name', 'deployment_url']
readonly_fields = ['created_at', 'updated_at']

View File

@@ -4,7 +4,13 @@ Billing Module Admin
from django.contrib import admin from django.contrib import admin
from django.utils.html import format_html from django.utils.html import format_html
from igny8_core.admin.base import AccountAdminMixin from igny8_core.admin.base import AccountAdminMixin
from igny8_core.business.billing.models import CreditCostConfig from igny8_core.business.billing.models import (
CreditCostConfig,
Invoice,
Payment,
CreditPackage,
PaymentMethodConfig,
)
from .models import CreditTransaction, CreditUsageLog, AccountPaymentMethod from .models import CreditTransaction, CreditUsageLog, AccountPaymentMethod
@@ -44,6 +50,56 @@ class CreditUsageLogAdmin(AccountAdminMixin, admin.ModelAdmin):
get_account_display.short_description = 'Account' get_account_display.short_description = 'Account'
@admin.register(Invoice)
class InvoiceAdmin(AccountAdminMixin, admin.ModelAdmin):
list_display = [
'invoice_number',
'account',
'status',
'total',
'currency',
'invoice_date',
'due_date',
'subscription',
]
list_filter = ['status', 'currency', 'invoice_date', 'account']
search_fields = ['invoice_number', 'account__name', 'subscription__id']
readonly_fields = ['created_at', 'updated_at']
@admin.register(Payment)
class PaymentAdmin(AccountAdminMixin, admin.ModelAdmin):
list_display = [
'id',
'invoice',
'account',
'payment_method',
'status',
'amount',
'currency',
'processed_at',
]
list_filter = ['status', 'payment_method', 'currency', 'created_at']
search_fields = ['invoice__invoice_number', 'account__name', 'stripe_payment_intent_id', 'paypal_order_id']
readonly_fields = ['created_at', 'updated_at']
@admin.register(CreditPackage)
class CreditPackageAdmin(admin.ModelAdmin):
list_display = ['name', 'slug', 'credits', 'price', 'discount_percentage', 'is_active', 'is_featured', 'sort_order']
list_filter = ['is_active', 'is_featured']
search_fields = ['name', 'slug']
readonly_fields = ['created_at', 'updated_at']
@admin.register(PaymentMethodConfig)
class PaymentMethodConfigAdmin(admin.ModelAdmin):
list_display = ['country_code', 'payment_method', 'is_enabled', 'display_name', 'sort_order']
list_filter = ['payment_method', 'is_enabled', 'country_code']
search_fields = ['country_code', 'display_name', 'payment_method']
readonly_fields = ['created_at', 'updated_at']
@admin.register(AccountPaymentMethod) @admin.register(AccountPaymentMethod)
class AccountPaymentMethodAdmin(AccountAdminMixin, admin.ModelAdmin): class AccountPaymentMethodAdmin(AccountAdminMixin, admin.ModelAdmin):
list_display = [ list_display = [

View File

@@ -91,3 +91,20 @@ class AISettingsAdmin(AccountAdminMixin, admin.ModelAdmin):
return '-' return '-'
get_account_display.short_description = 'Account' get_account_display.short_description = 'Account'
@admin.register(ModuleEnableSettings)
class ModuleEnableSettingsAdmin(AccountAdminMixin, admin.ModelAdmin):
list_display = [
'account',
'planner_enabled',
'writer_enabled',
'thinker_enabled',
'automation_enabled',
'site_builder_enabled',
'linker_enabled',
'optimizer_enabled',
'publisher_enabled',
]
list_filter = ['planner_enabled', 'writer_enabled', 'thinker_enabled', 'automation_enabled', 'site_builder_enabled', 'linker_enabled', 'optimizer_enabled', 'publisher_enabled']
search_fields = ['account__name']

View File

@@ -1,7 +1,7 @@
from django.contrib import admin from django.contrib import admin
from igny8_core.admin.base import SiteSectorAdminMixin from igny8_core.admin.base import SiteSectorAdminMixin
from .models import Tasks, Images, Content from .models import Tasks, Images, Content
from igny8_core.business.content.models import ContentTaxonomy, ContentAttribute, ContentTaxonomyRelation from igny8_core.business.content.models import ContentTaxonomy, ContentAttribute, ContentTaxonomyRelation, ContentClusterMap
class ContentTaxonomyInline(admin.TabularInline): class ContentTaxonomyInline(admin.TabularInline):
@@ -223,3 +223,18 @@ class ContentAttributeAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
qs = super().get_queryset(request) qs = super().get_queryset(request)
return qs.select_related('content', 'cluster', 'site', 'sector') 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']