""" Admin interface for auth models """ from django.contrib import admin from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from igny8_core.admin.base import AccountAdminMixin from .models import User, Account, Plan, Subscription, Site, Sector, SiteUserAccess, Industry, IndustrySector, SeedKeyword, PasswordResetToken @admin.register(Plan) class PlanAdmin(admin.ModelAdmin): """Plan admin - Global, no account filtering needed""" list_display = ['name', 'slug', 'price', 'billing_cycle', 'max_sites', 'max_users', 'included_credits', 'is_active'] list_filter = ['is_active', 'billing_cycle'] search_fields = ['name', 'slug'] readonly_fields = ['created_at'] fieldsets = ( ('Plan Info', { 'fields': ('name', 'slug', 'price', 'billing_cycle', 'features', 'is_active') }), ('Account Management Limits', { 'fields': ('max_users', 'max_sites', 'max_industries', 'max_author_profiles') }), ('Billing & Credits', { 'fields': ('included_credits', 'extra_credit_price', 'allow_credit_topup', 'auto_credit_topup_threshold', 'auto_credit_topup_amount', 'credits_per_month') }), ('Stripe Integration', { 'fields': ('stripe_product_id', 'stripe_price_id') }), ) @admin.register(Account) class AccountAdmin(AccountAdminMixin, admin.ModelAdmin): list_display = ['name', 'slug', 'owner', 'plan', 'status', 'credits', 'created_at'] list_filter = ['status', 'plan'] search_fields = ['name', 'slug'] readonly_fields = ['created_at', 'updated_at'] def get_queryset(self, request): """Override to filter by account for non-superusers""" qs = super().get_queryset(request) if request.user.is_superuser or (hasattr(request.user, 'is_developer') and request.user.is_developer()): return qs # Owners can see their own accounts if hasattr(request.user, 'role') and request.user.role == 'owner': return qs.filter(owner=request.user) # Admins can see their account try: user_account = getattr(request.user, 'account', None) if user_account: return qs.filter(id=user_account.id) except (AttributeError, Exception): # If account access fails (e.g., column mismatch), return empty pass return qs.none() @admin.register(Subscription) class SubscriptionAdmin(AccountAdminMixin, admin.ModelAdmin): list_display = ['account', 'status', 'current_period_start', 'current_period_end'] list_filter = ['status'] search_fields = ['account__name', 'stripe_subscription_id'] readonly_fields = ['created_at', 'updated_at'] @admin.register(PasswordResetToken) class PasswordResetTokenAdmin(admin.ModelAdmin): list_display = ['user', 'token', 'used', 'expires_at', 'created_at'] list_filter = ['used', 'expires_at', 'created_at'] search_fields = ['user__email', 'token'] readonly_fields = ['created_at', 'token'] def get_queryset(self, request): """Filter by account for non-superusers""" qs = super().get_queryset(request) if request.user.is_superuser or (hasattr(request.user, 'is_developer') and request.user.is_developer()): return qs user_account = getattr(request.user, 'account', None) if user_account: return qs.filter(user__account=user_account) return qs.none() class SectorInline(admin.TabularInline): """Inline admin for sectors within Site admin.""" model = Sector extra = 0 fields = ['industry_sector', 'name', 'slug', 'status', 'is_active', 'get_keywords_count', 'get_clusters_count'] readonly_fields = ['get_keywords_count', 'get_clusters_count'] def get_keywords_count(self, obj): if obj.pk: return getattr(obj, 'keywords_set', obj.keywords_set).count() return 0 get_keywords_count.short_description = 'Keywords' def get_clusters_count(self, obj): if obj.pk: return getattr(obj, 'clusters_set', obj.clusters_set).count() return 0 get_clusters_count.short_description = 'Clusters' @admin.register(Site) class SiteAdmin(AccountAdminMixin, admin.ModelAdmin): list_display = ['name', 'slug', 'account', 'industry', 'domain', 'status', 'is_active', 'get_api_key_status', 'get_sectors_count'] list_filter = ['status', 'is_active', 'account', 'industry', 'hosting_type'] search_fields = ['name', 'slug', 'domain', 'industry__name'] readonly_fields = ['created_at', 'updated_at', 'get_api_key_display'] inlines = [SectorInline] actions = ['generate_api_keys'] fieldsets = ( ('Site Info', { 'fields': ('name', 'slug', 'account', 'domain', 'description', 'industry', 'site_type', 'hosting_type', 'status', 'is_active') }), ('WordPress Integration', { 'fields': ('wp_url', 'wp_username', 'wp_app_password', 'get_api_key_display'), 'description': 'Legacy WordPress integration fields. For WordPress sites using the IGNY8 WP Bridge plugin.' }), ('SEO Metadata', { 'fields': ('seo_metadata',), 'classes': ('collapse',) }), ('Timestamps', { 'fields': ('created_at', 'updated_at'), 'classes': ('collapse',) }), ) def get_api_key_display(self, obj): """Display API key with copy button""" if obj.wp_api_key: from django.utils.html import format_html return format_html( '
' '{}' '' '
', obj.wp_api_key, obj.wp_api_key ) return format_html('No API key generated') get_api_key_display.short_description = 'WordPress API Key' def get_api_key_status(self, obj): """Show API key status in list view""" if obj.wp_api_key: from django.utils.html import format_html return format_html(' Active') return format_html(' None') get_api_key_status.short_description = 'API Key' def generate_api_keys(self, request, queryset): """Generate API keys for selected sites""" import secrets updated_count = 0 for site in queryset: if not site.wp_api_key: site.wp_api_key = f"igny8_{''.join(secrets.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for _ in range(40))}" site.save() updated_count += 1 self.message_user(request, f'Generated API keys for {updated_count} site(s). Sites with existing keys were skipped.') generate_api_keys.short_description = 'Generate WordPress API Keys' def get_sectors_count(self, obj): try: return obj.get_active_sectors_count() except: return 0 get_sectors_count.short_description = 'Active Sectors' def get_industry_display(self, obj): """Safely get industry name""" try: return obj.industry.name if obj.industry else '-' except: return '-' get_industry_display.short_description = 'Industry' @admin.register(Sector) class SectorAdmin(AccountAdminMixin, admin.ModelAdmin): list_display = ['name', 'slug', 'site', 'industry_sector', 'get_industry', 'status', 'is_active', 'get_keywords_count', 'get_clusters_count'] list_filter = ['status', 'is_active', 'site', 'industry_sector__industry'] search_fields = ['name', 'slug', 'site__name', 'industry_sector__name'] readonly_fields = ['created_at', 'updated_at'] def get_industry(self, obj): """Safely get industry name""" try: if obj.industry_sector and obj.industry_sector.industry: return obj.industry_sector.industry.name except: pass return '-' get_industry.short_description = 'Industry' def get_keywords_count(self, obj): """Safely get keywords count""" try: if obj.pk: return getattr(obj, 'keywords_set', obj.keywords_set).count() except: pass return 0 get_keywords_count.short_description = 'Keywords' def get_clusters_count(self, obj): """Safely get clusters count""" try: if obj.pk: return getattr(obj, 'clusters_set', obj.clusters_set).count() except: pass return 0 get_clusters_count.short_description = 'Clusters' @admin.register(SiteUserAccess) class SiteUserAccessAdmin(admin.ModelAdmin): list_display = ['user', 'site', 'granted_at', 'granted_by'] list_filter = ['granted_at'] search_fields = ['user__email', 'site__name'] readonly_fields = ['granted_at'] class IndustrySectorInline(admin.TabularInline): """Inline admin for industry sectors within Industry admin.""" model = IndustrySector extra = 0 fields = ['name', 'slug', 'description', 'is_active'] readonly_fields = [] @admin.register(Industry) class IndustryAdmin(admin.ModelAdmin): list_display = ['name', 'slug', 'is_active', 'get_sectors_count', 'created_at'] list_filter = ['is_active'] search_fields = ['name', 'slug', 'description'] readonly_fields = ['created_at', 'updated_at'] inlines = [IndustrySectorInline] actions = ['delete_selected'] # Enable bulk delete change_list_template = 'admin/igny8_core_auth/industry/change_list.html' def get_sectors_count(self, obj): return obj.sectors.filter(is_active=True).count() get_sectors_count.short_description = 'Active Sectors' def has_delete_permission(self, request, obj=None): """Allow deletion for superusers and developers""" return request.user.is_superuser or (hasattr(request.user, 'is_developer') and request.user.is_developer()) @admin.register(IndustrySector) class IndustrySectorAdmin(admin.ModelAdmin): list_display = ['name', 'slug', 'industry', 'is_active'] list_filter = ['is_active', 'industry'] search_fields = ['name', 'slug', 'description'] readonly_fields = ['created_at', 'updated_at'] actions = ['delete_selected'] # Enable bulk delete change_list_template = 'admin/igny8_core_auth/industrysector/change_list.html' def has_delete_permission(self, request, obj=None): """Allow deletion for superusers and developers""" return request.user.is_superuser or (hasattr(request.user, 'is_developer') and request.user.is_developer()) @admin.register(SeedKeyword) class SeedKeywordAdmin(admin.ModelAdmin): """SeedKeyword admin - Global reference data, no account filtering""" list_display = ['keyword', 'industry', 'sector', 'volume', 'difficulty', 'intent', 'is_active', 'created_at'] list_filter = ['is_active', 'industry', 'sector', 'intent'] search_fields = ['keyword'] readonly_fields = ['created_at', 'updated_at'] actions = ['delete_selected'] # Enable bulk delete change_list_template = 'admin/igny8_core_auth/seedkeyword/change_list.html' fieldsets = ( ('Keyword Info', { 'fields': ('keyword', 'industry', 'sector', 'is_active') }), ('SEO Metrics', { 'fields': ('volume', 'difficulty', 'intent') }), ('Timestamps', { 'fields': ('created_at', 'updated_at') }), ) def has_delete_permission(self, request, obj=None): """Allow deletion for superusers and developers""" return request.user.is_superuser or (hasattr(request.user, 'is_developer') and request.user.is_developer()) @admin.register(User) class UserAdmin(BaseUserAdmin): list_display = ['email', 'username', 'account', 'role', 'is_active', 'is_staff', 'created_at'] list_filter = ['role', 'account', 'is_active', 'is_staff'] search_fields = ['email', 'username'] readonly_fields = ['created_at', 'updated_at'] fieldsets = BaseUserAdmin.fieldsets + ( ('IGNY8 Info', {'fields': ('account', 'role')}), ('Timestamps', {'fields': ('created_at', 'updated_at')}), ) add_fieldsets = BaseUserAdmin.add_fieldsets + ( ('IGNY8 Info', {'fields': ('account', 'role')}), ) def get_queryset(self, request): """Filter users by account for non-superusers""" qs = super().get_queryset(request) if request.user.is_superuser or (hasattr(request.user, 'is_developer') and request.user.is_developer()): return qs user_account = getattr(request.user, 'account', None) if user_account: return qs.filter(account=user_account) return qs.none() def get_account_display(self, obj): """Safely get account name""" try: account = getattr(obj, 'account', None) return account.name if account else '-' except: return '-' get_account_display.short_description = 'Account'