"""
自动命名工具Admin
独立的Admin模块
"""
from __future__ import annotations
from pathlib import Path
from typing import Any, ClassVar
from django import forms
from django.contrib import admin
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
from django.middleware.csrf import get_token
from django.urls import path, reverse
from apps.automation.models import NamerTool
from apps.automation.services.ai.prompts import DEFAULT_FILENAME_PROMPT
from apps.automation.services.document.document_processing import process_uploaded_document
from apps.core.interfaces import ServiceLocator
class AutoNamerToolForm(forms.Form):
"""自动命名工具表单"""
upload = forms.FileField(required=True, help_text="支持PDF、DOCX和图片文件(JPG、PNG、BMP、TIFF等)")
prompt = forms.CharField(
required=True,
widget=forms.Textarea(attrs={"rows": 8}),
initial=DEFAULT_FILENAME_PROMPT,
help_text="AI提示词,用于指导模型生成合适的文件名",
)
model = forms.CharField(required=True, initial="qwen3:0.6b", help_text="使用的AI模型名称")
limit = forms.IntegerField(required=False, help_text="文字提取限制(留空使用默认值1500字)")
preview_page = forms.IntegerField(required=False, min_value=1, help_text="PDF预览页码(留空使用默认值第1页)")
@admin.register(NamerTool)
class AutoNamerToolAdmin(admin.ModelAdmin[NamerTool]):
"""自动命名工具管理类"""
change_list_template = None
def get_urls(self) -> list[Any]:
urls = super().get_urls()
info = self.model._meta.app_label, self.model._meta.model_name
custom: list[Any] = [
path("process/", self.admin_site.admin_view(self.process_view), name="{}_{}_process".format(*info)),
path("", self.admin_site.admin_view(self.redirect_to_process)),
]
return custom + urls
def redirect_to_process(self, request: HttpRequest) -> HttpResponseRedirect:
info = self.model._meta.app_label, self.model._meta.model_name
return HttpResponseRedirect(reverse("admin:{}_{}_process".format(*info)))
def _process_view_post(self, request: Any, form: AutoNamerToolForm) -> HttpResponse:
"""处理 POST 请求"""
fp = form.cleaned_data["upload"]
prompt = form.cleaned_data["prompt"]
model = form.cleaned_data["model"]
limit = form.cleaned_data.get("limit")
preview_page = form.cleaned_data.get("preview_page")
info = self.model._meta.app_label, self.model._meta.model_name
return_url = reverse("admin:{}_{}_process".format(*info))
try:
extraction = process_uploaded_document(fp, limit=limit, preview_page=preview_page)
except ValueError as e:
return HttpResponse(str(e))
text = extraction.text or ""
if extraction.kind not in {"pdf", "docx", "image"}:
return HttpResponse("不支持的文件类型,支持PDF、DOCX和图片文件(JPG、PNG等)")
if not text.strip():
return self._render_no_text_error(extraction, return_url)
return self._render_ai_result(model, prompt, text, extraction, return_url)
def _render_no_text_error(self, extraction: Any, return_url: str) -> HttpResponse:
"""渲染无文字内容错误页"""
error_msg = "文档中没有提取到文字内容,无法生成命名。"
if extraction.kind == "image":
error_msg += "
提示:图片文件可能包含手写文字、复杂排版或图片质量较差,建议尝试更清晰的扫描件。"
elif extraction.kind == "pdf":
error_msg += "
提示:PDF可能是扫描版或图片格式,系统已尝试OCR识别但未成功。"
html = f"""
{error_msg}
文件信息:
📄 文件信息:
{text}
{response_text}
{error_detail}
{error_traceback}