154 lines
4.2 KiB
Python
154 lines
4.2 KiB
Python
from django.db import models
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
import uuid
|
|
|
|
from accounts.models import CustomUser
|
|
from games.models import Game
|
|
|
|
|
|
class OCRStatus(models.TextChoices):
|
|
TO_PROCESS = "to_process", _("To Process")
|
|
PROCESSING = "processing", _("Processing")
|
|
PROCESSED = "processed", _("Processed")
|
|
ERROR = "error", _("Error processing")
|
|
|
|
|
|
def submission_file_upload_path(instance, filename):
|
|
ext = filename.split(".")[-1] if "." in filename else ""
|
|
|
|
new_filename = f"{instance.id}_{filename}" if ext else str(instance.id)
|
|
date = instance.created_at.strftime("%Y/%m/%d")
|
|
user = instance.submission.user.username
|
|
|
|
return f"{instance.submission.game.path.lstrip('/')}/{date}/{user}/{instance.submission.id}/{new_filename}"
|
|
|
|
|
|
class BaseModel(models.Model):
|
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
|
|
|
# Timestamps
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
|
|
class Meta:
|
|
abstract = True
|
|
|
|
|
|
class Submission(BaseModel):
|
|
game = models.ForeignKey(
|
|
Game,
|
|
on_delete=models.CASCADE,
|
|
null=True,
|
|
blank=True,
|
|
related_name="submissions",
|
|
)
|
|
|
|
user = models.ForeignKey(
|
|
CustomUser,
|
|
on_delete=models.CASCADE,
|
|
null=True,
|
|
blank=True,
|
|
help_text="User who made the submission (null for anonymous)",
|
|
related_name="user_submissions",
|
|
)
|
|
|
|
# Submission metadata
|
|
notes = models.TextField(
|
|
null=True,
|
|
blank=True,
|
|
help_text="Optional notes about the submission",
|
|
)
|
|
|
|
|
|
class SubmissionFile(BaseModel):
|
|
submission = models.ForeignKey(
|
|
Submission,
|
|
on_delete=models.CASCADE,
|
|
related_name="files",
|
|
)
|
|
|
|
file = models.FileField(
|
|
upload_to=submission_file_upload_path,
|
|
help_text=_("Uploaded file"),
|
|
)
|
|
|
|
original_filename = models.CharField(
|
|
max_length=255,
|
|
help_text=_("Original filename as uploaded by user"),
|
|
)
|
|
file_size = models.PositiveIntegerField(help_text=_("File size in bytes"))
|
|
content_type = models.CharField(
|
|
max_length=100,
|
|
help_text=_("MIME type of the file"),
|
|
)
|
|
|
|
# OCR metadata
|
|
ocr_status = models.CharField(
|
|
max_length=20,
|
|
choices=OCRStatus.choices,
|
|
default=OCRStatus.TO_PROCESS,
|
|
help_text=_("OCR processing status for the entire file"),
|
|
)
|
|
ocr_raw_data = models.TextField(
|
|
blank=True,
|
|
help_text=_("Raw OCR data for the entire file"),
|
|
)
|
|
|
|
class Meta:
|
|
ordering = ["file", "created_at"]
|
|
verbose_name = "Submission File"
|
|
verbose_name_plural = "Submission Files"
|
|
|
|
@property
|
|
def file_url(self) -> str | None:
|
|
if self.file:
|
|
return self.file.url
|
|
|
|
def save(self, *args, **kwargs):
|
|
if self.file and not self.file_size:
|
|
self.file_size = self.file.size
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
|
|
class ImageCrop(BaseModel):
|
|
submission_file = models.ForeignKey(
|
|
SubmissionFile,
|
|
on_delete=models.CASCADE,
|
|
related_name="crops",
|
|
)
|
|
|
|
# Crop coordinates (pixels): x1, y1, x2, y2
|
|
x1 = models.PositiveIntegerField(help_text=_("Top-left x coordinate"))
|
|
y1 = models.PositiveIntegerField(help_text=_("Top-left y coordinate"))
|
|
x2 = models.PositiveIntegerField(help_text=_("Bottom-right x coordinate"))
|
|
y2 = models.PositiveIntegerField(help_text=_("Bottom-right y coordinate"))
|
|
|
|
# Label/name for the crop
|
|
label = models.CharField(
|
|
max_length=255,
|
|
blank=True,
|
|
help_text=_("Optional label for this crop region"),
|
|
)
|
|
|
|
# OCR results for this crop
|
|
ocr_status = models.CharField(
|
|
max_length=20,
|
|
choices=OCRStatus.choices,
|
|
default=OCRStatus.TO_PROCESS,
|
|
help_text=_("OCR processing status for this crop"),
|
|
)
|
|
ocr_raw_data = models.TextField(
|
|
blank=True,
|
|
help_text=_("Raw OCR data for this crop"),
|
|
)
|
|
|
|
class Meta:
|
|
ordering = ["created_at"]
|
|
verbose_name = "Image Crop"
|
|
verbose_name_plural = "Image Crops"
|
|
|
|
def __str__(self):
|
|
return f"{self.label or f'Crop ({self.x1}, {self.y1}, {self.x2}, {self.y2})'}"
|