fc / apps /pdf_splitting /models.py
jun2024's picture
Upload 2241 files
b4f2238 verified
Raw History Blame Contribute Delete
7.53 kB
from __future__ import annotations
import uuid
from typing import ClassVar
from django.conf import settings
from django.db import models
from django.db.models.signals import post_delete
from django.dispatch import receiver
from django.utils.translation import gettext_lazy as _
class PdfSplitJobStatus(models.TextChoices):
PENDING = "pending", _("待处理")
PROCESSING = "processing", _("处理中")
REVIEW_REQUIRED = "review_required", _("待复核")
EXPORTING = "exporting", _("导出中")
COMPLETED = "completed", _("已完成")
FAILED = "failed", _("失败")
CANCELLED = "cancelled", _("已取消")
class PdfSplitSourceType(models.TextChoices):
UPLOAD = "upload", _("上传文件")
LOCAL_PATH = "local_path", _("本地路径")
class PdfSplitMode(models.TextChoices):
CONTENT_ANALYSIS = "content_analysis", _("内容识别拆分")
PAGE_SPLIT = "page_split", _("按页拆分")
MANUAL_SPLIT = "manual_split", _("手动拆分")
class PdfSplitOcrProfile(models.TextChoices):
FAST = "fast", _("快速")
BALANCED = "balanced", _("均衡")
ACCURATE = "accurate", _("高精度")
class PdfSplitSegmentType(models.TextChoices):
COMPLAINT = "complaint", _("起诉状")
EVIDENCE_LIST = "evidence_list", _("证据清单及明细")
PRESERVATION_MATERIALS = "preservation_materials", _("财产保全资料")
PARTY_IDENTITY = "party_identity", _("双方当事人主体信息")
AUTHORIZATION_MATERIALS = "authorization_materials", _("授权委托材料")
DELIVERY_ADDRESS_CONFIRMATION = "delivery_address_confirmation", _("送达地址确认书")
REFUND_ACCOUNT_CONFIRMATION = "refund_account_confirmation", _("诉讼费用退费账户确认书")
UNRECOGNIZED = "unrecognized", _("未识别材料")
class PdfSplitReviewFlag(models.TextChoices):
NORMAL = "normal", _("正常")
LOW_CONFIDENCE = "low_confidence", _("低置信度")
UNRECOGNIZED = "unrecognized", _("未识别")
OCR_FAILED = "ocr_failed", _("OCR失败")
class PdfSplittingTool(models.Model):
name = models.CharField(max_length=64, default="Pdf Splitting")
class Meta:
managed = False
verbose_name = _("PDF 拆解")
verbose_name_plural = _("PDF 拆解")
class PdfSplitJob(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
source_type = models.CharField(
max_length=16,
choices=PdfSplitSourceType.choices,
verbose_name=_("来源类型"),
)
source_abs_path = models.CharField(max_length=2048, blank=True, default="", verbose_name=_("源绝对路径"))
source_relpath = models.CharField(max_length=1024, blank=True, default="", verbose_name=_("源相对路径"))
source_original_name = models.CharField(max_length=255, verbose_name=_("源文件名"))
split_mode = models.CharField(
max_length=32,
choices=PdfSplitMode.choices,
default=PdfSplitMode.CONTENT_ANALYSIS,
verbose_name=_("拆分模式"),
)
template_key = models.CharField(max_length=64, default="filing_materials_v1", verbose_name=_("模板键"))
template_version = models.CharField(max_length=32, default="1", verbose_name=_("模板版本"))
ocr_profile = models.CharField(
max_length=16,
choices=PdfSplitOcrProfile.choices,
default=PdfSplitOcrProfile.BALANCED,
verbose_name=_("OCR 档位"),
)
status = models.CharField(
max_length=32,
choices=PdfSplitJobStatus.choices,
default=PdfSplitJobStatus.PENDING,
verbose_name=_("状态"),
)
total_pages = models.PositiveIntegerField(default=0, verbose_name=_("总页数"))
processed_pages = models.PositiveIntegerField(default=0, verbose_name=_("已处理页数"))
progress = models.PositiveIntegerField(default=0, verbose_name=_("进度"))
current_page = models.PositiveIntegerField(default=0, verbose_name=_("当前页"))
task_id = models.CharField(max_length=64, blank=True, default="", verbose_name=_("任务ID"))
cancel_requested = models.BooleanField(default=False, verbose_name=_("请求取消"))
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="pdf_split_jobs",
verbose_name=_("创建人"),
)
summary_payload = models.JSONField(default=dict, blank=True, verbose_name=_("摘要"))
error_message = models.TextField(blank=True, default="", verbose_name=_("错误信息"))
export_zip_relpath = models.CharField(max_length=1024, blank=True, default="", verbose_name=_("导出ZIP路径"))
started_at = models.DateTimeField(null=True, blank=True, verbose_name=_("开始时间"))
finished_at = models.DateTimeField(null=True, blank=True, verbose_name=_("完成时间"))
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("创建时间"))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("更新时间"))
class Meta:
verbose_name = _("PDF 拆解任务")
verbose_name_plural = _("PDF 拆解任务")
ordering: ClassVar[list[str]] = ["-created_at"]
indexes: ClassVar[list[models.Index]] = [
models.Index(fields=["status", "-created_at"]),
models.Index(fields=["created_by", "-created_at"]),
]
def __str__(self) -> str:
return f"{self.source_original_name} ({self.get_status_display()})"
class PdfSplitSegment(models.Model):
job = models.ForeignKey(
PdfSplitJob,
on_delete=models.CASCADE,
related_name="segments",
verbose_name=_("任务"),
)
order = models.PositiveIntegerField(default=1, verbose_name=_("排序"))
page_start = models.PositiveIntegerField(verbose_name=_("起始页"))
page_end = models.PositiveIntegerField(verbose_name=_("结束页"))
segment_type = models.CharField(
max_length=48,
choices=PdfSplitSegmentType.choices,
default=PdfSplitSegmentType.UNRECOGNIZED,
verbose_name=_("段类型"),
)
filename = models.CharField(max_length=255, verbose_name=_("文件名"))
confidence = models.FloatField(default=0.0, verbose_name=_("置信度"))
source_method = models.CharField(max_length=32, default="rule", verbose_name=_("来源方法"))
review_flag = models.CharField(
max_length=32,
choices=PdfSplitReviewFlag.choices,
default=PdfSplitReviewFlag.NORMAL,
verbose_name=_("复核标记"),
)
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("创建时间"))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("更新时间"))
class Meta:
verbose_name = _("PDF 拆解片段")
verbose_name_plural = _("PDF 拆解片段")
ordering: ClassVar[list[str]] = ["order", "id"]
constraints: ClassVar[list[models.BaseConstraint]] = [
models.UniqueConstraint(fields=["job", "order"], name="pdf_splitting_segment_job_order_uniq"),
]
def __str__(self) -> str:
return f"{self.job_id}:{self.page_start}-{self.page_end} {self.segment_type}"
@receiver(post_delete, sender=PdfSplitJob)
def delete_job_files(sender: type, instance: PdfSplitJob, **kwargs: object) -> None:
"""删除任务时清理关联的文件目录"""
from apps.pdf_splitting.services.storage import PdfSplitStorage
storage = PdfSplitStorage(instance.id)
storage.cleanup()