feat: submissions app + django-storages + whitenoise

This commit is contained in:
2026-05-24 23:37:22 +02:00
parent 3e91ef77b6
commit f2fe87c12b
11 changed files with 387 additions and 0 deletions
@@ -16,6 +16,9 @@ import os
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
os.environ.setdefault("AWS_REQUEST_CHECKSUM_CALCULATION", "when_required")
os.environ.setdefault("AWS_RESPONSE_CHECKSUM_VALIDATION", "when_required")
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
@@ -45,10 +48,12 @@ INSTALLED_APPS = [
"noita",
"games",
"market",
"submissions",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
@@ -195,6 +200,16 @@ STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static_source/vite"),
]
STORAGES = {
"default": {
"BACKEND": "storages.backends.s3.S3Storage",
},
"staticfiles": {
"BACKEND": "storages.backends.s3.S3Storage",
},
}
from polylan_submitter.settingsLocal import * # noqa
+69
View File
@@ -0,0 +1,69 @@
from django.contrib import admin
from .models import Submission, SubmissionFile
class SubmissionFileInline(admin.TabularInline):
model = SubmissionFile
extra = 0
fields = [
"original_filename",
"file_size",
"content_type",
"ocr_processed",
"ocr_error",
"created_at",
]
readonly_fields = [
"file_size",
"content_type",
"ocr_processed",
"ocr_error",
"created_at",
]
def get_queryset(self, request):
return super().get_queryset(request)
@admin.register(Submission)
class SubmissionAdmin(admin.ModelAdmin):
list_display = ["id_short", "user", "notes_preview", "created_at"]
list_filter = ["created_at", "updated_at"]
search_fields = ["id", "user__email", "notes"]
readonly_fields = ["id", "created_at", "updated_at"]
inlines = [SubmissionFileInline]
def get_queryset(self, request):
return super().get_queryset(request).select_related("user")
@admin.display(description="ID")
def id_short(self, obj):
return str(obj.id)[:8]
@admin.display(description="Notes")
def notes_preview(self, obj):
if obj.notes:
return obj.notes[:50] + "..." if len(obj.notes) > 50 else obj.notes
return "-"
@admin.register(SubmissionFile)
class SubmissionFileAdmin(admin.ModelAdmin):
list_display = [
"original_filename",
"submission_id",
"file_size",
"content_type",
"ocr_processed",
"created_at",
]
list_filter = ["content_type", "ocr_processed", "created_at"]
search_fields = ["original_filename", "submission__id"]
readonly_fields = ["file_size", "content_type", "created_at", "updated_at"]
def get_queryset(self, request):
return super().get_queryset(request).select_related("submission")
@admin.display(description="Submission ID")
def submission_id(self, obj):
return str(obj.submission.id)[:8]
+6
View File
@@ -0,0 +1,6 @@
from django.apps import AppConfig
class SubmissionsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "submissions"
@@ -0,0 +1,123 @@
# Generated by Django 5.2.7 on 2026-05-24 21:34
import django.db.models.deletion
import submissions.models
import uuid
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="Submission",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
(
"notes",
models.TextField(
blank=True,
help_text="Optional notes about the submission",
null=True,
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"user",
models.ForeignKey(
blank=True,
help_text="User who made the submission (null for anonymous)",
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="submissions",
to=settings.AUTH_USER_MODEL,
),
),
],
),
migrations.CreateModel(
name="SubmissionFile",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
(
"file",
models.FileField(
help_text="Uploaded file",
upload_to=submissions.models.submission_file_upload_path,
),
),
(
"original_filename",
models.CharField(
help_text="Original filename as uploaded by user",
max_length=255,
),
),
(
"file_size",
models.PositiveIntegerField(help_text="File size in bytes"),
),
(
"content_type",
models.CharField(help_text="MIME type of the file", max_length=100),
),
(
"ocr_processed",
models.BooleanField(
default=False,
help_text="Whether OCR has been processed for this file",
),
),
(
"ocr_raw_data",
models.JSONField(
blank=True, help_text="Raw OCR data as JSON", null=True
),
),
(
"ocr_error",
models.TextField(
blank=True, help_text="OCR processing error message"
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"submission",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="files",
to="submissions.submission",
),
),
],
options={
"verbose_name": "Submission File",
"verbose_name_plural": "Submission Files",
"ordering": ["file", "created_at"],
},
),
]
+91
View File
@@ -0,0 +1,91 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
import uuid
from accounts.models import CustomUser
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)
return f"submissions/{instance.submission.id}/{new_filename}"
class Submission(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(
CustomUser,
on_delete=models.CASCADE,
null=True,
blank=True,
help_text="User who made the submission (null for anonymous)",
related_name="submissions",
)
# Submission metadata
notes = models.TextField(
null=True,
blank=True,
help_text="Optional notes about the submission",
)
# Timestamps
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class SubmissionFile(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
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_processed = models.BooleanField(
default=False, help_text="Whether OCR has been processed for this file"
)
ocr_raw_data = models.JSONField(
null=True, blank=True, help_text="Raw OCR data as JSON"
)
ocr_error = models.TextField(blank=True, help_text="OCR processing error message")
# Timestamps
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
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)
+1
View File
@@ -0,0 +1 @@
# Create your tests here.
+1
View File
@@ -0,0 +1 @@
# Create your views here.