from django.contrib import admin
from django.utils.html import format_html
from django.urls import reverse
from django.utils.safestring import mark_safe
from .models import Website, CrawlTask, CrawledContent, CrawlLog, SearchKeyword, MediaFile
from .tasks import crawl_websites_task
@admin.register(Website)
class WebsiteAdmin(admin.ModelAdmin):
list_display = ['name', 'region', 'url', 'is_active', 'created_at']
list_filter = ['region', 'is_active', 'created_at']
search_fields = ['name', 'url', 'region']
list_editable = ['is_active']
ordering = ['region', 'name']
@admin.register(CrawlTask)
class CrawlTaskAdmin(admin.ModelAdmin):
list_display = ['name', 'status', 'created_by', 'progress_display', 'created_at', 'completed_at']
list_filter = ['status', 'created_by', 'created_at']
search_fields = ['name', 'keywords']
readonly_fields = ['created_at', 'started_at', 'completed_at', 'progress_display']
filter_horizontal = ['websites']
actions = ['execute_crawl_task']
def progress_display(self, obj):
"""显示任务进度"""
if obj.status == 'completed':
color = 'green'
elif obj.status == 'failed':
color = 'red'
elif obj.status == 'running':
color = 'orange'
else:
color = 'gray'
return format_html(
'{}%',
color,
f'{obj.progress_percentage:.1f} ({obj.crawled_pages}/{obj.total_pages})'
)
progress_display.short_description = '进度'
def execute_crawl_task(self, request, queryset):
"""执行选中的爬取任务"""
for task in queryset:
# 更新任务状态为pending
task.status = 'pending'
task.save()
# 异步执行爬取任务
crawl_websites_task.delay(task.id)
self.message_user(request, f"已启动 {queryset.count()} 个爬取任务。")
execute_crawl_task.short_description = "执行选中的爬取任务"
@admin.register(CrawledContent)
class CrawledContentAdmin(admin.ModelAdmin):
list_display = ['title_display', 'website', 'task', 'keywords_matched', 'media_count', 'publish_date', 'is_local_saved', 'created_at']
list_filter = ['website', 'task', 'created_at', 'publish_date', 'is_local_saved']
search_fields = ['title', 'content', 'keywords_matched']
readonly_fields = ['created_at', 'preview_content', 'media_files_display']
ordering = ['-created_at']
class Media:
css = {
'all': ('admin/css/custom_admin.css',)
}
def title_display(self, obj):
"""显示完整的标题,但在列表中限制宽度"""
if len(obj.title) > 100:
return format_html(
'
{}
',
obj.title,
obj.title[:100] + '...'
)
return format_html(
'{}
',
obj.title
)
title_display.short_description = '标题'
title_display.admin_order_field = 'title'
def media_count(self, obj):
"""显示媒体文件数量"""
count = obj.media_files.count()
if count > 0:
return format_html(
'{}',
count
)
return "0"
media_count.short_description = '媒体文件'
def preview_content(self, obj):
"""预览内容"""
if obj.is_local_saved:
url = reverse('preview_crawled_content', args=[obj.id])
return format_html(
'预览文章',
url
)
elif obj.content:
return format_html(
'{}
',
obj.get_preview_content(500)
)
else:
return "无内容"
preview_content.short_description = '内容预览'
def media_files_display(self, obj):
"""显示媒体文件列表"""
media_files = obj.media_files.all()
if not media_files:
return "无媒体文件"
html = ""
for media_file in media_files:
if media_file.media_type == 'image':
html += format_html(
'
'
'
图片: {}
'
'

'
'
大小: {}'
'
',
media_file.alt_text or '无标题',
media_file.local_file.name,
media_file.file_size_display
)
elif media_file.media_type == 'video':
html += format_html(
'
'
'视频:
'
'
'
'大小: {}'
'
',
media_file.local_file.name,
media_file.mime_type,
media_file.file_size_display
)
else:
html += format_html(
'
',
media_file.get_media_type_display(),
media_file.local_file.name,
media_file.file_size_display
)
html += "
"
return format_html(html)
media_files_display.short_description = '媒体文件'
# @admin.register(CrawlLog)
# class CrawlLogAdmin(admin.ModelAdmin):
# list_display = ['level', 'message_short', 'website', 'task', 'created_at']
# list_filter = ['level', 'website', 'task', 'created_at']
# search_fields = ['message']
# readonly_fields = ['created_at']
# ordering = ['-created_at']
#
# def message_short(self, obj):
# """显示缩短的消息"""
# return obj.message[:100] + '...' if len(obj.message) > 100 else obj.message
# message_short.short_description = '消息'
# @admin.register(MediaFile)
# class MediaFileAdmin(admin.ModelAdmin):
# list_display = ['content', 'media_type', 'file_size_display', 'mime_type', 'created_at']
# list_filter = ['media_type', 'created_at']
# search_fields = ['content__title', 'original_url', 'alt_text']
# readonly_fields = ['created_at', 'file_size_display', 'media_preview']
# ordering = ['-created_at']
#
# def media_preview(self, obj):
# """媒体文件预览"""
# if obj.media_type == 'image' and obj.local_file:
# return format_html(
# '
',
# obj.local_file.name
# )
# elif obj.media_type == 'video' and obj.local_file:
# return format_html(
# '',
# obj.local_file.name,
# obj.mime_type
# )
# elif obj.media_type == 'audio' and obj.local_file:
# return format_html(
# '',
# obj.local_file.name,
# obj.mime_type
# )
# else:
# return "无预览"
# media_preview.short_description = '预览'
# @admin.register(SearchKeyword)
# class SearchKeywordAdmin(admin.ModelAdmin):
# list_display = ['keyword', 'is_active', 'created_at', 'last_used']
# list_filter = ['is_active', 'created_at', 'last_used']
# search_fields = ['keyword']
# list_editable = ['is_active']
# ordering = ['-last_used', '-created_at']