479 lines
18 KiB
Python
479 lines
18 KiB
Python
"""
|
|
Zentrale Admin-Konfiguration für Kasico Fursuit Shop
|
|
"""
|
|
|
|
from django.contrib import admin
|
|
from django.contrib.admin import AdminSite
|
|
from django.utils.html import format_html
|
|
from django.urls import path
|
|
from django.db.models import Count, Sum, Avg
|
|
from django.utils import timezone
|
|
from datetime import timedelta
|
|
|
|
# Import aller Modelle
|
|
from products.models import (
|
|
Product, Order, OrderItem, Review, UserProfile, FAQ,
|
|
ContactMessage, CustomOrder, OrderProgress, GalleryImage
|
|
)
|
|
from shop.models import (
|
|
Category, ProductType, ProductImage, ProductVariant,
|
|
CustomDesign, PayPalPayment, PaymentError, Cart, CartItem
|
|
)
|
|
# Temporär deaktiviert
|
|
# from chat.models import (
|
|
# ChatRoom, ChatMessage, UserOnlineStatus, QuickResponse, ChatAnalytics
|
|
# )
|
|
# from auction.models import (
|
|
# Auction, Bid, AuctionWatch, AuctionAnalytics
|
|
# )
|
|
# from recommendations.models import (
|
|
# UserBehavior, UserProfile as RecUserProfile, ProductSimilarity,
|
|
# Recommendation, RecommendationModel, ABTest, RecommendationAnalytics
|
|
# )
|
|
# from mobile.models import (
|
|
# MobileDevice, PushNotification, OfflineSync, MobileAnalytics,
|
|
# MobileCache, MobileSession
|
|
# )
|
|
# from paypal_integration.models import PayPalConfig
|
|
|
|
|
|
# Custom Admin Site
|
|
class KasicoAdminSite(AdminSite):
|
|
site_header = "🐾 Kasico Fursuit Shop Administration"
|
|
site_title = "Kasico Admin"
|
|
index_title = "Willkommen in der Kasico Administration"
|
|
|
|
def get_app_list(self, request):
|
|
"""
|
|
Gruppiere Apps in logische Kategorien
|
|
"""
|
|
app_list = super().get_app_list(request)
|
|
|
|
# Definiere App-Gruppen
|
|
app_groups = {
|
|
'Shop Management': ['products', 'shop'],
|
|
'Customer Service': ['chat'],
|
|
'Analytics & ML': ['recommendations'],
|
|
'Mobile & API': ['mobile'],
|
|
'Payment & Integration': ['paypal_integration'],
|
|
}
|
|
|
|
# Gruppiere Apps
|
|
grouped_apps = {}
|
|
for app in app_list:
|
|
app_name = app['app_label']
|
|
for group, apps in app_groups.items():
|
|
if app_name in apps:
|
|
if group not in grouped_apps:
|
|
grouped_apps[group] = []
|
|
grouped_apps[group].append(app)
|
|
break
|
|
else:
|
|
# Apps die nicht in Gruppen sind
|
|
if 'Other' not in grouped_apps:
|
|
grouped_apps['Other'] = []
|
|
grouped_apps['Other'].append(app)
|
|
|
|
# Flatten und sortieren
|
|
final_app_list = []
|
|
for group in sorted(grouped_apps.keys()):
|
|
if group != 'Other':
|
|
final_app_list.extend(grouped_apps[group])
|
|
if 'Other' in grouped_apps:
|
|
final_app_list.extend(grouped_apps['Other'])
|
|
|
|
return final_app_list
|
|
|
|
|
|
# Erstelle Admin Site Instanz
|
|
admin_site = KasicoAdminSite(name='kasico_admin')
|
|
|
|
|
|
# =============================================================================
|
|
# SHOP MANAGEMENT - PRODUCTS
|
|
# =============================================================================
|
|
|
|
@admin.register(Product, site=admin_site)
|
|
class ProductAdmin(admin.ModelAdmin):
|
|
list_display = ('name', 'price', 'stock', 'fursuit_type', 'style', 'is_featured', 'is_custom_order', 'admin_image')
|
|
list_filter = ('fursuit_type', 'style', 'is_featured', 'is_custom_order', 'created')
|
|
search_fields = ('name', 'description')
|
|
ordering = ('-created',)
|
|
list_editable = ('is_featured', 'is_custom_order')
|
|
readonly_fields = ('admin_image',)
|
|
|
|
fieldsets = (
|
|
('Grundinformationen', {
|
|
'fields': ('name', 'description', 'price', 'stock')
|
|
}),
|
|
('Fursuit-Details', {
|
|
'fields': ('fursuit_type', 'style')
|
|
}),
|
|
('Status & Features', {
|
|
'fields': ('is_featured', 'is_custom_order', 'image')
|
|
}),
|
|
('Vorschau', {
|
|
'fields': ('admin_image',),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
def admin_image(self, obj):
|
|
if obj.image:
|
|
return format_html('<img src="{}" style="max-height: 50px;" />', obj.image.url)
|
|
return "Kein Bild"
|
|
admin_image.short_description = 'Vorschau'
|
|
|
|
|
|
@admin.register(Category, site=admin_site)
|
|
class CategoryAdmin(admin.ModelAdmin):
|
|
list_display = ('name', 'slug', 'parent', 'product_count')
|
|
list_filter = ('parent',)
|
|
search_fields = ('name',)
|
|
prepopulated_fields = {'slug': ('name',)}
|
|
|
|
def product_count(self, obj):
|
|
return obj.products.count()
|
|
product_count.short_description = 'Produkte'
|
|
|
|
|
|
@admin.register(GalleryImage, site=admin_site)
|
|
class GalleryImageAdmin(admin.ModelAdmin):
|
|
list_display = ('title', 'fursuit_type', 'style', 'is_featured', 'order', 'created', 'admin_image')
|
|
list_filter = ('fursuit_type', 'style', 'is_featured')
|
|
search_fields = ('title', 'description')
|
|
ordering = ('order', '-created')
|
|
list_editable = ('order', 'is_featured')
|
|
readonly_fields = ('admin_image',)
|
|
|
|
def admin_image(self, obj):
|
|
if obj.image:
|
|
return format_html('<img src="{}" style="max-height: 50px;" />', obj.image.url)
|
|
return "Kein Bild"
|
|
admin_image.short_description = 'Vorschau'
|
|
|
|
|
|
# =============================================================================
|
|
# SHOP MANAGEMENT - ORDERS
|
|
# =============================================================================
|
|
|
|
class OrderItemInline(admin.TabularInline):
|
|
model = OrderItem
|
|
extra = 0
|
|
readonly_fields = ('product_name', 'price')
|
|
|
|
@admin.register(Order, site=admin_site)
|
|
class OrderAdmin(admin.ModelAdmin):
|
|
list_display = ('id', 'full_name', 'total_amount', 'status', 'payment_status', 'payment_method', 'created')
|
|
list_filter = ('status', 'payment_status', 'payment_method', 'created')
|
|
search_fields = ('full_name', 'email', 'id')
|
|
ordering = ('-created',)
|
|
readonly_fields = ('id', 'created', 'total_amount')
|
|
inlines = [OrderItemInline]
|
|
|
|
fieldsets = (
|
|
('Bestellinformationen', {
|
|
'fields': ('id', 'user', 'full_name', 'email', 'phone', 'address')
|
|
}),
|
|
('Zahlung & Status', {
|
|
'fields': ('status', 'payment_status', 'payment_method', 'total_amount')
|
|
}),
|
|
('Zeitstempel', {
|
|
'fields': ('created',),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
actions = ['mark_as_paid', 'mark_as_shipped']
|
|
|
|
def mark_as_paid(self, request, queryset):
|
|
updated = queryset.update(payment_status='paid')
|
|
self.message_user(request, f'{updated} Bestellungen als bezahlt markiert.')
|
|
mark_as_paid.short_description = "Als bezahlt markieren"
|
|
|
|
def mark_as_shipped(self, request, queryset):
|
|
updated = queryset.update(status='shipped')
|
|
self.message_user(request, f'{updated} Bestellungen als versendet markiert.')
|
|
mark_as_shipped.short_description = "Als versendet markieren"
|
|
|
|
|
|
@admin.register(CustomOrder, site=admin_site)
|
|
class CustomOrderAdmin(admin.ModelAdmin):
|
|
list_display = ('character_name', 'user', 'fursuit_type', 'style', 'budget_range', 'status', 'created')
|
|
list_filter = ('fursuit_type', 'style', 'status', 'budget_range', 'created')
|
|
search_fields = ('character_name', 'user__username', 'character_description')
|
|
ordering = ('-created',)
|
|
readonly_fields = ('created',)
|
|
|
|
fieldsets = (
|
|
('Character & Design', {
|
|
'fields': ('character_name', 'character_description', 'fursuit_type', 'style')
|
|
}),
|
|
('Kunde & Budget', {
|
|
'fields': ('user', 'budget_range', 'deadline_request')
|
|
}),
|
|
('Details & Dateien', {
|
|
'fields': ('color_preferences', 'special_requests', 'reference_images')
|
|
}),
|
|
('Status', {
|
|
'fields': ('status', 'created')
|
|
}),
|
|
)
|
|
|
|
|
|
@admin.register(OrderProgress, site=admin_site)
|
|
class OrderProgressAdmin(admin.ModelAdmin):
|
|
list_display = ('custom_order', 'stage', 'completed', 'created')
|
|
list_filter = ('stage', 'completed', 'created')
|
|
search_fields = ('custom_order__character_name', 'description')
|
|
ordering = ('custom_order', 'created')
|
|
|
|
|
|
# =============================================================================
|
|
# CUSTOMER SERVICE
|
|
# =============================================================================
|
|
|
|
@admin.register(ContactMessage, site=admin_site)
|
|
class ContactMessageAdmin(admin.ModelAdmin):
|
|
list_display = ('subject', 'name', 'email', 'category', 'status', 'created')
|
|
list_filter = ('category', 'status', 'created')
|
|
search_fields = ('name', 'email', 'subject', 'message')
|
|
ordering = ('-created',)
|
|
readonly_fields = ('created',)
|
|
|
|
fieldsets = (
|
|
('Nachricht', {
|
|
'fields': ('subject', 'name', 'email', 'category', 'message')
|
|
}),
|
|
('Status & Notizen', {
|
|
'fields': ('status', 'staff_notes')
|
|
}),
|
|
('Zeitstempel', {
|
|
'fields': ('created',),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
actions = ['mark_as_in_progress', 'mark_as_resolved']
|
|
|
|
def mark_as_in_progress(self, request, queryset):
|
|
updated = queryset.update(status='in_progress')
|
|
self.message_user(request, f'{updated} Nachrichten als in Bearbeitung markiert.')
|
|
mark_as_in_progress.short_description = "Als in Bearbeitung markieren"
|
|
|
|
def mark_as_resolved(self, request, queryset):
|
|
updated = queryset.update(status='resolved')
|
|
self.message_user(request, f'{updated} Nachrichten als erledigt markiert.')
|
|
mark_as_resolved.short_description = "Als erledigt markieren"
|
|
|
|
|
|
# =============================================================================
|
|
# CHAT & COMMUNICATION (Temporär deaktiviert)
|
|
# =============================================================================
|
|
|
|
# @admin.register(ChatRoom, site=admin_site)
|
|
# class ChatRoomAdmin(admin.ModelAdmin):
|
|
# list_display = ('id', 'customer', 'admin', 'status', 'subject', 'last_message_at', 'message_count')
|
|
# list_filter = ('status', 'created_at')
|
|
# search_fields = ('customer__username', 'admin__username', 'subject')
|
|
# ordering = ('-last_message_at',)
|
|
# readonly_fields = ('id', 'created_at', 'last_message_at')
|
|
|
|
# def message_count(self, obj):
|
|
# return obj.messages.count()
|
|
# message_count.short_description = 'Nachrichten'
|
|
|
|
|
|
# @admin.register(ChatMessage, site=admin_site)
|
|
# class ChatMessageAdmin(admin.ModelAdmin):
|
|
# list_display = ('room', 'sender', 'message_type', 'content_preview', 'created_at')
|
|
# list_filter = ('message_type', 'created_at')
|
|
# search_fields = ('content', 'sender__username')
|
|
# ordering = ('-created_at',)
|
|
# readonly_fields = ('created_at',)
|
|
|
|
# def content_preview(self, obj):
|
|
# return obj.content[:50] + "..." if len(obj.content) > 50 else obj.content
|
|
# content_preview.short_description = 'Inhalt'
|
|
|
|
|
|
# @admin.register(QuickResponse, site=admin_site)
|
|
# class QuickResponseAdmin(admin.ModelAdmin):
|
|
# list_display = ('title', 'category', 'is_active', 'use_count', 'created_by')
|
|
# list_filter = ('category', 'is_active', 'created_at')
|
|
# search_fields = ('title', 'content')
|
|
# ordering = ('category', 'title')
|
|
|
|
|
|
# =============================================================================
|
|
# ANALYTICS & ML (Temporär deaktiviert)
|
|
# =============================================================================
|
|
|
|
# @admin.register(UserBehavior, site=admin_site)
|
|
# class UserBehaviorAdmin(admin.ModelAdmin):
|
|
# list_display = ('user', 'behavior_type', 'product', 'created_at')
|
|
# list_filter = ('behavior_type', 'created_at')
|
|
# search_fields = ('user__username', 'product__name')
|
|
# ordering = ('-created_at',)
|
|
# readonly_fields = ('created_at',)
|
|
|
|
|
|
# @admin.register(Recommendation, site=admin_site)
|
|
# class RecommendationAdmin(admin.ModelAdmin):
|
|
# list_display = ('user', 'product', 'recommendation_type', 'confidence_score', 'is_clicked', 'is_purchased')
|
|
# list_filter = ('recommendation_type', 'is_clicked', 'is_purchased', 'created_at')
|
|
# search_fields = ('user__username', 'product__name')
|
|
# ordering = ('-created_at',)
|
|
# readonly_fields = ('created_at',)
|
|
|
|
|
|
# @admin.register(RecommendationModel, site=admin_site)
|
|
# class RecommendationModelAdmin(admin.ModelAdmin):
|
|
# list_display = ('model_name', 'model_type', 'model_version', 'accuracy_score', 'is_active', 'trained_at')
|
|
# list_filter = ('model_type', 'is_active', 'trained_at')
|
|
# search_fields = ('model_name',)
|
|
# readonly_fields = ('created_at', 'trained_at')
|
|
|
|
# fieldsets = (
|
|
# ('Model Info', {
|
|
# 'fields': ('model_type', 'model_name', 'model_version', 'model_file')
|
|
# }),
|
|
# ('Performance', {
|
|
# 'fields': ('training_data_size', 'accuracy_score', 'precision_score', 'recall_score', 'f1_score')
|
|
# }),
|
|
# ('Status', {
|
|
# 'fields': ('is_active', 'created_at', 'trained_at')
|
|
# }),
|
|
# )
|
|
|
|
|
|
# =============================================================================
|
|
# MOBILE & API (Temporär deaktiviert)
|
|
# =============================================================================
|
|
|
|
# @admin.register(MobileDevice, site=admin_site)
|
|
# class MobileDeviceAdmin(admin.ModelAdmin):
|
|
# list_display = ('user', 'device_type', 'device_name', 'is_active', 'last_seen')
|
|
# list_filter = ('device_type', 'is_active', 'created_at')
|
|
# search_fields = ('user__username', 'device_name', 'device_token')
|
|
# ordering = ('-created_at',)
|
|
# readonly_fields = ('created_at', 'last_seen')
|
|
|
|
|
|
# =============================================================================
|
|
# PAYMENT & INTEGRATION (Temporär deaktiviert)
|
|
# =============================================================================
|
|
|
|
# @admin.register(PayPalConfig, site=admin_site)
|
|
# class PayPalConfigAdmin(admin.ModelAdmin):
|
|
# list_display = ('get_mode', 'client_id', 'is_sandbox', 'updated_at')
|
|
# readonly_fields = ('created_at', 'updated_at')
|
|
|
|
# fieldsets = (
|
|
# ('API Konfiguration', {
|
|
# 'fields': ('client_id', 'client_secret', 'is_sandbox')
|
|
# }),
|
|
# ('Zeitstempel', {
|
|
# 'fields': ('created_at', 'updated_at'),
|
|
# 'classes': ('collapse',)
|
|
# }),
|
|
# )
|
|
|
|
|
|
# =============================================================================
|
|
# USERS & PROFILES
|
|
# =============================================================================
|
|
|
|
@admin.register(UserProfile, site=admin_site)
|
|
class UserProfileAdmin(admin.ModelAdmin):
|
|
list_display = ('user', 'phone', 'newsletter', 'created')
|
|
list_filter = ('newsletter', 'created')
|
|
search_fields = ('user__username', 'phone', 'address')
|
|
ordering = ('-created',)
|
|
readonly_fields = ('created',)
|
|
|
|
|
|
@admin.register(Review, site=admin_site)
|
|
class ReviewAdmin(admin.ModelAdmin):
|
|
list_display = ('product', 'user', 'rating', 'created')
|
|
list_filter = ('rating', 'created')
|
|
search_fields = ('comment', 'user__username', 'product__name')
|
|
ordering = ('-created',)
|
|
readonly_fields = ('created',)
|
|
|
|
|
|
@admin.register(FAQ, site=admin_site)
|
|
class FAQAdmin(admin.ModelAdmin):
|
|
list_display = ('question', 'category', 'order')
|
|
list_filter = ('category',)
|
|
search_fields = ('question', 'answer')
|
|
ordering = ('category', 'order')
|
|
list_editable = ('order',)
|
|
|
|
|
|
# =============================================================================
|
|
# DASHBOARD & STATISTICS
|
|
# =============================================================================
|
|
|
|
class DashboardAdmin(admin.ModelAdmin):
|
|
def get_urls(self):
|
|
urls = super().get_urls()
|
|
custom_urls = [
|
|
path('dashboard/', self.admin_site.admin_view(self.dashboard_view), name='dashboard'),
|
|
]
|
|
return custom_urls + urls
|
|
|
|
def dashboard_view(self, request):
|
|
# Dashboard Statistiken
|
|
context = {
|
|
'total_orders': Order.objects.count(),
|
|
'total_products': Product.objects.count(),
|
|
'total_users': UserProfile.objects.count(),
|
|
'recent_orders': Order.objects.order_by('-created')[:5],
|
|
'low_stock_products': Product.objects.filter(stock__lte=5),
|
|
'pending_contacts': ContactMessage.objects.filter(status='new'),
|
|
}
|
|
return render(request, 'admin/dashboard.html', context)
|
|
|
|
|
|
# =============================================================================
|
|
# ADMIN ACTIONS
|
|
# =============================================================================
|
|
|
|
@admin.action(description="Produkte als hervorgehoben markieren")
|
|
def mark_featured(modeladmin, request, queryset):
|
|
updated = queryset.update(is_featured=True)
|
|
modeladmin.message_user(request, f'{updated} Produkte als hervorgehoben markiert.')
|
|
|
|
|
|
@admin.action(description="Lagerbestand auf 0 setzen")
|
|
def set_stock_zero(modeladmin, request, queryset):
|
|
updated = queryset.update(stock=0)
|
|
modeladmin.message_user(request, f'{updated} Produkte auf Lagerbestand 0 gesetzt.')
|
|
|
|
|
|
# Füge Actions zu Admin-Klassen hinzu
|
|
ProductAdmin.actions = [mark_featured, set_stock_zero]
|
|
|
|
|
|
# =============================================================================
|
|
# ADMIN SITE REGISTRATION
|
|
# =============================================================================
|
|
|
|
# Registriere nur Modelle ohne eigene Admin-Klasse
|
|
admin_site.register(OrderItem)
|
|
admin_site.register(Cart)
|
|
admin_site.register(CartItem)
|
|
admin_site.register(ProductType)
|
|
admin_site.register(ProductImage)
|
|
admin_site.register(ProductVariant)
|
|
admin_site.register(CustomDesign)
|
|
# Temporär deaktiviert
|
|
# admin_site.register(PayPalPayment)
|
|
# admin_site.register(PaymentError)
|
|
# admin_site.register(UserOnlineStatus)
|
|
# admin_site.register(ChatAnalytics)
|
|
# admin_site.register(ProductSimilarity)
|
|
# admin_site.register(ABTest)
|
|
# admin_site.register(RecommendationAnalytics)
|
|
# admin_site.register(MobileSession) |