from django.contrib import admin
from django.utils import timezone
from django.utils.html import format_html
from django.urls import path
from django.shortcuts import render
from django.db import models
from django_ckeditor_5.widgets import CKEditor5Widget
from .models import Home, Solution, Competence, Recall, CallbackRequest, PageView
# ===== BASE ADMIN CLASSES =====
class BaseContentAdmin(admin.ModelAdmin):
"""
Базовый класс для контентных моделей.
CKEditor, actions публикации, date_hierarchy.
"""
list_display_links = ('id', 'title')
list_editable = ('is_published',)
list_filter = ('time_create', 'is_published')
search_fields = ('title',)
date_hierarchy = 'time_create'
actions = ['make_published', 'make_unpublished']
readonly_fields = ('time_create', 'time_update')
formfield_overrides = {
models.TextField: {'widget': CKEditor5Widget(config_name='default')},
}
def make_published(self, request, queryset):
updated = queryset.update(is_published=True)
self.message_user(request, f'{updated} объектов опубликовано.')
make_published.short_description = 'Опубликовать выбранные'
def make_unpublished(self, request, queryset):
updated = queryset.update(is_published=False)
self.message_user(request, f'{updated} объектов снято с публикации.')
make_unpublished.short_description = 'Снять с публикации'
# ===== MODEL ADMINS =====
@admin.register(Home)
class HomeAdmin(BaseContentAdmin):
# Поля: title, content, home_image, time_create, time_update, is_published
list_display = ('id', 'title', 'time_create', 'is_published')
search_fields = ('title', 'content')
fieldsets = (
(None, {
'fields': ('title', 'is_published'),
}),
('Содержание', {
'fields': ('content', 'home_image'),
'description': 'Основной текст главной страницы.',
}),
('Служебное', {
'fields': ('time_create', 'time_update'),
'classes': ('collapse',),
}),
)
@admin.register(Solution)
class SolutionAdmin(BaseContentAdmin):
# Поля: title, description, implementation, closing, time_create, time_update, is_published
list_display = ('id', 'title', 'time_create', 'is_published')
list_display_links = ('id', 'title')
search_fields = ('title', 'description', 'implementation')
fieldsets = (
(None, {
'fields': ('title', 'is_published'),
}),
('Содержание', {
'fields': ('description', 'implementation', 'closing'),
'description': 'Описание, реализация и заключение по проекту.',
}),
('Служебное', {
'fields': ('time_create', 'time_update'),
'classes': ('collapse',),
}),
)
@admin.register(Competence)
class CompetenceAdmin(BaseContentAdmin):
# Поля: title, content, photo, time_create, time_update, is_published
list_display = ('id', 'title', 'time_create', 'is_published')
search_fields = ('title', 'content')
fieldsets = (
(None, {
'fields': ('title', 'is_published'),
}),
('Содержание', {
'fields': ('content', 'photo'),
'description': 'Описание компетенции и фото.',
}),
('Служебное', {
'fields': ('time_create', 'time_update'),
'classes': ('collapse',),
}),
)
@admin.register(Recall)
class RecallAdmin(BaseContentAdmin):
# Поля: title, content, scan, time_create, time_update, is_published
list_display = ('id', 'title', 'time_create', 'scan_preview', 'is_published')
search_fields = ('title', 'content')
fieldsets = (
(None, {
'fields': ('title', 'is_published'),
}),
('Содержание', {
'fields': ('content', 'scan', 'scan_preview'),
'description': 'Текст отзыва и скан документа.',
}),
('Служебное', {
'fields': ('time_create', 'time_update'),
'classes': ('collapse',),
}),
)
readonly_fields = ('time_create', 'time_update', 'scan_preview')
def scan_preview(self, obj):
if obj.scan:
return format_html(
'
',
obj.scan.url
)
return '—'
scan_preview.short_description = 'Превью скана'
# ===== CALLBACK =====
@admin.register(CallbackRequest)
class CallbackAdmin(admin.ModelAdmin):
list_display = ('name', 'phone', 'email', 'time_create', 'is_processed', 'is_read', 'new_badge')
list_display_links = ('name', 'phone')
list_editable = ('is_processed', 'is_read')
list_filter = ('time_create', 'is_processed', 'is_read')
search_fields = ('name', 'phone', 'email')
readonly_fields = ('time_create',)
actions = ['mark_as_read', 'mark_as_unread', 'mark_as_processed', 'resend_notification']
def new_badge(self, obj):
if not obj.is_read:
return format_html('🆕 НОВАЯ')
return ''
new_badge.short_description = 'Статус'
def mark_as_read(self, request, queryset):
updated = queryset.update(is_read=True)
self.message_user(request, f'{updated} заявок отмечены как прочитанные')
mark_as_read.short_description = 'Отметить как прочитанные'
def mark_as_unread(self, request, queryset):
updated = queryset.update(is_read=False)
self.message_user(request, f'{updated} заявок отмечены как непрочитанные')
mark_as_unread.short_description = 'Отметить как непрочитанные'
def mark_as_processed(self, request, queryset):
updated = queryset.update(is_processed=True)
self.message_user(request, f'{updated} заявок отмечены как обработанные')
mark_as_processed.short_description = 'Отметить как обработанные'
def resend_notification(self, request, queryset):
from .utils.email_notifications import send_multiple_callback_notifications
count = send_multiple_callback_notifications(queryset)
self.message_user(request, f'Уведомления отправлены для {count} заявок')
resend_notification.short_description = 'Переотправить email уведомления'
def get_urls(self):
urls = super().get_urls()
custom_urls = [
path(
'callback-stats/',
self.admin_site.admin_view(self.callback_stats_view),
name='callback_stats',
),
]
return custom_urls + urls
def callback_stats_view(self, request):
today = timezone.now().date()
week_ago = today - timezone.timedelta(days=7)
stats = {
'total': CallbackRequest.objects.count(),
'today': CallbackRequest.objects.filter(time_create__date=today).count(),
'week': CallbackRequest.objects.filter(time_create__date__gte=week_ago).count(),
'unread': CallbackRequest.objects.filter(is_read=False).count(),
'unprocessed': CallbackRequest.objects.filter(is_processed=False).count(),
}
context = {
**self.admin_site.each_context(request),
'title': 'Статистика заявок',
'stats': stats,
}
return render(request, 'admin/callback_stats.html', context)
# ===== PAGE VIEWS =====
@admin.register(PageView)
class PageViewAdmin(admin.ModelAdmin):
list_display = ('url', 'timestamp', 'ip_address')
list_filter = ('timestamp', 'url')
search_fields = ('url', 'ip_address')
date_hierarchy = 'timestamp'
readonly_fields = ('url', 'timestamp', 'ip_address')
def get_queryset(self, request):
return super().get_queryset(request).order_by('-timestamp')
def has_add_permission(self, request):
return False
def has_change_permission(self, request, obj=None):
return False