From f015351c42ee427c48fe0168010349ec16124096 Mon Sep 17 00:00:00 2001 From: erjemin Date: Sun, 23 Aug 2026 18:27:22 +0300 Subject: [PATCH] =?UTF-8?q?mod:=20=D0=B0=D0=B4=D0=BC=D0=B8=D0=BD=D0=BA?= =?UTF-8?q?=D0=B0=20(11)=20=D0=B4=D1=80=D0=B0=D1=84=D1=82=20ver2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hypn0/hypn0_site/admin.py | 273 +++++++++++++++++++++++++++----------- 1 file changed, 199 insertions(+), 74 deletions(-) diff --git a/hypn0/hypn0_site/admin.py b/hypn0/hypn0_site/admin.py index 383c85c..3450d5a 100644 --- a/hypn0/hypn0_site/admin.py +++ b/hypn0/hypn0_site/admin.py @@ -1,74 +1,199 @@ -# from django.contrib import admin -# -# from .models import BlogPost, HalftoneItem, HalftoneVote -# -# -# @admin.register(HalftoneItem) -# class HalftoneItemAdmin(admin.ModelAdmin): -# list_display = ( -# "hash_id", -# "title", -# "likes_count", -# "views_count", -# "score", -# "is_curated", -# "is_public", -# "created_at", -# ) -# list_filter = ( -# "is_curated", -# "is_public", -# "created_at", -# ) -# search_fields = ( -# "hash_id", -# "title", -# "promo_title", -# ) -# readonly_fields = ( -# "created_at", -# "score", -# "file_size", -# ) -# -# -# @admin.register(HalftoneVote) -# class HalftoneVoteAdmin(admin.ModelAdmin): -# list_display = ( -# "item", -# "fingerprint", -# "created_at", -# ) -# list_filter = ("created_at",) -# search_fields = ( -# "item__hash_id", -# "item__title", -# "fingerprint", -# ) -# readonly_fields = ("created_at",) -# -# -# @admin.register(BlogPost) -# class BlogPostAdmin(admin.ModelAdmin): -# list_display = ( -# "title", -# "slug", -# "is_published", -# "published_at", -# "created_at", -# ) -# list_filter = ( -# "is_published", -# "published_at", -# ) -# search_fields = ( -# "title", -# "slug", -# "summary", -# "content", -# ) -# prepopulated_fields = {"slug": ("title",)} -# readonly_fields = ( -# "created_at", -# "updated_at", -# ) +from django.contrib import admin +from django.utils.html import format_html +from .models import TbHypn0Item, TbVote, TbBlogPost + + +@admin.register(TbHypn0Item) +class TbHypn0ItemAdmin(admin.ModelAdmin): + """ + Панель управления гипно-картинами (модерация, просмотр статистики, редактирование). + Создание новых картин через админку заблокировано (создание только на фронтенде). + """ + list_display = ( + "id", + "s_hash_id", + "title_preview", + "i_level", + "i_likes_count", + "i_claims_count", + "i_views_count", + "f_score", + "is_public", + "d_created_at", + ) + list_display_links = ("id", "s_hash_id", "title_preview") + list_filter = ("i_level", "is_public", "d_created_at") + search_fields = ("s_hash_id", "s_title", "s_promo_title", "s_promo_url") + readonly_fields = ( + "id", + "s_hash_id", + "i_file_size", + "i_likes_count", + "i_views_count", + "i_claims_count", + "i_promo_clicks", + "d_created_at", + "d_updated_at", + "svg_preview", + ) + fieldsets = ( + ( + "Основная информация", + { + "fields": ( + "id", + "s_hash_id", + "s_title", + "file_svg", + "svg_preview", + "i_file_size", + "is_public", + ) + }, + ), + ( + "Модерация и Smart Retention", + { + "fields": ( + "i_level", + "f_score", + ), + "description": "Уровень модерации и рейтинг 'гравитации' для ротации и очистки хранилища.", + }, + ), + ( + "Метрики популярности", + { + "fields": ( + "i_likes_count", + "i_claims_count", + "i_views_count", + ), + }, + ), + ( + "Промо-блок (Спонсоры и Авторы)", + { + "fields": ( + "s_promo_url", + "s_promo_title", + "i_promo_clicks", + ), + "classes": ("collapse",), + }, + ), + ( + "Метаданные генерации", + { + "fields": ("j_metadata",), + "classes": ("collapse",), + }, + ), + ( + "Временные метки", + { + "fields": ("d_created_at", "d_updated_at"), + "classes": ("collapse",), + }, + ), + ) + + def has_add_permission(self, request): + # Создание картин возможно только с фронтенда (требуется обработка растра и согласие пользователя) + return False + + @admin.display(description="Название (HTML)") + def title_preview(self, obj): + return format_html(obj.s_title) + + @admin.display(description="Превью SVG") + def svg_preview(self, obj): + if obj.file_svg: + return format_html( + '
' + '' + '
', + obj.file_svg.url, + ) + return "Нет файла" + + +@admin.register(TbVote) +class TbVoteAdmin(admin.ModelAdmin): + """ + Просмотр анонимного журнала голосов и отпечатков (только для чтения). + """ + list_display = ("id", "k_item", "i_direction", "fingerprint_short", "d_created_at") + list_filter = ("i_direction", "d_created_at") + search_fields = ("s_fingerprint", "k_item__s_hash_id", "k_item__s_title") + readonly_fields = ("id", "k_item", "i_direction", "s_fingerprint", "d_created_at") + ordering = ("-d_created_at",) + + def has_add_permission(self, request): + return False + + def has_change_permission(self, request, obj=None): + return False + + @admin.display(description="Отпечаток (хэш)") + def fingerprint_short(self, obj): + return f"{obj.s_fingerprint[:12]}..." + + +@admin.register(TbBlogPost) +class TbBlogPostAdmin(admin.ModelAdmin): + """ + Управление статьями блога, документацией и инфо-страницами (HTML + etpgrf). + """ + list_display = ("id", "s_title_display", "slug", "is_published", "d_published_at", "d_created_at") + list_display_links = ("id", "s_title_display") + list_filter = ("is_published", "d_published_at", "d_created_at") + search_fields = ("s_title", "slug", "s_teaser", "s_content") + prepopulated_fields = {"slug": ("s_title",)} + readonly_fields = ("id", "d_created_at", "d_updated_at") + fieldsets = ( + ( + "Публикация", + { + "fields": ( + "is_published", + "d_published_at", + ) + }, + ), + ( + "Заголовок и URL", + { + "fields": ( + "s_title", + "slug", + "f_cover_img", + ) + }, + ), + ( + "Контент статьи (HTML + etpgrf)", + { + "fields": ( + "s_teaser", + "s_content", + ), + "description": "HTML-текст, типографированный etpgrf. Поддерживается чистая верстка.", + }, + ), + ( + "Системные даты", + { + "fields": ( + "id", + "d_created_at", + "d_updated_at", + ), + "classes": ("collapse",), + }, + ), + ) + + @admin.display(description="Заголовок") + def s_title_display(self, obj): + return format_html(obj.s_title)